Solution 接口
当使用启发式算法时,首先要做的事情之一是定义如何对方案进行编码。编码方式在很大程度上取决于优化的问题以及需要进行的操作操作(如遗传算法的变异、交叉操作、本地搜索程序等)。因此,确定合适的编码方式对启发式算法的运行结果有着很大的影响。
下图描述了jMetal 5中用于表示Solution 类的基本组件:
基本组件总共有三种:二进制编码、实数编码、整数编码。这种方式可以为相同的编码提供许多实现,增加了灵活性。泛型的使用还允许编译器检查错误分配变量值所导致编译错误,例如,尝试将DoubleSolution 的变量值分配给int变量这种错误。
Solution 接口的代码如下所示
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 对象变量和目标函数的方法、复制方法以及获取solution 对象解决方案属性的方法。
编码定义
特定的编码定义需要实现或继承Solution 接口,实数编码和整数编码对应的接口如下
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) ;
}
这些接口要求实现获取上下限的方法,具体实现过程由实现类自行定义
此外二进制编码的接口代码如下:
import org.uma.jmetal.util.binarySet.BinarySet;
public interface BinarySolution extends Solution<BinarySet> {
public int getNumberOfBits(int index) ;
public int getTotalNumberOfBits() ;
}
假设我们打算表示一个二进制变量列表。 所采用的方法允许定义具有混合变量的编码。例如,此接口定义由double和int值列表组成的解决方案:
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() ;
}
接口的实现类
我们不仅提供了Solution 接口,还提供了接口的默认实现,首先就是 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 ;
这个类实现了Solution 接口中所有的方法,同时这个类也被所有的Solution 实现类所继承,包括: DefaultBinarySolution , DefaultIntegerSolution , DefaultDoubleSolution , DefaultIntegerDoubleSolution , DefaultIntegerPermutationSolution , 和 DefaultDoubleBinarySolution .
种群概念
在jMetal5中取消了专门作为“种群”的类,而是以 Solution 的集合代替:
List<DoubleSolution> doublePopulation ;
List<Solution<Double>> doublePopulation ;
List<BinarySolucion> binaryPopulation ;
名为SolutionListUtils的工具类提供了一系列种群的处理操作,比如查找最佳/最差方案,随机选择解决方案等
个体属性
jMetal同样支持引入属性。比如 NSGA-II 算法要求对个体进行排序以及拥挤度的相关计算,而SPEA2为个体设置了适应度函数。
可以直接操作属性,我们也涉及了包括此实用程序接口:
package org.uma.jmetal.util.solutionattribute;
public interface SolutionAttribute <S extends Solution<?>, V> {
public void setAttribute(S solution, V value) ;
public V getAttribute(S solution) ;
public Object getAttributeID() ;
}
默认实现类
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() ;
}
}
请注意,在当前实现中,GetAttributeId() 返回一个类标识符。这意味着同一类不能有两个不同的属性。
属性应用实例:约束条件
优化问题可能含有边界约束,这表明个体solution 的评估就不仅需要对目标函数进行计算,还要对约束进行处理。在jMetal 5中,属性被用于向问题中引入约束条件。
默认的约束处理机制是在NSGA-II中所定义的全局约束冲突处理机制,该类如下:
package org.uma.jmetal.util.solutionattribute.impl;
public class OverallConstraintViolation<S extends Solution<?>> extends GenericSolutionAttribute<S, Double> {
}
这是一个为属性指定了double类型的、继承了GenericSolutionAttribute 类的空类。通常约束条件被引入定义问题的类当中进行个体的评估,如下所示:
package org.uma.jmetal.problem.multiobjective;
public class Binh2 extends AbstractDoubleProblem implements ConstrainedProblem<DoubleSolution> {
public OverallConstraintViolation<DoubleSolution> overallConstraintViolationDegree ;
public NumberOfViolatedConstraints<DoubleSolution> numberOfViolatedConstraints ;
public Binh2() {
...
overallConstraintViolationDegree = new OverallConstraintViolation<DoubleSolution>() ;
numberOfViolatedConstraints = new NumberOfViolatedConstraints<DoubleSolution>() ;
}
@Override
public void evaluate(DoubleSolution solution) {
...
}
@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);
}
此外还包含了另外一个NumberOfViolatedConstraints 属性,用于设定特定的个体所违反的约束的个数
属性应用实例:范围值
针对属性的操作是可以被封装的,例如我们封装了以下接口来为solution进行排序(NSGA-II的排序):
package org.uma.jmetal.util.solutionattribute;
import java.util.List;
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() ;
}
因此客户端类(例如,NSGAII 类)只能使用:
Ranking ranking = computeRanking(jointPopulation);
通过这种方式,solution属性由实现排序的类在内部管理,并隐藏在启发式算法中。
|