Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3055_SMSEMOA/HeuristicLab.Algorithms.SMSEMOA/3.4/SMSEMOAlgorithmBase.cs @ 17425

Last change on this file since 17425 was 17425, checked in by kyang, 4 years ago

#3055: Added the first version of SMS-EMOA

File size: 31.2 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
22// 03/02/2020
23// TODO LIST:                                                                     
24// 1. Dynamic reference point strategy                                               
25// 2. Normalized fitness value strategy, desibility function.                         
26// 3. HVC calculation should be definitely improved, at least in the 2D case.
27// 4. multiple point strategy when $\lambda>1$
28
29using HEAL.Attic;
30using HeuristicLab.Analysis;
31using HeuristicLab.Common;
32using HeuristicLab.Core;
33using HeuristicLab.Data;
34using HeuristicLab.ExpressionGenerator;
35using HeuristicLab.Optimization;
36using HeuristicLab.Parameters;
37using HeuristicLab.Problems.DataAnalysis;
38using HeuristicLab.Problems.TestFunctions.MultiObjective;
39using HeuristicLab.Random;
40using System;
41using System.Collections.Generic;
42using System.Drawing;
43using System.Linq;
44using CancellationToken = System.Threading.CancellationToken;
45
46namespace HeuristicLab.Algorithms.SMSEMOA {
47  [Item("SMSEMOAAlgorithmBase", "Base class for all SMSEMOA algorithm variants.")]
48  [StorableType("7665F5BB-D539-4A1A-8C57-473029680939")]
49  public abstract class SMSEMOAAlgorithmBase : BasicAlgorithm {
50    #region data members
51    [StorableType("CC6121DC-5655-4FF5-B1DE-6009ACE1BC90")]
52    protected enum NeighborType { NEIGHBOR, POPULATION }
53
54    [StorableType("A2B499D8-B68C-42ED-91FC-486973076C25")]
55    // TCHE = Chebyshev (Tchebyshev)
56    // PBI  = Penalty-based boundary intersection
57    // AGG  = Weighted sum
58    public enum FunctionType { TCHE, PBI, AGG }
59
60    [Storable]
61    protected double[] IdealPoint { get; set; }
62    [Storable]
63    protected double[] NadirPoint { get; set; } // potentially useful for objective normalization
64
65    [Storable]
66    protected double[][] lambda_moead;
67
68    [Storable]
69    protected int[][] neighbourhood;
70
71    [Storable]
72    protected ISMSEMOASolution[] solutions;
73
74    [Storable]
75    protected FunctionType functionType;
76
77    [Storable]
78    protected ISMSEMOASolution[] population;
79
80    [Storable]
81    protected ISMSEMOASolution[] offspringPopulation;
82
83    [Storable]
84    protected ISMSEMOASolution[] jointPopulation;
85
86    [Storable]
87    protected int evaluatedSolutions;
88
89    [Storable]
90    protected ExecutionContext executionContext;
91
92    [Storable]
93    protected IScope globalScope;
94
95    [Storable]
96    protected ExecutionState previousExecutionState;
97
98    [Storable]
99    protected ExecutionState executionState;
100
101    private DoubleArray ReferencePoint {
102      get {
103        var problem = (MultiObjectiveTestFunctionProblem)Problem;
104        return problem.ReferencePoint;
105      }
106    }
107    #endregion
108
109    #region parameters
110    private const string SeedParameterName = "Seed";
111    private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
112    private const string PopulationSizeParameterName = "PopulationSize";
113    private const string ResultPopulationSizeParameterName = "ResultPopulationSize";
114    private const string CrossoverProbabilityParameterName = "CrossoverProbability";
115    private const string CrossoverParameterName = "Crossover";
116    private const string MutationProbabilityParameterName = "MutationProbability";
117    private const string MutatorParameterName = "Mutator";
118    private const string MaximumEvaluatedSolutionsParameterName = "MaximumEvaluatedSolutions";
119    private const string RandomParameterName = "Random";
120    private const string AnalyzerParameterName = "Analyzer";
121    // MOEA-D parameters
122    //private const string NeighbourSizeParameterName = "NeighbourSize";
123    //private const string NeighbourhoodSelectionProbabilityParameterName = "NeighbourhoodSelectionProbability";
124    //private const string MaximumNumberOfReplacedSolutionsParameterName = "MaximumNumberOfReplacedSolutions";
125    //private const string FunctionTypeParameterName = "FunctionType";
126    // private const string NormalizeObjectivesParameterName = "NormalizeObjectives";
127
128    // SMS-EMOA parameters:
129    private const string LambdaParameterName = "Lambda";   // The number of offspring size
130
131
132
133    // "Parameters" are defined in "HeuristicLab.Parameters"
134    // Contains: generic parameters of every class/algorithm/instance,
135    // It seems that "I***ValueParameter" is declared in "Heuristic.core", where "***ValueParameter" are defined in "HeuristicLab.Parameter"
136    // The function of "I***ValueParameter" is to bridge current scripts to "HeuristicLab.Parameter".
137    public IValueParameter<MultiAnalyzer> AnalyzerParameter {
138      get { return (ValueParameter<MultiAnalyzer>)Parameters[AnalyzerParameterName]; }
139    }
140
141    //public IConstrainedValueParameter<StringValue> FunctionTypeParameter
142    //{
143    //  get { return (IConstrainedValueParameter<StringValue>)Parameters[FunctionTypeParameterName]; }
144    //}
145    //public IFixedValueParameter<IntValue> NeighbourSizeParameter
146    //{
147    //  get { return (IFixedValueParameter<IntValue>)Parameters[NeighbourSizeParameterName]; }
148    //}
149    //public IFixedValueParameter<BoolValue> NormalizeObjectivesParameter
150    //{
151    //  get { return (IFixedValueParameter<BoolValue>)Parameters[NormalizeObjectivesParameterName]; }
152    //}
153    //public IFixedValueParameter<IntValue> MaximumNumberOfReplacedSolutionsParameter
154    //{
155    //  get { return (IFixedValueParameter<IntValue>)Parameters[MaximumNumberOfReplacedSolutionsParameterName]; }
156    //}
157    //public IFixedValueParameter<DoubleValue> NeighbourhoodSelectionProbabilityParameter
158    //{
159    //  get { return (IFixedValueParameter<DoubleValue>)Parameters[NeighbourhoodSelectionProbabilityParameterName]; }
160    //}
161    public IFixedValueParameter<IntValue> SeedParameter {
162      get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
163    }
164    public IFixedValueParameter<BoolValue> SetSeedRandomlyParameter {
165      get { return (IFixedValueParameter<BoolValue>)Parameters[SetSeedRandomlyParameterName]; }
166    }
167    private IValueParameter<IntValue> PopulationSizeParameter {
168      get { return (IValueParameter<IntValue>)Parameters[PopulationSizeParameterName]; }
169    }
170    // KF, SMS-EMOA
171    private IValueParameter<IntValue> LambdaParameter {
172      get { return (IValueParameter<IntValue>)Parameters[LambdaParameterName]; }
173    }
174
175    private IValueParameter<IntValue> ResultPopulationSizeParameter {
176      get { return (IValueParameter<IntValue>)Parameters[ResultPopulationSizeParameterName]; }
177    }
178    public IValueParameter<PercentValue> CrossoverProbabilityParameter {
179      get { return (IValueParameter<PercentValue>)Parameters[CrossoverProbabilityParameterName]; }
180    }
181    public IConstrainedValueParameter<ICrossover> CrossoverParameter {
182      get { return (IConstrainedValueParameter<ICrossover>)Parameters[CrossoverParameterName]; }
183    }
184    public IValueParameter<PercentValue> MutationProbabilityParameter {
185      get { return (IValueParameter<PercentValue>)Parameters[MutationProbabilityParameterName]; }
186    }
187    public IConstrainedValueParameter<IManipulator> MutatorParameter {
188      get { return (IConstrainedValueParameter<IManipulator>)Parameters[MutatorParameterName]; }
189    }
190    public IValueParameter<IntValue> MaximumEvaluatedSolutionsParameter {
191      get { return (IValueParameter<IntValue>)Parameters[MaximumEvaluatedSolutionsParameterName]; }
192    }
193    public IValueParameter<IRandom> RandomParameter {
194      get { return (IValueParameter<IRandom>)Parameters[RandomParameterName]; }
195    }
196    #endregion
197
198    #region parameter properties
199    public new IMultiObjectiveHeuristicOptimizationProblem Problem {
200      get { return (IMultiObjectiveHeuristicOptimizationProblem)base.Problem; }
201      set { base.Problem = value; }
202    }
203    public int Seed {
204      get { return SeedParameter.Value.Value; }
205      set { SeedParameter.Value.Value = value; }
206    }
207    public bool SetSeedRandomly {
208      get { return SetSeedRandomlyParameter.Value.Value; }
209      set { SetSeedRandomlyParameter.Value.Value = value; }
210    }
211    public IntValue PopulationSize {
212      get { return PopulationSizeParameter.Value; }
213      set { PopulationSizeParameter.Value = value; }
214    }
215    public IntValue Lambda {
216      get { return LambdaParameter.Value; }
217      set { LambdaParameter.Value = value; }
218    }
219
220
221    public IntValue ResultPopulationSize {
222      get { return ResultPopulationSizeParameter.Value; }
223      set { ResultPopulationSizeParameter.Value = value; }
224    }
225    public PercentValue CrossoverProbability {
226      get { return CrossoverProbabilityParameter.Value; }
227      set { CrossoverProbabilityParameter.Value = value; }
228    }
229    public ICrossover Crossover {
230      get { return CrossoverParameter.Value; }
231      set { CrossoverParameter.Value = value; }
232    }
233    public PercentValue MutationProbability {
234      get { return MutationProbabilityParameter.Value; }
235      set { MutationProbabilityParameter.Value = value; }
236    }
237    public IManipulator Mutator {
238      get { return MutatorParameter.Value; }
239      set { MutatorParameter.Value = value; }
240    }
241    public MultiAnalyzer Analyzer {
242      get { return AnalyzerParameter.Value; }
243      set { AnalyzerParameter.Value = value; }
244    }
245    public IntValue MaximumEvaluatedSolutions {
246      get { return MaximumEvaluatedSolutionsParameter.Value; }
247      set { MaximumEvaluatedSolutionsParameter.Value = value; }
248    }
249    #endregion
250
251    #region constructors
252    public SMSEMOAAlgorithmBase() {
253      // Add or define or specify the parameters that may be use in SMS-EMOA.   
254      // ***("Name", "Description", "Value")
255      //  Name                            Type                Description
256      //  FixedValueParameter:            ANY                 Not changed???
257      //  ValueParameter:                                     Changable??? What is the difference between "ValueParameter" and "FixedVlaueParameter"?????
258
259
260      // types:
261      //      IntValue
262      //      BoolValue
263      //      DoubleValue
264      //      PercentValue
265      //      ICrossover:       
266      //      IManipulator:     
267      //      IRandom:         
268      //      MultiAnalyzer:   
269      //      ---------
270      Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName, "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
271      Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName, "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
272      Parameters.Add(new ValueParameter<IntValue>(PopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100)));
273      Parameters.Add(new ValueParameter<IntValue>(ResultPopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100)));
274      Parameters.Add(new ValueParameter<PercentValue>(CrossoverProbabilityParameterName, "The probability that the crossover operator is applied.", new PercentValue(0.9)));
275      Parameters.Add(new ConstrainedValueParameter<ICrossover>(CrossoverParameterName, "The operator used to cross solutions."));
276      Parameters.Add(new ValueParameter<PercentValue>(MutationProbabilityParameterName, "The probability that the mutation operator is applied on a solution.", new PercentValue(0.25)));
277      Parameters.Add(new ConstrainedValueParameter<IManipulator>(MutatorParameterName, "The operator used to mutate solutions."));
278      Parameters.Add(new ValueParameter<MultiAnalyzer>("Analyzer", "The operator used to analyze each generation.", new MultiAnalyzer()));
279      Parameters.Add(new ValueParameter<IntValue>(MaximumEvaluatedSolutionsParameterName, "The maximum number of evaluated solutions (approximately).", new IntValue(100_000)));
280      Parameters.Add(new ValueParameter<IRandom>(RandomParameterName, new FastRandom()));
281
282      // SMS-EMOA, kf
283      Parameters.Add(new ValueParameter<IntValue>(LambdaParameterName, "The size of the offsprings. Now, it only works when lambda = 1", new IntValue(1)));
284    }
285
286    protected SMSEMOAAlgorithmBase(SMSEMOAAlgorithmBase original, Cloner cloner) : base(original, cloner) {
287      functionType = original.functionType;
288      evaluatedSolutions = original.evaluatedSolutions;
289      previousExecutionState = original.previousExecutionState;
290
291      if (original.IdealPoint != null) {
292        IdealPoint = (double[])original.IdealPoint.Clone();
293      }
294
295      if (original.NadirPoint != null) {
296        NadirPoint = (double[])original.NadirPoint.Clone();
297      }
298
299      if (original.lambda_moead != null) {
300        lambda_moead = (double[][])original.lambda_moead.Clone();
301      }
302
303      if (original.neighbourhood != null) {
304        neighbourhood = (int[][])original.neighbourhood.Clone();
305      }
306
307      if (original.solutions != null) {
308        solutions = original.solutions.Select(cloner.Clone).ToArray();
309      }
310
311      if (original.population != null) {
312        population = original.population.Select(cloner.Clone).ToArray();
313      }
314
315      if (original.offspringPopulation != null) {
316        offspringPopulation = original.offspringPopulation.Select(cloner.Clone).ToArray();
317      }
318
319      if (original.jointPopulation != null) {
320        jointPopulation = original.jointPopulation.Select(x => cloner.Clone(x)).ToArray();
321      }
322
323      if (original.executionContext != null) {
324        executionContext = cloner.Clone(original.executionContext);
325      }
326
327      if (original.globalScope != null) {
328        globalScope = cloner.Clone(original.globalScope);
329      }
330    }
331
332
333
334    [StorableConstructor]
335    protected SMSEMOAAlgorithmBase(StorableConstructorFlag deserializing) : base(deserializing) { }
336    #endregion
337
338    private void InitializePopulation(ExecutionContext executionContext, CancellationToken cancellationToken, IRandom random, bool[] maximization) {
339      // creator: how to create the initilized population. "UniformRandom" is used here.
340      // TODO: LHS, latin hypercube sampling? Exisit???
341      var creator = Problem.SolutionCreator;
342      var evaluator = Problem.Evaluator;
343
344      // dimensions: objective space
345      var dimensions = maximization.Length;
346      var populationSize = PopulationSize.Value;
347      population = new ISMSEMOASolution[populationSize];
348
349      var parentScope = executionContext.Scope;
350      // first, create all individuals
351      for (int i = 0; i < populationSize; ++i) {
352        var childScope = new Scope(i.ToString()) { Parent = parentScope };
353        ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(creator, childScope));
354        parentScope.SubScopes.Add(childScope);
355      }
356
357      for (int i = 0; i < populationSize; ++i) {
358        var childScope = parentScope.SubScopes[i];
359        ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(evaluator, childScope));
360
361        var qualities = (DoubleArray)childScope.Variables["Qualities"].Value;
362
363        // solution: a method, contains a decision vector and objecitve values     
364        //    solution.Qualities:     objective values, fitness values
365        //    solution.Individual:    decision vector
366        var solution = new SMSEMOASolution(childScope, dimensions, 0);
367        for (int j = 0; j < dimensions; ++j) {
368          // TODO: convert maximization problems into minimization problems.
369          solution.Qualities[j] = maximization[j] ? 1 - qualities[j] : qualities[j];
370        }
371
372        // population is a collection of solution. 
373        population[i] = solution;
374
375        // kf, SMS-EMOA
376        population[i].HypervolumeContribution[0] = -0;
377        population[i].NondominanceRanking[0] = -0;
378      }
379    }
380
381    protected void InitializeAlgorithm(CancellationToken cancellationToken) { // Type of random operator, "FastRandom" in this script.
382      // RandomParameter <-- Parameters in "HeuristicLab.Core.ParameterizedNameItem",
383      var rand = RandomParameter.Value;
384
385      // Initialize random seed
386      // If random seed exist, get it; otherwise,
387      if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed();
388
389      // Call
390      rand.Reset(Seed);
391
392      bool[] maximization = ((BoolArray)Problem.MaximizationParameter.ActualValue).CloneAsArray();
393
394      // dimensions: the dimension in an objective space
395      var dimensions = maximization.Length;
396
397
398      var populationSize = PopulationSize.Value;
399
400      InitializePopulation(executionContext, cancellationToken, rand, maximization);
401
402      IdealPoint = new double[dimensions];
403      IdealPoint.UpdateIdeal(population);
404
405      NadirPoint = Enumerable.Repeat(double.MinValue, dimensions).ToArray();
406      //NadirPoint = new double[dimensions];
407      NadirPoint.UpdateNadir(population);
408
409
410      evaluatedSolutions = populationSize;
411    }
412
413    protected override void Initialize(CancellationToken cancellationToken) {
414      globalScope = new Scope("Global Scope");
415      executionContext = new ExecutionContext(null, this, globalScope);
416
417      // set the execution context for parameters to allow lookup
418      foreach (var parameter in Problem.Parameters.OfType<IValueParameter>()) {
419        // we need all of these in order for the wiring of the operators to work
420        globalScope.Variables.Add(new Variable(parameter.Name, parameter.Value));
421      }
422      globalScope.Variables.Add(new Variable("Results", Results)); // make results available as a parameter for analyzers etc.
423
424      base.Initialize(cancellationToken);
425    }
426
427    public override bool SupportsPause => true;
428
429
430
431
432    // Mate Selection.
433    // Randomly select a specific number of individuals for later operators.
434    // Inputs:
435    //    1. random:                      Random number generate method
436    //    2. numberOfSolutionToSelect:    The number of selection   
437    // Outputs:
438    //    1. listOfSolutions:             The selection individuals
439    protected List<int> MatingSelection(IRandom random, int numberOfSolutionsToSelect) {
440      int populationSize = PopulationSize.Value;
441
442      var listOfSolutions = new List<int>(numberOfSolutionsToSelect);
443
444      while (listOfSolutions.Count < numberOfSolutionsToSelect) {
445        var selectedSolution = random.Next(populationSize);
446
447        bool flag = true;
448        foreach (int individualId in listOfSolutions) {
449          if (individualId == selectedSolution) {
450            flag = false;
451            break;
452          }
453        }
454
455        if (flag) {
456          listOfSolutions.Add(selectedSolution);
457        }
458      }
459      return listOfSolutions;
460    }
461
462    // Select/Discard the individual(s) according to HVC
463    protected void SmetricSelection(int lambda) {
464      var qualities = jointPopulation.Select(x => x.Qualities).ToArray();
465
466      var maximization = Enumerable.Repeat(false, IdealPoint.Length).ToArray(); // Minimization or maximization ????
467      var pf2 = DominationCalculator<ISMSEMOASolution>.CalculateAllParetoFronts(jointPopulation, qualities, maximization, out int[] ranking);
468
469      int numberOfLayer;             // number of layers in PF
470      int numberOfLastLayer;          // number of discarded points in PF (the number of points in the last layer)
471
472      pf2.RemoveAt(pf2.Count() - 1);
473      numberOfLayer = pf2.Count();
474      numberOfLastLayer = pf2[numberOfLayer - 1].Count();
475      double[] hvc = new double[numberOfLastLayer];
476      int discardIndex;
477      if (numberOfLastLayer > lambda) {
478        double tempHV;
479        double smetric;
480        var lastLayer = pf2.Last();
481
482        try { // TODO: This can be use for dynamic reference point strategy later. Kaifeng , 02/2020
483          // smetric = Hypervolume.Calculate(lastLayer.Select(x => x.Item2), Enumerable.Repeat(11d, NadirPoint.Length).ToArray(), maximization);
484          smetric = Hypervolume.Calculate(lastLayer.Select(x => x.Item2), ReferencePoint.ToArray(), maximization);
485        }
486        catch {
487          smetric = int.MinValue;
488        }
489
490        var indices = Enumerable.Range(0, lastLayer.Count()).ToList();
491
492        for (int ii = 0; ii < lastLayer.Count(); ++ii) {
493          try { // TODO: This can be use for dynamic reference point strategy later. Kaifeng , 02/2020
494            // tempHV = Hypervolume.Calculate(indices.Where(idx => idx != ii).Select(idx => lastLayer[idx].Item2), Enumerable.Repeat(11d, NadirPoint.Length).ToArray(), maximization);
495            tempHV = Hypervolume.Calculate(indices.Where(idx => idx != ii).Select(idx => lastLayer[idx].Item2), ReferencePoint.ToArray(), maximization);
496          }
497          catch {
498            tempHV = int.MinValue;
499          }
500          hvc[ii] = smetric - tempHV;
501          tempHV = 0;
502        }
503        discardIndex = Array.IndexOf(hvc, hvc.Min());
504        pf2[numberOfLayer - 1].RemoveAt(discardIndex);
505      }
506      else {
507        // TODO: This should be updated when $lambda > 1$
508        pf2.RemoveAt(pf2.Count() - 1);
509        numberOfLayer = numberOfLayer - 1;
510      }
511      population = pf2.SelectMany(x => x.Select(y => y.Item1)).ToArray();
512    }
513
514
515
516    // Update the Pareto-front approximation set and scatter the solutions in PF approximation set.
517    protected void UpdateParetoFronts() {
518      //var qualities = population.Select(x => Enumerable.Range(0, NadirPoint.Length).Select(i => x.Qualities[i] / NadirPoint[i]).ToArray()).ToArray();
519      var qualities = population.Select(x => x.Qualities).ToArray();
520      var maximization = Enumerable.Repeat(false, IdealPoint.Length).ToArray();                             // SMSEMOA minimizes everything internally
521      var pf = DominationCalculator<ISMSEMOASolution>.CalculateBestParetoFront(population, qualities, maximization);
522
523      var pf2 = DominationCalculator<ISMSEMOASolution>.CalculateAllParetoFronts(population, qualities, maximization, out int[] ranking);
524      var n = (int)EnumerableExtensions.BinomialCoefficient(IdealPoint.Length, 2);
525
526
527      // Struture hypervolume
528      // [0,0]:  Value of HV
529      // [0,1]:  PF size, $|PF|$
530      var hypervolumes = new DoubleMatrix(n == 1 ? 1 : n + 1, 2) { ColumnNames = new[] { "PF hypervolume", "PF size" } };
531
532
533      // HV calculation
534      // pf.Select(x => x.Item2): the "Item2" in var "pd"
535      // Enumerable.Repeat(1d, NadirPoint.Length).ToArray():     reference point
536      // maximization:   type of optimization problem:
537      //               True:  maximization problem
538      //               False: minimization problem
539      hypervolumes[0, 0] = Hypervolume.Calculate(pf.Select(x => x.Item2), Enumerable.Repeat(11d, NadirPoint.Length).ToArray(), maximization);
540      hypervolumes[0, 1] = pf.Count;
541      Console.WriteLine("Current HV is", hypervolumes[0, 0]);
542
543      var elementNames = new List<string>() { "Pareto Front" };
544
545      ResultCollection results;
546      if (Results.ContainsKey("Hypervolume Analysis")) {
547        results = (ResultCollection)Results["Hypervolume Analysis"].Value;
548      }
549      else {
550        results = new ResultCollection();
551        Results.AddOrUpdateResult("Hypervolume Analysis", results);
552      }
553
554      ScatterPlot sp;
555      if (IdealPoint.Length == 2) {
556        var points = pf.Select(x => new Point2D<double>(x.Item2[0], x.Item2[1]));
557        var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error);
558        if (error != OnlineCalculatorError.None) { r = double.NaN; }
559        var resultName = "Pareto Front Analysis ";
560        if (!results.ContainsKey(resultName)) {
561          sp = new ScatterPlot() {
562            VisualProperties = {
563              XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d,
564              YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d
565            }
566          };
567          sp.Rows.Add(new ScatterPlotDataRow(resultName, "", points) { VisualProperties = { PointSize = 8 } });
568          results.AddOrUpdateResult(resultName, sp);
569        }
570        else {
571          sp = (ScatterPlot)results[resultName].Value;
572          sp.Rows[resultName].Points.Replace(points);
573        }
574        sp.Name = $"Dimensions [0, 1], correlation: {r.ToString("N2")}";
575      }
576      else if (IdealPoint.Length > 2) {
577        var indices = Enumerable.Range(0, IdealPoint.Length).ToArray();
578        var visualProperties = new ScatterPlotDataRowVisualProperties { PointSize = 8, Color = Color.LightGray };
579        var combinations = indices.Combinations(2).ToArray();
580        var maximization2d = new[] { false, false };
581        var solutions2d = pf.Select(x => x.Item1).ToArray();
582        for (int i = 0; i < combinations.Length; ++i) {
583          var c = combinations[i].ToArray();
584
585          // calculate the hypervolume in the 2d coordinate space
586          var reference2d = new[] { 1d, 1d };
587          var qualities2d = pf.Select(x => new[] { x.Item2[c[0]], x.Item2[c[1]] }).ToArray();
588          var pf2d = DominationCalculator<ISMSEMOASolution>.CalculateBestParetoFront(solutions2d, qualities2d, maximization2d);
589
590          hypervolumes[i + 1, 0] = pf2d.Count > 0 ? Hypervolume.Calculate(pf2d.Select(x => x.Item2), reference2d, maximization2d) : 0d;
591          hypervolumes[i + 1, 1] = pf2d.Count;
592
593          var resultName = $"Pareto Front Analysis [{c[0]}, {c[1]}]";
594          elementNames.Add(resultName);
595
596          var points = pf.Select(x => new Point2D<double>(x.Item2[c[0]], x.Item2[c[1]]));
597          var pf2dPoints = pf2d.Select(x => new Point2D<double>(x.Item2[0], x.Item2[1]));
598
599          if (!results.ContainsKey(resultName)) {
600            sp = new ScatterPlot() {
601              VisualProperties = {
602                XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d,
603                YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d
604              }
605            };
606            sp.Rows.Add(new ScatterPlotDataRow("Pareto Front", "", points) { VisualProperties = visualProperties });
607            sp.Rows.Add(new ScatterPlotDataRow($"Pareto Front [{c[0]}, {c[1]}]", "", pf2dPoints) { VisualProperties = { PointSize = 10, Color = Color.OrangeRed } });
608            results.AddOrUpdateResult(resultName, sp);
609          }
610          else {
611            sp = (ScatterPlot)results[resultName].Value;
612            sp.Rows["Pareto Front"].Points.Replace(points);
613            sp.Rows[$"Pareto Front [{c[0]}, {c[1]}]"].Points.Replace(pf2dPoints);
614          }
615          var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error);
616          var r2 = r * r;
617          sp.Name = $"Pareto Front [{c[0]}, {c[1]}], correlation: {r2.ToString("N2")}";
618        }
619      }
620      hypervolumes.RowNames = elementNames;
621      results.AddOrUpdateResult("Hypervolumes", hypervolumes);
622    }
623
624    #region operator wiring and events
625    protected void ExecuteOperation(ExecutionContext executionContext, CancellationToken cancellationToken, IOperation operation) {
626      Stack<IOperation> executionStack = new Stack<IOperation>();
627      executionStack.Push(operation);
628      while (executionStack.Count > 0) {
629        cancellationToken.ThrowIfCancellationRequested();
630        IOperation next = executionStack.Pop();
631        if (next is OperationCollection) {
632          OperationCollection coll = (OperationCollection)next;
633          for (int i = coll.Count - 1; i >= 0; i--)
634            if (coll[i] != null) executionStack.Push(coll[i]);
635        }
636        else if (next is IAtomicOperation) {
637          IAtomicOperation op = (IAtomicOperation)next;
638          next = op.Operator.Execute((IExecutionContext)op, cancellationToken);
639          if (next != null) executionStack.Push(next);
640        }
641      }
642    }
643
644    private void UpdateAnalyzers() {
645      Analyzer.Operators.Clear();
646      if (Problem != null) {
647        foreach (IAnalyzer analyzer in Problem.Operators.OfType<IAnalyzer>()) {
648          foreach (IScopeTreeLookupParameter param in analyzer.Parameters.OfType<IScopeTreeLookupParameter>())
649            param.Depth = 1;
650          Analyzer.Operators.Add(analyzer, analyzer.EnabledByDefault);
651        }
652      }
653    }
654
655    private void UpdateCrossovers() {
656      ICrossover oldCrossover = CrossoverParameter.Value;
657      CrossoverParameter.ValidValues.Clear();
658      ICrossover defaultCrossover = Problem.Operators.OfType<ICrossover>().FirstOrDefault();
659
660      foreach (ICrossover crossover in Problem.Operators.OfType<ICrossover>().OrderBy(x => x.Name))
661        CrossoverParameter.ValidValues.Add(crossover);
662
663      if (oldCrossover != null) {
664        ICrossover crossover = CrossoverParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldCrossover.GetType());
665        if (crossover != null) CrossoverParameter.Value = crossover;
666        else oldCrossover = null;
667      }
668      if (oldCrossover == null && defaultCrossover != null)
669        CrossoverParameter.Value = defaultCrossover;
670    }
671
672    private void UpdateMutators() {
673      IManipulator oldMutator = MutatorParameter.Value;
674      MutatorParameter.ValidValues.Clear();
675      IManipulator defaultMutator = Problem.Operators.OfType<IManipulator>().FirstOrDefault();
676
677      foreach (IManipulator mutator in Problem.Operators.OfType<IManipulator>().OrderBy(x => x.Name))
678        MutatorParameter.ValidValues.Add(mutator);
679
680      if (oldMutator != null) {
681        IManipulator mutator = MutatorParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldMutator.GetType());
682        if (mutator != null) MutatorParameter.Value = mutator;
683        else oldMutator = null;
684      }
685
686      if (oldMutator == null && defaultMutator != null)
687        MutatorParameter.Value = defaultMutator;
688    }
689
690    protected override void OnProblemChanged() {
691      UpdateCrossovers();
692      UpdateMutators();
693      UpdateAnalyzers();
694      base.OnProblemChanged();
695    }
696
697    protected override void OnExecutionStateChanged() {
698      previousExecutionState = executionState;
699      executionState = ExecutionState;
700      base.OnExecutionStateChanged();
701    }
702
703    public void ClearState() {
704      solutions = null;
705      population = null;
706      offspringPopulation = null;
707      jointPopulation = null;
708      lambda_moead = null;
709      neighbourhood = null;
710      if (executionContext != null && executionContext.Scope != null) {
711        executionContext.Scope.SubScopes.Clear();
712      }
713    }
714
715    protected override void OnStopped() {
716      ClearState();
717      base.OnStopped();
718    }
719    #endregion
720  }
721}
Note: See TracBrowser for help on using the repository browser.