// Spea2Fitness.java
//
// Author:
// Antonio J. Nebro <antonio@lcc.uma.es>
// Juan J. Durillo <durillo@lcc.uma.es>
//
// Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package jmetal.util;
import jmetal.core.SolutionSet;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.FitnessComparator;
import java.util.*;
/**
* This class implements some facilities for calculating the Spea2 fitness
*/
public class Spea2Fitness {
/**
* Stores the distance between solutions
*/
private double [][] distance = null;
/**
* Stores the solutionSet to assign the fitness
*/
private SolutionSet solutionSet_ = null;
/**
* stores a <code>Distance</code> object
*/
private static final Distance distance_ = new Distance();
/**
* stores a <code>Comparator</code> for distance between nodes checking
*/
private static final Comparator distanceNodeComparator = new DistanceNodeComparator();
/**
* stores a <code>Comparator</code> for dominance checking
*/
private static final Comparator dominance_ = new DominanceComparator();
/**
* Constructor.
* Creates a new instance of Spea2Fitness for a given <code>SolutionSet</code>.
* @param solutionSet The <code>SolutionSet</code>
*/
public Spea2Fitness(SolutionSet solutionSet) {
distance = distance_.distanceMatrix(solutionSet);
solutionSet_ = solutionSet;
for (int i = 0; i < solutionSet_.size(); i++) {
solutionSet_.get(i).setLocation(i);
} // for
} // Spea2Fitness
/**
* Assigns fitness for all the solutions.
*/
public void fitnessAssign() {
double [] strength = new double[solutionSet_.size()];
double [] rawFitness = new double[solutionSet_.size()];
double kDistance ;
//Calculate the strength value
// strength(i) = |{j | j <- SolutionSet and i dominate j}|
for (int i = 0; i < solutionSet_.size(); i++) {
for (int j = 0; j < solutionSet_.size();j++) {
if (dominance_.compare(solutionSet_.get(i),solutionSet_.get(j))==-1) {
strength[i] += 1.0;
} // if
} // for
} // for
//Calculate the raw fitness
// rawFitness(i) = |{sum strenght(j) | j <- SolutionSet and j dominate i}|
for (int i = 0;i < solutionSet_.size(); i++) {
for (int j = 0; j < solutionSet_.size();j++) {
if (dominance_.compare(solutionSet_.get(i),solutionSet_.get(j))==1) {
rawFitness[i] += strength[j];
} // if
} // for
} // for
// Add the distance to the k-th individual. In the reference paper of SPEA2,
// k = sqrt(population.size()), but a value of k = 1 recommended. See
// http://www.tik.ee.ethz.ch/pisa/selectors/spea2/spea2_documentation.txt
int k = 1 ;
for (int i = 0; i < distance.length; i++) {
Arrays.sort(distance[i]);
kDistance = 1.0 / (distance[i][k] + 2.0); // Calcule de D(i) distance
//population.get(i).setFitness(rawFitness[i]);
solutionSet_.get(i).setFitness(rawFitness[i] + kDistance);
} // for
} // fitnessAsign
/**
* Gets 'size' elements from a population of more than 'size' elements
* using for this de enviromentalSelection truncation
* @param size The number of elements to get.
*/
public SolutionSet environmentalSelection(int size){
if (solutionSet_.size() < size) {
size = solutionSet_.size();
}
// Create a new auxiliar population for no alter the original population
SolutionSet aux = new SolutionSet(solutionSet_.size());
int i = 0;
while (i < solutionSet_.size()){
if (solutionSet_.get(i).getFitness()<1.0){
aux.add(solutionSet_.get(i));
solutionSet_.remove(i);
} else {
i++;
} // if
} // while
if (aux.size() < size){
Comparator comparator = new FitnessComparator();
solutionSet_.sort(comparator);
int remain = size - aux.size();
for (i = 0; i < remain; i++){
aux.add(solutionSet_.get(i));
}
return aux;
} else if (aux.size() == size) {
return aux;
}
double [][] distance = distance_.distanceMatrix(aux);
List< List<DistanceNode> > distanceList = new LinkedList< List<DistanceNode> >();
for (int pos = 0; pos < aux.size(); pos++) {
aux.get(pos).setLocation(pos);
List<DistanceNode> distanceNodeList = new ArrayList<DistanceNode>();
for (int ref = 0; ref < aux.size(); ref++) {
if (pos != ref) {
distanceNodeList.add(new DistanceNode(distance[pos][ref],ref));
} // if
} // for
distanceList.add(distanceNodeList);
} // for
for (List<DistanceNode> aDistanceList : distanceList) {
Collections.sort(aDistanceList, distanceNodeComparator);
} // for
while (aux.size() > size) {
double minDistance = Double.MAX_VALUE;
int toRemove = 0;
i = 0;
for (List<DistanceNode> dn : distanceList) {
if (dn.get(0).getDistance() < minDistance) {
toRemove = i;
minDistance = dn.get(0).getDistance();
//i y toRemove have the same distance to the first solution
} else if (dn.get(0).getDistance() == minDistance) {
int k = 0;
while ((dn.get(k).getDistance() ==
distanceList.get(toRemove).get(k).getDistance()) &&
k < (distanceList.get(i).size() - 1)) {
k++;
}
if (dn.get(k).getDistance() <
distanceList.get(toRemove).get(k).getDistance()) {
toRemove = i;
} // if
} // if
i++;
} // while
int tmp = aux.get(toRemove).getLocation();
aux.remove(toRemove);
distanceList.remove(toRemove);
for (List<DistanceNode> aDistanceList : distanceList) {
Iterator<DistanceNode> interIterator = aDistanceList.iterator();
while (interIterator.hasNext()) {
if (interIterator.next().getReference() == tmp) {
interIterator.remove();
} // if
} // while
} // while
} // while
return aux;
} // environmentalSelection
} // Spea2Fitness