public void DeriveExpressions() { var formatter = new InfixExpressionFormatter(); var parser = new InfixExpressionParser(); Assert.AreEqual("0", Derive("3", "x")); Assert.AreEqual("1", Derive("x", "x")); Assert.AreEqual("10", Derive("10*x", "x")); Assert.AreEqual("10", Derive("x*10", "x")); Assert.AreEqual("(2*'x')", Derive("x*x", "x")); Assert.AreEqual("((('x' * 'x') * 2) + ('x' * 'x'))", Derive("x*x*x", "x")); // simplifier does not merge (x*x)*2 + x*x to 3*x*x Assert.AreEqual("0", Derive("10*x", "y")); Assert.AreEqual("20", Derive("10*x+20*y", "y")); Assert.AreEqual("6", Derive("2*3*x", "x")); Assert.AreEqual("(10*'y')", Derive("10*x*y+20*y", "x")); Assert.AreEqual("(1 / (SQR('x') * (-1)))", Derive("1/x", "x")); Assert.AreEqual("('y' / (SQR('x') * (-1)))", Derive("y/x", "x")); Assert.AreEqual("((((-2*'x') + (-1)) * ('a' + 'b')) / SQR(('x' + ('x' * 'x'))))", Derive("(a+b)/(x+x*x)", "x")); Assert.AreEqual("((((-2*'x') + (-1)) * ('a' + 'b')) / SQR(('x' + SQR('x'))))", Derive("(a+b)/(x+SQR(x))", "x")); Assert.AreEqual("EXP('x')", Derive("exp(x)", "x")); Assert.AreEqual("(EXP((3*'x')) * 3)", Derive("exp(3*x)", "x")); Assert.AreEqual("(1 / 'x')", Derive("log(x)", "x")); Assert.AreEqual("(1 / 'x')", Derive("log(3*x)", "x")); // 3 * 1/(3*x) Assert.AreEqual("(1 / ('x' + (0.333333333333333*'y')))", Derive("log(3*x+y)", "x")); // simplifier does not try to keep fractions Assert.AreEqual("(1 / (SQRT(((3*'x') + 'y')) * 0.666666666666667))", Derive("sqrt(3*x+y)", "x")); // 3 / (2 * sqrt(3*x+y)) = 1 / ((2/3) * sqrt(3*x+y)) Assert.AreEqual("(COS((3*'x')) * 3)", Derive("sin(3*x)", "x")); Assert.AreEqual("(SIN((3*'x')) * (-3))", Derive("cos(3*x)", "x")); Assert.AreEqual("(1 / (SQR(COS((3*'x'))) * 0.333333333333333))", Derive("tan(3*x)", "x")); // diff(tan(f(x)), x) = 1.0 / cos²(f(x)), simplifier puts constant factor into the denominator Assert.AreEqual("((9*'x') / ABS((3*'x')))", Derive("abs(3*x)", "x")); Assert.AreEqual("(SQR('x') * 3)", Derive("cube(x)", "x")); Assert.AreEqual("(1 / (SQR(CUBEROOT('x')) * 3))", Derive("cuberoot(x)", "x")); Assert.AreEqual("0", Derive("(a+b)/(x+SQR(x))", "y")); // df(a,b,x) / dy = 0 Assert.AreEqual("('a' * 'b' * 'c')", Derive("a*b*c*d", "d")); Assert.AreEqual("('a' / ('b' * 'c' * SQR('d') * (-1)))", Derive("a/b/c/d", "d")); Assert.AreEqual("('x' * ((SQR(TANH(SQR('x'))) * (-1)) + 1) * 2)", Derive("tanh(sqr(x))", "x")); // (2*'x'*(1 - SQR(TANH(SQR('x')))) { // special case: Inv(x) using only one argument to the division symbol // f(x) = 1/x var root = new ProgramRootSymbol().CreateTreeNode(); var start = new StartSymbol().CreateTreeNode(); var div = new Division().CreateTreeNode(); var varNode = (VariableTreeNode)(new Variable().CreateTreeNode()); varNode.Weight = 1.0; varNode.VariableName = "x"; div.AddSubtree(varNode); start.AddSubtree(div); root.AddSubtree(start); var t = new SymbolicExpressionTree(root); Assert.AreEqual("(1 / (SQR('x') * (-1)))", formatter.Format(DerivativeCalculator.Derive(t, "x"))); } { // special case: multiplication with only one argument var root = new ProgramRootSymbol().CreateTreeNode(); var start = new StartSymbol().CreateTreeNode(); var mul = new Multiplication().CreateTreeNode(); var varNode = (VariableTreeNode)(new Variable().CreateTreeNode()); varNode.Weight = 3.0; varNode.VariableName = "x"; mul.AddSubtree(varNode); start.AddSubtree(mul); root.AddSubtree(start); var t = new SymbolicExpressionTree(root); Assert.AreEqual("3", formatter.Format(DerivativeCalculator.Derive(t, "x"))); } { // division with multiple arguments // div(x, y, z) is interpreted as (x / y) / z var root = new ProgramRootSymbol().CreateTreeNode(); var start = new StartSymbol().CreateTreeNode(); var div = new Division().CreateTreeNode(); var varNode1 = (VariableTreeNode)(new Variable().CreateTreeNode()); varNode1.Weight = 3.0; varNode1.VariableName = "x"; var varNode2 = (VariableTreeNode)(new Variable().CreateTreeNode()); varNode2.Weight = 4.0; varNode2.VariableName = "y"; var varNode3 = (VariableTreeNode)(new Variable().CreateTreeNode()); varNode3.Weight = 5.0; varNode3.VariableName = "z"; div.AddSubtree(varNode1); div.AddSubtree(varNode2); div.AddSubtree(varNode3); start.AddSubtree(div); root.AddSubtree(start); var t = new SymbolicExpressionTree(root); Assert.AreEqual("(('y' * 'z' * 60) / (SQR('y') * SQR('z') * 400))", // actually 3 / (4y 5z) but simplifier is not smart enough to cancel numerator and denominator // 60 y z / y² z² 20² == 6 / y z 40 == 3 / y z 20 formatter.Format(DerivativeCalculator.Derive(t, "x"))); Assert.AreEqual("(('x' * 'z' * (-60)) / (SQR('y') * SQR('z') * 400))", // actually 3x * -(4 5 z) / (4y 5z)² = -3x / (20 y² z) // -3 4 5 x z / 4² y² 5² z² = -60 x z / 20² z² y² == -60 x z / y² z² 20² formatter.Format(DerivativeCalculator.Derive(t, "y"))); Assert.AreEqual("(('x' * 'y' * (-60)) / (SQR('y') * SQR('z') * 400))", formatter.Format(DerivativeCalculator.Derive(t, "z"))); } }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then for each matching node j from the second parent, calculate the behavioral distance based on the range: /// d(i,j) = 0.5 * ( abs(max(i) - max(j)) + abs(min(i) - min(j)) ). /// Next, assign probabilities for the selection of a node j based on the inversed and normalized behavioral distance, then make a random weighted choice. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, T problemData, IList <int> rows, int maxDepth, int maxLength) { var crossoverPoints0 = new List <CutPoint>(); parent0.Root.ForEachNodePostfix((n) => { // the if clauses prevent the root or the startnode from being selected, although the startnode can be the parent of the node being swapped. if (n.Parent != null && n.Parent != parent0.Root) { crossoverPoints0.Add(new CutPoint(n.Parent, n)); } }); var crossoverPoint0 = crossoverPoints0.SampleRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) { allowedBranches.Add(n); } } }); if (allowedBranches.Count == 0) { return(parent0); } var dataset = problemData.Dataset; // create symbols in order to improvize an ad-hoc tree so that the child can be evaluated var rootSymbol = new ProgramRootSymbol(); var startSymbol = new StartSymbol(); var tree0 = CreateTreeFromNode(random, crossoverPoint0.Child, rootSymbol, startSymbol); // this will change crossoverPoint0.Child.Parent double min0 = 0.0, max0 = 0.0; foreach (double v in interpreter.GetSymbolicExpressionTreeValues(tree0, dataset, rows)) { if (min0 > v) { min0 = v; } if (max0 < v) { max0 = v; } } crossoverPoint0.Child.Parent = crossoverPoint0.Parent; // restore correct parent var weights = new List <double>(); foreach (var node in allowedBranches) { var parent = node.Parent; var tree1 = CreateTreeFromNode(random, node, rootSymbol, startSymbol); double min1 = 0.0, max1 = 0.0; foreach (double v in interpreter.GetSymbolicExpressionTreeValues(tree1, dataset, rows)) { if (min1 > v) { min1 = v; } if (max1 < v) { max1 = v; } } double behavioralDistance = (Math.Abs(min0 - min1) + Math.Abs(max0 - max1)) / 2; // this can be NaN of Infinity because some trees are crazy like exp(exp(exp(...))), we correct that below weights.Add(behavioralDistance); node.Parent = parent; // restore correct node parent } // remove branches with an infinite or NaN behavioral distance for (int i = weights.Count - 1; i >= 0; --i) { if (Double.IsNaN(weights[i]) || Double.IsInfinity(weights[i])) { weights.RemoveAt(i); allowedBranches.RemoveAt(i); } } // check if there are any allowed branches left if (allowedBranches.Count == 0) { return(parent0); } ISymbolicExpressionTreeNode selectedBranch; double sum = weights.Sum(); if (sum.IsAlmost(0.0) || weights.Count == 1) // if there is only one allowed branch, or if all weights are zero { selectedBranch = allowedBranches[0]; } else { for (int i = 0; i != weights.Count; ++i) // normalize and invert values { weights[i] = 1 - weights[i] / sum; } sum = weights.Sum(); // take new sum // compute the probabilities (selection weights) for (int i = 0; i != weights.Count; ++i) { weights[i] /= sum; } #pragma warning disable 612, 618 selectedBranch = allowedBranches.SelectRandom(weights, random); #pragma warning restore 612, 618 } Swap(crossoverPoint0, selectedBranch); return(parent0); }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then get a node j from the second parent that matches the semantic similarity criteria. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, T problemData, List <int> rows, int maxDepth, int maxLength, DoubleRange range) { var crossoverPoints0 = new List <CutPoint>(); parent0.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent0.Root) { crossoverPoints0.Add(new CutPoint(n.Parent, n)); } }); var crossoverPoint0 = crossoverPoints0.SampleRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) { allowedBranches.Add(n); } } }); if (allowedBranches.Count == 0) { return(parent0); } var dataset = problemData.Dataset; // create symbols in order to improvize an ad-hoc tree so that the child can be evaluated var rootSymbol = new ProgramRootSymbol(); var startSymbol = new StartSymbol(); var tree0 = CreateTreeFromNode(random, crossoverPoint0.Child, rootSymbol, startSymbol); List <double> estimatedValues0 = interpreter.GetSymbolicExpressionTreeValues(tree0, dataset, rows).ToList(); crossoverPoint0.Child.Parent = crossoverPoint0.Parent; // restore parent ISymbolicExpressionTreeNode selectedBranch = null; // pick the first node that fulfills the semantic similarity conditions foreach (var node in allowedBranches) { var parent = node.Parent; var tree1 = CreateTreeFromNode(random, node, startSymbol, rootSymbol); // this will affect node.Parent List <double> estimatedValues1 = interpreter.GetSymbolicExpressionTreeValues(tree1, dataset, rows).ToList(); node.Parent = parent; // restore parent OnlineCalculatorError errorState; double ssd = OnlineMeanAbsoluteErrorCalculator.Calculate(estimatedValues0, estimatedValues1, out errorState); if (range.Start <= ssd && ssd <= range.End) { selectedBranch = node; break; } } // perform the actual swap if (selectedBranch != null) { Swap(crossoverPoint0, selectedBranch); } return(parent0); }