public void AssignCrowdingDistance_InfiniteReferencePoint_Works() { ParetoFrontMetrics.AssignCrowdingDistance(new[] { moTestInd, indParetoEqual1, indParetoEqual2, indParetoEqual3 }, "crowdingDistance", minimise, moTestInd.SolutionVector.Select(i => double.MaxValue)); Assert.True(moTestInd.GetProperty <double>("crowdingDistance") > 1e300); Assert.True(indParetoEqual1.GetProperty <double>("crowdingDistance") > 1e300); // Crowding distance should be 0.09/0.1 + 0.18/0.2 + 0.09/0.1 Assert.True(Math.Abs(indParetoEqual2.GetProperty <double>("crowdingDistance") - 2.7) < 1e-6); Assert.True(indParetoEqual3.GetProperty <double>("crowdingDistance") > 1e300); }
public void AssignCrowdingDistance_InsideReferencePoint_Works() { ParetoFrontMetrics.AssignCrowdingDistance(new[] { moTestInd, indParetoEqual1, indParetoEqual2, indParetoEqual3 }, "crowdingDistance", minimise, new[] { WorstSolution1 + 0.1, WorstSolution2 - 0.02, WorstSolution3 + 0.1 }); // Best for objective 3 Assert.True(moTestInd.GetProperty <double>("crowdingDistance") > 1e300); // Out of bounds on objective 2 Assert.Equal(0.0, indParetoEqual1.GetProperty <double>("crowdingDistance")); // Crowding distance should be 0.09/0.1 + 0.16/0.18 + 0.09/0.1 Assert.True(Math.Abs(indParetoEqual2.GetProperty <double>("crowdingDistance") - 2.68888889) < 1e-6); // Best for objective 2 Assert.True(indParetoEqual3.GetProperty <double>("crowdingDistance") > 1e300); }
/// <summary> /// Performs the fitness calculation based on: /// 1) Non-dominated sorting into Pareto Fronts; /// 2) Crowding distance assignment and comparison /// </summary> /// <remarks> /// If the number of individuals provided is less than the population size provided in the constructor, /// then only a simple fitness assignment is performed (assumes this is used during initial population /// creation: <see cref="EvolutionaryAlgorithm"/>). /// </remarks> /// <param name="individuals">All individuals to be considered for fitness calculation.</param> public void CalculateAndAssignFitness(IEnumerable <Individual> individuals) { var inds = individuals as Individual[] ?? individuals.ToArray(); if (inds.Length <= populationSize) { // Don't bother with Pareto Front calculation, it's too time consuming. // Just assign sum of ranks on each objective. for (var m = 0; m < inds[0].SolutionVector.Length; m++) { var individualsOrderedByThisObjective = inds.OrderBy(i => i.SolutionVector.ElementAt(m) * (minimise[m] ? 1 : -1)).ToArray(); for (int o = 0; o < individualsOrderedByThisObjective.Count(); o++) { individualsOrderedByThisObjective[o].SetFitness(o + individualsOrderedByThisObjective[o].Fitness); } } return; } if (useReferencePoint & double.IsInfinity(referencePoint.ElementAt(0))) { // Calculate a reference point for (var m = 0; m < inds[0].SolutionVector.Length; m++) { // Use the worst value found for each objective, after the initial population generation. referencePoint[m] = minimise[m] ? inds.Max(i => i.SolutionVector.ElementAt(m)) : inds.Min(i => i.SolutionVector.ElementAt(m)); } } // Calculate Pareto Fronts sorter.PerformSort(inds, minimise); // Go through, assigning fitness var paretoFront = 1; var currentParetoFront = inds.Where(i => i.GetProperty <int>(OptimiserPropertyNames.ParetoFront) == paretoFront).ToArray(); var individualsAssessed = 0; while (currentParetoFront.Length > 0) { foreach (var ind in currentParetoFront) { ind.SetFitness(paretoFront); } if (individualsAssessed < populationSize && individualsAssessed + currentParetoFront.Length > populationSize) { // We're in the Pareto Front which partially overlaps with the population size. // Perform crowding distance assignment ParetoFrontMetrics.AssignCrowdingDistance(currentParetoFront, CrowdingDistance, minimise, referencePoint); var sortedParetoFront = currentParetoFront .OrderByDescending(i => i.GetProperty <double>(CrowdingDistance)) .ToArray(); var crowdingComparison = 0.0; for (int i = 1; i < sortedParetoFront.Length; i++) { if (sortedParetoFront[i - 1].GetProperty <double>(CrowdingDistance) > sortedParetoFront[i].GetProperty <double>(CrowdingDistance)) { crowdingComparison += 1.0 / (currentParetoFront.Length + 10.0); // Small enough that we won't end up more than 1 in total... } sortedParetoFront[i].SetFitness(sortedParetoFront[i].Fitness + crowdingComparison); } } individualsAssessed += currentParetoFront.Length; // Get next Pareto Front paretoFront++; currentParetoFront = inds.Where(i => i.GetProperty <int>(OptimiserPropertyNames.ParetoFront) == paretoFront).ToArray(); } }