https://github.com/jMetal/jMetalDocumentation/blob/master/solution.md I'm just translating this roughly. The text below.
Solution interface When using metaheuristics, we must first define how to express and encode the solution to the problem to be solved. The expression of a solution is strongly dependent on the problem, and the applicable operations (e.g., recombination with other solutions, local search procedure, etc.) are determined by the expression. Therefore, the choice of solution expression method has a great influence on the behavior of metaheuristics and, by extension, the quality of the results obtained.
The figure below shows the basic classes used to represent a solution in jMetal 5.
This figure contains three solution expressions: Binary, Double, and Integer.
This approach provides many implementations for the same encoding and provides greater flexibility.
Also, by using generics, incorrect variable value assignment can be found with a compile error.
For example, if you try to assign the value of DoubleSolution
to an int variable, an error will occur.
The code for the Solution
interface is as follows.
package org.uma.jmetal.solution;
public interface Solution<T> extends Serializable {
public void setObjective(int index, double value) ;
public double getObjective(int index) ;
public T getVariableValue(int index) ;
public void setVariableValue(int index, T value) ;
public String getVariableValueString(int index) ;
public int getNumberOfVariables() ;
public int getNumberOfObjectives() ;
public Solution<T> copy() ;
public void setAttribute(Object id, Object value) ;
public Object getAttribute(Object id) ;
}
Solution
has a method to access variables and objective functions, a copy method, and a method to access attributes.
Defining encodings
A specific encoding is defined by the implementation or extension of the Solution
interface.
The interface for a solution with a list of Double and Integer variables is defined as follows:
package org.uma.jmetal.solution;
public interface DoubleSolution extends Solution<Double> {
public Double getLowerBound(int index) ;
public Double getUpperBound(int index) ;
}
package org.uma.jmetal.solution;
public interface IntegerSolution extends Solution<Integer> {
public Integer getLowerBound(int index) ;
public Integer getUpperBound(int index) ;
}
These interfaces provide methods to get the lower and upper bounds of Double and Intefer variables. The method of setting these values is left to the implementation class.
In the case of BinarySolution
, it is as follows.
import org.uma.jmetal.util.binarySet.BinarySet;
public interface BinarySolution extends Solution<BinarySet> {
public int getNumberOfBits(int index) ;
public int getTotalNumberOfBits() ;
}
It is assumed that it represents a list of binary variables.
Allows you to define encodings with mixed variables. For example, the following interface defines a Solution that consists of a list of double and integer values.
package org.uma.jmetal.solution;
public interface IntegerDoubleSolution extends Solution<Number> {
public Number getLowerBound(int index) ;
public Number getUpperBound(int index) ;
public int getNumberOfIntegerVariables() ;
public int getNumberOfDoubleVariables() ;
}
Implementing solutions We have defined interfaces for various solutions. The following provides default implementations for all of them. Let's start with an abstract class called ʻAbstractGenericSolution`.
package org.uma.jmetal.solution.impl;
public abstract class AbstractGenericSolution<T, P extends Problem<?>> implements Solution<T> {
private double[] objectives;
private List<T> variables;
protected P problem ;
protected double overallConstraintViolationDegree ;
protected int numberOfViolatedConstraints ;
protected Map<Object, Object> attributes ;
protected final JMetalRandom randomGenerator ;
ʻAbstractGenericSolution contains all the methods of
Solution. This class is a superclass of all
Solution implementations of jMetal5. (
DefaultBinarySolution,
DefaultIntegerSolution,
DefaultDoubleSolution,
DefaultIntegerDoubleSolution,
DefaultIntegerPermutationSolution,
DefaultDoubleBinarySolution` .)
Where are the populations?
In jMetal5, there is no class that expresses population.
Instead, List <Solution>
is used as the population.
Example:
/* A population of double solutions */
List<DoubleSolution> doublePopulation ;
/* The same population using the generic interface */
List<Solution<Double>> doublePopulation ;
/* A population of binary solutions */
List<BinarySolucion> binaryPopulation ;
The utility class SolutionListUtils
provides a set of operations forList <Solution>
.
(For example, finding the best / worst individual, randomly selecting a solution, etc.)
Solution attributes By incorporating attributes, you can add specific fields to the solution that are needed by some algorithms. For example, in SPEA2, the fitness is assigned to the solution as it is, but in NSGA-II, it is necessary to rank the solution and assign the value of the congestion distance.
Attributes can be manipulated directly, but the following interfaces are also defined.
package org.uma.jmetal.util.solutionattribute;
/**
* Attributes allows to extend the {@link Solution} classes to incorporate data required by
* operators or algorithms manipulating them.
*
* @author Antonio J. Nebro <[email protected]>
*/
public interface SolutionAttribute <S extends Solution<?>, V> {
public void setAttribute(S solution, V value) ;
public V getAttribute(S solution) ;
public Object getAttributeID() ;
}
The default implementation is as follows.
package org.uma.jmetal.util.solutionattribute.impl;
public class GenericSolutionAttribute <S extends Solution<?>, V> implements SolutionAttribute<S, V>{
@SuppressWarnings("unchecked")
@Override
public V getAttribute(S solution) {
return (V)solution.getAttribute(getAttributeID());
}
@Override
public void setAttribute(S solution, V value) {
solution.setAttribute(getAttributeID(), value);
}
@Override
public Object getAttributeID() {
return this.getClass() ;
}
}
Note that in this implementation getAttributedID ()
returns the class identifier.
This means that the solution cannot have another attribute of the same class.
Example of attrivute: constraints
Optimization problems can be constrained.
This means that in order to evaluate the solution, it is necessary to evaluate not only the objective function but also the constraints for applying some kind of constraint processing mechanism.
SoluttionAttribute
is used to incorporate constraint information into Solution
.
The default constraint processing mechanism of jMetal is defined in NSGA-II. Since it is ʻOverallConstraintViolation`, the following classes are prepared.
package org.uma.jmetal.util.solutionattribute.impl;
public class OverallConstraintViolation<S extends Solution<?>> extends GenericSolutionAttribute<S, Double> {
}
This is an empty class that extends GenericSolutionAttribute
to specify continuous values.
Normally, constraints are evaluated in the Solultion
class as follows.
package org.uma.jmetal.problem.multiobjective;
/** Class representing problem Binh2 */
public class Binh2 extends AbstractDoubleProblem implements ConstrainedProblem<DoubleSolution> {
public OverallConstraintViolation<DoubleSolution> overallConstraintViolationDegree ;
public NumberOfViolatedConstraints<DoubleSolution> numberOfViolatedConstraints ;
/**
* Constructor
* Creates a default instance of the Binh2 problem
*/
public Binh2() {
...
overallConstraintViolationDegree = new OverallConstraintViolation<DoubleSolution>() ;
numberOfViolatedConstraints = new NumberOfViolatedConstraints<DoubleSolution>() ;
}
/** Evaluate() method */
@Override
public void evaluate(DoubleSolution solution) {
...
}
/** EvaluateConstraints() method */
@Override
public void evaluateConstraints(DoubleSolution solution) {
double[] constraint = new double[this.getNumberOfConstraints()];
double x0 = solution.getVariableValue(0) ;
double x1 = solution.getVariableValue(1) ;
constraint[0] = -1.0 * (x0 - 5) * (x0 - 5) - x1 * x1 + 25.0;
constraint[1] = (x0 - 8) * (x0 - 8) + (x1 + 3) * (x1 + 3) - 7.7;
double overallConstraintViolation = 0.0;
int violatedConstraints = 0;
for (int i = 0; i < this.getNumberOfConstraints(); i++) {
if (constraint[i] < 0.0) {
overallConstraintViolation += constraint[i];
violatedConstraints++;
}
}
overallConstraintViolationDegree.setAttribute(solution, overallConstraintViolation);
numberOfViolatedConstraints.setAttribute(solution, violatedConstraints);
}
This code also contains another attribute called NumberOfViolatedConstraints
to set the number of constraints that the specified solution violates.
(As far as this code is seen, it seems that the constraint value is implemented as satisfying the constraint when it is positive and violating the constraint when it is negative.)
Example of attribute: ranking
It can be encapsulated by using SolutionAttribute
.
As an example, an interface that assigns ranks (NSGA-II's ranking) to the following solutions is defined.
package org.uma.jmetal.util.solutionattribute;
import java.util.List;
/**
* Ranks a list of solutions according to the dominance relationship
*
* @author Antonio J. Nebro <[email protected]>
*/
public interface Ranking<S extends Solution<?>> extends SolutionAttribute<S, Integer>{
public Ranking<S> computeRanking(List<S> solutionList) ;
public List<S> getSubfront(int rank) ;
public int getNumberOfSubfronts() ;
}
Therefore, the client class (eg NSGA-II class) can be used simply as follows.
Ranking ranking = computeRanking(jointPopulation);
In this way, the attributes of the solution are managed internally by the class that implements Ranking
and are hidden from the algorithm.