it.units.malelab.ege.core.evolver.StandardEvolver.java Source code

Java tutorial

Introduction

Here is the source code for it.units.malelab.ege.core.evolver.StandardEvolver.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.units.malelab.ege.core.evolver;

import it.units.malelab.ege.core.fitness.Fitness;
import com.google.common.base.Stopwatch;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import it.units.malelab.ege.core.Individual;
import it.units.malelab.ege.core.Node;
import it.units.malelab.ege.core.listener.EvolverListener;
import it.units.malelab.ege.core.listener.event.BirthEvent;
import it.units.malelab.ege.core.listener.event.GenerationEvent;
import it.units.malelab.ege.core.listener.event.EvolutionEndEvent;
import it.units.malelab.ege.core.listener.event.EvolutionStartEvent;
import it.units.malelab.ege.util.Utils;
import it.units.malelab.ege.core.operator.GeneticOperator;
import it.units.malelab.ege.core.listener.event.MappingEvent;
import it.units.malelab.ege.core.listener.event.OperatorApplicationEvent;
import it.units.malelab.ege.core.mapper.MappingException;
import it.units.malelab.ege.util.Pair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author eric
 */
public class StandardEvolver<G, T, F extends Fitness> implements Evolver<G, T, F> {

    protected static final int CACHE_SIZE = 200000;
    public final static String MAPPING_CACHE_NAME = "mapping";
    public final static String FITNESS_CACHE_NAME = "fitness";

    private final StandardConfiguration<G, T, F> configuration;
    protected final boolean saveAncestry;

    public StandardEvolver(StandardConfiguration<G, T, F> configuration, boolean saveAncestry) {
        this.configuration = configuration;
        this.saveAncestry = saveAncestry;
    }

    @Override
    public Configuration<G, T, F> getConfiguration() {
        return configuration;
    }

    @Override
    public List<Node<T>> solve(ExecutorService executor, Random random, List<EvolverListener<G, T, F>> listeners)
            throws InterruptedException, ExecutionException {
        LoadingCache<G, Pair<Node<T>, Map<String, Object>>> mappingCache = CacheBuilder.newBuilder()
                .maximumSize(CACHE_SIZE).recordStats().build(getMappingCacheLoader());
        LoadingCache<Node<T>, F> fitnessCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).recordStats()
                .build(getFitnessCacheLoader());
        Stopwatch stopwatch = Stopwatch.createStarted();
        //initialize population
        int births = 0;
        List<Callable<List<Individual<G, T, F>>>> tasks = new ArrayList<>();
        for (G genotype : configuration.getPopulationInitializer().build(configuration.getPopulationSize(),
                configuration.getInitGenotypeValidator(), random)) {
            tasks.add(individualFromGenotypeCallable(genotype, 0, mappingCache, fitnessCache, listeners, null, null,
                    executor));
            births = births + 1;
        }
        List<Individual<G, T, F>> population = new ArrayList<>(Utils.getAll(executor.invokeAll(tasks)));
        int lastBroadcastGeneration = (int) Math
                .floor(actualBirths(births, fitnessCache) / configuration.getPopulationSize());
        Utils.broadcast(new EvolutionStartEvent<>(this, cacheStats(mappingCache, fitnessCache)), listeners,
                executor);
        Utils.broadcast(new GenerationEvent<>(configuration.getRanker().rank(population, random),
                lastBroadcastGeneration, this, cacheStats(mappingCache, fitnessCache)), listeners, executor);
        //iterate
        while (Math.round(actualBirths(births, fitnessCache) / configuration.getPopulationSize()) < configuration
                .getNumberOfGenerations()) {
            int currentGeneration = (int) Math.floor(births / configuration.getPopulationSize());
            tasks.clear();
            //re-rank
            List<List<Individual<G, T, F>>> rankedPopulation = configuration.getRanker().rank(population, random);
            //produce offsprings
            int i = 0;
            while (i < configuration.getOffspringSize()) {
                GeneticOperator<G> operator = Utils.selectRandom(configuration.getOperators(), random);
                List<Individual<G, T, F>> parents = new ArrayList<>(operator.getParentsArity());
                for (int j = 0; j < operator.getParentsArity(); j++) {
                    parents.add(configuration.getParentSelector().select(rankedPopulation, random));
                }
                tasks.add(operatorApplicationCallable(operator, parents, random, currentGeneration, mappingCache,
                        fitnessCache, listeners, executor));
                i = i + operator.getChildrenArity();
            }
            List<Individual<G, T, F>> newPopulation = new ArrayList<>(Utils.getAll(executor.invokeAll(tasks)));
            births = births + newPopulation.size();
            //build new population
            if (configuration.isOverlapping()) {
                population.addAll(newPopulation);
            } else {
                if (newPopulation.size() >= configuration.getPopulationSize()) {
                    population = newPopulation;
                } else {
                    //keep missing individuals from old population
                    int targetSize = population.size() - newPopulation.size();
                    while (population.size() > targetSize) {
                        Individual<G, T, F> individual = configuration.getUnsurvivalSelector()
                                .select(rankedPopulation, random);
                        population.remove(individual);
                    }
                    population.addAll(newPopulation);
                }
            }
            //select survivals
            while (population.size() > configuration.getPopulationSize()) {
                //re-rank
                rankedPopulation = configuration.getRanker().rank(population, random);
                Individual<G, T, F> individual = configuration.getUnsurvivalSelector().select(rankedPopulation,
                        random);
                population.remove(individual);
            }
            if ((int) Math.floor(actualBirths(births, fitnessCache)
                    / configuration.getPopulationSize()) > lastBroadcastGeneration) {
                lastBroadcastGeneration = (int) Math
                        .floor(actualBirths(births, fitnessCache) / configuration.getPopulationSize());
                Utils.broadcast(new GenerationEvent<>((List) rankedPopulation, lastBroadcastGeneration, this,
                        cacheStats(mappingCache, fitnessCache)), listeners, executor);
            }
            if (configuration.getMaxRelativeElapsed() > 0) {
                //check if relative elapsed time exceeded
                double avgFitnessComputationNanos = fitnessCache.stats().averageLoadPenalty();
                double elapsedNanos = stopwatch.elapsed(TimeUnit.NANOSECONDS);
                if (elapsedNanos / avgFitnessComputationNanos > configuration.getMaxRelativeElapsed()) {
                    break;
                }
            }
            if (configuration.getMaxElapsed() > 0) {
                //check if elapsed time exceeded
                if (stopwatch.elapsed(TimeUnit.SECONDS) > configuration.getMaxElapsed()) {
                    break;
                }
            }
            if (configuration.getProblem().getLearningFitnessComputer().bestValue() != null) {
                //check if optimal solution found
                if (rankedPopulation.get(0).get(0).getFitness()
                        .equals(configuration.getProblem().getLearningFitnessComputer().bestValue())) {
                    break;
                }
            }
        }
        //end
        List<Node<T>> bestPhenotypes = new ArrayList<>();
        List<List<Individual<G, T, F>>> rankedPopulation = configuration.getRanker().rank(population, random);
        Utils.broadcast(new EvolutionEndEvent<>((List) rankedPopulation,
                (int) Math.floor(actualBirths(births, fitnessCache) / configuration.getPopulationSize()), this,
                cacheStats(mappingCache, fitnessCache)), listeners, executor);
        for (Individual<G, T, F> individual : rankedPopulation.get(0)) {
            bestPhenotypes.add(individual.getPhenotype());
        }
        return bestPhenotypes;
    }

    protected int actualBirths(int births, LoadingCache<Node<T>, F> fitnessCache) {
        return configuration.isActualEvaluations() ? (int) fitnessCache.stats().missCount() : births;
    }

    protected CacheLoader<G, Pair<Node<T>, Map<String, Object>>> getMappingCacheLoader() {
        return new CacheLoader<G, Pair<Node<T>, Map<String, Object>>>() {
            @Override
            public Pair<Node<T>, Map<String, Object>> load(G genotype) throws Exception {
                Node<T> phenotype = null;
                Map<String, Object> report = new LinkedHashMap<>();
                try {
                    phenotype = configuration.getMapper().map(genotype, report);
                } catch (MappingException ex) {
                    phenotype = Node.EMPTY_TREE;
                }
                return new Pair<>(phenotype, report);
            }
        };
    }

    protected CacheLoader<Node<T>, F> getFitnessCacheLoader() {
        return new CacheLoader<Node<T>, F>() {
            @Override
            public F load(Node<T> phenotype) throws Exception {
                if (Node.EMPTY_TREE.equals(phenotype)) {
                    return configuration.getProblem().getLearningFitnessComputer().worstValue();
                }
                return configuration.getProblem().getLearningFitnessComputer().compute(phenotype);
            }
        };
    }

    protected Callable<List<Individual<G, T, F>>> individualFromGenotypeCallable(final G genotype,
            final int generation, final LoadingCache<G, Pair<Node<T>, Map<String, Object>>> mappingCache,
            final LoadingCache<Node<T>, F> fitnessCache, final List<EvolverListener<G, T, F>> listeners,
            final GeneticOperator<G> operator, final List<Individual<G, T, F>> parents,
            final ExecutorService executor) {
        final Evolver<G, T, F> evolver = this;
        return new Callable<List<Individual<G, T, F>>>() {
            @Override
            public List<Individual<G, T, F>> call() throws Exception {
                Stopwatch stopwatch = Stopwatch.createStarted();
                Pair<Node<T>, Map<String, Object>> mappingOutcome = mappingCache.getUnchecked(genotype);
                Node<T> phenotype = mappingOutcome.getFirst();
                long elapsed = stopwatch.stop().elapsed(TimeUnit.NANOSECONDS);
                Utils.broadcast(new MappingEvent<>(genotype, phenotype, elapsed, generation, evolver, null),
                        listeners, executor);
                stopwatch.reset().start();
                F fitness = fitnessCache.getUnchecked(phenotype);
                elapsed = stopwatch.stop().elapsed(TimeUnit.NANOSECONDS);
                Individual<G, T, F> individual = new Individual<>(genotype, phenotype, fitness, generation,
                        saveAncestry ? (List) parents : null, mappingOutcome.getSecond());
                Utils.broadcast(new BirthEvent<>(individual, elapsed, generation, evolver, null), listeners,
                        executor);
                return Collections.singletonList(individual);
            }
        };
    }

    protected Map<String, Object> cacheStats(LoadingCache mappingCache, LoadingCache fitnessCache) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put(MAPPING_CACHE_NAME, mappingCache.stats());
        map.put(FITNESS_CACHE_NAME, fitnessCache.stats());
        return map;
    }

    protected Callable<List<Individual<G, T, F>>> operatorApplicationCallable(final GeneticOperator<G> operator,
            final List<Individual<G, T, F>> parents, final Random random, final int generation,
            final LoadingCache<G, Pair<Node<T>, Map<String, Object>>> mappingCache,
            final LoadingCache<Node<T>, F> fitnessCache, final List<EvolverListener<G, T, F>> listeners,
            final ExecutorService executor) {
        final Evolver<G, T, F> evolver = this;
        return new Callable<List<Individual<G, T, F>>>() {
            @Override
            public List<Individual<G, T, F>> call() throws Exception {
                List<Individual<G, T, F>> children = new ArrayList<>(operator.getChildrenArity());
                List<G> parentGenotypes = new ArrayList<>(operator.getParentsArity());
                for (Individual<G, T, F> parent : parents) {
                    parentGenotypes.add(parent.getGenotype());
                }
                Stopwatch stopwatch = Stopwatch.createStarted();
                List<G> childGenotypes = operator.apply(parentGenotypes, random).subList(0,
                        operator.getChildrenArity());
                long elapsed = stopwatch.elapsed(TimeUnit.NANOSECONDS);
                if (childGenotypes != null) {
                    for (G childGenotype : childGenotypes) {
                        children.addAll(individualFromGenotypeCallable(childGenotype, generation, mappingCache,
                                fitnessCache, listeners, operator, parents, executor).call());
                    }
                }
                Utils.broadcast(new OperatorApplicationEvent<>(parents, children, operator, elapsed, generation,
                        evolver, null), listeners, executor);
                return children;
            }
        };
    }
}