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());
    }

}