// AbYSS.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.metaheuristics.abyss;
import jmetal.core.*;
import jmetal.operators.localSearch.LocalSearch;
import jmetal.util.Distance;
import jmetal.util.JMException;
import jmetal.util.PseudoRandom;
import jmetal.util.Spea2Fitness;
import jmetal.util.archive.CrowdingArchive;
import jmetal.util.comparators.CrowdingDistanceComparator;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.EqualSolutions;
import jmetal.util.comparators.FitnessComparator;
import jmetal.util.wrapper.XReal;
import java.util.Comparator;
/**
* This class implements the AbYSS algorithm. This algorithm is an adaptation
* of the single-objective scatter search template defined by F. Glover in:
* F. Glover. "A template for scatter search and path relinking", Lecture Notes
* in Computer Science, Springer Verlag, 1997. AbYSS is described in:
* A.J. Nebro, F. Luna, E. Alba, B. Dorronsoro, J.J. Durillo, A. Beham
* "AbYSS: Adapting Scatter Search to Multiobjective Optimization."
* IEEE Transactions on Evolutionary Computation. Vol. 12,
* No. 4 (August 2008), pp. 439-457
*/
public class AbYSS extends Algorithm {
/**
* Stores the number of subranges in which each encodings.variable is divided. Used in
* the diversification method. By default it takes the value 4 (see the method
* <code>initParams</code>).
*/
int numberOfSubranges_ ;
/**
* These variables are used in the diversification method.
*/
int [] sumOfFrequencyValues_ ;
int [] sumOfReverseFrequencyValues_ ;
int [][] frequency_ ;
int [][] reverseFrequency_ ;
/**
* Stores the initial solution set
*/
private SolutionSet solutionSet_;
/**
* Stores the external solution archive
*/
private CrowdingArchive archive_ ;
/**
* Stores the reference set one
*/
private SolutionSet refSet1_ ;
/**
* Stores the reference set two
*/
private SolutionSet refSet2_ ;
/**
* Stores the solutions provided by the subset generation method of the
* scatter search template
*/
private SolutionSet subSet_ ;
/**
* Maximum number of solution allowed for the initial solution set
*/
private int solutionSetSize_;
/**
* Maximum size of the external archive
*/
private int archiveSize_;
/**
* Maximum size of the reference set one
*/
private int refSet1Size_;
/**
* Maximum size of the reference set two
*/
private int refSet2Size_;
/**
* Maximum number of getEvaluations to carry out
*/
private int maxEvaluations;
/**
* Stores the current number of performed getEvaluations
*/
private int evaluations_ ;
/**
* Stores the comparators for dominance and equality, respectively
*/
private Comparator dominance_ ;
private Comparator equal_ ;
private Comparator fitness_ ;
private Comparator crowdingDistance_;
/**
* Stores the crossover operator
*/
private Operator crossoverOperator_;
/**
* Stores the improvement operator
*/
private LocalSearch improvementOperator_;
/**
* Stores a <code>Distance</code> object
*/
private Distance distance_;
/**
* Constructor.
* @param problem Problem to solve
*/
public AbYSS(Problem problem){
super (problem) ;
//Initialize the fields
solutionSet_ = null ;
archive_ = null ;
refSet1_ = null ;
refSet2_ = null ;
subSet_ = null ;
} // AbYSS
/**
* Reads the parameter from the parameter list using the
* <code>getInputParameter</code> method.
*/
public void initParam(){
//Read the parameters
solutionSetSize_= ((Integer)getInputParameter("populationSize")).intValue();
refSet1Size_ = ((Integer)getInputParameter("refSet1Size")).intValue();
refSet2Size_ = ((Integer)getInputParameter("refSet2Size")).intValue();
archiveSize_ = ((Integer)getInputParameter("archiveSize")).intValue();
maxEvaluations = ((Integer)getInputParameter("maxEvaluations")).intValue();
//Initialize the variables
solutionSet_ = new SolutionSet(solutionSetSize_);
archive_ = new CrowdingArchive(archiveSize_,problem_.getNumberOfObjectives());
refSet1_ = new SolutionSet(refSet1Size_);
refSet2_ = new SolutionSet(refSet2Size_);
subSet_ = new SolutionSet(solutionSetSize_*1000);
evaluations_ = 0 ;
numberOfSubranges_ = 4 ;
dominance_ = new DominanceComparator();
equal_ = new EqualSolutions();
fitness_ = new FitnessComparator();
crowdingDistance_ = new CrowdingDistanceComparator();
distance_ = new Distance();
sumOfFrequencyValues_ = new int[problem_.getNumberOfVariables()] ;
sumOfReverseFrequencyValues_ = new int[problem_.getNumberOfVariables()] ;
frequency_ = new int[numberOfSubranges_][problem_.getNumberOfVariables()] ;
reverseFrequency_ = new int[numberOfSubranges_][problem_.getNumberOfVariables()] ;
//Read the operators of crossover and improvement
crossoverOperator_ = operators_.get("crossover");
improvementOperator_ = (LocalSearch) operators_.get("improvement");
improvementOperator_.setParameter("archive",archive_);
} // initParam
/**
* Returns a <code>Solution</code> using the diversification generation method
* described in the scatter search template.
* @throws JMException
* @throws ClassNotFoundException
*/
public Solution diversificationGeneration() throws JMException, ClassNotFoundException{
Solution solution ;
solution = new Solution(problem_) ;
XReal wrapperSolution = new XReal(solution) ;
double value ;
int range ;
for (int i = 0; i < problem_.getNumberOfVariables(); i++) {
sumOfReverseFrequencyValues_[i] = 0 ;
for (int j = 0; j < numberOfSubranges_; j++) {
reverseFrequency_[j][i] = sumOfFrequencyValues_[i] - frequency_[j][i] ;
sumOfReverseFrequencyValues_[i] += reverseFrequency_[j][i] ;
} // for
if (sumOfReverseFrequencyValues_[i] == 0) {
range = PseudoRandom.randInt(0, numberOfSubranges_ - 1) ;
} else {
value = PseudoRandom.randInt(0, sumOfReverseFrequencyValues_[i] - 1) ;
range = 0 ;
while (value > reverseFrequency_[range][i]) {
value -= reverseFrequency_[range][i] ;
range++ ;
} // while
} // else
frequency_[range][i] ++ ;
sumOfFrequencyValues_[i] ++ ;
double low = problem_.getLowerLimit(i) + range*(problem_.getUpperLimit(i) -
problem_.getLowerLimit(i)) / numberOfSubranges_ ;
double high = low + (problem_.getUpperLimit(i) -
problem_.getLowerLimit(i)) / numberOfSubranges_ ;
value = PseudoRandom.randDouble(low, high) ;
//solution.getDecisionVariables()[i].setValue(value);
wrapperSolution.setValue(i, value);
} // for
return solution ;
} // diversificationGeneration
/**
* Implements the referenceSetUpdate method.
* @param build if true, indicates that the reference has to be build for the
* first time; if false, indicates that the reference set has to be
* updated with new solutions
* @throws JMException
*/
public void referenceSetUpdate(boolean build) throws JMException{
if (build) { // Build a new reference set
// STEP 1. Select the p best individuals of P, where p is refSet1Size_.
// Selection Criterium: Spea2Fitness
Solution individual;
(new Spea2Fitness(solutionSet_)).fitnessAssign();
solutionSet_.sort(fitness_);
// STEP 2. Build the RefSet1 with these p individuals
for (int i = 0; i < refSet1Size_; i++) {
individual = solutionSet_.get(0);
solutionSet_.remove(0);
individual.unMarked();
refSet1_.add(individual);
}
// STEP 3. Compute Euclidean distances in SolutionSet to obtain q
// individuals, where q is refSet2Size_
for (int i = 0; i < solutionSet_.size();i++){
individual = solutionSet_.get(i);
individual.setDistanceToSolutionSet(distance_.distanceToSolutionSetInSolutionSpace(individual,refSet1_));
}
int size = refSet2Size_;
if (solutionSet_.size() < refSet2Size_) {
size = solutionSet_.size();
}
// STEP 4. Build the RefSet2 with these q individuals
for (int i = 0; i < size; i++){
// Find the maximumMinimunDistanceToPopulation
double maxMinimum = 0.0;
int index = 0;
for (int j = 0; j < solutionSet_.size(); j++) {
if (solutionSet_.get(j).getDistanceToSolutionSet() > maxMinimum){
maxMinimum = solutionSet_.get(j).getDistanceToSolutionSet();
index = j;
}
}
individual = solutionSet_.get(index);
solutionSet_.remove(index);
// Update distances to REFSET in population
for (int j = 0; j < solutionSet_.size(); j++){
double aux = distance_.distanceBetweenSolutions(solutionSet_.get(j),individual);
if (aux < individual.getDistanceToSolutionSet()){
solutionSet_.get(j).setDistanceToSolutionSet(aux);
}
}
// Insert the individual into REFSET2
refSet2_.add(individual);
// Update distances in REFSET2
for (int j = 0; j < refSet2_.size();j++){
for (int k = 0; k < refSet2_.size();k++){
if (i != j){
double aux = distance_.distanceBetweenSolutions(refSet2_.get(j),refSet2_.get(k));
if (aux < refSet2_.get(j).getDistanceToSolutionSet()){
refSet2_.get(j).setDistanceToSolutionSet(aux);
} // if
} // if
} // for
} // for
} // for
} else { // Update the reference set from the subset generation result
Solution individual;
for (int i = 0; i < subSet_.size();i++){
individual = (Solution)improvementOperator_.execute(subSet_.get(i));
evaluations_ += improvementOperator_.getEvaluations();
if (refSet1Test(individual)){ //Update distance of RefSet2
for (int indSet2 = 0; indSet2 < refSet2_.size();indSet2++) {
double aux = distance_.distanceBetweenSolutions(individual,
refSet2_.get(indSet2));
if (aux < refSet2_.get(indSet2).getDistanceToSolutionSet()) {
refSet2_.get(indSet2).setDistanceToSolutionSet(aux);
} // if
} // for
} else {
refSet2Test(individual);
} // if
}
subSet_.clear();
}
} // referenceSetUpdate
/**
* Tries to update the reference set 2 with a <code>Solution</code>
* @param solution The <code>Solution</code>
* @return true if the <code>Solution</code> has been inserted, false
* otherwise.
* @throws JMException
*/
public boolean refSet2Test(Solution solution) throws JMException{
if (refSet2_.size() < refSet2Size_){
solution.setDistanceToSolutionSet(distance_.distanceToSolutionSetInSolutionSpace(solution,refSet1_));
double aux = distance_.distanceToSolutionSetInSolutionSpace(solution,refSet2_);
if (aux < solution.getDistanceToSolutionSet()) {
solution.setDistanceToSolutionSet(aux);
}
refSet2_.add(solution);
return true;
}
solution.setDistanceToSolutionSet(distance_.distanceToSolutionSetInSolutionSpace(solution,refSet1_));
double aux = distance_.distanceToSolutionSetInSolutionSpace(solution,refSet2_);
if (aux < solution.getDistanceToSolutionSet()) {
solution.setDistanceToSolutionSet(aux);
}
double peor = 0.0;
int index = 0;
for (int i = 0; i < refSet2_.size();i++){
aux = refSet2_.get(i).getDistanceToSolutionSet();
if (aux > peor){
peor = aux;
index = i;
}
}
if (solution.getDistanceToSolutionSet() < peor){
refSet2_.remove(index);
//Update distances in REFSET2
for (int j = 0; j < refSet2_.size();j++){
aux = distance_.distanceBetweenSolutions(refSet2_.get(j),solution);
if (aux < refSet2_.get(j).getDistanceToSolutionSet()){
refSet2_.get(j).setDistanceToSolutionSet(aux);
}
}
solution.unMarked();
refSet2_.add(solution);
return true;
}
return false;
} // refSet2Test
/**
* Tries to update the reference set one with a <code>Solution</code>.
* @param solution The <code>Solution</code>
* @return true if the <code>Solution</code> has been inserted, false
* otherwise.
*/
public boolean refSet1Test(Solution solution){
boolean dominated = false;
int flag;
int i = 0;
while (i < refSet1_.size()){
flag = dominance_.compare(solution,refSet1_.get(i));
if (flag == -1) { //This is: solution dominates
refSet1_.remove(i);
} else if (flag == 1) {
dominated = true;
i++;
} else {
flag = equal_.compare(solution,refSet1_.get(i));
if (flag == 0) {
return true;
} // if
i++;
} // if
} // while
if (!dominated){
solution.unMarked();
if (refSet1_.size() < refSet1Size_) { //refSet1 isn't full
refSet1_.add(solution);
} else {
archive_.add(solution);
} // if
} else {
return false;
} // if
return true;
} // refSet1Test
/**
* Implements the subset generation method described in the scatter search
* template
* @return Number of solutions created by the method
* @throws JMException
*/
public int subSetGeneration() throws JMException{
Solution [] parents = new Solution[2];
Solution [] offSpring;
subSet_.clear();
//All pairs from refSet1
for (int i = 0; i < refSet1_.size();i++){
parents[0] = refSet1_.get(i);
for (int j = i+1; j < refSet1_.size();j++){
parents[1] = refSet1_.get(j);
if (!parents[0].isMarked() || !parents[1].isMarked()){
//offSpring = parent1.crossover(1.0,parent2);
offSpring = (Solution [])crossoverOperator_.execute(parents);
problem_.evaluate(offSpring[0]);
problem_.evaluate(offSpring[1]);
problem_.evaluateConstraints(offSpring[0]);
problem_.evaluateConstraints(offSpring[1]);
evaluations_ += 2;
if (evaluations_ < maxEvaluations){
subSet_.add(offSpring[0]);
subSet_.add(offSpring[1]);
}
parents[0].marked();
parents[1].marked();
}
}
}
// All pairs from refSet2
for (int i = 0; i < refSet2_.size();i++){
parents[0] = refSet2_.get(i);
for (int j = i+1; j < refSet2_.size();j++){
parents[1] = refSet2_.get(j);
if (!parents[0].isMarked() || !parents[1].isMarked()){
//offSpring = parents[0].crossover(1.0,parent2);
offSpring = (Solution []) crossoverOperator_.execute(parents);
problem_.evaluateConstraints(offSpring[0]);
problem_.evaluateConstraints(offSpring[1]);
problem_.evaluate(offSpring[0]);
problem_.evaluate(offSpring[1]);
evaluations_+=2;
if (evaluations_ < maxEvaluations){
subSet_.add(offSpring[0]);
subSet_.add(offSpring[1]);
}
parents[0].marked();
parents[1].marked();
}
}
}
return subSet_.size();
} // subSetGeneration
/**
* Runs of the AbYSS algorithm.
* @return a <code>SolutionSet</code> that is a set of non dominated solutions
* as a result of the algorithm execution
* @throws JMException
*/
public SolutionSet execute() throws JMException, ClassNotFoundException {
// STEP 1. Initialize parameters
initParam();
// STEP 2. Build the initial solutionSet
Solution solution;
for (int i = 0; i < solutionSetSize_; i++) {
solution = diversificationGeneration();
problem_.evaluate(solution);
problem_.evaluateConstraints(solution);
evaluations_++;
solution = (Solution)improvementOperator_.execute(solution);
evaluations_ += improvementOperator_.getEvaluations();
solutionSet_.add(solution);
} // fpr
// STEP 3. Main loop
int newSolutions = 0;
while (evaluations_ < maxEvaluations) {
referenceSetUpdate(true);
newSolutions = subSetGeneration();
while (newSolutions > 0) { // New solutions are created
referenceSetUpdate(false);
if (evaluations_ >= maxEvaluations)
return archive_;
newSolutions = subSetGeneration();
} // while
// RE-START
if (evaluations_ < maxEvaluations){
solutionSet_.clear();
// Add refSet1 to SolutionSet
for (int i = 0; i < refSet1_.size();i++){
solution = refSet1_.get(i);
solution.unMarked();
solution = (Solution)improvementOperator_.execute(solution);
evaluations_ += improvementOperator_.getEvaluations();
solutionSet_.add(solution);
}
// Remove refSet1 and refSet2
refSet1_.clear();
refSet2_.clear();
// Sort the archive and insert the best solutions
distance_.crowdingDistanceAssignment(archive_,
problem_.getNumberOfObjectives());
archive_.sort(crowdingDistance_);
int insert = solutionSetSize_ / 2;
if (insert > archive_.size())
insert = archive_.size();
if (insert > (solutionSetSize_ - solutionSet_.size()))
insert = solutionSetSize_ - solutionSet_.size();
// Insert solutions
for (int i = 0; i < insert; i++){
solution = new Solution(archive_.get(i));
//solution = improvement(solution);
solution.unMarked();
solutionSet_.add(solution);
}
// Create the rest of solutions randomly
while (solutionSet_.size() < solutionSetSize_){
solution = diversificationGeneration();
problem_.evaluateConstraints(solution);
problem_.evaluate(solution);
evaluations_++;
solution = (Solution)improvementOperator_.execute(solution);
evaluations_ += improvementOperator_.getEvaluations();
solution.unMarked();
solutionSet_.add(solution);
} // while
} // if
} // while
archive_.printFeasibleFUN("FUN_AbYSS") ;
// STEP 4. Return the archive
return archive_;
} // execute
} // AbYSS