Index: /branches/DataAnalysis Refactoring/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Compiler/SymbolicExpressionTreeCompiler.cs
===================================================================
--- /branches/DataAnalysis Refactoring/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Compiler/SymbolicExpressionTreeCompiler.cs	(revision 5570)
+++ /branches/DataAnalysis Refactoring/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Compiler/SymbolicExpressionTreeCompiler.cs	(revision 5571)
@@ -29,5 +29,5 @@
     private List<Func<Instruction, Instruction>> postInstructionCompiledHooks = new List<Func<Instruction, Instruction>>();
 
-    public Instruction[] Compile(SymbolicExpressionTree tree, Func<ISymbolicExpressionTreeNode, byte> opCodeMapper) {
+    public Instruction[] Compile(ISymbolicExpressionTree tree, Func<ISymbolicExpressionTreeNode, byte> opCodeMapper) {
       List<Instruction> code = new List<Instruction>();
       entryPoint.Clear();
Index: /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 5570)
+++ /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 5571)
@@ -120,4 +120,6 @@
     <Compile Include="Interfaces\ISymbolicDataAnalysisValidatingAnalyzer.cs" />
     <Compile Include="Interfaces\ISymbolicDataAnalysisAnalyzer.cs" />
+    <Compile Include="SymbolicDataAnalysisExpressionTreeInterpreter.cs" />
+    <Compile Include="SymbolicDataAnalysisExpressionTreeSimplifier.cs" />
     <Compile Include="Symbols\Addition.cs" />
     <Compile Include="Symbols\And.cs" />
Index: /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeInterpreter.cs
===================================================================
--- /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeInterpreter.cs	(revision 5571)
+++ /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeInterpreter.cs	(revision 5571)
@@ -0,0 +1,437 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2011 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  [Item("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")]
+  public sealed class SymbolicDataAnalysisExpressionTreeInterpreter : NamedItem, ISymbolicDataAnalysisTreeInterpreter {
+    private class InterpreterState {
+      private const int ARGUMENT_STACK_SIZE = 1024;
+      private double[] argumentStack;
+      private int argumentStackPointer;
+      private Instruction[] code;
+      private int pc;
+      public int ProgramCounter {
+        get { return pc; }
+        set { pc = value; }
+      }
+      internal InterpreterState(Instruction[] code) {
+        this.code = code;
+        this.pc = 0;
+        this.argumentStack = new double[ARGUMENT_STACK_SIZE];
+        this.argumentStackPointer = 0;
+      }
+
+      internal void Reset() {
+        this.pc = 0;
+        this.argumentStackPointer = 0;
+      }
+
+      internal Instruction NextInstruction() {
+        return code[pc++];
+      }
+      private void Push(double val) {
+        argumentStack[argumentStackPointer++] = val;
+      }
+      private double Pop() {
+        return argumentStack[--argumentStackPointer];
+      }
+
+      internal void CreateStackFrame(double[] argValues) {
+        // push in reverse order to make indexing easier
+        for (int i = argValues.Length - 1; i >= 0; i--) {
+          argumentStack[argumentStackPointer++] = argValues[i];
+        }
+        Push(argValues.Length);
+      }
+
+      internal void RemoveStackFrame() {
+        int size = (int)Pop();
+        argumentStackPointer -= size;
+      }
+
+      internal double GetStackFrameValue(ushort index) {
+        // layout of stack:
+        // [0]   <- argumentStackPointer
+        // [StackFrameSize = N + 1]
+        // [Arg0] <- argumentStackPointer - 2 - 0
+        // [Arg1] <- argumentStackPointer - 2 - 1
+        // [...]
+        // [ArgN] <- argumentStackPointer - 2 - N
+        // <Begin of stack frame>
+        return argumentStack[argumentStackPointer - index - 2];
+      }
+    }
+
+    private class OpCodes {
+      public const byte Add = 1;
+      public const byte Sub = 2;
+      public const byte Mul = 3;
+      public const byte Div = 4;
+
+      public const byte Sin = 5;
+      public const byte Cos = 6;
+      public const byte Tan = 7;
+
+      public const byte Log = 8;
+      public const byte Exp = 9;
+
+      public const byte IfThenElse = 10;
+
+      public const byte GT = 11;
+      public const byte LT = 12;
+
+      public const byte AND = 13;
+      public const byte OR = 14;
+      public const byte NOT = 15;
+
+
+      public const byte Average = 16;
+
+      public const byte Call = 17;
+
+      public const byte Variable = 18;
+      public const byte LagVariable = 19;
+      public const byte Constant = 20;
+      public const byte Arg = 21;
+
+      public const byte Power = 22;
+      public const byte Root = 23;
+      public const byte TimeLag = 24;
+      public const byte Integral = 25;
+      public const byte Derivative = 26;
+
+      public const byte VariableCondition = 27;
+    }
+
+    private Dictionary<Type, byte> symbolToOpcode = new Dictionary<Type, byte>() {
+      { typeof(Addition), OpCodes.Add },
+      { typeof(Subtraction), OpCodes.Sub },
+      { typeof(Multiplication), OpCodes.Mul },
+      { typeof(Division), OpCodes.Div },
+      { typeof(Sine), OpCodes.Sin },
+      { typeof(Cosine), OpCodes.Cos },
+      { typeof(Tangent), OpCodes.Tan },
+      { typeof(Logarithm), OpCodes.Log },
+      { typeof(Exponential), OpCodes.Exp },
+      { typeof(IfThenElse), OpCodes.IfThenElse },
+      { typeof(GreaterThan), OpCodes.GT },
+      { typeof(LessThan), OpCodes.LT },
+      { typeof(And), OpCodes.AND },
+      { typeof(Or), OpCodes.OR },
+      { typeof(Not), OpCodes.NOT},
+      { typeof(Average), OpCodes.Average},
+      { typeof(InvokeFunction), OpCodes.Call },
+      { typeof(HeuristicLab.Problems.DataAnalysis.Symbolic.Variable), OpCodes.Variable },
+      { typeof(LaggedVariable), OpCodes.LagVariable },
+      { typeof(Constant), OpCodes.Constant },
+      { typeof(Argument), OpCodes.Arg },
+      { typeof(Power),OpCodes.Power},
+      { typeof(Root),OpCodes.Root},
+      { typeof(TimeLag), OpCodes.TimeLag}, 
+      { typeof(Integral), OpCodes.Integral},
+      { typeof(Derivative), OpCodes.Derivative},
+      { typeof(VariableCondition),OpCodes.VariableCondition}
+    };
+
+
+    public override bool CanChangeName {
+      get { return false; }
+    }
+    public override bool CanChangeDescription {
+      get { return false; }
+    }
+
+    [StorableConstructor]
+    private SymbolicDataAnalysisExpressionTreeInterpreter(bool deserializing) : base(deserializing) { }
+    private SymbolicDataAnalysisExpressionTreeInterpreter(SymbolicDataAnalysisExpressionTreeInterpreter original, Cloner cloner) : base(original, cloner) { }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicDataAnalysisExpressionTreeInterpreter(this, cloner);
+    }
+
+    public SymbolicDataAnalysisExpressionTreeInterpreter()
+      : base() {
+    }
+
+    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows) {
+      var compiler = new SymbolicExpressionTreeCompiler();
+      Instruction[] code = compiler.Compile(tree, MapSymbolToOpCode);
+
+      for (int i = 0; i < code.Length; i++) {
+        Instruction instr = code[i];
+        if (instr.opCode == OpCodes.Variable) {
+          var variableTreeNode = instr.dynamicNode as VariableTreeNode;
+          instr.iArg0 = (ushort)dataset.GetVariableIndex(variableTreeNode.VariableName);
+          code[i] = instr;
+        } else if (instr.opCode == OpCodes.LagVariable) {
+          var variableTreeNode = instr.dynamicNode as LaggedVariableTreeNode;
+          instr.iArg0 = (ushort)dataset.GetVariableIndex(variableTreeNode.VariableName);
+          code[i] = instr;
+        } else if (instr.opCode == OpCodes.VariableCondition) {
+          var variableConditionTreeNode = instr.dynamicNode as VariableConditionTreeNode;
+          instr.iArg0 = (ushort)dataset.GetVariableIndex(variableConditionTreeNode.VariableName);
+        }
+      }
+      var state = new InterpreterState(code);
+
+      foreach (var rowEnum in rows) {
+        int row = rowEnum;
+        state.Reset();
+        yield return Evaluate(dataset, ref row, state);
+      }
+    }
+
+    private double Evaluate(Dataset dataset, ref int row, InterpreterState state) {
+      Instruction currentInstr = state.NextInstruction();
+      switch (currentInstr.opCode) {
+        case OpCodes.Add: {
+            double s = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              s += Evaluate(dataset, ref row, state);
+            }
+            return s;
+          }
+        case OpCodes.Sub: {
+            double s = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              s -= Evaluate(dataset, ref row, state);
+            }
+            if (currentInstr.nArguments == 1) s = -s;
+            return s;
+          }
+        case OpCodes.Mul: {
+            double p = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              p *= Evaluate(dataset, ref row, state);
+            }
+            return p;
+          }
+        case OpCodes.Div: {
+            double p = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              p /= Evaluate(dataset, ref row, state);
+            }
+            if (currentInstr.nArguments == 1) p = 1.0 / p;
+            return p;
+          }
+        case OpCodes.Average: {
+            double sum = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              sum += Evaluate(dataset, ref row, state);
+            }
+            return sum / currentInstr.nArguments;
+          }
+        case OpCodes.Cos: {
+            return Math.Cos(Evaluate(dataset, ref row, state));
+          }
+        case OpCodes.Sin: {
+            return Math.Sin(Evaluate(dataset, ref row, state));
+          }
+        case OpCodes.Tan: {
+            return Math.Tan(Evaluate(dataset, ref row, state));
+          }
+        case OpCodes.Power: {
+            double x = Evaluate(dataset, ref row, state);
+            double y = Math.Round(Evaluate(dataset, ref row, state));
+            return Math.Pow(x, y);
+          }
+        case OpCodes.Root: {
+            double x = Evaluate(dataset, ref row, state);
+            double y = Math.Round(Evaluate(dataset, ref row, state));
+            return Math.Pow(x, 1 / y);
+          }
+        case OpCodes.Exp: {
+            return Math.Exp(Evaluate(dataset, ref row, state));
+          }
+        case OpCodes.Log: {
+            return Math.Log(Evaluate(dataset, ref row, state));
+          }
+        case OpCodes.IfThenElse: {
+            double condition = Evaluate(dataset, ref row, state);
+            double result;
+            if (condition > 0.0) {
+              result = Evaluate(dataset, ref row, state); SkipInstructions(state);
+            } else {
+              SkipInstructions(state); result = Evaluate(dataset, ref row, state);
+            }
+            return result;
+          }
+        case OpCodes.AND: {
+            double result = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              if (result <= 0.0) SkipInstructions(state);
+              else {
+                result = Evaluate(dataset, ref row, state);
+              }
+            }
+            return result <= 0.0 ? -1.0 : 1.0;
+          }
+        case OpCodes.OR: {
+            double result = Evaluate(dataset, ref row, state);
+            for (int i = 1; i < currentInstr.nArguments; i++) {
+              if (result > 0.0) SkipInstructions(state);
+              else {
+                result = Evaluate(dataset, ref row, state);
+              }
+            }
+            return result > 0.0 ? 1.0 : -1.0;
+          }
+        case OpCodes.NOT: {
+            return Evaluate(dataset, ref row, state) > 0.0 ? 1.0 : -1.0;
+          }
+        case OpCodes.GT: {
+            double x = Evaluate(dataset, ref row, state);
+            double y = Evaluate(dataset, ref row, state);
+            if (x > y) return 1.0;
+            else return -1.0;
+          }
+        case OpCodes.LT: {
+            double x = Evaluate(dataset, ref row, state);
+            double y = Evaluate(dataset, ref row, state);
+            if (x < y) return 1.0;
+            else return -1.0;
+          }
+        case OpCodes.TimeLag: {
+            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
+            if (row + timeLagTreeNode.Lag < 0 || row + timeLagTreeNode.Lag >= dataset.Rows)
+              return double.NaN;
+
+            row += timeLagTreeNode.Lag;
+            double result = Evaluate(dataset, ref row, state);
+            row -= timeLagTreeNode.Lag;
+            return result;
+          }
+        case OpCodes.Integral: {
+            int savedPc = state.ProgramCounter;
+            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
+            if (row + timeLagTreeNode.Lag < 0 || row + timeLagTreeNode.Lag >= dataset.Rows)
+              return double.NaN;
+            double sum = 0.0;
+            for (int i = 0; i < Math.Abs(timeLagTreeNode.Lag); i++) {
+              row += Math.Sign(timeLagTreeNode.Lag);
+              sum += Evaluate(dataset, ref row, state);
+              state.ProgramCounter = savedPc;
+            }
+            row -= timeLagTreeNode.Lag;
+            sum += Evaluate(dataset, ref row, state);
+            return sum;
+          }
+
+        //mkommend: derivate calculation taken from: 
+        //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
+        //one sided smooth differentiatior, N = 4
+        // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
+        case OpCodes.Derivative: {
+            if (row - 4 < 0) return double.NaN;
+            int savedPc = state.ProgramCounter;
+            double f_0 = Evaluate(dataset, ref row, state); ; row--;
+            state.ProgramCounter = savedPc;
+            double f_1 = Evaluate(dataset, ref row, state); ; row -= 2;
+            state.ProgramCounter = savedPc;
+            double f_3 = Evaluate(dataset, ref row, state); ; row--;
+            state.ProgramCounter = savedPc;
+            double f_4 = Evaluate(dataset, ref row, state); ;
+            row += 4;
+
+            return (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
+          }
+        case OpCodes.Call: {
+            // evaluate sub-trees
+            double[] argValues = new double[currentInstr.nArguments];
+            for (int i = 0; i < currentInstr.nArguments; i++) {
+              argValues[i] = Evaluate(dataset, ref row, state);
+            }
+            // push on argument values on stack 
+            state.CreateStackFrame(argValues);
+
+            // save the pc
+            int savedPc = state.ProgramCounter;
+            // set pc to start of function  
+            state.ProgramCounter = currentInstr.iArg0;
+            // evaluate the function
+            double v = Evaluate(dataset, ref row, state);
+
+            // delete the stack frame
+            state.RemoveStackFrame();
+
+            // restore the pc => evaluation will continue at point after my subtrees  
+            state.ProgramCounter = savedPc;
+            return v;
+          }
+        case OpCodes.Arg: {
+            return state.GetStackFrameValue(currentInstr.iArg0);
+          }
+        case OpCodes.Variable: {
+            var variableTreeNode = currentInstr.dynamicNode as VariableTreeNode;
+            return dataset[row, currentInstr.iArg0] * variableTreeNode.Weight;
+          }
+        case OpCodes.LagVariable: {
+            var laggedVariableTreeNode = currentInstr.dynamicNode as LaggedVariableTreeNode;
+            int actualRow = row + laggedVariableTreeNode.Lag;
+            if (actualRow < 0 || actualRow >= dataset.Rows) throw new ArgumentException("Out of range access to dataset row: " + row);
+            return dataset[actualRow, currentInstr.iArg0] * laggedVariableTreeNode.Weight;
+          }
+        case OpCodes.Constant: {
+            var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
+            return constTreeNode.Value;
+          }
+
+        //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) ) 
+        //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
+        case OpCodes.VariableCondition: {
+            var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
+            double variableValue = dataset[row, currentInstr.iArg0];
+            double x = variableValue - variableConditionTreeNode.Threshold;
+            double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
+
+            double trueBranch = Evaluate(dataset, ref row, state);
+            double falseBranch = Evaluate(dataset, ref row, state);
+
+            return trueBranch * p + falseBranch * (1 - p);
+          }
+        default: throw new NotSupportedException();
+      }
+    }
+
+    private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode) {
+      if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType()))
+        return symbolToOpcode[treeNode.Symbol.GetType()];
+      else
+        throw new NotSupportedException("Symbol: " + treeNode.Symbol);
+    }
+
+    // skips a whole branch
+    private void SkipInstructions(InterpreterState state) {
+      int i = 1;
+      while (i > 0) {
+        i += state.NextInstruction().nArguments;
+        i--;
+      }
+    }
+  }
+}
Index: /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs
===================================================================
--- /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs	(revision 5571)
+++ /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs	(revision 5571)
@@ -0,0 +1,942 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2011 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  /// <summary>
+  /// Simplifier for symbolic expressions
+  /// </summary>
+  public class SymbolicDataAnalysisExpressionTreeSimplifier {
+    private Addition addSymbol = new Addition();
+    private Subtraction subSymbol = new Subtraction();
+    private Multiplication mulSymbol = new Multiplication();
+    private Division divSymbol = new Division();
+    private Constant constSymbol = new Constant();
+    private Variable varSymbol = new Variable();
+    private Logarithm logSymbol = new Logarithm();
+    private Exponential expSymbol = new Exponential();
+    private Root rootSymbol = new Root();
+    private Power powSymbol = new Power();
+    private Sine sineSymbol = new Sine();
+    private Cosine cosineSymbol = new Cosine();
+    private Tangent tanSymbol = new Tangent();
+    private IfThenElse ifThenElseSymbol = new IfThenElse();
+    private And andSymbol = new And();
+    private Or orSymbol = new Or();
+    private Not notSymbol = new Not();
+    private GreaterThan gtSymbol = new GreaterThan();
+    private LessThan ltSymbol = new LessThan();
+
+    public ISymbolicExpressionTree Simplify(ISymbolicExpressionTree originalTree) {
+      var clone = (ISymbolicExpressionTreeNode)originalTree.Root.Clone();
+      // macro expand (initially no argument trees)
+      var macroExpandedTree = MacroExpand(clone, clone.GetSubTree(0), new List<ISymbolicExpressionTreeNode>());
+      ISymbolicExpressionTreeNode rootNode = (new ProgramRootSymbol()).CreateTreeNode();
+      rootNode.AddSubTree(GetSimplifiedTree(macroExpandedTree));
+      return new SymbolicExpressionTree(rootNode);
+    }
+
+    // the argumentTrees list contains already expanded trees used as arguments for invocations
+    private ISymbolicExpressionTreeNode MacroExpand(ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode node, IList<ISymbolicExpressionTreeNode> argumentTrees) {
+      List<ISymbolicExpressionTreeNode> subtrees = new List<ISymbolicExpressionTreeNode>(node.SubTrees);
+      while (node.SubTrees.Count() > 0) node.RemoveSubTree(0);
+      if (node.Symbol is InvokeFunction) {
+        var invokeSym = node.Symbol as InvokeFunction;
+        var defunNode = FindFunctionDefinition(root, invokeSym.FunctionName);
+        var macroExpandedArguments = new List<ISymbolicExpressionTreeNode>();
+        foreach (var subtree in subtrees) {
+          macroExpandedArguments.Add(MacroExpand(root, subtree, argumentTrees));
+        }
+        return MacroExpand(root, defunNode, macroExpandedArguments);
+      } else if (node.Symbol is Argument) {
+        var argSym = node.Symbol as Argument;
+        // return the correct argument sub-tree (already macro-expanded)
+        return (SymbolicExpressionTreeNode)argumentTrees[argSym.ArgumentIndex].Clone();
+      } else {
+        // recursive application
+        foreach (var subtree in subtrees) {
+          node.AddSubTree(MacroExpand(root, subtree, argumentTrees));
+        }
+        return node;
+      }
+    }
+
+    private ISymbolicExpressionTreeNode FindFunctionDefinition(ISymbolicExpressionTreeNode root, string functionName) {
+      foreach (var subtree in root.SubTrees.OfType<DefunTreeNode>()) {
+        if (subtree.FunctionName == functionName) return subtree.GetSubTree(0);
+      }
+
+      throw new ArgumentException("Definition of function " + functionName + " not found.");
+    }
+
+
+    #region symbol predicates
+    // arithmetic
+    private bool IsDivision(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Division;
+    }
+
+    private bool IsMultiplication(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Multiplication;
+    }
+
+    private bool IsSubtraction(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Subtraction;
+    }
+
+    private bool IsAddition(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Addition;
+    }
+
+    private bool IsAverage(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Average;
+    }
+    // exponential
+    private bool IsLog(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Logarithm;
+    }
+    private bool IsExp(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Exponential;
+    }
+    private bool IsRoot(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Root;
+    }
+    private bool IsPower(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Power;
+    }
+    // trigonometric
+    private bool IsSine(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Sine;
+    }
+    private bool IsCosine(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Cosine;
+    }
+    private bool IsTangent(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Tangent;
+    }
+    // boolean
+    private bool IsIfThenElse(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is IfThenElse;
+    }
+    private bool IsAnd(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is And;
+    }
+    private bool IsOr(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Or;
+    }
+    private bool IsNot(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Not;
+    }
+    // comparison
+    private bool IsGreaterThan(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is GreaterThan;
+    }
+    private bool IsLessThan(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is LessThan;
+    }
+
+    private bool IsBoolean(ISymbolicExpressionTreeNode node) {
+      return
+        node.Symbol is GreaterThan ||
+        node.Symbol is LessThan ||
+        node.Symbol is And ||
+        node.Symbol is Or;
+    }
+
+    // terminals
+    private bool IsVariable(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Variable;
+    }
+
+    private bool IsConstant(ISymbolicExpressionTreeNode node) {
+      return node.Symbol is Constant;
+    }
+
+    #endregion
+
+    /// <summary>
+    /// Creates a new simplified tree
+    /// </summary>
+    /// <param name="original"></param>
+    /// <returns></returns>
+    public ISymbolicExpressionTreeNode GetSimplifiedTree(ISymbolicExpressionTreeNode original) {
+      if (IsConstant(original) || IsVariable(original)) {
+        return (ISymbolicExpressionTreeNode)original.Clone();
+      } else if (IsAddition(original)) {
+        return SimplifyAddition(original);
+      } else if (IsSubtraction(original)) {
+        return SimplifySubtraction(original);
+      } else if (IsMultiplication(original)) {
+        return SimplifyMultiplication(original);
+      } else if (IsDivision(original)) {
+        return SimplifyDivision(original);
+      } else if (IsAverage(original)) {
+        return SimplifyAverage(original);
+      } else if (IsLog(original)) {
+        return SimplifyLog(original);
+      } else if (IsExp(original)) {
+        return SimplifyExp(original);
+      } else if (IsRoot(original)) {
+        return SimplifyRoot(original);
+      } else if (IsPower(original)) {
+        return SimplifyPower(original);
+      } else if (IsSine(original)) {
+        return SimplifySine(original);
+      } else if (IsCosine(original)) {
+        return SimplifyCosine(original);
+      } else if (IsTangent(original)) {
+        return SimplifyTangent(original);
+      } else if (IsIfThenElse(original)) {
+        return SimplifyIfThenElse(original);
+      } else if (IsGreaterThan(original)) {
+        return SimplifyGreaterThan(original);
+      } else if (IsLessThan(original)) {
+        return SimplifyLessThan(original);
+      } else if (IsAnd(original)) {
+        return SimplifyAnd(original);
+      } else if (IsOr(original)) {
+        return SimplifyOr(original);
+      } else if (IsNot(original)) {
+        return SimplifyNot(original);
+      } else {
+        return SimplifyAny(original);
+      }
+    }
+
+
+    #region specific simplification routines
+    private ISymbolicExpressionTreeNode SimplifyAny(ISymbolicExpressionTreeNode original) {
+      // can't simplify this function but simplify all subtrees 
+      List<ISymbolicExpressionTreeNode> subTrees = new List<ISymbolicExpressionTreeNode>(original.SubTrees);
+      while (original.SubTrees.Count() > 0) original.RemoveSubTree(0);
+      var clone = (SymbolicExpressionTreeNode)original.Clone();
+      List<ISymbolicExpressionTreeNode> simplifiedSubTrees = new List<ISymbolicExpressionTreeNode>();
+      foreach (var subTree in subTrees) {
+        simplifiedSubTrees.Add(GetSimplifiedTree(subTree));
+        original.AddSubTree(subTree);
+      }
+      foreach (var simplifiedSubtree in simplifiedSubTrees) {
+        clone.AddSubTree(simplifiedSubtree);
+      }
+      if (simplifiedSubTrees.TrueForAll(t => IsConstant(t))) {
+        SimplifyConstantExpression(clone);
+      }
+      return clone;
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyConstantExpression(ISymbolicExpressionTreeNode original) {
+      // not yet implemented
+      return original;
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyAverage(ISymbolicExpressionTreeNode original) {
+      if (original.SubTrees.Count() == 1) {
+        return GetSimplifiedTree(original.GetSubTree(0));
+      } else {
+        // simplify expressions x0..xn
+        // make sum(x0..xn) / n
+        Trace.Assert(original.SubTrees.Count() > 1);
+        var sum = original.SubTrees
+          .Select(x => GetSimplifiedTree(x))
+          .Aggregate((a, b) => MakeSum(a, b));
+        return MakeFraction(sum, MakeConstant(original.SubTrees.Count()));
+      }
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyDivision(ISymbolicExpressionTreeNode original) {
+      if (original.SubTrees.Count() == 1) {
+        return Invert(GetSimplifiedTree(original.GetSubTree(0)));
+      } else {
+        // simplify expressions x0..xn
+        // make multiplication (x0 * 1/(x1 * x1 * .. * xn))
+        Trace.Assert(original.SubTrees.Count() > 1);
+        var simplifiedTrees = original.SubTrees.Select(x => GetSimplifiedTree(x));
+        return
+          MakeProduct(simplifiedTrees.First(), Invert(simplifiedTrees.Skip(1).Aggregate((a, b) => MakeProduct(a, b))));
+      }
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyMultiplication(ISymbolicExpressionTreeNode original) {
+      if (original.SubTrees.Count() == 1) {
+        return GetSimplifiedTree(original.GetSubTree(0));
+      } else {
+        Trace.Assert(original.SubTrees.Count() > 1);
+        return original.SubTrees
+          .Select(x => GetSimplifiedTree(x))
+          .Aggregate((a, b) => MakeProduct(a, b));
+      }
+    }
+
+    private ISymbolicExpressionTreeNode SimplifySubtraction(ISymbolicExpressionTreeNode original) {
+      if (original.SubTrees.Count() == 1) {
+        return Negate(GetSimplifiedTree(original.GetSubTree(0)));
+      } else {
+        // simplify expressions x0..xn
+        // make addition (x0,-x1..-xn)
+        Trace.Assert(original.SubTrees.Count() > 1);
+        var simplifiedTrees = original.SubTrees.Select(x => GetSimplifiedTree(x));
+        return simplifiedTrees.Take(1)
+          .Concat(simplifiedTrees.Skip(1).Select(x => Negate(x)))
+          .Aggregate((a, b) => MakeSum(a, b));
+      }
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyAddition(ISymbolicExpressionTreeNode original) {
+      if (original.SubTrees.Count() == 1) {
+        return GetSimplifiedTree(original.GetSubTree(0));
+      } else {
+        // simplify expression x0..xn
+        // make addition (x0..xn)
+        Trace.Assert(original.SubTrees.Count() > 1);
+        return original.SubTrees
+          .Select(x => GetSimplifiedTree(x))
+          .Aggregate((a, b) => MakeSum(a, b));
+      }
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyNot(ISymbolicExpressionTreeNode original) {
+      return MakeNot(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyOr(ISymbolicExpressionTreeNode original) {
+      return original.SubTrees
+        .Select(x => GetSimplifiedTree(x))
+        .Aggregate((a, b) => MakeOr(a, b));
+    }
+    private ISymbolicExpressionTreeNode SimplifyAnd(ISymbolicExpressionTreeNode original) {
+      return original.SubTrees
+        .Select(x => GetSimplifiedTree(x))
+        .Aggregate((a, b) => MakeAnd(a, b));
+    }
+    private ISymbolicExpressionTreeNode SimplifyLessThan(ISymbolicExpressionTreeNode original) {
+      return MakeLessThan(GetSimplifiedTree(original.GetSubTree(0)), GetSimplifiedTree(original.GetSubTree(1)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyGreaterThan(ISymbolicExpressionTreeNode original) {
+      return MakeGreaterThan(GetSimplifiedTree(original.GetSubTree(0)), GetSimplifiedTree(original.GetSubTree(1)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyIfThenElse(ISymbolicExpressionTreeNode original) {
+      return MakeIfThenElse(GetSimplifiedTree(original.GetSubTree(0)), GetSimplifiedTree(original.GetSubTree(1)), GetSimplifiedTree(original.GetSubTree(2)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyTangent(ISymbolicExpressionTreeNode original) {
+      return MakeTangent(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyCosine(ISymbolicExpressionTreeNode original) {
+      return MakeCosine(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+    private ISymbolicExpressionTreeNode SimplifySine(ISymbolicExpressionTreeNode original) {
+      return MakeSine(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyExp(ISymbolicExpressionTreeNode original) {
+      return MakeExp(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyLog(ISymbolicExpressionTreeNode original) {
+      return MakeLog(GetSimplifiedTree(original.GetSubTree(0)));
+    }
+    private ISymbolicExpressionTreeNode SimplifyRoot(ISymbolicExpressionTreeNode original) {
+      return MakeRoot(GetSimplifiedTree(original.GetSubTree(0)), GetSimplifiedTree(original.GetSubTree(1)));
+    }
+
+    private ISymbolicExpressionTreeNode SimplifyPower(ISymbolicExpressionTreeNode original) {
+      return MakePower(GetSimplifiedTree(original.GetSubTree(0)), GetSimplifiedTree(original.GetSubTree(1)));
+    }
+    #endregion
+
+    #region low level tree restructuring
+    private ISymbolicExpressionTreeNode MakeNot(ISymbolicExpressionTreeNode t) {
+      if (IsConstant(t.GetSubTree(0))) {
+        var constNode = t as ConstantTreeNode;
+        if (constNode.Value > 0) return MakeConstant(1.0);
+        else return MakeConstant(-1.0);
+      } else if (IsNot(t.GetSubTree(0))) {
+        return t.GetSubTree(0).GetSubTree(0);
+      } else if (!IsBoolean(t.GetSubTree(0))) {
+        var gtNode = gtSymbol.CreateTreeNode();
+        gtNode.AddSubTree(t.GetSubTree(0)); gtNode.AddSubTree(MakeConstant(0.0));
+        return gtNode;
+      } else return t;
+    }
+
+    private ISymbolicExpressionTreeNode MakeOr(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        var constA = a as ConstantTreeNode;
+        var constB = b as ConstantTreeNode;
+        if (constA.Value > 0.0 || constB.Value > 0.0) {
+          return MakeConstant(1.0);
+        } else {
+          return MakeConstant(-1.0);
+        }
+      } else if (IsConstant(a)) {
+        return MakeOr(b, a);
+      } else if (IsConstant(b)) {
+        var constT = b as ConstantTreeNode;
+        if (constT.Value > 0.0) {
+          // boolean expression is necessarily true
+          return MakeConstant(1.0);
+        } else {
+          // the constant value has no effect on the result of the boolean condition so we can drop the constant term
+          var orNode = orSymbol.CreateTreeNode();
+          orNode.AddSubTree(a);
+          return orNode;
+        }
+      } else {
+        var orNode = orSymbol.CreateTreeNode();
+        orNode.AddSubTree(a);
+        orNode.AddSubTree(b);
+        return orNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeAnd(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        var constA = a as ConstantTreeNode;
+        var constB = b as ConstantTreeNode;
+        if (constA.Value > 0.0 && constB.Value > 0.0) {
+          return MakeConstant(1.0);
+        } else {
+          return MakeConstant(-1.0);
+        }
+      } else if (IsConstant(a)) {
+        return MakeAnd(b, a);
+      } else if (IsConstant(b)) {
+        var constB = b as ConstantTreeNode;
+        if (constB.Value > 0.0) {
+          // the constant value has no effect on the result of the boolean condition so we can drop the constant term
+          var andNode = andSymbol.CreateTreeNode();
+          andNode.AddSubTree(a);
+          return andNode;
+        } else {
+          // boolean expression is necessarily false
+          return MakeConstant(-1.0);
+        }
+      } else {
+        var andNode = andSymbol.CreateTreeNode();
+        andNode.AddSubTree(a);
+        andNode.AddSubTree(b);
+        return andNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeLessThan(ISymbolicExpressionTreeNode leftSide, ISymbolicExpressionTreeNode rightSide) {
+      if (IsConstant(leftSide) && IsConstant(rightSide)) {
+        var lsConst = leftSide as ConstantTreeNode;
+        var rsConst = rightSide as ConstantTreeNode;
+        if (lsConst.Value < rsConst.Value) return MakeConstant(1.0);
+        else return MakeConstant(-1.0);
+      } else {
+        var ltNode = ltSymbol.CreateTreeNode();
+        ltNode.AddSubTree(leftSide);
+        ltNode.AddSubTree(rightSide);
+        return ltNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeGreaterThan(ISymbolicExpressionTreeNode leftSide, ISymbolicExpressionTreeNode rightSide) {
+      if (IsConstant(leftSide) && IsConstant(rightSide)) {
+        var lsConst = leftSide as ConstantTreeNode;
+        var rsConst = rightSide as ConstantTreeNode;
+        if (lsConst.Value > rsConst.Value) return MakeConstant(1.0);
+        else return MakeConstant(-1.0);
+      } else {
+        var gtNode = gtSymbol.CreateTreeNode();
+        gtNode.AddSubTree(leftSide);
+        gtNode.AddSubTree(rightSide);
+        return gtNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeIfThenElse(ISymbolicExpressionTreeNode condition, ISymbolicExpressionTreeNode trueBranch, ISymbolicExpressionTreeNode falseBranch) {
+      if (IsConstant(condition)) {
+        var constT = condition as ConstantTreeNode;
+        if (constT.Value > 0.0) return trueBranch;
+        else return falseBranch;
+      } else {
+        var ifNode = ifThenElseSymbol.CreateTreeNode();
+        if (IsBoolean(condition)) {
+          ifNode.AddSubTree(condition);
+        } else {
+          var gtNode = gtSymbol.CreateTreeNode();
+          gtNode.AddSubTree(condition); gtNode.AddSubTree(MakeConstant(0.0));
+          ifNode.AddSubTree(gtNode);
+        }
+        ifNode.AddSubTree(trueBranch);
+        ifNode.AddSubTree(falseBranch);
+        return ifNode;
+      }
+    }
+
+    private ISymbolicExpressionTreeNode MakeSine(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Sin(constT.Value));
+      } else {
+        var sineNode = sineSymbol.CreateTreeNode();
+        sineNode.AddSubTree(node);
+        return sineNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeTangent(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Tan(constT.Value));
+      } else {
+        var tanNode = tanSymbol.CreateTreeNode();
+        tanNode.AddSubTree(node);
+        return tanNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeCosine(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Cos(constT.Value));
+      } else {
+        var cosNode = cosineSymbol.CreateTreeNode();
+        cosNode.AddSubTree(node);
+        return cosNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeExp(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Exp(constT.Value));
+      } else if (IsLog(node)) {
+        return node.GetSubTree(0);
+      } else if (IsAddition(node)) {
+        return node.SubTrees.Select(s => MakeExp(s)).Aggregate((s, t) => MakeProduct(s, t));
+      } else if (IsSubtraction(node)) {
+        return node.SubTrees.Select(s => MakeExp(s)).Aggregate((s, t) => MakeProduct(s, Negate(t)));
+      } else {
+        var expNode = expSymbol.CreateTreeNode();
+        expNode.AddSubTree(node);
+        return expNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeLog(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Log(constT.Value));
+      } else if (IsExp(node)) {
+        return node.GetSubTree(0);
+      } else if (IsMultiplication(node)) {
+        return node.SubTrees.Select(s => MakeLog(s)).Aggregate((x, y) => MakeSum(x, y));
+      } else if (IsDivision(node)) {
+        var subtractionNode = subSymbol.CreateTreeNode();
+        foreach (var subTree in node.SubTrees) {
+          subtractionNode.AddSubTree(MakeLog(subTree));
+        }
+        return subtractionNode;
+      } else {
+        var logNode = logSymbol.CreateTreeNode();
+        logNode.AddSubTree(node);
+        return logNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakeRoot(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        var constA = a as ConstantTreeNode;
+        var constB = b as ConstantTreeNode;
+        return MakeConstant(Math.Pow(constA.Value, 1.0 / Math.Round(constB.Value)));
+      } else if (IsConstant(b)) {
+        var constB = b as ConstantTreeNode;
+        var constBValue = Math.Round(constB.Value);
+        if (constBValue.IsAlmost(1.0)) {
+          return a;
+        } else if (constBValue.IsAlmost(0.0)) {
+          return MakeConstant(1.0);
+        } else if (constBValue.IsAlmost(-1.0)) {
+          return MakeFraction(MakeConstant(1.0), a);
+        } else if (constBValue < 0) {
+          var rootNode = rootSymbol.CreateTreeNode();
+          rootNode.AddSubTree(a);
+          rootNode.AddSubTree(MakeConstant(-1.0 * constBValue));
+          return MakeFraction(MakeConstant(1.0), rootNode);
+        } else {
+          var rootNode = rootSymbol.CreateTreeNode();
+          rootNode.AddSubTree(a);
+          rootNode.AddSubTree(MakeConstant(constBValue));
+          return rootNode;
+        }
+      } else {
+        var rootNode = rootSymbol.CreateTreeNode();
+        rootNode.AddSubTree(a);
+        rootNode.AddSubTree(b);
+        return rootNode;
+      }
+    }
+    private ISymbolicExpressionTreeNode MakePower(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        var constA = a as ConstantTreeNode;
+        var constB = b as ConstantTreeNode;
+        return MakeConstant(Math.Pow(constA.Value, Math.Round(constB.Value)));
+      } else if (IsConstant(b)) {
+        var constB = b as ConstantTreeNode;
+        double exponent = Math.Round(constB.Value);
+        if (exponent.IsAlmost(0.0)) {
+          return MakeConstant(1.0);
+        } else if (exponent.IsAlmost(1.0)) {
+          return a;
+        } else if (exponent.IsAlmost(-1.0)) {
+          return MakeFraction(MakeConstant(1.0), a);
+        } else if (exponent < 0) {
+          var powNode = powSymbol.CreateTreeNode();
+          powNode.AddSubTree(a);
+          powNode.AddSubTree(MakeConstant(-1.0 * exponent));
+          return MakeFraction(MakeConstant(1.0), powNode);
+        } else {
+          var powNode = powSymbol.CreateTreeNode();
+          powNode.AddSubTree(a);
+          powNode.AddSubTree(MakeConstant(exponent));
+          return powNode;
+        }
+      } else {
+        var powNode = powSymbol.CreateTreeNode();
+        powNode.AddSubTree(a);
+        powNode.AddSubTree(b);
+        return powNode;
+      }
+    }
+
+
+    // MakeFraction, MakeProduct and MakeSum take two already simplified trees and create a new simplified tree
+    private ISymbolicExpressionTreeNode MakeFraction(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        // fold constants
+        return MakeConstant(((ConstantTreeNode)a).Value / ((ConstantTreeNode)b).Value);
+      } if (IsConstant(a) && !((ConstantTreeNode)a).Value.IsAlmost(1.0)) {
+        return MakeFraction(MakeConstant(1.0), MakeProduct(b, Invert(a)));
+      } else if (IsVariable(a) && IsConstant(b)) {
+        // merge constant values into variable weights
+        var constB = ((ConstantTreeNode)b).Value;
+        ((VariableTreeNode)a).Weight /= constB;
+        return a;
+      } else if (IsVariable(a) && IsVariable(b) && AreSameVariable(a, b)) {
+        // cancel variables
+        var aVar = a as VariableTreeNode;
+        var bVar = b as VariableTreeNode;
+        return MakeConstant(aVar.Weight / bVar.Weight);
+      } else if (IsAddition(a) && IsConstant(b)) {
+        return a.SubTrees
+          .Select(x => GetSimplifiedTree(x))
+         .Select(x => MakeFraction(x, b))
+         .Aggregate((c, d) => MakeSum(c, d));
+      } else if (IsMultiplication(a) && IsConstant(b)) {
+        return MakeProduct(a, Invert(b));
+      } else if (IsDivision(a) && IsConstant(b)) {
+        // (a1 / a2) / c => (a1 / (a2 * c))
+        Trace.Assert(a.SubTrees.Count() == 2);
+        return MakeFraction(a.GetSubTree(0), MakeProduct(a.GetSubTree(1), b));
+      } else if (IsDivision(a) && IsDivision(b)) {
+        // (a1 / a2) / (b1 / b2) => 
+        Trace.Assert(a.SubTrees.Count() == 2);
+        Trace.Assert(b.SubTrees.Count() == 2);
+        return MakeFraction(MakeProduct(a.GetSubTree(0), b.GetSubTree(1)), MakeProduct(a.GetSubTree(1), b.GetSubTree(0)));
+      } else if (IsDivision(a)) {
+        // (a1 / a2) / b => (a1 / (a2 * b))
+        Trace.Assert(a.SubTrees.Count() == 2);
+        return MakeFraction(a.GetSubTree(0), MakeProduct(a.GetSubTree(1), b));
+      } else if (IsDivision(b)) {
+        // a / (b1 / b2) => (a * b2) / b1
+        Trace.Assert(b.SubTrees.Count() == 2);
+        return MakeFraction(MakeProduct(a, b.GetSubTree(1)), b.GetSubTree(0));
+      } else {
+        var div = divSymbol.CreateTreeNode();
+        div.AddSubTree(a);
+        div.AddSubTree(b);
+        return div;
+      }
+    }
+
+    private ISymbolicExpressionTreeNode MakeSum(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        // fold constants
+        ((ConstantTreeNode)a).Value += ((ConstantTreeNode)b).Value;
+        return a;
+      } else if (IsConstant(a)) {
+        // c + x => x + c
+        // b is not constant => make sure constant is on the right
+        return MakeSum(b, a);
+      } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(0.0)) {
+        // x + 0 => x
+        return a;
+      } else if (IsAddition(a) && IsAddition(b)) {
+        // merge additions
+        var add = addSymbol.CreateTreeNode();
+        // add all sub trees except for the last
+        for (int i = 0; i < a.SubTrees.Count() - 1; i++) add.AddSubTree(a.GetSubTree(i));
+        for (int i = 0; i < b.SubTrees.Count() - 1; i++) add.AddSubTree(b.GetSubTree(i));
+        if (IsConstant(a.SubTrees.Last()) && IsConstant(b.SubTrees.Last())) {
+          add.AddSubTree(MakeSum(a.SubTrees.Last(), b.SubTrees.Last()));
+        } else if (IsConstant(a.SubTrees.Last())) {
+          add.AddSubTree(b.SubTrees.Last());
+          add.AddSubTree(a.SubTrees.Last());
+        } else {
+          add.AddSubTree(a.SubTrees.Last());
+          add.AddSubTree(b.SubTrees.Last());
+        }
+        MergeVariablesInSum(add);
+        if (add.SubTrees.Count() == 1) {
+          return add.GetSubTree(0);
+        } else {
+          return add;
+        }
+      } else if (IsAddition(b)) {
+        return MakeSum(b, a);
+      } else if (IsAddition(a) && IsConstant(b)) {
+        // a is an addition and b is a constant => append b to a and make sure the constants are merged
+        var add = addSymbol.CreateTreeNode();
+        // add all sub trees except for the last
+        for (int i = 0; i < a.SubTrees.Count() - 1; i++) add.AddSubTree(a.GetSubTree(i));
+        if (IsConstant(a.SubTrees.Last()))
+          add.AddSubTree(MakeSum(a.SubTrees.Last(), b));
+        else {
+          add.AddSubTree(a.SubTrees.Last());
+          add.AddSubTree(b);
+        }
+        return add;
+      } else if (IsAddition(a)) {
+        // a is already an addition => append b
+        var add = addSymbol.CreateTreeNode();
+        add.AddSubTree(b);
+        foreach (var subTree in a.SubTrees) {
+          add.AddSubTree(subTree);
+        }
+        MergeVariablesInSum(add);
+        if (add.SubTrees.Count() == 1) {
+          return add.GetSubTree(0);
+        } else {
+          return add;
+        }
+      } else {
+        var add = addSymbol.CreateTreeNode();
+        add.AddSubTree(a);
+        add.AddSubTree(b);
+        MergeVariablesInSum(add);
+        if (add.SubTrees.Count() == 1) {
+          return add.GetSubTree(0);
+        } else {
+          return add;
+        }
+      }
+    }
+
+    // makes sure variable symbols in sums are combined
+    // possible improvement: combine sums of products where the products only reference the same variable
+    private void MergeVariablesInSum(ISymbolicExpressionTreeNode sum) {
+      var subtrees = new List<ISymbolicExpressionTreeNode>(sum.SubTrees);
+      while (sum.SubTrees.Count() > 0) sum.RemoveSubTree(0);
+      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
+                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
+                            group node by node.VariableName + lag into g
+                            select g;
+      var unchangedSubTrees = subtrees.Where(t => !(t is VariableTreeNode));
+
+      foreach (var variableNodeGroup in groupedVarNodes) {
+        var weightSum = variableNodeGroup.Select(t => t.Weight).Sum();
+        var representative = variableNodeGroup.First();
+        representative.Weight = weightSum;
+        sum.AddSubTree(representative);
+      }
+      foreach (var unchangedSubtree in unchangedSubTrees)
+        sum.AddSubTree(unchangedSubtree);
+    }
+
+
+    private ISymbolicExpressionTreeNode MakeProduct(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (IsConstant(a) && IsConstant(b)) {
+        // fold constants
+        ((ConstantTreeNode)a).Value *= ((ConstantTreeNode)b).Value;
+        return a;
+      } else if (IsConstant(a)) {
+        // a * $ => $ * a
+        return MakeProduct(b, a);
+      } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(1.0)) {
+        // $ * 1.0 => $
+        return a;
+      } else if (IsConstant(b) && IsVariable(a)) {
+        // multiply constants into variables weights
+        ((VariableTreeNode)a).Weight *= ((ConstantTreeNode)b).Value;
+        return a;
+      } else if (IsConstant(b) && IsAddition(a)) {
+        // multiply constants into additions
+        return a.SubTrees.Select(x => MakeProduct(x, b)).Aggregate((c, d) => MakeSum(c, d));
+      } else if (IsDivision(a) && IsDivision(b)) {
+        // (a1 / a2) * (b1 / b2) => (a1 * b1) / (a2 * b2)
+        Trace.Assert(a.SubTrees.Count() == 2);
+        Trace.Assert(b.SubTrees.Count() == 2);
+        return MakeFraction(MakeProduct(a.GetSubTree(0), b.GetSubTree(0)), MakeProduct(a.GetSubTree(1), b.GetSubTree(1)));
+      } else if (IsDivision(a)) {
+        // (a1 / a2) * b => (a1 * b) / a2
+        Trace.Assert(a.SubTrees.Count() == 2);
+        return MakeFraction(MakeProduct(a.GetSubTree(0), b), a.GetSubTree(1));
+      } else if (IsDivision(b)) {
+        // a * (b1 / b2) => (b1 * a) / b2
+        Trace.Assert(b.SubTrees.Count() == 2);
+        return MakeFraction(MakeProduct(b.GetSubTree(0), a), b.GetSubTree(1));
+      } else if (IsMultiplication(a) && IsMultiplication(b)) {
+        // merge multiplications (make sure constants are merged)
+        var mul = mulSymbol.CreateTreeNode();
+        for (int i = 0; i < a.SubTrees.Count(); i++) mul.AddSubTree(a.GetSubTree(i));
+        for (int i = 0; i < b.SubTrees.Count(); i++) mul.AddSubTree(b.GetSubTree(i));
+        MergeVariablesAndConstantsInProduct(mul);
+        return mul;
+      } else if (IsMultiplication(b)) {
+        return MakeProduct(b, a);
+      } else if (IsMultiplication(a)) {
+        // a is already an multiplication => append b
+        a.AddSubTree(b);
+        MergeVariablesAndConstantsInProduct(a);
+        return a;
+      } else {
+        var mul = mulSymbol.CreateTreeNode();
+        mul.AddSubTree(a);
+        mul.AddSubTree(b);
+        MergeVariablesAndConstantsInProduct(mul);
+        return mul;
+      }
+    }
+    #endregion
+
+
+    #region helper functions
+
+    private bool AreSameVariable(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      var aLaggedVar = a as LaggedVariableTreeNode;
+      var bLaggedVar = b as LaggedVariableTreeNode;
+      if (aLaggedVar != null && bLaggedVar != null) {
+        return aLaggedVar.VariableName == bLaggedVar.VariableName &&
+          aLaggedVar.Lag == bLaggedVar.Lag;
+      }
+      var aVar = a as VariableTreeNode;
+      var bVar = b as VariableTreeNode;
+      if (aVar != null && bVar != null) {
+        return aVar.VariableName == bVar.VariableName;
+      }
+      return false;
+    }
+
+    // helper to combine the constant factors in products and to combine variables (powers of 2, 3...)
+    private void MergeVariablesAndConstantsInProduct(ISymbolicExpressionTreeNode prod) {
+      var subtrees = new List<ISymbolicExpressionTreeNode>(prod.SubTrees);
+      while (prod.SubTrees.Count() > 0) prod.RemoveSubTree(0);
+      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
+                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
+                            group node by node.VariableName + lag into g
+                            orderby g.Count()
+                            select g;
+      var constantProduct = (from node in subtrees.OfType<VariableTreeNode>()
+                             select node.Weight)
+                            .Concat(from node in subtrees.OfType<ConstantTreeNode>()
+                                    select node.Value)
+                            .DefaultIfEmpty(1.0)
+                            .Aggregate((c1, c2) => c1 * c2);
+
+      var unchangedSubTrees = from tree in subtrees
+                              where !(tree is VariableTreeNode)
+                              where !(tree is ConstantTreeNode)
+                              select tree;
+
+      foreach (var variableNodeGroup in groupedVarNodes) {
+        var representative = variableNodeGroup.First();
+        representative.Weight = 1.0;
+        if (variableNodeGroup.Count() > 1) {
+          var poly = mulSymbol.CreateTreeNode();
+          for (int p = 0; p < variableNodeGroup.Count(); p++) {
+            poly.AddSubTree((ISymbolicExpressionTreeNode)representative.Clone());
+          }
+          prod.AddSubTree(poly);
+        } else {
+          prod.AddSubTree(representative);
+        }
+      }
+
+      foreach (var unchangedSubtree in unchangedSubTrees)
+        prod.AddSubTree(unchangedSubtree);
+
+      if (!constantProduct.IsAlmost(1.0)) {
+        prod.AddSubTree(MakeConstant(constantProduct));
+      }
+    }
+
+
+    /// <summary>
+    /// x => x * -1
+    /// Doesn't create new trees and manipulates x
+    /// </summary>
+    /// <param name="x"></param>
+    /// <returns>-x</returns>
+    private ISymbolicExpressionTreeNode Negate(ISymbolicExpressionTreeNode x) {
+      if (IsConstant(x)) {
+        ((ConstantTreeNode)x).Value *= -1;
+      } else if (IsVariable(x)) {
+        var variableTree = (VariableTreeNode)x;
+        variableTree.Weight *= -1.0;
+      } else if (IsAddition(x)) {
+        // (x0 + x1 + .. + xn) * -1 => (-x0 + -x1 + .. + -xn)        
+        List<ISymbolicExpressionTreeNode> subTrees = new List<ISymbolicExpressionTreeNode>(x.SubTrees);
+        while (x.SubTrees.Count() > 0) x.RemoveSubTree(0);
+        foreach (var subTree in subTrees) {
+          x.AddSubTree(Negate(subTree));
+        }
+      } else if (IsMultiplication(x) || IsDivision(x)) {
+        // x0 * x1 * .. * xn * -1 => x0 * x1 * .. * -xn
+        x.RemoveSubTree(x.SubTrees.Count() - 1);
+        x.AddSubTree(Negate(x.SubTrees.Last())); // last is maybe a constant, prefer to negate the constant
+      } else {
+        // any other function
+        return MakeProduct(x, MakeConstant(-1));
+      }
+      return x;
+    }
+
+    /// <summary>
+    /// x => 1/x
+    /// Doesn't create new trees and manipulates x
+    /// </summary>
+    /// <param name="x"></param>
+    /// <returns></returns>
+    private ISymbolicExpressionTreeNode Invert(ISymbolicExpressionTreeNode x) {
+      if (IsConstant(x)) {
+        return MakeConstant(1.0 / ((ConstantTreeNode)x).Value);
+      } else if (IsDivision(x)) {
+        Trace.Assert(x.SubTrees.Count() == 2);
+        return MakeFraction(x.GetSubTree(1), x.GetSubTree(0));
+      } else {
+        // any other function
+        return MakeFraction(MakeConstant(1), x);
+      }
+    }
+
+    private ISymbolicExpressionTreeNode MakeConstant(double value) {
+      ConstantTreeNode constantTreeNode = (ConstantTreeNode)(constSymbol.CreateTreeNode());
+      constantTreeNode.Value = value;
+      return constantTreeNode;
+    }
+
+    private ISymbolicExpressionTreeNode MakeVariable(double weight, string name) {
+      var tree = (VariableTreeNode)varSymbol.CreateTreeNode();
+      tree.Weight = weight;
+      tree.VariableName = name;
+      return tree;
+    }
+    #endregion
+  }
+}
