Search code examples
javaoptimizationgenetic-algorithmjenetics

Multi-objective problem with non-primitive types in Jenetics


I would like to implement a multi-objective knapsack problem with two knapsacks in Jenetics, but I struggle with some typing issues. I had a look at the DTLZ1 problem from the Jenetics manual—which is to my knowledge the only MOO example available—and mapped it to the Problem class:

public class DTLZ1 implements Problem<double[], DoubleGene, Vec<double[]>> {

    // Constants...

    public static void main(String[] args) {
        // Engine setup and evolution stream execution...
    }

    @Override
    public Function<double[], Vec<double[]>> fitness() {
        // Original fitness function...
    }

    @Override
    public Codec<double[], DoubleGene> codec() {
        // Original codec...
    }

}

I have previously implemented a single-objective knapsack problem in Scala using the following type signature (converted to Java):

Problem<ISeq<BitGene>, BitGene, Integer>

Where:

  • <ISeq<BitGene>: a knapsack as a (immutable) sequence of bits.
  • BitGene: the gene type of the evolution engine.
  • Integer: the fitness of the knapsack, i.e., its profit.

Using two knapsacks, I thought about something like (based on the DTLZ1 example):

Problem<ISeq<BitGene>[], BitGene, Vec<int[]>>

Where:

  • ISeq<BitGene>[]: multiple knapsacks as (immutable) sequences of bits, wrapped in an array.
  • BitGene: the gene type of the evolution engine (same as above).
  • int[]: the fitness of the knapsacks, i.e., their profit.

Apart from ISeq<BitGene>[], which takes some time getting used to (could I also use List or something similar?), I don't know how to create a proper codec:

@Override
public Codec<ISeq<BitGene>[], BitGene> codec() {
    return Codecs.ofVector(
            () -> {
                // What kind of supplier do I need?
            },
            NUMBER_OF_KNAPSACKS);
}

Solution

  • If I understand your problem correctly, the codec would look like this:

    public static <T> Codec<ISeq<ISeq<T>>, BitGene>
    codec(final ISeq<? extends T> items, final int knapsackCount) {
        return Codec.of(
            Genotype.of(
                BitChromosome.of(items.length()).instances()
                    .limit(knapsackCount)
                    .collect(ISeq.toISeq())
            ),
            gt -> gt.stream()
                .map(ch -> ch.as(BitChromosome.class))
                .map(ch -> ch.ones()
                    .<T>mapToObj(items)
                    .collect(ISeq.toISeq()))
                .collect(ISeq.toISeq())
        );
    }
    

    Instead of an ISeq<T>[] array, I'm using a ISeq<ISeq<T>>, but the size of the first sequence will be knapsackCount and the size of the nested sequence is itmes.length(). The signature of your problem will be Problem<ISeq<ISeq<T>>, BitGene, Vec<double[]>>.