#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
using HeuristicLab.Common;
using HeuristicLab.Core;
using System;
using System.Collections.Generic;
using System.Linq;
namespace HeuristicLab.Algorithms.DynamicALPS
{
public static class DynamicALPSUtil
{
public static void QuickSort(double[] array, int[] idx, int from, int to)
{
if (from < to)
{
double temp = array[to];
int tempIdx = idx[to];
int i = from - 1;
for (int j = from; j < to; j++)
{
if (array[j] <= temp)
{
i++;
double tempValue = array[j];
array[j] = array[i];
array[i] = tempValue;
int tempIndex = idx[j];
idx[j] = idx[i];
idx[i] = tempIndex;
}
}
array[to] = array[i + 1];
array[i + 1] = temp;
idx[to] = idx[i + 1];
idx[i + 1] = tempIdx;
QuickSort(array, idx, from, i);
QuickSort(array, idx, i + 1, to);
}
}
public static void MinFastSort(double[] x, int[] idx, int n, int m)
{
for (int i = 0; i < m; i++)
{
for (int j = i + 1; j < n; j++)
{
if (x[i] > x[j])
{
double temp = x[i];
x[i] = x[j];
x[j] = temp;
int id = idx[i];
idx[i] = idx[j];
idx[j] = id;
}
}
}
}
public static IList GetSubsetOfEvenlyDistributedSolutions(IRandom random, IList solutionList, int newSolutionListSize)
{
if (solutionList == null || solutionList.Count == 0)
{
throw new ArgumentException("Solution list is null or empty.");
}
return solutionList[0].Dimensions == 2
? TwoObjectivesCase(solutionList, newSolutionListSize)
: MoreThanTwoObjectivesCase(random, solutionList, newSolutionListSize);
}
private static IList TwoObjectivesCase(IList solutionList, int newSolutionListSize)
{
var resultSolutionList = new IDynamicALPSSolution[newSolutionListSize];
// compute weight vectors
double[][] lambda_moead = new double[newSolutionListSize][];
var values = SequenceGenerator.GenerateSteps(0m, 1m, 1m / newSolutionListSize).ToArray();
for (int i = 0; i < newSolutionListSize; ++i)
{
var weights = new double[newSolutionListSize];
weights[0] = (double)values[i];
weights[1] = 1 - weights[0];
lambda_moead[i] = weights;
}
var idealPoint = new double[2];
foreach (var solution in solutionList)
{
// update ideal point
idealPoint.UpdateIdeal(solution.Qualities);
}
// Select the best solution for each weight vector
for (int i = 0; i < newSolutionListSize; i++)
{
var currentBest = solutionList[0];
double value = ScalarizingFitnessFunction(currentBest, lambda_moead[i], idealPoint);
for (int j = 1; j < solutionList.Count; j++)
{
double aux = ScalarizingFitnessFunction(solutionList[j], lambda_moead[i], idealPoint); // we are looking for the best for the weight i
if (aux < value)
{ // solution in position j is better!
value = aux;
currentBest = solutionList[j];
}
}
resultSolutionList[i] = (DynamicALPSSolution)currentBest.Clone();
}
return resultSolutionList;
}
private static IList MoreThanTwoObjectivesCase(IRandom random, IList solutionList, int newSolutionListSize)
{
var resultSolutionList = new List();
int randomIndex = random.Next(0, solutionList.Count);
var candidate = new List();
resultSolutionList.Add(solutionList[randomIndex]);
for (int i = 0; i < solutionList.Count; ++i)
{
if (i != randomIndex)
{
candidate.Add(solutionList[i]);
}
}
while (resultSolutionList.Count < newSolutionListSize)
{
int index = 0;
var selected = candidate[0]; // it should be a next! (n <= population size!)
double aux = CalculateBestDistance(selected, solutionList);
int i = 1;
while (i < candidate.Count)
{
var nextCandidate = candidate[i];
double distanceValue = CalculateBestDistance(nextCandidate, solutionList);
if (aux < distanceValue)
{
index = i;
aux = distanceValue;
}
i++;
}
// add the selected to res and remove from candidate list
var removedSolution = candidate[index];
candidate.RemoveAt(index);
resultSolutionList.Add((DynamicALPSSolution)removedSolution.Clone());
}
return resultSolutionList;
}
private static double ScalarizingFitnessFunction(IDynamicALPSSolution currentBest, double[] lambda_moead, double[] idealPoint)
{
double maxFun = -1.0e+30;
for (int n = 0; n < idealPoint.Length; n++)
{
double diff = Math.Abs(currentBest.Qualities[n] - idealPoint[n]);
double functionValue;
if (lambda_moead[n] == 0)
{
functionValue = 0.0001 * diff;
}
else
{
functionValue = diff * lambda_moead[n];
}
if (functionValue > maxFun)
{
maxFun = functionValue;
}
}
return maxFun;
}
public static void UpdateIdeal(this double[] idealPoint, double[] point)
{
for (int i = 0; i < point.Length; ++i)
{
if (double.IsInfinity(point[i]) || double.IsNaN(point[i]))
{
continue;
}
if (idealPoint[i] > point[i])
{
idealPoint[i] = point[i];
}
}
}
public static void UpdateNadir(this double[] nadirPoint, double[] point)
{
for (int i = 0; i < point.Length; ++i)
{
if (double.IsInfinity(point[i]) || double.IsNaN(point[i]))
{
continue;
}
if (nadirPoint[i] < point[i])
{
nadirPoint[i] = point[i];
}
}
}
public static void UpdateIdeal(this double[] idealPoint, IList solutions)
{
foreach (var s in solutions)
{
idealPoint.UpdateIdeal(s.Qualities);
}
}
public static void UpdateNadir(this double[] nadirPoint, IList solutions)
{
foreach (var s in solutions)
{
nadirPoint.UpdateNadir(s.Qualities);
}
}
private static double CalculateBestDistance(IDynamicALPSSolution solution, IList solutionList)
{
var best = solutionList.Min(x => EuclideanDistance(solution.Qualities, x.Qualities));
if (double.IsNaN(best) || double.IsInfinity(best))
{
best = double.MaxValue;
}
return best;
}
public static double EuclideanDistance(double[] a, double[] b)
{
if (a.Length != b.Length)
{
throw new ArgumentException("Euclidean distance: the arrays have different lengths.");
}
var distance = 0d;
for (int i = 0; i < a.Length; ++i)
{
var d = a[i] - b[i];
distance += d * d;
}
return Math.Sqrt(distance);
}
}
}