Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2987_MOEAD_Algorithm/HeuristicLab.Algorithms.MOEAD/3.4/MOEADAlgorithmBase.cs @ 16649

Last change on this file since 16649 was 16649, checked in by bburlacu, 5 years ago

#2987: Prevent updating the Ideal and Nadir points with NaN or Infinity values. Simplify algorithm code (use arrays instead of lists). Add missing StorableType attributes. Add hypervolume analysis for the pareto fronts.

File size: 31.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 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 HEAL.Attic;
23using HeuristicLab.Analysis;
24using HeuristicLab.Common;
25using HeuristicLab.Core;
26using HeuristicLab.Data;
27using HeuristicLab.ExpressionGenerator;
28using HeuristicLab.Optimization;
29using HeuristicLab.Parameters;
30using HeuristicLab.Problems.DataAnalysis;
31using HeuristicLab.Problems.TestFunctions.MultiObjective;
32using HeuristicLab.Random;
33using System;
34using System.Collections.Generic;
35using System.Drawing;
36using System.Linq;
37using CancellationToken = System.Threading.CancellationToken;
38
39namespace HeuristicLab.Algorithms.MOEAD {
40  [Item("MOEADAlgorithmBase", "Base class for all MOEA/D algorithm variants.")]
41  [StorableType("E00BAC79-C6F9-42B6-8468-DEEC7FFCE804")]
42  public abstract class MOEADAlgorithmBase : BasicAlgorithm {
43    #region data members
44    [StorableType("C04DB21A-316F-4210-9CA7-A915BE8EBC96")]
45    protected enum NeighborType { NEIGHBOR, POPULATION }
46
47    [StorableType("FE35F480-E522-45E0-A229-99E61DA7B8BC")]
48    // TCHE = Chebyshev (Tchebyshev)
49    // PBI  = Penalty-based boundary intersection
50    // AGG  = Weighted sum
51    public enum FunctionType { TCHE, PBI, AGG }
52
53    [Storable]
54    protected double[] IdealPoint { get; set; }
55    [Storable]
56    protected double[] NadirPoint { get; set; } // potentially useful for objective normalization
57
58    [Storable]
59    protected double[][] lambda;
60
61    [Storable]
62    protected int[][] neighbourhood;
63
64    [Storable]
65    protected IMOEADSolution[] solutions;
66
67    [Storable]
68    protected FunctionType functionType;
69
70    [Storable]
71    protected IMOEADSolution[] population;
72
73    [Storable]
74    protected IMOEADSolution[] offspringPopulation;
75
76    [Storable]
77    protected IMOEADSolution[] jointPopulation;
78
79    [Storable]
80    protected int evaluatedSolutions;
81
82    [Storable]
83    protected ExecutionContext executionContext;
84
85    [Storable]
86    protected IScope globalScope;
87
88    [Storable]
89    protected ExecutionState previousExecutionState;
90
91    [Storable]
92    protected ExecutionState executionState;
93    #endregion
94
95    #region parameters
96    private const string SeedParameterName = "Seed";
97    private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
98    private const string PopulationSizeParameterName = "PopulationSize";
99    private const string ResultPopulationSizeParameterName = "ResultPopulationSize";
100    private const string CrossoverProbabilityParameterName = "CrossoverProbability";
101    private const string CrossoverParameterName = "Crossover";
102    private const string MutationProbabilityParameterName = "MutationProbability";
103    private const string MutatorParameterName = "Mutator";
104    private const string MaximumEvaluatedSolutionsParameterName = "MaximumEvaluatedSolutions";
105    private const string RandomParameterName = "Random";
106    private const string AnalyzerParameterName = "Analyzer";
107    // MOEA-D parameters
108    private const string NeighbourSizeParameterName = "NeighbourSize";
109    private const string NeighbourhoodSelectionProbabilityParameterName = "NeighbourhoodSelectionProbability";
110    private const string MaximumNumberOfReplacedSolutionsParameterName = "MaximumNumberOfReplacedSolutions";
111    private const string FunctionTypeParameterName = "FunctionType";
112    private const string NormalizeObjectivesParameterName = "NormalizeObjectives";
113
114    public IValueParameter<MultiAnalyzer> AnalyzerParameter {
115      get { return (ValueParameter<MultiAnalyzer>)Parameters[AnalyzerParameterName]; }
116    }
117
118    public IConstrainedValueParameter<StringValue> FunctionTypeParameter {
119      get { return (IConstrainedValueParameter<StringValue>)Parameters[FunctionTypeParameterName]; }
120    }
121    public IFixedValueParameter<IntValue> NeighbourSizeParameter {
122      get { return (IFixedValueParameter<IntValue>)Parameters[NeighbourSizeParameterName]; }
123    }
124    public IFixedValueParameter<BoolValue> NormalizeObjectivesParameter {
125      get { return (IFixedValueParameter<BoolValue>)Parameters[NormalizeObjectivesParameterName]; }
126    }
127    public IFixedValueParameter<IntValue> MaximumNumberOfReplacedSolutionsParameter {
128      get { return (IFixedValueParameter<IntValue>)Parameters[MaximumNumberOfReplacedSolutionsParameterName]; }
129    }
130    public IFixedValueParameter<DoubleValue> NeighbourhoodSelectionProbabilityParameter {
131      get { return (IFixedValueParameter<DoubleValue>)Parameters[NeighbourhoodSelectionProbabilityParameterName]; }
132    }
133    public IFixedValueParameter<IntValue> SeedParameter {
134      get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
135    }
136    public IFixedValueParameter<BoolValue> SetSeedRandomlyParameter {
137      get { return (IFixedValueParameter<BoolValue>)Parameters[SetSeedRandomlyParameterName]; }
138    }
139    private IValueParameter<IntValue> PopulationSizeParameter {
140      get { return (IValueParameter<IntValue>)Parameters[PopulationSizeParameterName]; }
141    }
142    private IValueParameter<IntValue> ResultPopulationSizeParameter {
143      get { return (IValueParameter<IntValue>)Parameters[ResultPopulationSizeParameterName]; }
144    }
145    public IValueParameter<PercentValue> CrossoverProbabilityParameter {
146      get { return (IValueParameter<PercentValue>)Parameters[CrossoverProbabilityParameterName]; }
147    }
148    public IConstrainedValueParameter<ICrossover> CrossoverParameter {
149      get { return (IConstrainedValueParameter<ICrossover>)Parameters[CrossoverParameterName]; }
150    }
151    public IValueParameter<PercentValue> MutationProbabilityParameter {
152      get { return (IValueParameter<PercentValue>)Parameters[MutationProbabilityParameterName]; }
153    }
154    public IConstrainedValueParameter<IManipulator> MutatorParameter {
155      get { return (IConstrainedValueParameter<IManipulator>)Parameters[MutatorParameterName]; }
156    }
157    public IValueParameter<IntValue> MaximumEvaluatedSolutionsParameter {
158      get { return (IValueParameter<IntValue>)Parameters[MaximumEvaluatedSolutionsParameterName]; }
159    }
160    public IValueParameter<IRandom> RandomParameter {
161      get { return (IValueParameter<IRandom>)Parameters[RandomParameterName]; }
162    }
163    #endregion
164
165    #region parameter properties
166    public new IMultiObjectiveHeuristicOptimizationProblem Problem {
167      get { return (IMultiObjectiveHeuristicOptimizationProblem)base.Problem; }
168    }
169    public int Seed {
170      get { return SeedParameter.Value.Value; }
171      set { SeedParameter.Value.Value = value; }
172    }
173    public bool SetSeedRandomly {
174      get { return SetSeedRandomlyParameter.Value.Value; }
175      set { SetSeedRandomlyParameter.Value.Value = value; }
176    }
177    public bool NormalizeObjectives {
178      get { return NormalizeObjectivesParameter.Value.Value; }
179      set { NormalizeObjectivesParameter.Value.Value = value; }
180    }
181    public IntValue PopulationSize {
182      get { return PopulationSizeParameter.Value; }
183      set { PopulationSizeParameter.Value = value; }
184    }
185    public IntValue ResultPopulationSize {
186      get { return ResultPopulationSizeParameter.Value; }
187      set { ResultPopulationSizeParameter.Value = value; }
188    }
189    public PercentValue CrossoverProbability {
190      get { return CrossoverProbabilityParameter.Value; }
191      set { CrossoverProbabilityParameter.Value = value; }
192    }
193    public ICrossover Crossover {
194      get { return CrossoverParameter.Value; }
195      set { CrossoverParameter.Value = value; }
196    }
197    public PercentValue MutationProbability {
198      get { return MutationProbabilityParameter.Value; }
199      set { MutationProbabilityParameter.Value = value; }
200    }
201    public IManipulator Mutator {
202      get { return MutatorParameter.Value; }
203      set { MutatorParameter.Value = value; }
204    }
205    public MultiAnalyzer Analyzer {
206      get { return AnalyzerParameter.Value; }
207      set { AnalyzerParameter.Value = value; }
208    }
209    public IntValue MaximumEvaluatedSolutions {
210      get { return MaximumEvaluatedSolutionsParameter.Value; }
211      set { MaximumEvaluatedSolutionsParameter.Value = value; }
212    }
213    public int NeighbourSize {
214      get { return NeighbourSizeParameter.Value.Value; }
215      set { NeighbourSizeParameter.Value.Value = value; }
216    }
217    public int MaximumNumberOfReplacedSolutions {
218      get { return MaximumNumberOfReplacedSolutionsParameter.Value.Value; }
219      set { MaximumNumberOfReplacedSolutionsParameter.Value.Value = value; }
220    }
221    public double NeighbourhoodSelectionProbability {
222      get { return NeighbourhoodSelectionProbabilityParameter.Value.Value; }
223      set { NeighbourhoodSelectionProbabilityParameter.Value.Value = value; }
224    }
225    #endregion
226
227    #region constructors
228    public MOEADAlgorithmBase() {
229      Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName, "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
230      Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName, "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
231      Parameters.Add(new ValueParameter<IntValue>(PopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100)));
232      Parameters.Add(new ValueParameter<IntValue>(ResultPopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100)));
233      Parameters.Add(new ValueParameter<PercentValue>(CrossoverProbabilityParameterName, "The probability that the crossover operator is applied.", new PercentValue(0.9)));
234      Parameters.Add(new ConstrainedValueParameter<ICrossover>(CrossoverParameterName, "The operator used to cross solutions."));
235      Parameters.Add(new ValueParameter<PercentValue>(MutationProbabilityParameterName, "The probability that the mutation operator is applied on a solution.", new PercentValue(0.25)));
236      Parameters.Add(new ConstrainedValueParameter<IManipulator>(MutatorParameterName, "The operator used to mutate solutions."));
237      Parameters.Add(new ValueParameter<MultiAnalyzer>("Analyzer", "The operator used to analyze each generation.", new MultiAnalyzer()));
238      Parameters.Add(new ValueParameter<IntValue>(MaximumEvaluatedSolutionsParameterName, "The maximum number of evaluated solutions (approximately).", new IntValue(100_000)));
239      Parameters.Add(new ValueParameter<IRandom>(RandomParameterName, new FastRandom()));
240      Parameters.Add(new FixedValueParameter<IntValue>(NeighbourSizeParameterName, new IntValue(20)));
241      Parameters.Add(new FixedValueParameter<IntValue>(MaximumNumberOfReplacedSolutionsParameterName, new IntValue(2)));
242      Parameters.Add(new FixedValueParameter<DoubleValue>(NeighbourhoodSelectionProbabilityParameterName, new DoubleValue(0.1)));
243      Parameters.Add(new FixedValueParameter<BoolValue>(NormalizeObjectivesParameterName, new BoolValue(true)));
244
245      var functionTypeParameter = new ConstrainedValueParameter<StringValue>(FunctionTypeParameterName);
246      foreach (var s in new[] { "Chebyshev", "PBI", "Weighted Sum" }) {
247        functionTypeParameter.ValidValues.Add(new StringValue(s));
248      }
249      Parameters.Add(functionTypeParameter);
250    }
251
252    protected MOEADAlgorithmBase(MOEADAlgorithmBase original, Cloner cloner) : base(original, cloner) {
253      functionType = original.functionType;
254      evaluatedSolutions = original.evaluatedSolutions;
255      previousExecutionState = original.previousExecutionState;
256
257      if (original.IdealPoint != null) {
258        IdealPoint = (double[])original.IdealPoint.Clone();
259      }
260
261      if (original.NadirPoint != null) {
262        NadirPoint = (double[])original.NadirPoint.Clone();
263      }
264
265      if (original.lambda != null) {
266        lambda = (double[][])original.lambda.Clone();
267      }
268
269      if (original.neighbourhood != null) {
270        neighbourhood = (int[][])original.neighbourhood.Clone();
271      }
272
273      if (original.solutions != null) {
274        solutions = original.solutions.Select(cloner.Clone).ToArray();
275      }
276
277      if (original.population != null) {
278        population = original.population.Select(cloner.Clone).ToArray();
279      }
280
281      if (original.offspringPopulation != null) {
282        offspringPopulation = original.offspringPopulation.Select(cloner.Clone).ToArray();
283      }
284
285      if (original.jointPopulation != null) {
286        jointPopulation = original.jointPopulation.Select(x => cloner.Clone(x)).ToArray();
287      }
288
289      if (original.executionContext != null) {
290        executionContext = cloner.Clone(original.executionContext);
291      }
292
293      if (original.globalScope != null) {
294        globalScope = cloner.Clone(original.globalScope);
295      }
296    }
297
298
299    [StorableHook(HookType.AfterDeserialization)]
300    private void AfterDeserialization() {
301      if (!Parameters.ContainsKey(NormalizeObjectivesParameterName)) {
302        Parameters.Add(new FixedValueParameter<BoolValue>(NormalizeObjectivesParameterName, new BoolValue(true)));
303      }
304    }
305
306    [StorableConstructor]
307    protected MOEADAlgorithmBase(StorableConstructorFlag deserializing) : base(deserializing) { }
308    #endregion
309
310    private void InitializePopulation(ExecutionContext executionContext, CancellationToken cancellationToken, IRandom random, bool[] maximization) {
311      var creator = Problem.SolutionCreator;
312      var evaluator = Problem.Evaluator;
313
314      var dimensions = maximization.Length;
315      var populationSize = PopulationSize.Value;
316      population = new IMOEADSolution[populationSize];
317
318      var parentScope = executionContext.Scope;
319      // first, create all individuals
320      for (int i = 0; i < populationSize; ++i) {
321        var childScope = new Scope(i.ToString()) { Parent = parentScope };
322        ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(creator, childScope));
323        parentScope.SubScopes.Add(childScope);
324      }
325
326      // then, evaluate them and update qualities
327      for (int i = 0; i < populationSize; ++i) {
328        var childScope = parentScope.SubScopes[i];
329        ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(evaluator, childScope));
330
331        var qualities = (DoubleArray)childScope.Variables["Qualities"].Value;
332        var solution = new MOEADSolution(childScope, dimensions, 0);
333        for (int j = 0; j < dimensions; ++j) {
334          solution.Qualities[j] = maximization[j] ? 1 - qualities[j] : qualities[j];
335        }
336        population[i] = solution;
337      }
338    }
339
340    protected void InitializeAlgorithm(CancellationToken cancellationToken) {
341      var rand = RandomParameter.Value;
342      if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
343      rand.Reset(Seed);
344
345      bool[] maximization = ((BoolArray)Problem.MaximizationParameter.ActualValue).CloneAsArray();
346      var dimensions = maximization.Length;
347
348      var populationSize = PopulationSize.Value;
349
350      InitializePopulation(executionContext, cancellationToken, rand, maximization);
351      InitializeUniformWeights(rand, populationSize, dimensions);
352      InitializeNeighbourHood(lambda, populationSize, NeighbourSize);
353
354      //IdealPoint = Enumerable.Repeat(double.MaxValue, dimensions).ToArray();
355      IdealPoint = new double[dimensions];
356      IdealPoint.UpdateIdeal(population);
357
358      NadirPoint = Enumerable.Repeat(double.MinValue, dimensions).ToArray();
359      //NadirPoint = new double[dimensions];
360      NadirPoint.UpdateNadir(population);
361
362      var functionTypeString = FunctionTypeParameter.Value.Value;
363      switch (functionTypeString) {
364        case "Chebyshev":
365          functionType = FunctionType.TCHE;
366          break;
367        case "PBI":
368          functionType = FunctionType.PBI;
369          break;
370        case "Weighted Sum":
371          functionType = FunctionType.AGG;
372          break;
373      }
374
375      evaluatedSolutions = populationSize;
376    }
377
378    protected override void Initialize(CancellationToken cancellationToken) {
379      globalScope = new Scope("Global Scope");
380      executionContext = new ExecutionContext(null, this, globalScope);
381
382      // set the execution context for parameters to allow lookup
383      foreach (var parameter in Problem.Parameters.OfType<IValueParameter>()) {
384        // we need all of these in order for the wiring of the operators to work
385        globalScope.Variables.Add(new Variable(parameter.Name, parameter.Value));
386      }
387      globalScope.Variables.Add(new Variable("Results", Results)); // make results available as a parameter for analyzers etc.
388
389      base.Initialize(cancellationToken);
390    }
391
392    public override bool SupportsPause => true;
393
394    protected void InitializeUniformWeights(IRandom random, int populationSize, int dimensions) {
395      lambda = Enumerable.Range(0, populationSize).Select(_ => GenerateSample(random, dimensions)).ToArray();
396    }
397
398    // implements random number generation from https://en.wikipedia.org/wiki/Dirichlet_distribution#Random_number_generation
399    private double[] GenerateSample(IRandom random, int dim) {
400      var sum = 0d;
401      var sample = new double[dim];
402      for (int i = 0; i < dim; ++i) {
403        sample[i] = GammaDistributedRandom.NextDouble(random, 1, 1);
404        sum += sample[i];
405      }
406      for (int i = 0; i < dim; ++i) {
407        sample[i] /= sum;
408      }
409      return sample;
410    }
411
412    protected void InitializeNeighbourHood(double[][] lambda, int populationSize, int neighbourSize) {
413      neighbourhood = new int[populationSize][];
414
415      var x = new double[populationSize];
416      var idx = new int[populationSize];
417
418      for (int i = 0; i < populationSize; ++i) {
419        for (int j = 0; j < populationSize; ++j) {
420          x[j] = MOEADUtil.EuclideanDistance(lambda[i], lambda[j]);
421          idx[j] = j;
422        }
423
424        MOEADUtil.MinFastSort(x, idx, populationSize, neighbourSize);
425        neighbourhood[i] = (int[])idx.Clone();
426      }
427    }
428
429    protected NeighborType ChooseNeighborType(IRandom random, double neighbourhoodSelectionProbability) {
430      return random.NextDouble() < neighbourhoodSelectionProbability
431        ? NeighborType.NEIGHBOR
432        : NeighborType.POPULATION;
433    }
434
435    protected IList<IMOEADSolution> ParentSelection(IRandom random, int subProblemId, NeighborType neighbourType) {
436      List<int> matingPool = MatingSelection(random, subProblemId, 2, neighbourType);
437
438      var parents = new IMOEADSolution[3];
439
440      parents[0] = population[matingPool[0]];
441      parents[1] = population[matingPool[1]];
442      parents[2] = population[subProblemId];
443
444      return parents;
445    }
446
447    protected List<int> MatingSelection(IRandom random, int subproblemId, int numberOfSolutionsToSelect, NeighborType neighbourType) {
448      int populationSize = PopulationSize.Value;
449
450      var listOfSolutions = new List<int>(numberOfSolutionsToSelect);
451
452      int neighbourSize = neighbourhood[subproblemId].Length;
453      while (listOfSolutions.Count < numberOfSolutionsToSelect) {
454        var selectedSolution = neighbourType == NeighborType.NEIGHBOR
455          ? neighbourhood[subproblemId][random.Next(neighbourSize)]
456          : random.Next(populationSize);
457
458        bool flag = true;
459        foreach (int individualId in listOfSolutions) {
460          if (individualId == selectedSolution) {
461            flag = false;
462            break;
463          }
464        }
465
466        if (flag) {
467          listOfSolutions.Add(selectedSolution);
468        }
469      }
470
471      return listOfSolutions;
472    }
473
474    protected void UpdateNeighbourHood(IRandom random, IMOEADSolution individual, int subProblemId, NeighborType neighbourType) {
475      int replacedSolutions = 0;
476      int size = neighbourType == NeighborType.NEIGHBOR ? NeighbourSize : population.Length;
477
478      foreach (var i in Enumerable.Range(0, size).Shuffle(random)) {
479        int k = neighbourType == NeighborType.NEIGHBOR
480          ? neighbourhood[subProblemId][i]
481          : i;
482
483        double f1 = CalculateFitness(population[k].Qualities, lambda[k]);
484        double f2 = CalculateFitness(individual.Qualities, lambda[k]);
485
486        if (f2 < f1) {
487          population[k] = (IMOEADSolution)individual.Clone();
488          replacedSolutions++;
489        }
490
491        if (replacedSolutions >= MaximumNumberOfReplacedSolutions) {
492          return;
493        }
494      }
495    }
496
497    private double CalculateFitness(double[] qualities, double[] lambda) {
498      int dim = qualities.Length;
499      switch (functionType) {
500        case FunctionType.TCHE: {
501            double maxFun = double.MinValue;
502
503            for (int n = 0; n < dim; n++) {
504              var diff = qualities[n] - IdealPoint[n];
505              if (NormalizeObjectives) {
506                diff /= NadirPoint[n] - IdealPoint[n];
507              }
508              var l = lambda[n].IsAlmost(0) ? 1e-4 : lambda[n];
509              var feval = l * Math.Abs(diff);
510
511              if (feval > maxFun) {
512                maxFun = feval;
513              }
514            }
515
516            return maxFun;
517          }
518        case FunctionType.AGG: {
519            double sum = 0.0;
520            for (int n = 0; n < dim; n++) {
521              sum += lambda[n] * qualities[n];
522            }
523            return sum;
524          }
525        case FunctionType.PBI: {
526            double d1, d2, nl;
527            double theta = 5.0;
528            int dimensions = dim;
529
530            d1 = d2 = nl = 0.0;
531
532            for (int i = 0; i < dimensions; i++) {
533              d1 += (qualities[i] - IdealPoint[i]) * lambda[i];
534              nl += Math.Pow(lambda[i], 2.0);
535            }
536            nl = Math.Sqrt(nl);
537            d1 = Math.Abs(d1) / nl;
538
539            for (int i = 0; i < dimensions; i++) {
540              d2 += Math.Pow((qualities[i] - IdealPoint[i]) - d1 * (lambda[i] / nl), 2.0);
541            }
542            d2 = Math.Sqrt(d2);
543            return d1 + theta * d2;
544          }
545        default: {
546            throw new ArgumentException($"Unknown function type: {functionType}");
547          }
548      }
549    }
550
551    public IList<IMOEADSolution> GetResult(IRandom random) {
552      var populationSize = PopulationSize.Value;
553      var resultPopulationSize = ResultPopulationSize.Value;
554
555      if (populationSize > resultPopulationSize) {
556        return MOEADUtil.GetSubsetOfEvenlyDistributedSolutions(random, population, resultPopulationSize);
557      } else {
558        return population;
559      }
560    }
561
562    protected void UpdateParetoFronts() {
563      var qualities = population.Select(x => Enumerable.Range(0, NadirPoint.Length).Select(i => x.Qualities[i] / NadirPoint[i]).ToArray()).ToArray();
564      var maximization = Enumerable.Repeat(false, IdealPoint.Length).ToArray(); // MOEA/D minimizes everything internally\
565      var pf = DominationCalculator<IMOEADSolution>.CalculateBestParetoFront(population, qualities, maximization);
566
567      var n = (int)EnumerableExtensions.BinomialCoefficient(IdealPoint.Length, 2);
568      var hypervolumes = new DoubleMatrix(n == 1 ? 1 : n + 1, 2) { ColumnNames = new[] { "PF hypervolume", "PF size" } };
569      hypervolumes[0, 0] = Hypervolume.Calculate(pf.Select(x => x.Item2), Enumerable.Repeat(1d, NadirPoint.Length).ToArray(), maximization);
570      hypervolumes[0, 1] = pf.Count;
571      var elementNames = new List<string>() { "Pareto Front" };
572
573      ResultCollection results;
574      if (Results.ContainsKey("Hypervolume Analysis")) {
575        results = (ResultCollection)Results["Hypervolume Analysis"].Value;
576      } else {
577        results = new ResultCollection();
578        Results.AddOrUpdateResult("Hypervolume Analysis", results);
579      }
580
581      ScatterPlot sp;
582      if (IdealPoint.Length == 2) {
583        var points = pf.Select(x => new Point2D<double>(x.Item2[0], x.Item2[1]));
584        var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error);
585        if (error != OnlineCalculatorError.None) { r = double.NaN; }
586        var resultName = "Pareto Front Analysis ";
587        if (!results.ContainsKey(resultName)) {
588          sp = new ScatterPlot() {
589            VisualProperties = {
590              XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d,
591              YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d
592            }
593          };
594          sp.Rows.Add(new ScatterPlotDataRow(resultName, "", points) { VisualProperties = { PointSize = 8 } });
595          results.AddOrUpdateResult(resultName, sp);
596        } else {
597          sp = (ScatterPlot)results[resultName].Value;
598          sp.Rows[resultName].Points.Replace(points);
599        }
600        sp.Name = $"Dimensions [0, 1], correlation: {r.ToString("N2")}";
601      } else if (IdealPoint.Length > 2) {
602        var indices = Enumerable.Range(0, IdealPoint.Length).ToArray();
603        var visualProperties = new ScatterPlotDataRowVisualProperties { PointSize = 8, Color = Color.LightGray };
604        var combinations = indices.Combinations(2).ToArray();
605        var maximization2d = new[] { false, false };
606        var solutions2d = pf.Select(x => x.Item1).ToArray();
607        for (int i = 0; i < combinations.Length; ++i) {
608          var c = combinations[i].ToArray();
609
610          // calculate the hypervolume in the 2d coordinate space
611          var reference2d = new[] { 1d, 1d };
612          var qualities2d = pf.Select(x => new[] { x.Item2[c[0]], x.Item2[c[1]] }).ToArray();
613          var pf2d = DominationCalculator<IMOEADSolution>.CalculateBestParetoFront(solutions2d, qualities2d, maximization2d);
614
615          hypervolumes[i + 1, 0] = pf2d.Count > 0 ? Hypervolume.Calculate(pf2d.Select(x => x.Item2), reference2d, maximization2d) : 0d;
616          hypervolumes[i + 1, 1] = pf2d.Count;
617
618          var resultName = $"Pareto Front Analysis [{c[0]}, {c[1]}]";
619          elementNames.Add(resultName);
620
621          var points = pf.Select(x => new Point2D<double>(x.Item2[c[0]], x.Item2[c[1]]));
622          var pf2dPoints = pf2d.Select(x => new Point2D<double>(x.Item2[0], x.Item2[1]));
623
624          if (!results.ContainsKey(resultName)) {
625            sp = new ScatterPlot() {
626              VisualProperties = {
627                XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d,
628                YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d
629              }
630            };
631            sp.Rows.Add(new ScatterPlotDataRow("Pareto Front", "", points) { VisualProperties = visualProperties });
632            sp.Rows.Add(new ScatterPlotDataRow($"Pareto Front [{c[0]}, {c[1]}]", "", pf2dPoints) { VisualProperties = { PointSize = 10, Color = Color.OrangeRed } });
633            results.AddOrUpdateResult(resultName, sp);
634          } else {
635            sp = (ScatterPlot)results[resultName].Value;
636            sp.Rows["Pareto Front"].Points.Replace(points);
637            sp.Rows[$"Pareto Front [{c[0]}, {c[1]}]"].Points.Replace(pf2dPoints);
638          }
639          var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error);
640          if (error != OnlineCalculatorError.None) { r = double.NaN; }
641          sp.Name = $"Pareto Front [{c[0]}, {c[1]}], correlation: {r.ToString("N2")}";
642        }
643      }
644      hypervolumes.RowNames = elementNames;
645      results.AddOrUpdateResult("Hypervolumes", hypervolumes);
646    }
647
648    #region operator wiring and events
649    protected void ExecuteOperation(ExecutionContext executionContext, CancellationToken cancellationToken, IOperation operation) {
650      Stack<IOperation> executionStack = new Stack<IOperation>();
651      executionStack.Push(operation);
652      while (executionStack.Count > 0) {
653        cancellationToken.ThrowIfCancellationRequested();
654        IOperation next = executionStack.Pop();
655        if (next is OperationCollection) {
656          OperationCollection coll = (OperationCollection)next;
657          for (int i = coll.Count - 1; i >= 0; i--)
658            if (coll[i] != null) executionStack.Push(coll[i]);
659        } else if (next is IAtomicOperation) {
660          IAtomicOperation op = (IAtomicOperation)next;
661          next = op.Operator.Execute((IExecutionContext)op, cancellationToken);
662          if (next != null) executionStack.Push(next);
663        }
664      }
665    }
666
667    private void UpdateAnalyzers() {
668      Analyzer.Operators.Clear();
669      if (Problem != null) {
670        foreach (IAnalyzer analyzer in Problem.Operators.OfType<IAnalyzer>()) {
671          foreach (IScopeTreeLookupParameter param in analyzer.Parameters.OfType<IScopeTreeLookupParameter>())
672            param.Depth = 1;
673          Analyzer.Operators.Add(analyzer, analyzer.EnabledByDefault);
674        }
675      }
676    }
677
678    private void UpdateCrossovers() {
679      ICrossover oldCrossover = CrossoverParameter.Value;
680      CrossoverParameter.ValidValues.Clear();
681      ICrossover defaultCrossover = Problem.Operators.OfType<ICrossover>().FirstOrDefault();
682
683      foreach (ICrossover crossover in Problem.Operators.OfType<ICrossover>().OrderBy(x => x.Name))
684        CrossoverParameter.ValidValues.Add(crossover);
685
686      if (oldCrossover != null) {
687        ICrossover crossover = CrossoverParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldCrossover.GetType());
688        if (crossover != null) CrossoverParameter.Value = crossover;
689        else oldCrossover = null;
690      }
691      if (oldCrossover == null && defaultCrossover != null)
692        CrossoverParameter.Value = defaultCrossover;
693    }
694
695    private void UpdateMutators() {
696      IManipulator oldMutator = MutatorParameter.Value;
697      MutatorParameter.ValidValues.Clear();
698      IManipulator defaultMutator = Problem.Operators.OfType<IManipulator>().FirstOrDefault();
699
700      foreach (IManipulator mutator in Problem.Operators.OfType<IManipulator>().OrderBy(x => x.Name))
701        MutatorParameter.ValidValues.Add(mutator);
702
703      if (oldMutator != null) {
704        IManipulator mutator = MutatorParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldMutator.GetType());
705        if (mutator != null) MutatorParameter.Value = mutator;
706        else oldMutator = null;
707      }
708
709      if (oldMutator == null && defaultMutator != null)
710        MutatorParameter.Value = defaultMutator;
711    }
712
713    protected override void OnProblemChanged() {
714      UpdateCrossovers();
715      UpdateMutators();
716      UpdateAnalyzers();
717      base.OnProblemChanged();
718    }
719
720    protected override void OnExecutionStateChanged() {
721      previousExecutionState = executionState;
722      executionState = ExecutionState;
723      base.OnExecutionStateChanged();
724    }
725
726    public void ClearState() {
727      solutions = null;
728      population = null;
729      offspringPopulation = null;
730      jointPopulation = null;
731      lambda = null;
732      neighbourhood = null;
733      if (executionContext != null && executionContext.Scope != null) {
734        executionContext.Scope.SubScopes.Clear();
735      }
736    }
737
738    protected override void OnStopped() {
739      ClearState();
740      base.OnStopped();
741    }
742    #endregion
743  }
744}
Note: See TracBrowser for help on using the repository browser.