// AdaptiveGrid.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.Solution;
import jmetal.core.SolutionSet;
/**
* This class defines an adaptive grid over a SolutionSet as the one used the
* algorithm PAES.
*/
public class AdaptiveGrid {
/**
* Number of bi-divisions of the objective space
*/
private int bisections_ ;
/**
* Objectives of the problem
*/
private int objectives_ ;
/**
* Number of solutions into a specific hypercube in the adaptative grid
*/
private int [] hypercubes_ ;
/**
*
* Grid lower bounds
*/
private double [] lowerLimits_ ;
/**
* Grid upper bounds
*/
private double [] upperLimits_ ;
/**
* Size of hypercube for each dimension
*/
private double [] divisionSize_ ;
/**
* Hypercube with maximum number of solutions
*/
private int mostPopulated_ ;
/**
* Hndicates when an hypercube has solutions
*/
private int [] occupied_ ;
/**
* Constructor.
* Creates an instance of AdaptativeGrid.
* @param bisections Number of bi-divisions of the objective space.
* @param objetives Number of objectives of the problem.
*/
public AdaptiveGrid(int bisections, int objetives) {
bisections_ = bisections;
objectives_ = objetives ;
lowerLimits_ = new double[objectives_];
upperLimits_ = new double[objectives_];
divisionSize_ = new double[objectives_];
hypercubes_ = new int[(int)Math.pow(2.0,bisections_*objectives_)];
for (int i = 0; i < hypercubes_.length;i++)
hypercubes_[i] = 0;
} //AdaptativeGrid
/**
* Updates the grid limits considering the solutions contained in a
* <code>SolutionSet</code>.
* @param solutionSet The <code>SolutionSet</code> considered.
*/
private void updateLimits(SolutionSet solutionSet){
//Init the lower and upper limits
for (int obj = 0; obj < objectives_; obj++){
//Set the lower limits to the max real
lowerLimits_[obj] = Double.MAX_VALUE;
//Set the upper limits to the min real
upperLimits_[obj] = Double.MIN_VALUE;
} // for
//Find the max and min limits of objetives into the population
for (int ind = 0; ind < solutionSet.size(); ind++){
Solution tmpIndividual = solutionSet.get(ind);
for (int obj = 0; obj < objectives_; obj++) {
if (tmpIndividual.getObjective(obj) < lowerLimits_[obj]) {
lowerLimits_[obj] = tmpIndividual.getObjective(obj);
}
if (tmpIndividual.getObjective(obj) > upperLimits_[obj]) {
upperLimits_[obj] = tmpIndividual.getObjective(obj);
}
} // for
} // for
} //updateLimits
/**
* Updates the grid adding solutions contained in a specific
* <code>SolutionSet</code>.
* <b>REQUIRE</b> The grid limits must have been previously calculated.
* @param solutionSet The <code>SolutionSet</code> considered.
*/
private void addSolutionSet(SolutionSet solutionSet){
//Calculate the location of all individuals and update the grid
mostPopulated_ = 0;
int location;
for (int ind = 0; ind < solutionSet.size();ind++){
location = location(solutionSet.get(ind));
hypercubes_[location]++;
if (hypercubes_[location]>hypercubes_[mostPopulated_])
mostPopulated_ = location;
} // for
//The grid has been updated, so also update ocuppied's hypercubes
calculateOccupied();
} // addSolutionSet
/**
* Updates the grid limits and the grid content adding the solutions contained
* in a specific <code>SolutionSet</code>.
* @param solutionSet The <code>SolutionSet</code>.
*/
public void updateGrid(SolutionSet solutionSet){
//Update lower and upper limits
updateLimits(solutionSet);
//Calculate the division size
for (int obj = 0; obj < objectives_; obj++){
divisionSize_[obj] = upperLimits_[obj] - lowerLimits_[obj];
} // for
//Clean the hypercubes
for (int i = 0; i < hypercubes_.length;i++) {
hypercubes_[i] = 0;
}
//Add the population
addSolutionSet(solutionSet);
} //updateGrid
/**
* Updates the grid limits and the grid content adding a new
* <code>Solution</code>.
* If the solution falls out of the grid bounds, the limits and content of the
* grid must be re-calculated.
* @param solution <code>Solution</code> considered to update the grid.
* @param solutionSet <code>SolutionSet</code> used to update the grid.
*/
public void updateGrid(Solution solution, SolutionSet solutionSet){
int location = location(solution);
if (location == -1) {//Re-build the Adaptative-Grid
//Update lower and upper limits
updateLimits(solutionSet);
//Actualize the lower and upper limits whit the individual
for (int obj = 0; obj < objectives_; obj++){
if (solution.getObjective(obj) < lowerLimits_[obj])
lowerLimits_[obj] = solution.getObjective(obj);
if (solution.getObjective(obj) > upperLimits_[obj])
upperLimits_[obj] = solution.getObjective(obj);
} // for
//Calculate the division size
for (int obj = 0; obj < objectives_; obj++){
divisionSize_[obj] = upperLimits_[obj] - lowerLimits_[obj];
}
//Clean the hypercube
for (int i = 0; i < hypercubes_.length; i++) {
hypercubes_[i] = 0;
}
//add the population
addSolutionSet(solutionSet);
} // if
} //updateGrid
/**
* Calculates the hypercube of a solution.
* @param solution The <code>Solution</code>.
*/
public int location(Solution solution){
//Create a int [] to store the range of each objetive
int [] position = new int[objectives_];
//Calculate the position for each objetive
for (int obj = 0; obj < objectives_; obj++) {
if ((solution.getObjective(obj) > upperLimits_[obj])
|| (solution.getObjective(obj) < lowerLimits_[obj]))
return -1;
else if (solution.getObjective(obj) ==lowerLimits_[obj])
position[obj] = 0;
else if (solution.getObjective(obj) ==upperLimits_[obj])
position[obj] = ((int)Math.pow(2.0,bisections_))-1;
else {
double tmpSize = divisionSize_[obj];
double value = solution.getObjective(obj);
double account = lowerLimits_[obj];
int ranges = (int)Math.pow(2.0,bisections_);
for (int b = 0; b < bisections_; b++){
tmpSize /= 2.0;
ranges /= 2;
if (value > (account + tmpSize)){
position[obj] += ranges;
account += tmpSize;
} // if
} // for
} // if
}
//Calcualate the location into the hypercubes
int location = 0;
for (int obj = 0; obj < objectives_; obj++) {
location += position[obj] * Math.pow(2.0,obj * bisections_);
}
return location;
} //location
/**
* Returns the value of the most populated hypercube.
* @return The hypercube with the maximum number of solutions.
*/
public int getMostPopulated(){
return mostPopulated_;
} // getMostPopulated
/**
* Returns the number of solutions into a specific hypercube.
* @param location Number of the hypercube.
* @return The number of solutions into a specific hypercube.
*/
public int getLocationDensity(int location){
return hypercubes_[location];
} //getLocationDensity
/**
* Decreases the number of solutions into a specific hypercube.
* @param location Number of hypercube.
*/
public void removeSolution(int location) {
//Decrease the solutions in the location specified.
hypercubes_[location]--;
//Update the most poblated hypercube
if (location == mostPopulated_)
for (int i = 0; i < hypercubes_.length;i++)
if (hypercubes_[i] > hypercubes_[mostPopulated_])
mostPopulated_ = i;
//If hypercubes[location] now becomes to zero, then update ocupped hypercubes
if (hypercubes_[location]==0)
this.calculateOccupied();
} //removeSolution
/**
* Increases the number of solutions into a specific hypercube.
* @param location Number of hypercube.
*/
public void addSolution(int location) {
//Increase the solutions in the location specified.
hypercubes_[location]++;
//Update the most poblated hypercube
if (hypercubes_[location] > hypercubes_[mostPopulated_])
mostPopulated_ = location;
//if hypercubes[location] becomes to one, then recalculate
//the occupied hypercubes
if (hypercubes_[location] == 1)
this.calculateOccupied();
} //addSolution
/**
* Returns the number of bi-divisions performed in each objective.
* @return the number of bi-divisions.
*/
public int getBisections() {
return bisections_;
} //getBisections
/**
* Retunrns a String representing the grid.
* @return The String.
*/
public String toString() {
String result = "Grid\n";
for (int obj = 0; obj < objectives_; obj++){
result += "Objective " + obj + " " + lowerLimits_[obj] + " "
+ upperLimits_[obj]+"\n";
} // for
return result;
} // toString
/**
* Returns a random hypercube using a rouleteWheel method.
* @return the number of the selected hypercube.
*/
public int rouletteWheel(){
//Calculate the inverse sum
double inverseSum = 0.0;
for (int aHypercubes_ : hypercubes_) {
if (aHypercubes_ > 0) {
inverseSum += 1.0 / (double) aHypercubes_;
}
}
//Calculate a random value between 0 and sumaInversa
double random = PseudoRandom.randDouble(0.0,inverseSum);
int hypercube = 0;
double accumulatedSum = 0.0;
while (hypercube < hypercubes_.length){
if (hypercubes_[hypercube] > 0) {
accumulatedSum += 1.0 / (double)hypercubes_[hypercube];
} // if
if (accumulatedSum > random) {
return hypercube;
} // if
hypercube++;
} // while
return hypercube;
} //rouletteWheel
/**
* Calculates the number of hypercubes having one or more solutions.
* return the number of hypercubes with more than zero solutions.
*/
public int calculateOccupied(){
int total = 0;
for (int aHypercubes_ : hypercubes_) {
if (aHypercubes_ > 0) {
total++;
} // if
} // for
occupied_ = new int[total];
int base = 0;
for (int i = 0; i < hypercubes_.length;i++){
if (hypercubes_[i] > 0){
occupied_[base] = i;
base++;
} // if
} // for
return total;
} //calculateOcuppied
/**
* Returns the number of hypercubes with more than zero solutions.
* @return the number of hypercubes with more than zero solutions.
*/
public int occupiedHypercubes(){
return occupied_.length;
} // occupiedHypercubes
/**
* Returns a random hypercube that has more than zero solutions.
* @return The hypercube.
*/
public int randomOccupiedHypercube(){
int rand = PseudoRandom.randInt(0,occupied_.length-1);
return occupied_[rand];
} //randomOccupiedHypercube
} //AdaptativeGrid