Jenetics 中用于嵌套的自定义基因型
Custom Genotype in Jenetics for nesting
我正在使用 Jenetics 使用 NoFitPolygon 嵌套多边形列表
我有一个函数可以按照列表中的顺序给出多边形嵌套列表。
我修改了 TravellingSalesman 问题,以便获得代表多边形列表的基因型,并在进化过程中改变顺序。
现在我想将旋转添加到基因型中,以获得更好的结果,但我不知道如何将其添加到我的代码中。
目前嵌套路径的顺序决定了适应性,我想做的是为每个嵌套路径添加一个双精度值(doubleChromosome?)到表示嵌套路径旋转的基因型。
嵌套路径是代表多边形的class
public class NFP_Nesting implements Problem<ISeq<NestPath>, EnumGene<NestPath>, Double>{
static Phenotype<EnumGene<NestPath>,Double> tmpBest = null;
static Result tmpBestResult =null;
private NestPath _binPolygon;
Map<String,List<NestPath>> nfpCache=new HashMap<>();
private final ISeq<NestPath> _list;
public NFP_Nesting(ISeq<NestPath> lista,NestPath binpolygon ,double binw, double binh)
{
_binPolygon = binpolygon;
_list=Objects.requireNonNull(lista);
}
@Override
public Codec<ISeq<NestPath>, EnumGene<NestPath>> codec() {
return Codecs.ofPermutation(_list);
}
@Override
public Function<ISeq<NestPath>, Double> fitness() {
return this::scalar_fitness;
}
/**
* @param seq_nestpath
* @return Fitness of the model
*/
Double scalar_fitness(final ISeq<NestPath> seq_nestpath) {
List<NestPath> paths = seq_nestpath.asList();
final Random random = RandomRegistry.random();
//TODO NOT RANDOM ROTATION
List<Integer> ids = new ArrayList<>();
for(int i = 0 ; i < paths.size(); i ++){
ids.add(paths.get(i).getId());
NestPath n = paths.get(i);
if(n.getPossibleRotations()!= null)
{
n.setRotation(n.getPossibleRotations()[random.nextInt(n.getPossibleRotations().length)]);
}
}
///complicated function here
return fitness;
}
private static NFP_Nesting of (List<NestPath> l, NestPath binpol, double binw, double binh)
{
final MSeq<NestPath> paths = MSeq.ofLength(l.size());
final Random random = RandomRegistry.random();
for ( int i = 0 ; i < l.size(); ++i ) {
paths.set(i, l.get(i));
}
//initial shuffle list of polygons
for(int j=paths.length()-1; j>0;--j)
{
final int i = random.nextInt(j+1);
final NestPath tmp=paths.get(i);
paths.set(i, paths.get(j));
paths.set(j, tmp);
}
return new NFP_Nesting(paths.toISeq(),binpol,binw,binh);
}
主要:
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1);
NFP_Nesting nst = NFP_Nesting.of(tree,binPolygon,binWidth,binHeight);
Engine<EnumGene<NestPath>,Double> engine = Engine
.builder(nst)
.optimize(Optimize.MINIMUM)
.populationSize(config.POPULATION_SIZE)
.executor(executor)
.alterers(
new SwapMutator<>(0.25),
new PartiallyMatchedCrossover<>(0.35)
)
.build();
final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
Phenotype<EnumGene<NestPath>,Double> best=
engine.stream()
.limit(Limits.bySteadyFitness(5))
.limit(Limits.byExecutionTime(Duration.ofSeconds(MAX_SEC_DURATION)))
//.limit(10)
.peek(NFP_Nesting::update)
.peek(statistics)
.collect(toBestPhenotype());
System.out.println(statistics);
//System.out.println(best);
}
我不确定我是否完全理解您要解决的问题,但是排列编解码器 (Codecs.ofPermutation(Seq)
) 用于给定对象序列的顺序决定其适合度的问题。所以,只有 ISeq<NestPath>
中的 NestPath
元素的顺序定义了适应度,对吗?健身功能不负责通过副作用改变您的对象。
我也不明白你说的旋转基因型是什么意思。 Jenetics 中的 Genotype
只是 染色体序列。也许您可以更详细地解释您的优化问题。
对您的代码本身的一些评论
NFP_Nesting::of
对给定的 List<NestPath>
进行洗牌。这不是必需的。
- 用
.peek(NFP_Nesting::update)
做一些更新看起来也很可疑。如果您需要更新 NestPath
对象,则不再进行排列优化。 NestPath
对象应该是不可变的。
- 不要在
Problem
实现中存储任何 static 变量。 Problem
接口只是结合了 Codec
和适应度函数,应该是不可变的。
所以,如果你能更清楚地解释你的问题,我可以尝试帮助找到合适的编码。
我想我现在明白你的问题了。你想要做的是在基因型中编码一个额外的旋转。但是 Jenetics 只允许每个基因型有一种基因类型。使用 permutation-codec,您将 gene-type 固定为 EnumGene
,对于轮换,您将需要 DoubleGene
。通过一个简单的技巧,我们也可以通过使用染色体内基因的顺序将路径排列表示为 DoubleGene
。
以下代码将向您展示如何执行此操作。
import static java.util.Objects.requireNonNull;
import java.util.function.Function;
import java.util.stream.IntStream;
import io.jenetics.DoubleChromosome;
import io.jenetics.DoubleGene;
import io.jenetics.Genotype;
import io.jenetics.MeanAlterer;
import io.jenetics.Phenotype;
import io.jenetics.SwapMutator;
import io.jenetics.engine.Codec;
import io.jenetics.engine.Engine;
import io.jenetics.engine.EvolutionResult;
import io.jenetics.engine.Limits;
import io.jenetics.engine.Problem;
import io.jenetics.example.NfpNesting.Solution;
import io.jenetics.util.DoubleRange;
import io.jenetics.util.ISeq;
import io.jenetics.util.ProxySorter;
public class NfpNesting implements Problem<Solution, DoubleGene, Double> {
record NestPath() {}
record Solution(ISeq<NestPath> paths, double[] rotations) {
Solution {
assert paths.length() == rotations.length;
}
}
private final Codec<Solution, DoubleGene> code;
public NfpNesting(final ISeq<NestPath> paths) {
requireNonNull(paths);
code = Codec.of(
Genotype.of(
// Encoding the order of the `NestPath` as double chromosome.
// The order is given by the sorted gene values.
DoubleChromosome.of(DoubleRange.of(0, 1), paths.length()),
// Encoding the rotation of each `NestPath`.
DoubleChromosome.of(DoubleRange.of(0, 2*Math.PI), paths.length())
),
gt -> {
/*
The `order` array contains the sorted indexes.
This for-loop will print the genes in ascending order.
for (var index : order) {
System.out.println(gt.get(0).get(index));
}
*/
final int[] order = ProxySorter.sort(gt.get(0));
// Use the `order` indexes to "permute" your path elements.
final ISeq<NestPath> pseq = IntStream.of(order)
.mapToObj(paths::get)
.collect(ISeq.toISeq());
// The second chromosome just contains your rotation values.
final double[] rotations = gt.get(1)
.as(DoubleChromosome.class)
.toArray();
return new Solution(pseq, rotations);
}
);
}
@Override
public Codec<Solution, DoubleGene> codec() {
return code;
}
@Override
public Function<Solution, Double> fitness() {
return solution -> {
final ISeq<NestPath> paths = solution.paths();
final double[] rotations = solution.rotations();
// Do your fitness calculation.
return 0.0;
};
}
public static void main(final String[] args) {
final var paths = ISeq.<NestPath>of();
final var nesting = new NfpNesting(paths);
final Engine<DoubleGene, Double> engine = Engine.builder(nesting)
.alterers(
new MeanAlterer<>(),
new SwapMutator<>())
.build();
final Phenotype<DoubleGene, Double> best = engine.stream()
.limit(Limits.bySteadyFitness(5))
.collect(EvolutionResult.toBestPhenotype());
System.out.println("Best Fitness: " + best.fitness());
System.out.println("Best paths: " + nesting.decode(best.genotype()).paths());
}
}
我正在使用 Jenetics 使用 NoFitPolygon 嵌套多边形列表
我有一个函数可以按照列表中的顺序给出多边形嵌套列表。
我修改了 TravellingSalesman 问题,以便获得代表多边形列表的基因型,并在进化过程中改变顺序。
现在我想将旋转添加到基因型中,以获得更好的结果,但我不知道如何将其添加到我的代码中。
目前嵌套路径的顺序决定了适应性,我想做的是为每个嵌套路径添加一个双精度值(doubleChromosome?)到表示嵌套路径旋转的基因型。
嵌套路径是代表多边形的class
public class NFP_Nesting implements Problem<ISeq<NestPath>, EnumGene<NestPath>, Double>{
static Phenotype<EnumGene<NestPath>,Double> tmpBest = null;
static Result tmpBestResult =null;
private NestPath _binPolygon;
Map<String,List<NestPath>> nfpCache=new HashMap<>();
private final ISeq<NestPath> _list;
public NFP_Nesting(ISeq<NestPath> lista,NestPath binpolygon ,double binw, double binh)
{
_binPolygon = binpolygon;
_list=Objects.requireNonNull(lista);
}
@Override
public Codec<ISeq<NestPath>, EnumGene<NestPath>> codec() {
return Codecs.ofPermutation(_list);
}
@Override
public Function<ISeq<NestPath>, Double> fitness() {
return this::scalar_fitness;
}
/**
* @param seq_nestpath
* @return Fitness of the model
*/
Double scalar_fitness(final ISeq<NestPath> seq_nestpath) {
List<NestPath> paths = seq_nestpath.asList();
final Random random = RandomRegistry.random();
//TODO NOT RANDOM ROTATION
List<Integer> ids = new ArrayList<>();
for(int i = 0 ; i < paths.size(); i ++){
ids.add(paths.get(i).getId());
NestPath n = paths.get(i);
if(n.getPossibleRotations()!= null)
{
n.setRotation(n.getPossibleRotations()[random.nextInt(n.getPossibleRotations().length)]);
}
}
///complicated function here
return fitness;
}
private static NFP_Nesting of (List<NestPath> l, NestPath binpol, double binw, double binh)
{
final MSeq<NestPath> paths = MSeq.ofLength(l.size());
final Random random = RandomRegistry.random();
for ( int i = 0 ; i < l.size(); ++i ) {
paths.set(i, l.get(i));
}
//initial shuffle list of polygons
for(int j=paths.length()-1; j>0;--j)
{
final int i = random.nextInt(j+1);
final NestPath tmp=paths.get(i);
paths.set(i, paths.get(j));
paths.set(j, tmp);
}
return new NFP_Nesting(paths.toISeq(),binpol,binw,binh);
}
主要:
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1);
NFP_Nesting nst = NFP_Nesting.of(tree,binPolygon,binWidth,binHeight);
Engine<EnumGene<NestPath>,Double> engine = Engine
.builder(nst)
.optimize(Optimize.MINIMUM)
.populationSize(config.POPULATION_SIZE)
.executor(executor)
.alterers(
new SwapMutator<>(0.25),
new PartiallyMatchedCrossover<>(0.35)
)
.build();
final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
Phenotype<EnumGene<NestPath>,Double> best=
engine.stream()
.limit(Limits.bySteadyFitness(5))
.limit(Limits.byExecutionTime(Duration.ofSeconds(MAX_SEC_DURATION)))
//.limit(10)
.peek(NFP_Nesting::update)
.peek(statistics)
.collect(toBestPhenotype());
System.out.println(statistics);
//System.out.println(best);
}
我不确定我是否完全理解您要解决的问题,但是排列编解码器 (Codecs.ofPermutation(Seq)
) 用于给定对象序列的顺序决定其适合度的问题。所以,只有 ISeq<NestPath>
中的 NestPath
元素的顺序定义了适应度,对吗?健身功能不负责通过副作用改变您的对象。
我也不明白你说的旋转基因型是什么意思。 Jenetics 中的 Genotype
只是 染色体序列。也许您可以更详细地解释您的优化问题。
对您的代码本身的一些评论
NFP_Nesting::of
对给定的List<NestPath>
进行洗牌。这不是必需的。- 用
.peek(NFP_Nesting::update)
做一些更新看起来也很可疑。如果您需要更新NestPath
对象,则不再进行排列优化。NestPath
对象应该是不可变的。 - 不要在
Problem
实现中存储任何 static 变量。Problem
接口只是结合了Codec
和适应度函数,应该是不可变的。
所以,如果你能更清楚地解释你的问题,我可以尝试帮助找到合适的编码。
我想我现在明白你的问题了。你想要做的是在基因型中编码一个额外的旋转。但是 Jenetics 只允许每个基因型有一种基因类型。使用 permutation-codec,您将 gene-type 固定为 EnumGene
,对于轮换,您将需要 DoubleGene
。通过一个简单的技巧,我们也可以通过使用染色体内基因的顺序将路径排列表示为 DoubleGene
。
以下代码将向您展示如何执行此操作。
import static java.util.Objects.requireNonNull;
import java.util.function.Function;
import java.util.stream.IntStream;
import io.jenetics.DoubleChromosome;
import io.jenetics.DoubleGene;
import io.jenetics.Genotype;
import io.jenetics.MeanAlterer;
import io.jenetics.Phenotype;
import io.jenetics.SwapMutator;
import io.jenetics.engine.Codec;
import io.jenetics.engine.Engine;
import io.jenetics.engine.EvolutionResult;
import io.jenetics.engine.Limits;
import io.jenetics.engine.Problem;
import io.jenetics.example.NfpNesting.Solution;
import io.jenetics.util.DoubleRange;
import io.jenetics.util.ISeq;
import io.jenetics.util.ProxySorter;
public class NfpNesting implements Problem<Solution, DoubleGene, Double> {
record NestPath() {}
record Solution(ISeq<NestPath> paths, double[] rotations) {
Solution {
assert paths.length() == rotations.length;
}
}
private final Codec<Solution, DoubleGene> code;
public NfpNesting(final ISeq<NestPath> paths) {
requireNonNull(paths);
code = Codec.of(
Genotype.of(
// Encoding the order of the `NestPath` as double chromosome.
// The order is given by the sorted gene values.
DoubleChromosome.of(DoubleRange.of(0, 1), paths.length()),
// Encoding the rotation of each `NestPath`.
DoubleChromosome.of(DoubleRange.of(0, 2*Math.PI), paths.length())
),
gt -> {
/*
The `order` array contains the sorted indexes.
This for-loop will print the genes in ascending order.
for (var index : order) {
System.out.println(gt.get(0).get(index));
}
*/
final int[] order = ProxySorter.sort(gt.get(0));
// Use the `order` indexes to "permute" your path elements.
final ISeq<NestPath> pseq = IntStream.of(order)
.mapToObj(paths::get)
.collect(ISeq.toISeq());
// The second chromosome just contains your rotation values.
final double[] rotations = gt.get(1)
.as(DoubleChromosome.class)
.toArray();
return new Solution(pseq, rotations);
}
);
}
@Override
public Codec<Solution, DoubleGene> codec() {
return code;
}
@Override
public Function<Solution, Double> fitness() {
return solution -> {
final ISeq<NestPath> paths = solution.paths();
final double[] rotations = solution.rotations();
// Do your fitness calculation.
return 0.0;
};
}
public static void main(final String[] args) {
final var paths = ISeq.<NestPath>of();
final var nesting = new NfpNesting(paths);
final Engine<DoubleGene, Double> engine = Engine.builder(nesting)
.alterers(
new MeanAlterer<>(),
new SwapMutator<>())
.build();
final Phenotype<DoubleGene, Double> best = engine.stream()
.limit(Limits.bySteadyFitness(5))
.collect(EvolutionResult.toBestPhenotype());
System.out.println("Best Fitness: " + best.fitness());
System.out.println("Best paths: " + nesting.decode(best.genotype()).paths());
}
}