#region License Information /* HeuristicLab * Copyright (C) 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 using System; using System.Collections.Generic; using System.Linq; using HEAL.Attic; namespace HeuristicLab.Optimization { [StorableType("d76eb753-5088-4490-ad18-e78d3629c60b")] public enum DominationResult { Dominates, IsDominated, IsNonDominated }; public static class DominationCalculator { /// /// Calculates the best pareto front only. The fast non-dominated sorting algorithm is used /// as described in Deb, K., Pratap, A., Agarwal, S., and Meyarivan, T. (2002). /// A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. /// IEEE Transactions on Evolutionary Computation, 6(2), 182-197. /// /// /// When there are plateaus in the fitness landscape several solutions might have exactly /// the same fitness vector. In this case parameter /// can be set to true to avoid plateaus becoming too attractive for the search process. /// /// The solutions of the population. /// The qualities resp. fitness for each solution. /// The objective in each dimension. /// Whether solutions of exactly equal quality should dominate one another. /// The pareto front containing the best solutions and their associated quality resp. fitness. public static List> CalculateBestParetoFront(T[] solutions, double[][] qualities, bool[] maximization, bool dominateOnEqualQualities = true) { var populationSize = solutions.Length; Dictionary> dominatedIndividuals; int[] dominationCounter, rank; return CalculateBestFront(solutions, qualities, maximization, dominateOnEqualQualities, populationSize, out dominatedIndividuals, out dominationCounter, out rank); } /// /// Calculates all pareto fronts. The first in the list is the best front. /// The fast non-dominated sorting algorithm is used as described in /// Deb, K., Pratap, A., Agarwal, S., and Meyarivan, T. (2002). /// A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. /// IEEE Transactions on Evolutionary Computation, 6(2), 182-197. /// /// /// When there are plateaus in the fitness landscape several solutions might have exactly /// the same fitness vector. In this case parameter /// can be set to true to avoid plateaus becoming too attractive for the search process. /// /// The solutions of the population. /// The qualities resp. fitness for each solution. /// The objective in each dimension. /// The rank of each of the solutions, corresponds to the front it is put in. /// Whether solutions of exactly equal quality should dominate one another. /// A sorted list of the pareto fronts from best to worst. public static List>> CalculateAllParetoFronts(T[] solutions, double[][] qualities, bool[] maximization, out int[] rank, bool dominateOnEqualQualities = true) { var populationSize = solutions.Length; Dictionary> dominatedIndividuals; int[] dominationCounter; var fronts = new List>>(); fronts.Add(CalculateBestFront(solutions, qualities, maximization, dominateOnEqualQualities, populationSize, out dominatedIndividuals, out dominationCounter, out rank)); var i = 0; while (i < fronts.Count && fronts[i].Count > 0) { var nextFront = new List>(); foreach (var p in fronts[i]) { List dominatedIndividualsByp; if (dominatedIndividuals.TryGetValue(p.Item1, out dominatedIndividualsByp)) { for (var k = 0; k < dominatedIndividualsByp.Count; k++) { var dominatedIndividual = dominatedIndividualsByp[k]; dominationCounter[dominatedIndividual] -= 1; if (dominationCounter[dominatedIndividual] == 0) { rank[dominatedIndividual] = i + 1; nextFront.Add(Tuple.Create(solutions[dominatedIndividual], qualities[dominatedIndividual])); } } } } i += 1; fronts.Add(nextFront); } return fronts; } private static List> CalculateBestFront(T[] solutions, double[][] qualities, bool[] maximization, bool dominateOnEqualQualities, int populationSize, out Dictionary> dominatedIndividuals, out int[] dominationCounter, out int[] rank) { var front = new List>(); dominatedIndividuals = new Dictionary>(); dominationCounter = new int[populationSize]; rank = new int[populationSize]; for (var pI = 0; pI < populationSize - 1; pI++) { var p = solutions[pI]; List dominatedIndividualsByp; if (!dominatedIndividuals.TryGetValue(p, out dominatedIndividualsByp)) dominatedIndividuals[p] = dominatedIndividualsByp = new List(); for (var qI = pI + 1; qI < populationSize; qI++) { var test = Dominates(qualities[pI], qualities[qI], maximization, dominateOnEqualQualities); if (test == DominationResult.Dominates) { dominatedIndividualsByp.Add(qI); dominationCounter[qI] += 1; } else if (test == DominationResult.IsDominated) { dominationCounter[pI] += 1; if (!dominatedIndividuals.ContainsKey(solutions[qI])) dominatedIndividuals.Add(solutions[qI], new List()); dominatedIndividuals[solutions[qI]].Add(pI); } if (pI == populationSize - 2 && qI == populationSize - 1 && dominationCounter[qI] == 0) { rank[qI] = 0; front.Add(Tuple.Create(solutions[qI], qualities[qI])); } } if (dominationCounter[pI] == 0) { rank[pI] = 0; front.Add(Tuple.Create(p, qualities[pI])); } } return front; } /// /// Calculates all pareto fronts by returning the index of the parameters in each front. /// The first in the list is the best front. /// The fast non-dominated sorting algorithm is used as described in /// Deb, K., Pratap, A., Agarwal, S., and Meyarivan, T. (2002). /// A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. /// IEEE Transactions on Evolutionary Computation, 6(2), 182-197. /// /// /// When there are plateaus in the fitness landscape several solutions might have exactly /// the same fitness vector. In this case parameter /// can be set to true to avoid plateaus becoming too attractive for the search process. /// /// The solutions of the population. /// The qualities resp. fitness for each solution. /// The objective in each dimension. /// Whether solutions of exactly equal quality should dominate one another. /// A sorted list of the pareto fronts where each front contains the indices of the and . public static List> CalculateAllParetoFrontsIndices(T[] solutions, double[][] qualities, bool[] maximization, bool dominateOnEqualQualities = true) { return CalculateAllParetoFrontsIndices(solutions, qualities, maximization, out var rank, dominateOnEqualQualities); } /// /// Calculates all pareto fronts by returning the index of the parameters in each front. /// The first in the list is the best front. /// The fast non-dominated sorting algorithm is used as described in /// Deb, K., Pratap, A., Agarwal, S., and Meyarivan, T. (2002). /// A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. /// IEEE Transactions on Evolutionary Computation, 6(2), 182-197. /// /// /// When there are plateaus in the fitness landscape several solutions might have exactly /// the same fitness vector. In this case parameter /// can be set to true to avoid plateaus becoming too attractive for the search process. /// /// The solutions of the population. /// The qualities resp. fitness for each solution. /// The objective in each dimension. /// The rank of each of the solutions, corresponds to the front it is put in. /// Whether solutions of exactly equal quality should dominate one another. /// A sorted list of the pareto fronts where each front contains the indices of the and . public static List> CalculateAllParetoFrontsIndices(T[] solutions, double[][] qualities, bool[] maximization, out int[] rank, bool dominateOnEqualQualities = true) { var populationSize = solutions.Length; var dominatedIndividuals = Enumerable.Range(0, qualities.Length).Select(x => new List()).ToArray(); var dominationCounter = new int[populationSize]; rank = new int[populationSize]; var fronts = new List>(); fronts.Add(CalculateBestFrontIndices(solutions, qualities, maximization, dominateOnEqualQualities, populationSize, dominatedIndividuals, dominationCounter, rank)); var i = 0; while (i < fronts.Count && fronts[i].Count > 0) { var nextFront = new List(); foreach (var p in fronts[i]) { if (dominatedIndividuals[p].Count > 0) { for (var k = 0; k < dominatedIndividuals[p].Count; k++) { var dominatedIndividual = dominatedIndividuals[p][k]; dominationCounter[dominatedIndividual] -= 1; if (dominationCounter[dominatedIndividual] == 0) { rank[dominatedIndividual] = i + 1; nextFront.Add(dominatedIndividual); } } } } i += 1; fronts.Add(nextFront); } return fronts; } private static List CalculateBestFrontIndices(T[] solutions, double[][] qualities, bool[] maximization, bool dominateOnEqualQualities, int populationSize, List[] dominatedIndividuals, int[] dominationCounter, int[] rank) { var front = new List(); for (var pI = 0; pI < populationSize - 1; pI++) { var p = solutions[pI]; for (var qI = pI + 1; qI < populationSize; qI++) { var test = Dominates(qualities[pI], qualities[qI], maximization, dominateOnEqualQualities); if (test == DominationResult.Dominates) { dominatedIndividuals[pI].Add(qI); dominationCounter[qI] += 1; } else if (test == DominationResult.IsDominated) { dominationCounter[pI] += 1; dominatedIndividuals[qI].Add(pI); } if (pI == populationSize - 2 && qI == populationSize - 1 && dominationCounter[qI] == 0) { rank[qI] = 0; front.Add(qI); } } if (dominationCounter[pI] == 0) { rank[pI] = 0; front.Add(pI); } } return front; } /// /// Calculates the domination result of two solutions which are given in form /// of their quality resp. fitness vector. /// /// The fitness of the solution that is to be compared. /// The fitness of the solution which is compared against. /// The objective in each dimension. /// Whether the result should be Dominates in case both fitness vectors are exactly equal /// Dominates if left dominates right, IsDominated if right dominates left and IsNonDominated otherwise. public static DominationResult Dominates(double[] left, double[] right, bool[] maximizations, bool dominateOnEqualQualities) { //mkommend Caution: do not use LINQ.SequenceEqual for comparing the two quality arrays (left and right) due to performance reasons if (dominateOnEqualQualities) { var equal = true; for (var i = 0; i < left.Length; i++) { if (left[i] != right[i]) { equal = false; break; } } if (equal) return DominationResult.Dominates; } bool leftIsBetter = false, rightIsBetter = false; for (var i = 0; i < left.Length; i++) { if (IsDominated(left[i], right[i], maximizations[i])) rightIsBetter = true; else if (IsDominated(right[i], left[i], maximizations[i])) leftIsBetter = true; if (leftIsBetter && rightIsBetter) break; } if (leftIsBetter && !rightIsBetter) return DominationResult.Dominates; if (!leftIsBetter && rightIsBetter) return DominationResult.IsDominated; return DominationResult.IsNonDominated; } /// /// A simple check if the quality resp. fitness in is better than /// that given in . /// /// The first fitness value /// The second fitness value /// The objective direction /// True if left is better than right, false if it is not. public static bool IsDominated(double left, double right, bool maximization) { return maximization && left < right || !maximization && left > right; } } }