#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion // 03/02/2020 // TODO LIST: // 1. Dynamic reference point strategy // 2. Normalized fitness value strategy, desibility function. // 3. HVC calculation should be definitely improved, at least in the 2D case. // 4. multiple point strategy when $\lambda>1$ using HEAL.Attic; using HeuristicLab.Analysis; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.ExpressionGenerator; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Problems.DataAnalysis; using HeuristicLab.Problems.TestFunctions.MultiObjective; using HeuristicLab.Random; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using CancellationToken = System.Threading.CancellationToken; namespace HeuristicLab.Algorithms.SMSEMOA { [Item("SMSEMOAAlgorithmBase", "Base class for all SMSEMOA algorithm variants.")] [StorableType("7665F5BB-D539-4A1A-8C57-473029680939")] public abstract class SMSEMOAAlgorithmBase : BasicAlgorithm { #region data members [StorableType("CC6121DC-5655-4FF5-B1DE-6009ACE1BC90")] protected enum NeighborType { NEIGHBOR, POPULATION } [StorableType("A2B499D8-B68C-42ED-91FC-486973076C25")] // TCHE = Chebyshev (Tchebyshev) // PBI = Penalty-based boundary intersection // AGG = Weighted sum public enum FunctionType { TCHE, PBI, AGG } [Storable] protected double[] IdealPoint { get; set; } [Storable] protected double[] NadirPoint { get; set; } // potentially useful for objective normalization [Storable] protected double[][] lambda_moead; [Storable] protected int[][] neighbourhood; [Storable] protected ISMSEMOASolution[] solutions; [Storable] protected FunctionType functionType; [Storable] protected ISMSEMOASolution[] population; [Storable] protected ISMSEMOASolution[] offspringPopulation; [Storable] protected ISMSEMOASolution[] jointPopulation; [Storable] protected int evaluatedSolutions; [Storable] protected ExecutionContext executionContext; [Storable] protected IScope globalScope; [Storable] protected ExecutionState previousExecutionState; [Storable] protected ExecutionState executionState; private DoubleArray ReferencePoint { get { if (Problem is MultiObjectiveTestFunctionProblem) { var problem = (MultiObjectiveTestFunctionProblem)Problem; return problem.ReferencePoint; } else { return null; } } } #endregion #region parameters private const string SeedParameterName = "Seed"; private const string SetSeedRandomlyParameterName = "SetSeedRandomly"; private const string PopulationSizeParameterName = "PopulationSize"; private const string ResultPopulationSizeParameterName = "ResultPopulationSize"; private const string CrossoverProbabilityParameterName = "CrossoverProbability"; private const string CrossoverParameterName = "Crossover"; private const string MutationProbabilityParameterName = "MutationProbability"; private const string MutatorParameterName = "Mutator"; private const string MaximumEvaluatedSolutionsParameterName = "MaximumEvaluatedSolutions"; private const string RandomParameterName = "Random"; private const string AnalyzerParameterName = "Analyzer"; // MOEA-D parameters //private const string NeighbourSizeParameterName = "NeighbourSize"; //private const string NeighbourhoodSelectionProbabilityParameterName = "NeighbourhoodSelectionProbability"; //private const string MaximumNumberOfReplacedSolutionsParameterName = "MaximumNumberOfReplacedSolutions"; //private const string FunctionTypeParameterName = "FunctionType"; // private const string NormalizeObjectivesParameterName = "NormalizeObjectives"; // SMS-EMOA parameters: private const string LambdaParameterName = "Lambda"; // The number of offspring size // "Parameters" are defined in "HeuristicLab.Parameters" // Contains: generic parameters of every class/algorithm/instance, // It seems that "I***ValueParameter" is declared in "Heuristic.core", where "***ValueParameter" are defined in "HeuristicLab.Parameter" // The function of "I***ValueParameter" is to bridge current scripts to "HeuristicLab.Parameter". public IValueParameter AnalyzerParameter { get { return (ValueParameter)Parameters[AnalyzerParameterName]; } } //public IConstrainedValueParameter FunctionTypeParameter //{ // get { return (IConstrainedValueParameter)Parameters[FunctionTypeParameterName]; } //} //public IFixedValueParameter NeighbourSizeParameter //{ // get { return (IFixedValueParameter)Parameters[NeighbourSizeParameterName]; } //} //public IFixedValueParameter NormalizeObjectivesParameter //{ // get { return (IFixedValueParameter)Parameters[NormalizeObjectivesParameterName]; } //} //public IFixedValueParameter MaximumNumberOfReplacedSolutionsParameter //{ // get { return (IFixedValueParameter)Parameters[MaximumNumberOfReplacedSolutionsParameterName]; } //} //public IFixedValueParameter NeighbourhoodSelectionProbabilityParameter //{ // get { return (IFixedValueParameter)Parameters[NeighbourhoodSelectionProbabilityParameterName]; } //} public IFixedValueParameter SeedParameter { get { return (IFixedValueParameter)Parameters[SeedParameterName]; } } public IFixedValueParameter SetSeedRandomlyParameter { get { return (IFixedValueParameter)Parameters[SetSeedRandomlyParameterName]; } } private IValueParameter PopulationSizeParameter { get { return (IValueParameter)Parameters[PopulationSizeParameterName]; } } // KF, SMS-EMOA private IValueParameter LambdaParameter { get { return (IValueParameter)Parameters[LambdaParameterName]; } } private IValueParameter ResultPopulationSizeParameter { get { return (IValueParameter)Parameters[ResultPopulationSizeParameterName]; } } public IValueParameter CrossoverProbabilityParameter { get { return (IValueParameter)Parameters[CrossoverProbabilityParameterName]; } } public IConstrainedValueParameter CrossoverParameter { get { return (IConstrainedValueParameter)Parameters[CrossoverParameterName]; } } public IValueParameter MutationProbabilityParameter { get { return (IValueParameter)Parameters[MutationProbabilityParameterName]; } } public IConstrainedValueParameter MutatorParameter { get { return (IConstrainedValueParameter)Parameters[MutatorParameterName]; } } public IValueParameter MaximumEvaluatedSolutionsParameter { get { return (IValueParameter)Parameters[MaximumEvaluatedSolutionsParameterName]; } } public IValueParameter RandomParameter { get { return (IValueParameter)Parameters[RandomParameterName]; } } #endregion #region parameter properties public new IMultiObjectiveHeuristicOptimizationProblem Problem { get { return (IMultiObjectiveHeuristicOptimizationProblem)base.Problem; } set { base.Problem = value; } } public int Seed { get { return SeedParameter.Value.Value; } set { SeedParameter.Value.Value = value; } } public bool SetSeedRandomly { get { return SetSeedRandomlyParameter.Value.Value; } set { SetSeedRandomlyParameter.Value.Value = value; } } public IntValue PopulationSize { get { return PopulationSizeParameter.Value; } set { PopulationSizeParameter.Value = value; } } public IntValue Lambda { get { return LambdaParameter.Value; } set { LambdaParameter.Value = value; } } public IntValue ResultPopulationSize { get { return ResultPopulationSizeParameter.Value; } set { ResultPopulationSizeParameter.Value = value; } } public PercentValue CrossoverProbability { get { return CrossoverProbabilityParameter.Value; } set { CrossoverProbabilityParameter.Value = value; } } public ICrossover Crossover { get { return CrossoverParameter.Value; } set { CrossoverParameter.Value = value; } } public PercentValue MutationProbability { get { return MutationProbabilityParameter.Value; } set { MutationProbabilityParameter.Value = value; } } public IManipulator Mutator { get { return MutatorParameter.Value; } set { MutatorParameter.Value = value; } } public MultiAnalyzer Analyzer { get { return AnalyzerParameter.Value; } set { AnalyzerParameter.Value = value; } } public IntValue MaximumEvaluatedSolutions { get { return MaximumEvaluatedSolutionsParameter.Value; } set { MaximumEvaluatedSolutionsParameter.Value = value; } } #endregion #region constructors public SMSEMOAAlgorithmBase() { // Add or define or specify the parameters that may be use in SMS-EMOA. // ***("Name", "Description", "Value") // Name Type Description // FixedValueParameter: ANY Not changed??? // ValueParameter: Changable??? What is the difference between "ValueParameter" and "FixedVlaueParameter"????? // types: // IntValue // BoolValue // DoubleValue // PercentValue // ICrossover: // IManipulator: // IRandom: // MultiAnalyzer: // --------- Parameters.Add(new FixedValueParameter(SeedParameterName, "The random seed used to initialize the new pseudo random number generator.", new IntValue(0))); Parameters.Add(new FixedValueParameter(SetSeedRandomlyParameterName, "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true))); Parameters.Add(new ValueParameter(PopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100))); Parameters.Add(new ValueParameter(ResultPopulationSizeParameterName, "The size of the population of solutions.", new IntValue(100))); Parameters.Add(new ValueParameter(CrossoverProbabilityParameterName, "The probability that the crossover operator is applied.", new PercentValue(0.9))); Parameters.Add(new ConstrainedValueParameter(CrossoverParameterName, "The operator used to cross solutions.")); Parameters.Add(new ValueParameter(MutationProbabilityParameterName, "The probability that the mutation operator is applied on a solution.", new PercentValue(0.25))); Parameters.Add(new ConstrainedValueParameter(MutatorParameterName, "The operator used to mutate solutions.")); Parameters.Add(new ValueParameter("Analyzer", "The operator used to analyze each generation.", new MultiAnalyzer())); Parameters.Add(new ValueParameter(MaximumEvaluatedSolutionsParameterName, "The maximum number of evaluated solutions (approximately).", new IntValue(100_000))); Parameters.Add(new ValueParameter(RandomParameterName, new FastRandom())); // SMS-EMOA, kf Parameters.Add(new ValueParameter(LambdaParameterName, "The size of the offsprings. Now, it only works when lambda = 1", new IntValue(1))); } protected SMSEMOAAlgorithmBase(SMSEMOAAlgorithmBase original, Cloner cloner) : base(original, cloner) { functionType = original.functionType; evaluatedSolutions = original.evaluatedSolutions; previousExecutionState = original.previousExecutionState; if (original.IdealPoint != null) { IdealPoint = (double[])original.IdealPoint.Clone(); } if (original.NadirPoint != null) { NadirPoint = (double[])original.NadirPoint.Clone(); } if (original.lambda_moead != null) { lambda_moead = (double[][])original.lambda_moead.Clone(); } if (original.neighbourhood != null) { neighbourhood = (int[][])original.neighbourhood.Clone(); } if (original.solutions != null) { solutions = original.solutions.Select(cloner.Clone).ToArray(); } if (original.population != null) { population = original.population.Select(cloner.Clone).ToArray(); } if (original.offspringPopulation != null) { offspringPopulation = original.offspringPopulation.Select(cloner.Clone).ToArray(); } if (original.jointPopulation != null) { jointPopulation = original.jointPopulation.Select(x => cloner.Clone(x)).ToArray(); } if (original.executionContext != null) { executionContext = cloner.Clone(original.executionContext); } if (original.globalScope != null) { globalScope = cloner.Clone(original.globalScope); } } [StorableConstructor] protected SMSEMOAAlgorithmBase(StorableConstructorFlag deserializing) : base(deserializing) { } #endregion private void InitializePopulation(ExecutionContext executionContext, CancellationToken cancellationToken, IRandom random, bool[] maximization) { // creator: how to create the initilized population. "UniformRandom" is used here. // TODO: LHS, latin hypercube sampling? Exisit??? var creator = Problem.SolutionCreator; var evaluator = Problem.Evaluator; // dimensions: objective space var dimensions = maximization.Length; var populationSize = PopulationSize.Value; population = new ISMSEMOASolution[populationSize]; var parentScope = executionContext.Scope; // first, create all individuals for (int i = 0; i < populationSize; ++i) { var childScope = new Scope(i.ToString()) { Parent = parentScope }; ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(creator, childScope)); parentScope.SubScopes.Add(childScope); } for (int i = 0; i < populationSize; ++i) { var childScope = parentScope.SubScopes[i]; ExecuteOperation(executionContext, cancellationToken, executionContext.CreateChildOperation(evaluator, childScope)); var qualities = (DoubleArray)childScope.Variables["Qualities"].Value; // solution: a method, contains a decision vector and objecitve values // solution.Qualities: objective values, fitness values // solution.Individual: decision vector var solution = new SMSEMOASolution(childScope, dimensions, 0); for (int j = 0; j < dimensions; ++j) { // TODO: convert maximization problems into minimization problems. solution.Qualities[j] = maximization[j] ? 1 - qualities[j] : qualities[j]; } // population is a collection of solution. population[i] = solution; // kf, SMS-EMOA population[i].HypervolumeContribution[0] = -0; population[i].NondominanceRanking[0] = -0; } } protected void InitializeAlgorithm(CancellationToken cancellationToken) { // Type of random operator, "FastRandom" in this script. // RandomParameter <-- Parameters in "HeuristicLab.Core.ParameterizedNameItem", var rand = RandomParameter.Value; // Initialize random seed // If random seed exist, get it; otherwise, if (SetSeedRandomly) Seed = RandomSeedGenerator.GetSeed(); // Call rand.Reset(Seed); bool[] maximization = ((BoolArray)Problem.MaximizationParameter.ActualValue).CloneAsArray(); // dimensions: the dimension in an objective space var dimensions = maximization.Length; var populationSize = PopulationSize.Value; InitializePopulation(executionContext, cancellationToken, rand, maximization); IdealPoint = new double[dimensions]; IdealPoint.UpdateIdeal(population); NadirPoint = Enumerable.Repeat(double.MinValue, dimensions).ToArray(); //NadirPoint = new double[dimensions]; NadirPoint.UpdateNadir(population); evaluatedSolutions = populationSize; } protected override void Initialize(CancellationToken cancellationToken) { globalScope = new Scope("Global Scope"); executionContext = new ExecutionContext(null, this, globalScope); // set the execution context for parameters to allow lookup foreach (var parameter in Problem.Parameters.OfType()) { // we need all of these in order for the wiring of the operators to work globalScope.Variables.Add(new Variable(parameter.Name, parameter.Value)); } globalScope.Variables.Add(new Variable("Results", Results)); // make results available as a parameter for analyzers etc. base.Initialize(cancellationToken); } public override bool SupportsPause => true; // Mate Selection. // Randomly select a specific number of individuals for later operators. // Inputs: // 1. random: Random number generate method // 2. numberOfSolutionToSelect: The number of selection // Outputs: // 1. listOfSolutions: The selection individuals protected List MatingSelection(IRandom random, int numberOfSolutionsToSelect) { int populationSize = PopulationSize.Value; var listOfSolutions = new List(numberOfSolutionsToSelect); while (listOfSolutions.Count < numberOfSolutionsToSelect) { var selectedSolution = random.Next(populationSize); bool flag = true; foreach (int individualId in listOfSolutions) { if (individualId == selectedSolution) { flag = false; break; } } if (flag) { listOfSolutions.Add(selectedSolution); } } return listOfSolutions; } // Select/Discard the individual(s) according to HVC protected void SmetricSelection(int lambda) { var qualities = jointPopulation.Select(x => x.Qualities).ToArray(); var maximization = Enumerable.Repeat(false, IdealPoint.Length).ToArray(); // Minimization or maximization ???? var pf2 = DominationCalculator.CalculateAllParetoFronts(jointPopulation, qualities, maximization, out int[] ranking); int numberOfLayer; // number of layers in PF int numberOfLastLayer; // number of discarded points in PF (the number of points in the last layer) pf2.RemoveAt(pf2.Count() - 1); numberOfLayer = pf2.Count(); numberOfLastLayer = pf2[numberOfLayer - 1].Count(); double[] hvc = new double[numberOfLastLayer]; int discardIndex; if (numberOfLastLayer > lambda) { double tempHV; double smetric; var lastLayer = pf2.Last(); // TODO: This can be use for dynamic reference point strategy later. Kaifeng , 02/2020 // smetric = Hypervolume.Calculate(lastLayer.Select(x => x.Item2), Enumerable.Repeat(11d, NadirPoint.Length).ToArray(), maximization); var reference = Enumerable.Repeat(double.MaxValue, maximization.Length).ToArray(); 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 for (int i = 0; i < reference.Length; i++) { reference[i] = 1.1 * reference[i]; if (reference[i] > 10000) { reference[i] = 9999; // set a upper bound for the reference point } } } else { reference = ReferencePoint.ToArray(); } //var reference = ReferencePoint.ToArray(); var nondominated = NonDominatedSelect.GetDominatingVectors(lastLayer.Select(x => x.Item2), reference, maximization, false); smetric = nondominated.Any() ? Hypervolume.Calculate(nondominated, reference, maximization) : int.MinValue; for (int ii = 0; ii < lastLayer.Count; ++ii) { try { // TODO: This can be use for dynamic reference point strategy later. Kaifeng , 02/2020 // tempHV = Hypervolume.Calculate(indices.Where(idx => idx != ii).Select(idx => lastLayer[idx].Item2), Enumerable.Repeat(11d, NadirPoint.Length).ToArray(), maximization); tempHV = Hypervolume.Calculate(Enumerable.Range(0, lastLayer.Count).Where(idx => idx != ii).Select(idx => lastLayer[idx].Item2), reference, maximization); } catch { tempHV = int.MinValue; } hvc[ii] = smetric - tempHV; tempHV = 0; } discardIndex = Array.IndexOf(hvc, hvc.Min()); pf2[numberOfLayer - 1].RemoveAt(discardIndex); } else { // TODO: This should be updated when $lambda > 1$ pf2.RemoveAt(pf2.Count() - 1); numberOfLayer = numberOfLayer - 1; } population = pf2.SelectMany(x => x.Select(y => y.Item1)).ToArray(); } // Update the Pareto-front approximation set and scatter the solutions in PF approximation set. protected void UpdateParetoFronts() { //var qualities = population.Select(x => Enumerable.Range(0, NadirPoint.Length).Select(i => x.Qualities[i] / NadirPoint[i]).ToArray()).ToArray(); var qualities = population.Select(x => x.Qualities).ToArray(); var maximization = Enumerable.Repeat(false, IdealPoint.Length).ToArray(); // SMSEMOA minimizes everything internally var pf = DominationCalculator.CalculateBestParetoFront(population, qualities, maximization); var pf2 = DominationCalculator.CalculateAllParetoFronts(population, qualities, maximization, out int[] ranking); var n = (int)EnumerableExtensions.BinomialCoefficient(IdealPoint.Length, 2); // Struture hypervolume // [0,0]: Value of HV // [0,1]: PF size, $|PF|$ var hypervolumes = new DoubleMatrix(n == 1 ? 1 : n + 1, 2) { ColumnNames = new[] { "PF hypervolume", "PF size" } }; // HV calculation // pf.Select(x => x.Item2): the "Item2" in var "pd" // Enumerable.Repeat(1d, NadirPoint.Length).ToArray(): reference point // maximization: type of optimization problem: // True: maximization problem // False: minimization problem var reference = Enumerable.Repeat(double.MaxValue, maximization.Length).ToArray(); 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 for (int i = 0; i < reference.Length; i++) { reference[i] = 1.1 * reference[i]; if (reference[i] > 10000) { reference[i] = 9999; // set a upper bound for the reference point } } } else { reference = ReferencePoint.ToArray(); } //var reference = ReferencePoint.ToArray() ?? Enumerable.Repeat(double.MaxValue, maximization.Length).ToArray(); //var reference = ReferencePoint.ToArray(); var nondominated = NonDominatedSelect.GetDominatingVectors(pf.Select(x => x.Item2), reference, maximization, false); hypervolumes[0, 0] = nondominated.Any() ? Hypervolume.Calculate(nondominated, reference, maximization) : int.MinValue; //hypervolumes[0, 0] = Hypervolume.Calculate(pf.Select(x => x.Item2), reference, maximization); hypervolumes[0, 1] = pf.Count; Console.WriteLine("Current HV is", hypervolumes[0, 0]); var elementNames = new List() { "Pareto Front" }; ResultCollection results; if (Results.ContainsKey("Hypervolume Analysis")) { results = (ResultCollection)Results["Hypervolume Analysis"].Value; } else { results = new ResultCollection(); Results.AddOrUpdateResult("Hypervolume Analysis", results); } ScatterPlot sp; if (IdealPoint.Length == 2) { var points = pf.Select(x => new Point2D(x.Item2[0], x.Item2[1])); var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error); if (error != OnlineCalculatorError.None) { r = double.NaN; } var resultName = "Pareto Front Analysis "; if (!results.ContainsKey(resultName)) { sp = new ScatterPlot() { VisualProperties = { XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d, YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d } }; sp.Rows.Add(new ScatterPlotDataRow(resultName, "", points) { VisualProperties = { PointSize = 8 } }); results.AddOrUpdateResult(resultName, sp); } else { sp = (ScatterPlot)results[resultName].Value; sp.Rows[resultName].Points.Replace(points); } sp.Name = $"Dimensions [0, 1], correlation: {r.ToString("N2")}"; } else if (IdealPoint.Length > 2) { var indices = Enumerable.Range(0, IdealPoint.Length).ToArray(); var visualProperties = new ScatterPlotDataRowVisualProperties { PointSize = 8, Color = Color.LightGray }; var combinations = indices.Combinations(2).ToArray(); var maximization2d = new[] { false, false }; var solutions2d = pf.Select(x => x.Item1).ToArray(); for (int i = 0; i < combinations.Length; ++i) { var c = combinations[i].ToArray(); // calculate the hypervolume in the 2d coordinate space var reference2d = new[] { 1d, 1d }; var qualities2d = pf.Select(x => new[] { x.Item2[c[0]], x.Item2[c[1]] }).ToArray(); var pf2d = DominationCalculator.CalculateBestParetoFront(solutions2d, qualities2d, maximization2d); hypervolumes[i + 1, 0] = pf2d.Count > 0 ? Hypervolume.Calculate(pf2d.Select(x => x.Item2), reference2d, maximization2d) : 0d; hypervolumes[i + 1, 1] = pf2d.Count; var resultName = $"Pareto Front Analysis [{c[0]}, {c[1]}]"; elementNames.Add(resultName); var points = pf.Select(x => new Point2D(x.Item2[c[0]], x.Item2[c[1]])); var pf2dPoints = pf2d.Select(x => new Point2D(x.Item2[0], x.Item2[1])); if (!results.ContainsKey(resultName)) { sp = new ScatterPlot() { VisualProperties = { XAxisMinimumAuto = false, XAxisMinimumFixedValue = 0d, XAxisMaximumAuto = false, XAxisMaximumFixedValue = 1d, YAxisMinimumAuto = false, YAxisMinimumFixedValue = 0d, YAxisMaximumAuto = false, YAxisMaximumFixedValue = 1d } }; sp.Rows.Add(new ScatterPlotDataRow("Pareto Front", "", points) { VisualProperties = visualProperties }); sp.Rows.Add(new ScatterPlotDataRow($"Pareto Front [{c[0]}, {c[1]}]", "", pf2dPoints) { VisualProperties = { PointSize = 10, Color = Color.OrangeRed } }); results.AddOrUpdateResult(resultName, sp); } else { sp = (ScatterPlot)results[resultName].Value; sp.Rows["Pareto Front"].Points.Replace(points); sp.Rows[$"Pareto Front [{c[0]}, {c[1]}]"].Points.Replace(pf2dPoints); } var r = OnlinePearsonsRCalculator.Calculate(points.Select(x => x.X), points.Select(x => x.Y), out OnlineCalculatorError error); var r2 = r * r; sp.Name = $"Pareto Front [{c[0]}, {c[1]}], correlation: {r2.ToString("N2")}"; } } hypervolumes.RowNames = elementNames; results.AddOrUpdateResult("Hypervolumes", hypervolumes); } #region operator wiring and events protected void ExecuteOperation(ExecutionContext executionContext, CancellationToken cancellationToken, IOperation operation) { Stack executionStack = new Stack(); executionStack.Push(operation); while (executionStack.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); IOperation next = executionStack.Pop(); if (next is OperationCollection) { OperationCollection coll = (OperationCollection)next; for (int i = coll.Count - 1; i >= 0; i--) if (coll[i] != null) executionStack.Push(coll[i]); } else if (next is IAtomicOperation) { IAtomicOperation op = (IAtomicOperation)next; next = op.Operator.Execute((IExecutionContext)op, cancellationToken); if (next != null) executionStack.Push(next); } } } private void UpdateAnalyzers() { Analyzer.Operators.Clear(); if (Problem != null) { foreach (IAnalyzer analyzer in Problem.Operators.OfType()) { foreach (IScopeTreeLookupParameter param in analyzer.Parameters.OfType()) param.Depth = 1; Analyzer.Operators.Add(analyzer, analyzer.EnabledByDefault); } } } private void UpdateCrossovers() { ICrossover oldCrossover = CrossoverParameter.Value; CrossoverParameter.ValidValues.Clear(); ICrossover defaultCrossover = Problem.Operators.OfType().FirstOrDefault(); foreach (ICrossover crossover in Problem.Operators.OfType().OrderBy(x => x.Name)) CrossoverParameter.ValidValues.Add(crossover); if (oldCrossover != null) { ICrossover crossover = CrossoverParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldCrossover.GetType()); if (crossover != null) CrossoverParameter.Value = crossover; else oldCrossover = null; } if (oldCrossover == null && defaultCrossover != null) CrossoverParameter.Value = defaultCrossover; } private void UpdateMutators() { IManipulator oldMutator = MutatorParameter.Value; MutatorParameter.ValidValues.Clear(); IManipulator defaultMutator = Problem.Operators.OfType().FirstOrDefault(); foreach (IManipulator mutator in Problem.Operators.OfType().OrderBy(x => x.Name)) MutatorParameter.ValidValues.Add(mutator); if (oldMutator != null) { IManipulator mutator = MutatorParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldMutator.GetType()); if (mutator != null) MutatorParameter.Value = mutator; else oldMutator = null; } if (oldMutator == null && defaultMutator != null) MutatorParameter.Value = defaultMutator; } protected override void OnProblemChanged() { UpdateCrossovers(); UpdateMutators(); UpdateAnalyzers(); base.OnProblemChanged(); } protected override void OnExecutionStateChanged() { previousExecutionState = executionState; executionState = ExecutionState; base.OnExecutionStateChanged(); } public void ClearState() { solutions = null; population = null; offspringPopulation = null; jointPopulation = null; lambda_moead = null; neighbourhood = null; if (executionContext != null && executionContext.Scope != null) { executionContext.Scope.SubScopes.Clear(); } } protected override void OnStopped() { ClearState(); base.OnStopped(); } #endregion } }