// HypervolumeArchive.java
//
// Author:
// Antonio J. Nebro <antonio@lcc.uma.es>
//
// Copyright (c) 2013 Antonio J. Nebro
//
// 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.archive;
import jmetal.core.Solution;
import jmetal.qualityIndicator.util.MetricsUtil;
import jmetal.util.Distance;
import jmetal.util.comparators.CrowdingDistanceComparator;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.EqualSolutions;
import java.util.Comparator;
/**
* This class implements a bounded archive based on crowding distances (as
* defined in NSGA-II).
*/
public class HypervolumeArchive extends Archive {
/**
* Stores the maximum size of the archive.
*/
private int maxSize_;
/**
* stores the number of the objectives.
*/
private int objectives_;
/**
* Stores a <code>Comparator</code> for dominance checking.
*/
private Comparator dominance_;
/**
* Stores a <code>Comparator</code> for equality checking (in the objective
* space).
*/
private Comparator equals_;
/**
* Stores a <code>Distance</code> object, for distances utilities
*/
private Distance distance_;
private MetricsUtil utils_ ;
private double offset_ ;
private Comparator crowdingDistance_;
/**
* Constructor.
* @param maxSize The maximum size of the archive.
* @param numberOfObjectives The number of objectives.
*/
public HypervolumeArchive(int maxSize, int numberOfObjectives) {
super(maxSize);
maxSize_ = maxSize;
objectives_ = numberOfObjectives;
dominance_ = new DominanceComparator();
equals_ = new EqualSolutions();
distance_ = new Distance();
utils_ = new MetricsUtil() ;
offset_ = 100 ;
crowdingDistance_ = new CrowdingDistanceComparator();
} // CrowdingArchive
/**
* Adds a <code>Solution</code> to the archive. If the <code>Solution</code>
* is dominated by any member of the archive, then it is discarded. If the
* <code>Solution</code> dominates some members of the archive, these are
* removed. If the archive is full and the <code>Solution</code> has to be
* inserted, the solutions are sorted by crowding distance and the one having
* the minimum crowding distance value.
* @param solution The <code>Solution</code>
* @return true if the <code>Solution</code> has been inserted, false
* otherwise.
*/
public boolean add(Solution solution){
int flag = 0;
int i = 0;
Solution aux; //Store an solution temporally
while (i < solutionsList_.size()){
aux = solutionsList_.get(i);
flag = dominance_.compare(solution,aux);
if (flag == 1) { // The solution to add is dominated
return false; // Discard the new solution
} else if (flag == -1) { // A solution in the archive is dominated
solutionsList_.remove(i); // Remove it from the population
} else {
if (equals_.compare(aux,solution)==0) { // There is an equal solution
// in the population
return false; // Discard the new solution
} // if
i++;
}
}
// Insert the solution into the archive
solutionsList_.add(solution);
if (size() > maxSize_) { // The archive is full
double[][] frontValues = this.writeObjectivesToMatrix();
int numberOfObjectives = objectives_;
// STEP 1. Obtain the maximum and minimum values of the Pareto front
double[] maximumValues = utils_.getMaximumValues(this.writeObjectivesToMatrix(), numberOfObjectives);
double[] minimumValues = utils_.getMinimumValues(this.writeObjectivesToMatrix(), numberOfObjectives);
// STEP 2. Get the normalized front
double[][] normalizedFront = utils_.getNormalizedFront(frontValues, maximumValues, minimumValues);
// compute offsets for reference point in normalized space
double[] offsets = new double[maximumValues.length];
for (i = 0; i < maximumValues.length; i++) {
offsets[i] = offset_ / (maximumValues[i] - minimumValues[i]);
}
// STEP 3. Inverse the pareto front. This is needed because the original
//metric by Zitzler is for maximization problems
double[][] invertedFront = utils_.invertedFront(normalizedFront);
// shift away from origin, so that boundary points also get a contribution > 0
for (double[] point : invertedFront) {
for (i = 0; i < point.length; i++) {
point[i] += offsets[i];
}
}
// calculate contributions and sort
double[] contributions = utils_.hvContributions(objectives_, invertedFront);
for (i = 0; i < contributions.length; i++) {
// contribution values are used analogously to crowding distance
this.get(i).setCrowdingDistance(contributions[i]);
}
this.sort(new CrowdingDistanceComparator());
//remove(indexWorst(crowdingDistance_));
remove(size()-1);
}
return true;
} // add
/**
* This method forces to compute the contribution of each solution (required for PAEShv)
*/
public void actualiseHVContribution() {
if (size() > 2) { // The contribution can be updated
double[][] frontValues = this.writeObjectivesToMatrix();
int numberOfObjectives = objectives_;
// STEP 1. Obtain the maximum and minimum values of the Pareto front
double[] maximumValues = utils_.getMaximumValues(this.writeObjectivesToMatrix(), numberOfObjectives);
double[] minimumValues = utils_.getMinimumValues(this.writeObjectivesToMatrix(), numberOfObjectives);
// STEP 2. Get the normalized front
double[][] normalizedFront = utils_.getNormalizedFront(frontValues, maximumValues, minimumValues);
// compute offsets for reference point in normalized space
double[] offsets = new double[maximumValues.length];
for (int i = 0; i < maximumValues.length; i++) {
offsets[i] = offset_ / (maximumValues[i] - minimumValues[i]);
}
// STEP 3. Inverse the pareto front. This is needed because the original
//metric by Zitzler is for maximization problems
double[][] invertedFront = utils_.invertedFront(normalizedFront);
// shift away from origin, so that boundary points also get a contribution > 0
for (double[] point : invertedFront) {
for (int i = 0; i < point.length; i++) {
point[i] += offsets[i];
}
}
// calculate contributions and sort
double[] contributions = utils_.hvContributions(objectives_, invertedFront);
for (int i = 0; i < contributions.length; i++) {
// contribution values are used analogously to crowding distance
this.get(i).setCrowdingDistance(contributions[i]);
}
}
} // computeHVContribution
/**
* This method returns the location (integer position) of a solution in the archive.
* For that, the equals_ comparator is used
*
*/
public int getLocation(Solution solution) {
int location = -1;
int index = 0;
while ((index < size()) && (location!=-1) ) {
if (equals_.compare(solution, get(index))==0) {
location = index;
}
index++;
}
return location;
}
} // HypervolumeArchive