Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator.cs @ 18234

Last change on this file since 18234 was 18234, checked in by pfleck, 2 years ago

#3040 Fixed vector-unrolling AutoDiff conversion.

File size: 10.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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;
23using System.Collections.Generic;
24using System.Linq;
25using System.Threading;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30using HeuristicLab.Parameters;
31using HEAL.Attic;
32using DoubleVector = MathNet.Numerics.LinearAlgebra.Vector<double>;
33
34namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
35  [StorableType("5F8DB251-C6F7-40AC-BC30-3C55AF655A2F")]
36  [Item("VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator", "")]
37  public class VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator : SymbolicRegressionConstantOptimizationEvaluator {
38
39    private const string ConstantOptimizationIterationsName = "ConstantOptimizationIterations";
40
41    #region Parameter Properties
42    public IFixedValueParameter<IntValue> ConstantOptimizationIterationsParameter {
43      get { return (IFixedValueParameter<IntValue>)Parameters[ConstantOptimizationIterationsName]; }
44    }
45    #endregion
46
47    #region Properties
48    public int ConstantOptimizationIterations {
49      get { return ConstantOptimizationIterationsParameter.Value.Value; }
50      set { ConstantOptimizationIterationsParameter.Value.Value = value; }
51    }
52    #endregion
53
54    public VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator()
55      : base() {
56      Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsName, "Determines how many iterations should be calculated while optimizing the constant of a symbolic expression tree(0 indicates other or default stopping criterion).", new IntValue(10)));
57    }
58
59    protected VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator(VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator original, Cloner cloner)
60      : base(original, cloner) {
61    }
62    public override IDeepCloneable Clone(Cloner cloner) {
63      return new VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator(this, cloner);
64    }
65    [StorableConstructor]
66    protected VectorUnrollingNonlinearLeastSquaresConstantOptimizationEvaluator(StorableConstructorFlag _) : base(_) { }
67
68    protected override ISymbolicExpressionTree OptimizeConstants(
69      ISymbolicExpressionTree tree, IRegressionProblemData problemData, IEnumerable<int> rows,
70      CancellationToken cancellationToken = default(CancellationToken), EvaluationsCounter counter = null) {
71      return OptimizeTree(tree, (SymbolicDataAnalysisExpressionTreeVectorInterpreter)SymbolicDataAnalysisTreeInterpreterParameter.ActualValue,
72        problemData, rows,
73        ApplyLinearScalingParameter.ActualValue.Value, ConstantOptimizationIterations, UpdateVariableWeights,
74        cancellationToken, counter);
75    }
76
77    public static ISymbolicExpressionTree OptimizeTree(
78      ISymbolicExpressionTree tree,
79      SymbolicDataAnalysisExpressionTreeVectorInterpreter interpreter,
80      IRegressionProblemData problemData, IEnumerable<int> rows,
81      bool applyLinearScaling, int maxIterations, bool updateVariableWeights,
82      CancellationToken cancellationToken = default(CancellationToken), EvaluationsCounter counter = null, Action<double[], double, object> iterationCallback = null) {
83
84      var vectorLengths = problemData.Dataset.DoubleVectorVariables
85        .SelectMany(var => problemData.Dataset.GetDoubleVectorValues(var, rows))
86        .Select(v => v.Count);
87      if (vectorLengths.Distinct().Count() > 1)
88        throw new InvalidOperationException("All vectors must be of same length.");
89      var evaluationTraces = interpreter.GetIntermediateNodeValues(tree, problemData.Dataset, rows);
90      var evaluationTrace = evaluationTraces.First(); // assume all vector lengths are the same
91
92
93      // numeric constants in the tree become variables for constant opt
94      // variables in the tree become parameters (fixed values) for constant opt
95      // for each parameter (variable in the original tree) we store the
96      // variable name, variable value (for factor vars) and lag as a DataForVariable object.
97      // A dictionary is used to find parameters
98      bool success = VectorUnrollingTreeToAutoDiffTermConverter.TryConvertToAutoDiff(
99        tree, evaluationTrace,
100        updateVariableWeights, applyLinearScaling,
101        out var parameters, out var initialConstants, out var func, out var func_grad);
102      if (!success)
103        throw new NotSupportedException("Could not optimize constants of symbolic expression tree due to not supported symbols used in the tree.");
104      if (parameters.Count == 0) return (ISymbolicExpressionTree)tree.Clone();
105      var parameterEntries = parameters.ToArray(); // order of entries must be the same for x
106
107      //extract initial constants
108      double[] c;
109      if (applyLinearScaling) {
110        c = new double[initialConstants.Length + 2];
111        c[0] = 0.0;
112        c[1] = 1.0;
113        Array.Copy(initialConstants, 0, c, 2, initialConstants.Length);
114      } else {
115        c = (double[])initialConstants.Clone();
116      }
117
118      IDataset ds = problemData.Dataset;
119      double[,] x = new double[rows.Count(), parameters.Count];
120      int row = 0;
121      foreach (var r in rows) {
122        int col = 0;
123        foreach (var info in parameterEntries) {
124          if (ds.VariableHasType<double>(info.variableName)) {
125            x[row, col] = ds.GetDoubleValue(info.variableName, r + info.lag);
126          } else if (ds.VariableHasType<string>(info.variableName)) {
127            x[row, col] = ds.GetStringValue(info.variableName, r) == info.variableValue ? 1 : 0;
128          } else if (ds.VariableHasType<DoubleVector>(info.variableName)) {
129            x[row, col] = ds.GetDoubleVectorValue(info.variableName, r)[info.index];
130          } else throw new InvalidProgramException("found a variable of unknown type");
131          col++;
132        }
133        row++;
134      }
135      double[] y = ds.GetDoubleValues(problemData.TargetVariable, rows).ToArray();
136      int n = x.GetLength(0);
137      int m = x.GetLength(1);
138      int k = c.Length;
139
140      alglib.ndimensional_pfunc function_cx_1_func = CreatePFunc(func);
141      alglib.ndimensional_pgrad function_cx_1_grad = CreatePGrad(func_grad);
142      alglib.ndimensional_rep xrep = (p, f, obj) => {
143        iterationCallback?.Invoke(p, f, obj);
144        cancellationToken.ThrowIfCancellationRequested();
145      };
146      var rowEvaluationsCounter = new EvaluationsCounter();
147
148      try {
149        alglib.lsfitcreatefg(x, y, c, n, m, k, false, out var state);
150        alglib.lsfitsetcond(state, 0.0, 0.0, maxIterations);
151        alglib.lsfitsetxrep(state, iterationCallback != null || cancellationToken != default(CancellationToken));
152        //alglib.lsfitsetgradientcheck(state, 0.001);
153        alglib.lsfitfit(state, function_cx_1_func, function_cx_1_grad, xrep, rowEvaluationsCounter);
154        alglib.lsfitresults(state, out var retVal, out c, out alglib.lsfitreport rep);
155
156        //retVal == -7  => constant optimization failed due to wrong gradient
157        if (retVal == -1)
158          return (ISymbolicExpressionTree)tree.Clone();
159      } catch (ArithmeticException) {
160        return (ISymbolicExpressionTree)tree.Clone();
161      } catch (alglib.alglibexception) {
162        return (ISymbolicExpressionTree)tree.Clone();
163      }
164
165      if (counter != null) {
166        counter.FunctionEvaluations += rowEvaluationsCounter.FunctionEvaluations / n;
167        counter.GradientEvaluations += rowEvaluationsCounter.GradientEvaluations / n;
168      }
169
170      var newTree = (ISymbolicExpressionTree)tree.Clone();
171      if (applyLinearScaling) {
172        var tmp = new double[c.Length - 2];
173        Array.Copy(c, 2, tmp, 0, tmp.Length);
174        UpdateConstants(newTree, tmp, updateVariableWeights);
175      } else
176        UpdateConstants(newTree, c, updateVariableWeights);
177
178      return newTree;
179    }
180
181    private static void UpdateConstants(ISymbolicExpressionTree tree, double[] constants, bool updateVariableWeights) {
182      int i = 0;
183      foreach (var node in tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>()) {
184        ConstantTreeNode constantTreeNode = node as ConstantTreeNode;
185        VariableTreeNodeBase variableTreeNodeBase = node as VariableTreeNodeBase;
186        FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode;
187        if (constantTreeNode != null)
188          constantTreeNode.Value = constants[i++];
189        else if (updateVariableWeights && variableTreeNodeBase != null)
190          variableTreeNodeBase.Weight = constants[i++];
191        else if (factorVarTreeNode != null) {
192          for (int j = 0; j < factorVarTreeNode.Weights.Length; j++)
193            factorVarTreeNode.Weights[j] = constants[i++];
194        }
195      }
196    }
197
198    private static alglib.ndimensional_pfunc CreatePFunc(VectorUnrollingTreeToAutoDiffTermConverter.ParametricFunction func) {
199      return (double[] c, double[] x, ref double fx, object o) => {
200        fx = func(c, x);
201        var counter = (EvaluationsCounter)o;
202        counter.FunctionEvaluations++;
203      };
204    }
205
206    private static alglib.ndimensional_pgrad CreatePGrad(VectorUnrollingTreeToAutoDiffTermConverter.ParametricFunctionGradient func_grad) {
207      return (double[] c, double[] x, ref double fx, double[] grad, object o) => {
208        var tuple = func_grad(c, x);
209        fx = tuple.Item2;
210        Array.Copy(tuple.Item1, grad, grad.Length);
211        var counter = (EvaluationsCounter)o;
212        counter.GradientEvaluations++;
213      };
214    }
215
216    public static bool CanOptimizeConstants(ISymbolicExpressionTree tree) {
217      return VectorUnrollingTreeToAutoDiffTermConverter.IsCompatible(tree);
218    }
219  }
220}
Note: See TracBrowser for help on using the repository browser.