Search code examples
javaframeworksgenetic-algorithmgenetic-programmingjenetics

Jenetics: proper usage of codec with column Genotype


currently I am working on a Jenetics (link to jenetics) implementation to optimize a particle accelerator beam line. My fitness function calls accelerator detector devices and is defined as follows:

private double fitness(final DoubleChromosome chromosomes) {
    // private double fitness(final Genotype<DoubleGene> chromosomes) {
    // Convert genes to a format the device scanner can understand
    // we will inject a 1:n Set<List<Double>>
    final Set<List<Double>> trimValues = new HashSet<>();

    final List<Double> valueList = new ArrayList<>();
    for (final DoubleGene chromosome : chromosomes) {
        valueList.add(Double.valueOf(chromosome.doubleValue()));
    }
    trimValues.add(valueList);

    ....
    more code specific to application
    }

Jenetics' stream engine is initialized in a specific method:

public void initAlgorithm(final Object scanParameters) throws Exception {
    if (scanParameters != null) {
        /// See constructor of EvolvingImagesWorker
        _geneticScanParameters = (GeneticScanParameters) scanParameters;
    }

    if (_geneticScanParameters.getTrimParameterSets() != null) {

        final int chromosomeCount = _geneticScanParameters.getTrimParameterSets().size();
        if (chromosomeCount > 0) {
            ISeq<DoubleChromosome> chromosomeSet = ISeq.empty();

            // create an ISeq of genes
            for (final TrimParameterValueSet valueSet : _geneticScanParameters.getTrimParameterSets()) {
                final double minValue = valueSet.getMinValue();
                final double maxValue = valueSet.getMaxValue();
                final double initialValue = (maxValue + minValue) / 2;

                final DoubleGene doubleGene = DoubleGene.of(initialValue, minValue, maxValue);
                final DoubleChromosome doubleChromosome = DoubleChromosome.of(doubleGene.newInstance());
                chromosomeSet = chromosomeSet.append(doubleChromosome.newInstance());
            }
            Codec<DoubleChromosome, DoubleGene> codec = null;
            try {
                final Genotype<DoubleGene> genotype = Genotype.of(chromosomeSet);
                codec = Codec.of(genotype.newInstance(), //
                        gt -> (DoubleChromosome) gt.getChromosome());
            } catch (final IllegalArgumentException ex) {
                MessageLogger.logError(getClass(), Thread.currentThread(), ex);
                throw ex;
            }

            _scannerEngine = Engine.builder(this::fitness, codec) //
                    .executor(Executors.newSingleThreadExecutor()) // without this command, engine will be executed
                                                                   // in
                                                                   // parallel threads
                    .populationSize(_geneticScanParameters.getPopulationSize()) //
                    .optimize(_geneticScanParameters.getOptimizationStrategy()) //
                    .offspringFraction(_geneticScanParameters.getOffspringSize()) //
                    .survivorsSelector(new RouletteWheelSelector<>()) //
                    .offspringSelector(new TournamentSelector<>(_geneticScanParameters.getTournamentSizeLimit())) //
                    .alterers( //
                            new Mutator<>(_geneticScanParameters.getMutator()), //
                            new MeanAlterer<>(_geneticScanParameters.getMeanAlterer()) //
                    ) //
                    .build();
        } else {
            throw new IllegalStateException(ERROR_INITSCANNER_NO_SETTING_DEVICE);
        }
    }
    }

where:

private Engine<DoubleGene, Double> _scannerEngine = null;

What I would like to do is to call the fitness function such that I have the Genotype available in the fitness function to have access to the genes' values (settings I send to the accelerator). I already tried to define fitness() as follows:

private double fitness(final Genotype<DoubleChromosome> genotype) {
...
}

but this call causes a compliation error.


Solution

  • I had a look at your code and I think you want to do something like this:

    class Foo {
    
        // Your parameter class.
        class TrimParameterSet {
            double min, max;
        }
    
        static double fitness(final double[] values) {
            // Your fitness function.
            return 0;
        }
    
        public static void main(final String[] args) {
            final List<TrimParameterSet> valueSets = ...;
    
            final DoubleRange[] ranges = valueSets.stream()
                .map(p -> DoubleRange.of(p.min, p.max))
                .toArray(DoubleRange[]::new);
    
            final Codec<double[], DoubleGene> codec = Codecs.ofVector(ranges);
            final Engine<DoubleGene, Double> engine = Engine.builder(Foo::fitness, codec)
                .build();
    
            // ...
        }
    }
    

    The double[] array of your fitness function has a different range, accoriding to the defined ranges in your TrimParameterSet class. If you want to define a direct fitness function, you have to define a genotype with a gene as parameter type.

    double fitness(Genotype<DoubleGene> gt) {...}