Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Mutators/NestedOptimizerSubVectorImprovementManipulator.cs @ 18235

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

#3040 Added GuidedRangeManipulator for nested index optimization.

File size: 21.7 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using HEAL.Attic;
5using HeuristicLab.Algorithms.EvolutionStrategy;
6using HeuristicLab.Algorithms.GeneticAlgorithm;
7using HeuristicLab.Algorithms.OffspringSelectionGeneticAlgorithm;
8using HeuristicLab.Algorithms.RandomSearch;
9using HeuristicLab.Common;
10using HeuristicLab.Core;
11using HeuristicLab.Data;
12using HeuristicLab.Encodings.IntegerVectorEncoding;
13using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
14using HeuristicLab.Optimization;
15using HeuristicLab.Parameters;
16using HeuristicLab.Random;
17using HeuristicLab.Selection;
18
19namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
20
21  [Item("NestedOptimizerSubVectorImprovementManipulator", "Mutator that optimizes the ranges for a subvector symbol by utilizing a nested optimizer.")]
22  [StorableType("32E58EEE-97B4-4396-98A8-B98AB897E3F0")]
23  public class NestedOptimizerSubVectorImprovementManipulator<T> : SymbolicDataAnalysisExpressionManipulator<T> where T : class, IDataAnalysisProblemData {
24    private const string BestSolutionParameterName = "BestSolution";
25
26    [Item("SubVectorOptimizationProblem", "")]
27    [StorableType("EA3D3221-B274-4F2F-8B58-23CB2D091FD7")]
28    public class SubVectorOptimizationProblem : SingleObjectiveBasicProblem<IntegerVectorEncoding> {
29      #region Fixed Problem Parameters
30      [Storable]
31      private ISymbolicDataAnalysisSingleObjectiveEvaluator<T> evaluator;
32      [Storable]
33      private T problemData;
34      [Storable]
35      private List<int> rows;
36      [Storable]
37      private IExecutionContext executionContext;
38      #endregion
39
40      #region Instance Parameters
41      [Storable]
42      private ISymbolicExpressionTree tree;
43      [Storable]
44      private IList<int> selectedSubVectorNodes;
45      #endregion
46
47      private IFixedValueParameter<BoolValue> UseCacheParameter {
48        get { return (IFixedValueParameter<BoolValue>)Parameters["UseCache"]; }
49      }
50
51      public bool UseCache {
52        get { return UseCacheParameter.Value.Value; }
53        set { UseCacheParameter.Value.Value = value; }
54      }
55
56      private readonly IDictionary<IntegerVector, double> cache;
57     
58      public override bool Maximization { get { return false; } }
59
60      public SubVectorOptimizationProblem() {
61        Encoding = new IntegerVectorEncoding("bounds");
62        Parameters.Add(new ResultParameter<IntegerVector>(BestSolutionParameterName, ""));
63
64        Parameters.Add(new FixedValueParameter<BoolValue>("UseCache", new BoolValue(true)));
65        cache = new Dictionary<IntegerVector, double>(new IntegerVectorEqualityComparer());
66      }
67
68      private SubVectorOptimizationProblem(SubVectorOptimizationProblem original, Cloner cloner)
69        : base(original, cloner) {
70        this.cache = new Dictionary<IntegerVector, double>(original.cache, new IntegerVectorEqualityComparer());
71      }
72      public override IDeepCloneable Clone(Cloner cloner) {
73        return new SubVectorOptimizationProblem(this, cloner);
74      }
75
76      [StorableConstructor]
77      private SubVectorOptimizationProblem(StorableConstructorFlag _) : base(_) {
78        cache = new Dictionary<IntegerVector, double>(new IntegerVectorEqualityComparer());
79      }
80      [StorableHook(HookType.AfterDeserialization)]
81      private void AfterDeserialization() {
82        if (!Parameters.ContainsKey("UseCache"))
83          Parameters.Add(new FixedValueParameter<BoolValue>("UseCache", new BoolValue(true)));
84      }
85
86      public override double Evaluate(Individual individual, IRandom random) {
87        return Evaluate(individual.IntegerVector(Encoding.Name));
88      }
89
90      public double Evaluate(IntegerVector solution) {
91        if (UseCache && cache.TryGetValue(solution, out double cachedQuality)) {
92          return cachedQuality;
93        }
94
95        var updatedTree = (ISymbolicExpressionTree)tree.Clone();
96        UpdateFromVector(updatedTree, selectedSubVectorNodes, solution, Encoding.Bounds[0, 1]);
97
98        var quality = evaluator.Evaluate(executionContext, updatedTree, problemData, rows);
99        if (evaluator.Maximization)
100          quality = -quality;
101
102        if (UseCache) {
103          cache.Add(solution, quality);
104        }
105
106        return quality;
107      }
108
109      public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {
110        var best = GetBestIndividual(individuals, qualities);
111        var vector = best.Item1.IntegerVector(Encoding.Name);
112
113        results.AddOrUpdateResult(BestSolutionParameterName, vector);
114      }
115
116      public void SetProblemData(ISymbolicDataAnalysisSingleObjectiveEvaluator<T> evaluator, T problemData, List<int> rows, IExecutionContext executionContext) {
117        this.evaluator = evaluator;
118        this.problemData = problemData;
119        this.rows = rows;
120        this.executionContext = executionContext;
121        cache.Clear();
122      }
123      public void SetInstanceData(ISymbolicExpressionTree tree, List<int> selectedSubVectorNodes, int vectorLength) {
124        this.tree = tree;
125        this.selectedSubVectorNodes = selectedSubVectorNodes;
126        Encoding.Length = selectedSubVectorNodes.Count * 2;
127        Encoding.Bounds = new IntMatrix(new int[,] { { 0, vectorLength + 1 } });
128        cache.Clear();
129      }
130    }
131
132    [Item("SubVectorGradientMutator", "")]
133    [StorableType("DC5EC7CE-AD51-4655-8F75-28601345B4C7")]
134    public abstract class SubVectorGradientMutator : BoundedIntegerVectorManipulator {
135
136      [Storable]
137      private readonly SubVectorOptimizationProblem problem;
138
139      protected SubVectorGradientMutator(SubVectorOptimizationProblem problem) {
140        this.problem = problem;
141      }
142      protected SubVectorGradientMutator(SubVectorGradientMutator original, Cloner cloner)
143        : base(original, cloner) {
144        this.problem = cloner.Clone(original.problem);
145      }
146
147      [StorableConstructor]
148      protected SubVectorGradientMutator(StorableConstructorFlag _) : base(_) {
149      }
150      [StorableHook(HookType.AfterDeserialization)]
151      private void AfterDeserialization() {
152      }
153
154      public double FivePointStencil(IntegerVector position, int dim, IntMatrix bounds, int h = 1) {
155        double f(int i) {
156          var modified = new IntegerVector(position);
157          modified[dim] = FloorFeasible(bounds[dim % bounds.Rows, 0], bounds[dim % bounds.Rows, 1], 1, i);
158          return problem.Evaluate(modified);
159        }
160
161        int x = position[dim];
162        var slope = (
163            + 1 * f(x - 2*h)
164            - 8 * f(x - 1*h)
165            + 8 * f(x + 1*h)
166            - 1 * f(x + 2*h)
167          ) / 12;
168
169        return slope;
170      }
171
172      public double[] CalculateGradient(IntegerVector position, IntMatrix bounds, int h = 1) {
173        return Enumerable.Range(0, position.Length)
174          .Select((x, dim) => FivePointStencil(position, dim, bounds, h)).ToArray();
175      }
176    }
177
178    [Item("GuidedDirectionManipulator", "")]
179    [StorableType("8781F827-BB46-4041-AAC4-25E76C5EF1F5")]
180    public class GuidedDirectionManipulator : SubVectorGradientMutator {
181
182      [StorableType("AED631BC-C1A3-4408-AA39-18A81018E159")]
183      public enum MutationType {
184        SinglePosition,
185        AllPosition
186      }
187
188      public IFixedValueParameter<EnumValue<MutationType>> MutationTypeParameter {
189        get { return (IFixedValueParameter<EnumValue<MutationType>>)Parameters["MutationType"]; }
190      }
191
192      public GuidedDirectionManipulator(SubVectorOptimizationProblem problem)
193      : base (problem) {
194        Parameters.Add(new FixedValueParameter<EnumValue<MutationType>>("MutationType", new EnumValue<MutationType>(MutationType.AllPosition)));
195      }
196
197      protected GuidedDirectionManipulator(GuidedDirectionManipulator original, Cloner cloner)
198        : base(original, cloner) {
199      }
200      public override IDeepCloneable Clone(Cloner cloner) {
201        return new GuidedDirectionManipulator(this, cloner);
202      }
203
204      [StorableConstructor]
205      protected GuidedDirectionManipulator(StorableConstructorFlag _) : base(_) {
206      }
207      [StorableHook(HookType.AfterDeserialization)]
208      private void AfterDeserialization() {
209      }
210
211      protected override void ManipulateBounded(IRandom random, IntegerVector integerVector, IntMatrix bounds) {
212        var mutationType = MutationTypeParameter.Value.Value;
213
214        if (mutationType == MutationType.AllPosition) {
215          var gradient = CalculateGradient(integerVector, bounds);
216          var limitedBounds = LimitBounds(bounds, integerVector, gradient);
217          UniformSomePositionsManipulator.Apply(random, integerVector, limitedBounds, probability: 1.0);
218        } else if(mutationType == MutationType.SinglePosition) {
219          int dim = random.Next(integerVector.Length);
220          var gradient = Enumerable.Repeat(0.0, integerVector.Length).ToArray();
221          gradient[dim] = FivePointStencil(integerVector, dim, bounds);
222          var limitedBounds = LimitBounds(bounds, integerVector, gradient);
223          UniformOnePositionManipulator.Manipulate(random, integerVector, limitedBounds, dim);
224        }
225      }
226
227      private static IntMatrix LimitBounds(IntMatrix bounds, IntegerVector position, double[] gradient) {
228        var limitedBounds = new IntMatrix(gradient.Length, 2);
229        for (int i = 0; i < gradient.Length; i++) {
230          int min = bounds[i % bounds.Rows, 0], max = bounds[i % bounds.Rows, 1];
231          int lower = gradient[i] < 0 ? position[i] - 1 : min; // exclude current
232          int upper = gradient[i] > 0 ? position[i] + 1 : max; // exclude current
233          limitedBounds[i, 0] = RoundFeasible(min, max, 1, lower);
234          limitedBounds[i, 1] = RoundFeasible(min, max, 1, upper);
235        }
236        return limitedBounds;
237      }
238    }
239
240    [Item("GuidedDirectionManipulator", "")]
241    [StorableType("3034E82F-FE7B-4723-90E6-887AE82BB86D")]
242    public class GuidedRangeManipulator : SubVectorGradientMutator {
243
244      [StorableType("560E2F2A-2B34-48CC-B747-DE82119DA652")]
245      public enum MutationType {
246        SinglePosition,
247        AllPosition
248      }
249
250      public IFixedValueParameter<EnumValue<MutationType>> MutationTypeParameter {
251        get { return (IFixedValueParameter<EnumValue<MutationType>>)Parameters["MutationType"]; }
252      }
253      public IFixedValueParameter<DoubleRange> StepSizeParameter {
254        get { return (IFixedValueParameter<DoubleRange>)Parameters["StepSize"]; }
255      }
256      public IFixedValueParameter<IntValue> RangeParameter {
257        get { return (IFixedValueParameter<IntValue>)Parameters["Range"]; }
258      }
259
260      public GuidedRangeManipulator(SubVectorOptimizationProblem problem)
261      : base(problem) {
262        Parameters.Add(new FixedValueParameter<EnumValue<MutationType>>("MutationType", new EnumValue<MutationType>(MutationType.AllPosition)));
263        Parameters.Add(new FixedValueParameter<DoubleRange>("StepSize", new DoubleRange(0.001, 1000.0)));
264        Parameters.Add(new FixedValueParameter<IntValue>("Range", new IntValue(10)));
265      }
266
267      protected GuidedRangeManipulator(GuidedRangeManipulator original, Cloner cloner)
268        : base(original, cloner) {
269      }
270      public override IDeepCloneable Clone(Cloner cloner) {
271        return new GuidedRangeManipulator(this, cloner);
272      }
273
274      [StorableConstructor]
275      protected GuidedRangeManipulator(StorableConstructorFlag _) : base(_) {
276      }
277      [StorableHook(HookType.AfterDeserialization)]
278      private void AfterDeserialization() {
279      }
280
281      protected override void ManipulateBounded(IRandom random, IntegerVector integerVector, IntMatrix bounds) {
282        var mutationType = MutationTypeParameter.Value.Value;
283        var stepSizeRange = StepSizeParameter.Value;
284        var range = RangeParameter.Value.Value;
285
286        var stepSize = LogUniformRandom(stepSizeRange, random);
287
288        if (mutationType == MutationType.AllPosition) {
289          var gradient = CalculateGradient(integerVector, bounds);
290          var limitedBounds = LimitBounds(bounds, integerVector, gradient, stepSize, range);
291          UniformSomePositionsManipulator.Apply(random, integerVector, limitedBounds, probability: 1.0);
292        } else if (mutationType == MutationType.SinglePosition) {
293          int dim = random.Next(integerVector.Length);
294          var gradient = Enumerable.Repeat(0.0, integerVector.Length).ToArray();
295          gradient[dim] = FivePointStencil(integerVector, dim, bounds);
296          var limitedBounds = LimitBounds(bounds, integerVector, gradient, stepSize, range);
297          UniformOnePositionManipulator.Manipulate(random, integerVector, limitedBounds, dim);
298        }
299      }
300
301      private static double LogUniformRandom(DoubleRange range, IRandom random) {
302        double logStart = Math.Log(range.Start);
303        double logEnd = Math.Log(range.End);
304        double logResult = logStart + random.NextDouble() * (logEnd - logStart);
305        return Math.Exp(logResult);
306      }
307
308      private static IntMatrix LimitBounds(IntMatrix bounds, IntegerVector position, double[] gradient, double stepSize, int range) {
309        var limitedBounds = new IntMatrix(gradient.Length, 2);
310        for (int i = 0; i < gradient.Length; i++) {
311          int min = bounds[i % bounds.Rows, 0], max = bounds[i % bounds.Rows, 1];
312          var newPoint = position[i] - gradient[i] * stepSize;
313          var lower = newPoint - range / 2.0;
314          var upper = newPoint + range / 2.0;
315          limitedBounds[i, 0] = RoundFeasible(min, max, 1, lower);
316          limitedBounds[i, 1] = RoundFeasible(min, max, 1, upper);
317        }
318        return limitedBounds;
319      }
320    }
321
322    #region Parameter Properties
323    public OptionalConstrainedValueParameter<IAlgorithm> NestedOptimizerParameter {
324      get { return (OptionalConstrainedValueParameter<IAlgorithm>)Parameters["NestedOptimizer"]; }
325    }
326
327    public IFixedValueParameter<PercentValue> PercentOptimizedSubVectorNodesParameter {
328      get { return (IFixedValueParameter<PercentValue>)Parameters["PercentOptimizedSubVectorNodes"]; }
329    }
330    #endregion
331
332    #region Properties
333    public IOptimizer NestedOptimizer {
334      get { return NestedOptimizerParameter.Value; }
335    }
336
337    public PercentValue PercentOptimizedSubVectorNodes {
338      get { return PercentOptimizedSubVectorNodesParameter.Value; }
339    }
340    #endregion
341
342    public NestedOptimizerSubVectorImprovementManipulator() : base() {
343      var problem = new SubVectorOptimizationProblem();
344
345      #region Create nested Algorithms
346      var rs = new RandomSearchAlgorithm() {
347        Problem = problem,
348        BatchSize = 100,
349        MaximumEvaluatedSolutions = 1000
350      };
351
352      var es = new EvolutionStrategy() {
353        Problem = problem,
354        PlusSelection = new BoolValue(true),
355        PopulationSize = new IntValue(10),
356        Children = new IntValue(10),
357        MaximumGenerations = new IntValue(100)
358      };
359      es.Mutator = es.MutatorParameter.ValidValues.OfType<UniformSomePositionsManipulator>().Single();
360
361      var gdes = new EvolutionStrategy() {
362        Problem = problem,
363        PlusSelection = new BoolValue(true),
364        PopulationSize = new IntValue(10),
365        Children = new IntValue(10),
366        MaximumGenerations = new IntValue(100)
367      };
368      gdes.Name = "Guided Direction " + gdes.Name;
369      var gdMutator = new GuidedDirectionManipulator(problem);
370      problem.Encoding.ConfigureOperator(gdMutator);
371      gdes.MutatorParameter.ValidValues.Add(gdMutator);
372      gdes.Mutator = gdMutator;
373
374      var gres = new EvolutionStrategy() {
375        Problem = problem,
376        PlusSelection = new BoolValue(true),
377        PopulationSize = new IntValue(10),
378        Children = new IntValue(10),
379        MaximumGenerations = new IntValue(100)
380      };
381      gres.Name = "Guided Range " + gres.Name;
382      var grMutator = new GuidedRangeManipulator(problem);
383      problem.Encoding.ConfigureOperator(grMutator);
384      gres.MutatorParameter.ValidValues.Add(grMutator);
385      gres.Mutator = grMutator;
386
387      var ga = new GeneticAlgorithm() {
388        Problem = problem,
389        PopulationSize = new IntValue(10),
390        MutationProbability = new PercentValue(0.1),
391        MaximumGenerations = new IntValue(100)
392      };
393      ga.Selector = ga.SelectorParameter.ValidValues.OfType<TournamentSelector>().Single();
394      ga.Crossover = ga.CrossoverParameter.ValidValues.OfType<RoundedBlendAlphaBetaCrossover>().Single();
395      ga.Mutator = ga.MutatorParameter.ValidValues.OfType<UniformOnePositionManipulator>().Single();
396
397      var osga = new OffspringSelectionGeneticAlgorithm() {
398        Problem = problem,
399        PopulationSize = new IntValue(10),
400        ComparisonFactorLowerBound = new DoubleValue(1.0),
401        ComparisonFactorUpperBound = new DoubleValue(1.0),
402        MutationProbability = new PercentValue(0.1),
403        MaximumGenerations = new IntValue(100),
404        MaximumEvaluatedSolutions = new IntValue(1000)
405      };
406      osga.Selector = osga.SelectorParameter.ValidValues.OfType<TournamentSelector>().Single();
407      osga.Crossover = osga.CrossoverParameter.ValidValues.OfType<RoundedBlendAlphaBetaCrossover>().Single();
408      osga.Mutator = osga.MutatorParameter.ValidValues.OfType<UniformOnePositionManipulator>().Single();
409      #endregion
410
411      var optimizers = new ItemSet<IAlgorithm>() { rs, es, gdes, gres, ga, osga };
412
413      Parameters.Add(new OptionalConstrainedValueParameter<IAlgorithm>("NestedOptimizer", optimizers, rs));
414      Parameters.Add(new FixedValueParameter<PercentValue>("PercentOptimizedSubVectorNodes", new PercentValue(1.0)));
415    }
416
417    private NestedOptimizerSubVectorImprovementManipulator(NestedOptimizerSubVectorImprovementManipulator<T> original, Cloner cloner) : base(original, cloner) { }
418
419    public override IDeepCloneable Clone(Cloner cloner) {
420      return new NestedOptimizerSubVectorImprovementManipulator<T>(this, cloner);
421    }
422
423    [StorableConstructor]
424    private NestedOptimizerSubVectorImprovementManipulator(StorableConstructorFlag _) : base(_) { }
425    [StorableHook(HookType.AfterDeserialization)]
426    private void AfterDeserialization() {
427      if (Parameters.TryGetValue("NestedOptimizer", out var param) && param is ConstrainedValueParameter<IAlgorithm> constrainedParam) {
428        Parameters.Remove("NestedOptimizer");
429        Parameters.Add(new OptionalConstrainedValueParameter<IAlgorithm>("NestedOptimizer",
430          new ItemSet<IAlgorithm>(constrainedParam.ValidValues), constrainedParam.Value)
431        );
432      }
433    }
434
435    public override void Manipulate(IRandom random, ISymbolicExpressionTree symbolicExpressionTree) {
436      if (NestedOptimizer == null)
437        return;
438     
439      int vectorLengths = GetVectorLengths(ProblemDataParameter.ActualValue);
440     
441      var selectedSubVectorNodes = GetSelectedSubVectorNodes(symbolicExpressionTree, random);
442      if (selectedSubVectorNodes.Count == 0)
443        return;
444
445      var algorithm = (IAlgorithm)NestedOptimizer.Clone();
446      PrepareAlgorithm(algorithm, symbolicExpressionTree, selectedSubVectorNodes, vectorLengths);
447
448      algorithm.Start(CancellationToken);
449
450      //if (algorithm.ExecutionState != ExecutionState.Stopped)
451      //  return;
452
453      if (!algorithm.Results.ContainsKey(BestSolutionParameterName))
454        return;
455     
456      // use the latest best result
457      var solution = (IntegerVector)algorithm.Results[BestSolutionParameterName].Value;
458      UpdateFromVector(symbolicExpressionTree, selectedSubVectorNodes, solution, vectorLengths);
459    }
460
461    private void PrepareAlgorithm(IAlgorithm algorithm, ISymbolicExpressionTree symbolicExpressionTree, List<int> selectedSubVectorNodes, int vectorLengths) {
462      var problem = (SubVectorOptimizationProblem)algorithm.Problem;
463      problem.SetProblemData(EvaluatorParameter.ActualValue, ProblemDataParameter.ActualValue, GenerateRowsToEvaluate().ToList(), ExecutionContext);
464      problem.SetInstanceData(symbolicExpressionTree, selectedSubVectorNodes, vectorLengths);
465    }
466
467    private List<int> GetSelectedSubVectorNodes(ISymbolicExpressionTree symbolicExpressionTree, IRandom random) {
468      var subVectorNodes = GetSubVectorNodes(symbolicExpressionTree).ToList();
469
470      int numSelect = (int)Math.Round(subVectorNodes.Count * PercentOptimizedSubVectorNodes.Value);
471      var selectedSubVectorNodes = Enumerable.Range(0, subVectorNodes.Count).SampleRandomWithoutRepetition(random, numSelect);
472      return selectedSubVectorNodes.ToList();
473    }
474
475    private static int GetVectorLengths(T problemData) { // ToDo evaluate a tree to get vector length per node
476      var vectorLengths = problemData.Dataset.DoubleVectorVariables
477        .Select(v => problemData.Dataset.GetDoubleVectorValue(v, row: 0).Count)
478        .Distinct();
479      return vectorLengths.Single();
480    }
481
482    private static void UpdateFromVector(ISymbolicExpressionTree tree, IList<int> selectedNodes, IntegerVector solution, int vectorLength) {
483      var nodes = GetSubVectorNodes(tree).ToList();
484
485      int i = 0;
486      foreach (var nodeIdx in selectedNodes) {
487        var node = nodes[nodeIdx];
488        node.Offset = (double)solution[i++] / (vectorLength - 1); // round in case of float
489        node.Length = (double)solution[i++] / (vectorLength - 1); // round in case of float
490      }
491    }
492
493    private static IEnumerable<WindowedSymbolTreeNode> GetSubVectorNodes(ISymbolicExpressionTree tree) {
494      return ActualRoot(tree)
495        .IterateNodesBreadth()
496        .OfType<WindowedSymbolTreeNode>()
497        .Where(n => n.HasLocalParameters);
498    }
499    private static ISymbolicExpressionTreeNode ActualRoot(ISymbolicExpressionTree tree) {
500      return tree.Root.GetSubtree(0).GetSubtree(0);
501    }
502  }
503}
Note: See TracBrowser for help on using the repository browser.