Free cookie consent management tool by TermsFeed Policy Generator

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

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

#3055: Fixed the bug of "no reference point" on real-world test problems

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