#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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;
namespace HeuristicLab.Optimization {
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) {
int 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) {
int 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));
int 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 (int k = 0; k < dominatedIndividualsByp.Count; k++) {
int 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 (int pI = 0; pI < populationSize - 1; pI++) {
var p = solutions[pI];
List dominatedIndividualsByp;
if (!dominatedIndividuals.TryGetValue(p, out dominatedIndividualsByp))
dominatedIndividuals[p] = dominatedIndividualsByp = new List();
for (int 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 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 (int 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 (int 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;
}
}
}