Skip to content

The f-evop is a simple plug&play "library" written in Java for classical evolutionary algorithms

License

Notifications You must be signed in to change notification settings

honzaMaly/f-evop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

f-evop

The f-evop is a simple plug&play "library" for classical evolutionary algorithms. The main advantage of the library is the ability to embed problem and processes easily. By default, most operations in the evolutionary cycle are executed in parallel.

Let's consider problem of finding x, that x maximise expression f(x) = 2.0 + (x / 50.0) + sin(x) + 2.0 * sin(pi * x / 5.0), x in (0, 150). Here is example of main class to do that using evolutionary approach:

public class Main {

    //parameters + configuration
    private static final Random RANDOM = new Random();
    private static final int MAX_GENES = 24;
    private static final double MIN_X = 0.0;
    private static final double MAX_X = 150.0;
    private static final double RANGE_X = MAX_X - MIN_X;
    private final static Logger logger = Logger.getLogger(Main.class.getName());

    public static void main(String[] args) {
        //types by order: genes, decoded genes - solution, fitness, container with statistics
        EvolutionConfiguration<int[], Double, Double, MyStatisticsPerEpoch> evolutionConfiguration = (new EvolutionConfigurationBuilder<int[], Double, Double, MyStatisticsPerEpoch>())
                //uniform crossover
                .crossover((firstParent, secondParent) -> {
                    int[] firstSetOfGenes = new int[MAX_GENES], secondSetOfGenes = new int[MAX_GENES];
                    // Each gene is inherited either from the 1st or the 2nd parent
                    for (int i = 0; i < MAX_GENES; i++) {
                        if (RANDOM.nextBoolean()) {
                            firstSetOfGenes[i] = firstParent.getGenes()[i];
                        } else {
                            firstSetOfGenes[i] = secondParent.getGenes()[i];
                        }

                        if (RANDOM.nextBoolean()) {
                            secondSetOfGenes[i] = secondParent.getGenes()[i];
                        } else {
                            secondSetOfGenes[i] = firstParent.getGenes()[i];
                        }
                    }
                    return Stream.of(new Individual<int[], Double>(firstSetOfGenes), new Individual<int[], Double>(secondSetOfGenes)).collect(Collectors.toList());
                })
                //simple bit-flip mutation
                .mutation(individual -> {
                    int[] genes = individual.getGenes().clone();
                    for (int i = 0; i < genes.length; i++) {
                        // Mutate each gene with probability Pm.
                        if (RANDOM.nextDouble() < 0.01) {
                            genes[i] = (genes[i] + 1) % 2;    // swap between 0 and 1
                        }
                    }
                    return Optional.of(new Individual<>(genes));
                })
                //tournament selection
                .selector(population -> {
                    //First member of tournament selection.
                    int winnerIndex = RANDOM.nextInt(population.size());

                    // Try and check another n randomly chosen individuals.
                    for (int i = 0; i < 3; i++) {
                        int candidate = RANDOM.nextInt(population.size());
                        if (population.get(candidate).getFitness() > population.get(winnerIndex).getFitness()) {
                            winnerIndex = candidate;
                        }
                    }

                    return population.get(winnerIndex);
                })
                //generational replacement strategy. keep nothing from previous population
                .replacement(currentPopulation -> new ArrayList<>())
                //strategy to initialize single individual - do it randomly
                .populationInitialization(() -> {
                    // Allocate memory for genes array.
                    int[] genes = new int[MAX_GENES];

                    // Randomly initialise genes of the individual.
                    for (int i = 0; i < MAX_GENES; i++) {
                        if (RANDOM.nextBoolean()) {
                            genes[i] = 0;
                        } else {
                            genes[i] = 1;
                        }
                    }
                    return new Individual<>(genes);
                })
                //strategy how to decode genes
                .decoding(Main::decode)
                //how fitness is computed
                .fitnessAssessment(Main::calculateFitness)
                .fitnessIsMaximized(true)
                .parallel(true)
                .probabilityOfCrossover(0.75)
                .populationSize(50)
                //when to terminate evolution, after 100 epochs has been reached
                .terminationCondition(epochs -> epochs.size() < 100)
                //use own statistics
                .statisticsCreation(MyStatisticsPerEpoch::new)
                .build();
        EvolutionExecutor<int[], Double, Double, MyStatisticsPerEpoch> evolutionExecutor = new EvolutionExecutor<>(evolutionConfiguration);
        List<MyStatisticsPerEpoch> statistics = evolutionExecutor.run();
        long time = statistics.stream().mapToLong(StatisticsPerEpoch::getExecution).sum();
        MyStatisticsPerEpoch bestEpoch = statistics.stream().max(Comparator.comparing(stats -> stats.getBestIndividual().getFitness())).get();
        logger.info("Executed in " + time + ", best solution in epoch " + bestEpoch.getEpoch());
    }

    /**
     * Decodes genotype stored in 'genes'.
     *
     * @return an value coded by binary genes
     */
    private static double decode(int[] genes) {
        double pom = Math.pow(2.0, MAX_GENES) - 1.0;
        int power = 1;
        double temp = 0.0;
        for (int i = 0; i < MAX_GENES; i++) {
            temp += (power * genes[i]);
            power *= 2;
        }
        return MIN_X + (temp / pom) * RANGE_X;
    }

    /**
     * Fitness Function Definition - highly multimodal function.
     * f(x) = 2.0 + (x / 50.0) + Math.sin(x) + 2.0 * Math.sin(Math.PI * x / 5.0), x in (0, 150)
     *
     * @param x
     */
    private static double calculateFitness(double x) {
        return 2.0 + (x / 50.0) + Math.sin(x) + 2.0 * Math.sin(Math.PI * x / 50.0);
    }

    /**
     * Own implementation of class with statistics, most important is method getSummary(). It is used to store and print results
     */
    private static class MyStatisticsPerEpoch extends StatisticsPerEpoch<int[], Double, Double> {

        MyStatisticsPerEpoch(int epoch, long execution, int countOfFitnessEvaluations, IndividualWithAssignedFitness<int[], Double, Double> bestIndividual, List<IndividualWithAssignedFitness<int[], Double, Double>> population) {
            super(epoch, execution, countOfFitnessEvaluations, bestIndividual, population);
        }

        public String getSummary() {
            return "Epoch " + epoch + ", avg. fitness: " + population.stream().mapToDouble(IndividualWithAssignedFitness::getFitness).average().orElse(0) + ", #fitness evaluations: " + countOfFitnessEvaluations + ", execution time:" + execution + "\n"
                    + "result: " + decode(bestIndividual.getGenes()) + ", best fitness: " + bestIndividual.getFitness().toString();
        }
    }

}

Releases

No releases published

Packages

No packages published

Languages