#region License Information
/* HeuristicLab
* Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using HeuristicLab.Core;
using HeuristicLab.Random;
namespace HeuristicLab.ExpressionGenerator {
public class Expression : IExpression {
// unique name for each expression
public string Name { get; set; }
// optional label (non-unique) - useful for string (infix) representations
public string Label { get; set; }
public ExpressionType Type { get; private set; }
private readonly List arguments; // only for functions
public IEnumerable Arguments {
get { return arguments ?? Enumerable.Empty(); }
}
public IRandom Distribution { get; private set; } // only for random variables
public Func, double> Transform { get; private set; } // only for functions
public double Value { get; private set; } // only for constants
public Expression(string name, double value) {
Type = ExpressionType.Constant;
Value = value;
Name = name;
}
public Expression(string name, IRandom distribution) {
Type = ExpressionType.RandomVariable;
Distribution = distribution;
Name = name;
}
public Expression(string name, Func, double> transform, IEnumerable arguments) {
Type = ExpressionType.Function;
Transform = transform;
this.arguments = this.arguments ?? new List();
this.arguments.AddRange(arguments);
Name = name;
}
public static Expression Constant(string name, double value) {
return new Expression(name, value);
}
public static Expression RandomVariable(string name, IRandom distribution) {
return new Expression(name, distribution);
}
public static Expression Function(string name, Func, double> transform, IEnumerable arguments) {
return new Expression(name, transform, arguments);
}
public override string ToString() {
var sb = new StringBuilder();
switch (Type) {
case ExpressionType.Constant:
sb.Append(Value.ToString("E4", CultureInfo.InvariantCulture));
break;
case ExpressionType.RandomVariable:
sb.Append(string.Format("{0} ~ {1}", Name, GetStringDescription(Distribution)));
break;
case ExpressionType.Function:
sb.Append(string.Format("{0} = f(", Name));
if (Arguments.Any()) {
for (int i = 0; i < arguments.Count - 1; ++i)
sb.Append(string.Format("{0}, ", arguments[i].Name));
sb.Append(string.Format("{0})", arguments.Last().Name));
}
break;
}
return sb.ToString();
}
public string PrintInfix() {
var sb = new StringBuilder();
switch (Type) {
case ExpressionType.Constant:
sb.Append(Value < 0
? string.Format("(- {0})", Math.Abs(Value).ToString("E4", CultureInfo.InvariantCulture))
: Value.ToString("E4", CultureInfo.InvariantCulture));
break;
case ExpressionType.RandomVariable:
sb.Append(Name);
break;
case ExpressionType.Function:
if (!Arguments.Any())
break;
var args = Arguments.ToList();
// the Label should be known to the infix parser
if (Label == "+" || Label == "-" || Label == "*" || Label == "/") {
if (args.Count == 1) {
sb.Append(string.Format(Label == "/" ? "1 / {0}" : "{0}", args[0].PrintInfix()));
} else {
if (Label == "/") {
sb.Append(string.Format("{0} / ", args[0].PrintInfix()));
var remaining = args.Count - 1;
if (remaining == 1)
sb.Append(args[1].PrintInfix());
else {
sb.Append("(");
var last = args.Last();
for (int i = 1; i < args.Count - 1; ++i) {
var arg = args[i];
sb.Append(string.Format("{0} {1} ", arg.PrintInfix(), "*"));
}
sb.Append(string.Format("{0})", last.PrintInfix()));
}
} else {
sb.Append("(");
var last = args.Last();
for (int i = 0; i < args.Count - 1; ++i) {
var arg = args[i];
sb.Append(string.Format("{0} {1} ", arg.PrintInfix(), Label));
}
sb.Append(string.Format("{0})", last.PrintInfix()));
}
}
} else {
sb.Append(string.Format("{0}(", Label));
var last = args.Last();
for (int i = 0; i < args.Count - 1; ++i) {
var arg = args[i];
sb.Append(string.Format("{0}, ", arg.PrintInfix()));
}
sb.Append(string.Format("{0})", last.PrintInfix()));
}
break;
}
return sb.ToString();
}
public string PrintDot() {
var stack = new Stack();
stack.Push(this);
var sb = new StringBuilder();
sb.AppendLine("digraph g {");
while (stack.Count > 0) {
var top = stack.Pop();
if (!top.Arguments.Any())
continue;
foreach (var arg in top.Arguments) {
var from = top.Arguments.Any() ? top.Name : top.ToString();
var to = arg.Arguments.Any() ? arg.Name : arg.ToString();
sb.AppendLine(string.Format("\"{0}\" -> \"{1}\";", from, to));
stack.Push(arg);
}
}
sb.AppendLine("}");
return sb.ToString();
}
private static string GetStringDescription(IRandom random) {
var normal = random as NormalDistributedRandom;
if (normal != null)
return string.Format("N({0}, {1})", normal.Mu, normal.Sigma);
var uniform = random as UniformDistributedRandom;
if (uniform != null)
return string.Format("U({0}, {1})", uniform.Min, uniform.Max);
var gamma = random as GammaDistributedRandom;
if (gamma != null)
return string.Format("G({0}, {1})", gamma.Shape, gamma.Rate);
return random.ToString();
}
}
}