Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Converters/DerivativeCalculator.cs

Last change on this file was 18220, checked in by gkronber, 2 years ago

#3136: reintegrated structure-template GP branch into trunk

File size: 13.0 KB
RevLine 
[16206]1#region License Information
2/* HeuristicLab
[17180]3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[16206]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
[16294]23using System.Collections.Generic;
[16206]24using System.Linq;
25using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
26
27namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
28  public static class DerivativeCalculator {
29    public static ISymbolicExpressionTree Derive(ISymbolicExpressionTree tree, string variableName) {
[16294]30      if (tree.Root.SubtreeCount != 1)
31        throw new NotImplementedException("Derive is not implemented for symbolic expressions with automatically defined functions (ADF)");
32      if (tree.Root.GetSubtree(0).SubtreeCount != 1)
33        throw new NotImplementedException("Derive is not implemented for multi-variate symbolic expressions");
[16206]34      var mainBranch = tree.Root.GetSubtree(0).GetSubtree(0);
35      var root = new ProgramRootSymbol().CreateTreeNode();
36      root.AddSubtree(new StartSymbol().CreateTreeNode());
37      var dTree = TreeSimplifier.GetSimplifiedTree(Derive(mainBranch, variableName));
[16294]38      //var dTree = Derive(mainBranch, variableName);
[16206]39      root.GetSubtree(0).AddSubtree(dTree);
40      return new SymbolicExpressionTree(root);
41    }
42
[18132]43    private static readonly Number numberSy = new Number();
[16294]44    private static readonly Addition addSy = new Addition();
45    private static readonly Subtraction subSy = new Subtraction();
46    private static readonly Multiplication mulSy = new Multiplication();
47    private static readonly Division divSy = new Division();
48    private static readonly Cosine cosSy = new Cosine();
49    private static readonly Square sqrSy = new Square();
[16543]50    private static readonly Absolute absSy = new Absolute();
51    private static readonly SquareRoot sqrtSy = new SquareRoot();
[16206]52
53    public static ISymbolicExpressionTreeNode Derive(ISymbolicExpressionTreeNode branch, string variableName) {
[18132]54      if (branch.Symbol is INumericSymbol) {
55        return CreateNumber(0.0);
[16206]56      }
57      if (branch.Symbol is Variable) {
58        var varNode = branch as VariableTreeNode;
59        if (varNode.VariableName == variableName) {
[18132]60          return CreateNumber(varNode.Weight);
[16206]61        } else {
[18132]62          return CreateNumber(0.0);
[16206]63        }
64      }
65      if (branch.Symbol is Addition) {
66        var sum = addSy.CreateTreeNode();
67        foreach (var subTree in branch.Subtrees) {
68          sum.AddSubtree(Derive(subTree, variableName));
69        }
70        return sum;
71      }
72      if (branch.Symbol is Subtraction) {
73        var sum = subSy.CreateTreeNode();
74        foreach (var subTree in branch.Subtrees) {
75          sum.AddSubtree(Derive(subTree, variableName));
76        }
77        return sum;
78      }
79      if (branch.Symbol is Multiplication) {
80        // (f * g)' = f'*g + f*g'
81        // for multiple factors: (f * g * h)' = ((f*g) * h)' = (f*g)' * h + (f*g) * h'
82
83        if (branch.SubtreeCount >= 2) {
84          var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
85          var fprime = Derive(f, variableName);
[16737]86          for (int i = 1; i < branch.SubtreeCount; i++) {
87            var g = (ISymbolicExpressionTreeNode)branch.GetSubtree(i).Clone();
[16206]88            var fg = Product((ISymbolicExpressionTreeNode)f.Clone(), (ISymbolicExpressionTreeNode)g.Clone());
[16737]89            var gPrime = Derive(g, variableName);
90            var fgPrime = Sum(Product(fprime, g), Product(gPrime, f));
91            // prepare for next iteration
92            f = fg;
93            fprime = fgPrime;
[16206]94          }
[16737]95          return fprime;
[16294]96        } else
97          // multiplication with only one argument has no effect -> derive the argument
98          return Derive(branch.GetSubtree(0), variableName);
[16206]99      }
100      if (branch.Symbol is Division) {
101        // (f/g)' = (f'g - g'f) / g²
102        if (branch.SubtreeCount == 1) {
103          var g = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]104          var gPrime = Product(CreateNumber(-1.0), Derive(g, variableName));
[16206]105          var sqrNode = new Square().CreateTreeNode();
106          sqrNode.AddSubtree(g);
107          return Div(gPrime, sqrNode);
[16294]108        } else {
109          // for two subtrees:
110          // (f/g)' = (f'g - fg')/g²
111
112          // if there are more than 2 subtrees
113          // div(x,y,z) is interpretered as (x/y)/z
114          // which is the same as x / (y*z)
115
116          // --> make a product of all but the first subtree and differentiate as for the 2-argument case above
[16206]117          var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[16294]118          var g = Product(branch.Subtrees.Skip(1).Select(n => (ISymbolicExpressionTreeNode)n.Clone()));
[16206]119          var fprime = Derive(f, variableName);
120          var gprime = Derive(g, variableName);
[16294]121          var gSqr = Square(g);
122          return Div(Subtract(Product(fprime, g), Product(f, gprime)), gSqr);
123        }
[16206]124      }
125      if (branch.Symbol is Logarithm) {
126        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]127        return Product(Div(CreateNumber(1.0), f), Derive(f, variableName));
[16206]128      }
129      if (branch.Symbol is Exponential) {
130        var f = (ISymbolicExpressionTreeNode)branch.Clone();
131        return Product(f, Derive(branch.GetSubtree(0), variableName));
132      }
[16294]133      if (branch.Symbol is Square) {
[16206]134        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]135        return Product(Product(CreateNumber(2.0), f), Derive(f, variableName));
[16294]136      }
137      if (branch.Symbol is SquareRoot) {
[16206]138        var f = (ISymbolicExpressionTreeNode)branch.Clone();
139        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]140        return Product(Div(CreateNumber(1.0), Product(CreateNumber(2.0), f)), Derive(u, variableName));
[16206]141      }
[16543]142      if (branch.Symbol is CubeRoot) {
143        var f = (ISymbolicExpressionTreeNode)branch.Clone();
144        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]145        return Product(Div(CreateNumber(1.0), Product(CreateNumber(3.0), Square(f))), Derive(u, variableName));  // 1/3 1/cbrt(f(x))^2 d/dx f(x)
[16543]146      }
147      if (branch.Symbol is Cube) {
148        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
[18132]149        return Product(Product(CreateNumber(3.0), Square(f)), Derive(f, variableName));
[16543]150      }
[17902]151      if (branch.Symbol is Power) {
152        // HL evaluators handle power strangely (exponent is rounded to an integer)
153        // here we only support the case when the exponent is a constant integer
[18132]154        var exponent = branch.GetSubtree(1) as INumericTreeNode;
[17902]155        if (exponent != null && Math.Truncate(exponent.Value) == exponent.Value) {
156          var newPower = (ISymbolicExpressionTreeNode)branch.Clone();
157          var f = (ISymbolicExpressionTreeNode)newPower.GetSubtree(0).Clone();
[18143]158          var newExponent = (NumberTreeNode)numberSy.CreateTreeNode();
159          newExponent.Value = ((NumberTreeNode)newPower.GetSubtree(1)).Value - 1;
[18174]160          newPower.RemoveSubtree(1);
161          newPower.AddSubtree(newExponent);
[18132]162          return Product(Product(CreateNumber(exponent.Value), newPower), Derive(f, variableName));
[17902]163        } else throw new NotSupportedException("Cannot derive non-integer powers");
164      }
[16543]165      if (branch.Symbol is Absolute) {
166        var f = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
167        var absf = Abs((ISymbolicExpressionTreeNode)f.Clone());
168        return Product(Div(f, absf), Derive(f, variableName));
169      }
170      if (branch.Symbol is AnalyticQuotient) {
171        // aq(a(x), b(x)) = a(x) / sqrt(b(x)²+1)
172        var a = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
173        var b = (ISymbolicExpressionTreeNode)branch.GetSubtree(1).Clone();
174
[18132]175        var definition = Div(a, SquareRoot(Sum(Square(b), CreateNumber(1.0))));
[16543]176        return Derive(definition, variableName);
177      }
[16206]178      if (branch.Symbol is Sine) {
179        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
180        var cos = (new Cosine()).CreateTreeNode();
181        cos.AddSubtree(u);
182        return Product(cos, Derive(u, variableName));
183      }
184      if (branch.Symbol is Cosine) {
185        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
186        var sin = (new Sine()).CreateTreeNode();
187        sin.AddSubtree(u);
[18132]188        return Product(CreateNumber(-1.0), Product(sin, Derive(u, variableName)));
[16206]189      }
[16294]190      if (branch.Symbol is Tangent) {
191        // tan(x)' = 1 / cos²(x)
192        var fxp = Derive(branch.GetSubtree(0), variableName);
193        var u = (ISymbolicExpressionTreeNode)branch.GetSubtree(0).Clone();
194        return Div(fxp, Square(Cosine(u)));
195      }
[17125]196      if (branch.Symbol is HyperbolicTangent) {
197        // tanh(f(x))' = f(x)'sech²(f(x)) = f(x)'(1 - tanh²(f(x)))
198        var fxp = Derive(branch.GetSubtree(0), variableName);
199        var tanh = (ISymbolicExpressionTreeNode)branch.Clone();
[18132]200        return Product(fxp, Subtract(CreateNumber(1.0), Square(tanh)));
[17125]201      }
[18220]202      if (branch.Symbol is SubFunctionSymbol) {
203        return Derive(branch.GetSubtree(0), variableName);
204      }
[16216]205      throw new NotSupportedException(string.Format("Symbol {0} is not supported.", branch.Symbol));
[16206]206    }
207
208
209    private static ISymbolicExpressionTreeNode Product(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
210      var product = mulSy.CreateTreeNode();
211      product.AddSubtree(f);
212      product.AddSubtree(g);
213      return product;
214    }
[16294]215    private static ISymbolicExpressionTreeNode Product(IEnumerable<ISymbolicExpressionTreeNode> fs) {
216      var product = mulSy.CreateTreeNode();
217      foreach (var f in fs) product.AddSubtree(f);
218      return product;
219    }
[16206]220    private static ISymbolicExpressionTreeNode Div(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
221      var div = divSy.CreateTreeNode();
222      div.AddSubtree(f);
223      div.AddSubtree(g);
224      return div;
225    }
226
227    private static ISymbolicExpressionTreeNode Sum(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
228      var sum = addSy.CreateTreeNode();
229      sum.AddSubtree(f);
230      sum.AddSubtree(g);
231      return sum;
232    }
233    private static ISymbolicExpressionTreeNode Subtract(ISymbolicExpressionTreeNode f, ISymbolicExpressionTreeNode g) {
234      var sum = subSy.CreateTreeNode();
235      sum.AddSubtree(f);
236      sum.AddSubtree(g);
237      return sum;
238    }
[16294]239    private static ISymbolicExpressionTreeNode Cosine(ISymbolicExpressionTreeNode f) {
240      var cos = cosSy.CreateTreeNode();
241      cos.AddSubtree(f);
242      return cos;
243    }
[16543]244    private static ISymbolicExpressionTreeNode Abs(ISymbolicExpressionTreeNode f) {
245      var abs = absSy.CreateTreeNode();
246      abs.AddSubtree(f);
247      return abs;
248    }
[16294]249    private static ISymbolicExpressionTreeNode Square(ISymbolicExpressionTreeNode f) {
250      var sqr = sqrSy.CreateTreeNode();
251      sqr.AddSubtree(f);
252      return sqr;
253    }
[16543]254    private static ISymbolicExpressionTreeNode SquareRoot(ISymbolicExpressionTreeNode f) {
255      var sqrt = sqrtSy.CreateTreeNode();
256      sqrt.AddSubtree(f);
257      return sqrt;
258    }
[16294]259
[18132]260    private static ISymbolicExpressionTreeNode CreateNumber(double v) {
261      var numberNode = (NumberTreeNode)numberSy.CreateTreeNode();
262      numberNode.Value = v;
263      return numberNode;
[16206]264    }
265
266    public static bool IsCompatible(ISymbolicExpressionTree tree) {
267      var containsUnknownSymbol = (
268        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
269        where
270          !(n.Symbol is Variable) &&
[18132]271          !(n.Symbol is Number) &&
[16206]272          !(n.Symbol is Constant) &&
273          !(n.Symbol is Addition) &&
274          !(n.Symbol is Subtraction) &&
275          !(n.Symbol is Multiplication) &&
276          !(n.Symbol is Division) &&
277          !(n.Symbol is Logarithm) &&
278          !(n.Symbol is Exponential) &&
279          !(n.Symbol is Square) &&
280          !(n.Symbol is SquareRoot) &&
[16543]281          !(n.Symbol is Cube) &&
282          !(n.Symbol is CubeRoot) &&
[17902]283          !(n.Symbol is Power) &&
[16543]284          !(n.Symbol is Absolute) &&
285          !(n.Symbol is AnalyticQuotient) &&
[17902]286          !(n.Symbol is HyperbolicTangent) &&
[16206]287          !(n.Symbol is Sine) &&
288          !(n.Symbol is Cosine) &&
[16294]289          !(n.Symbol is Tangent) &&
[18220]290          !(n.Symbol is StartSymbol) &&
291          !(n.Symbol is SubFunctionSymbol)
[16206]292        select n).Any();
293      return !containsUnknownSymbol;
294    }
295  }
296}
Note: See TracBrowser for help on using the repository browser.