All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bnd.math.business.evo.GeneticAlgorithmBO Maven / Gradle / Ivy

The newest version!
package com.bnd.math.business.evo;

import java.util.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bnd.core.ReverseComparator;
import com.bnd.core.domain.um.User;
import com.bnd.core.reflection.ReflectionProvider;
import com.bnd.core.util.ObjectUtil;
import com.bnd.core.util.RandomUtil;
import com.bnd.math.BndMathException;
import com.bnd.math.domain.evo.*;
import com.bnd.math.domain.evo.Chromosome.ChromosomeComparator;
import com.bnd.math.task.EvoPopulationContentStoreOption;
import com.bnd.math.task.EvoPopulationSelection;
import com.bnd.math.task.EvoRunTask;

/**
 * Functional object for genetic algorithm.
 */
public abstract class GeneticAlgorithmBO, C, T> {

	private final EvoGaSetting gaSetting;
	private final EvoTaskBO evoTaskBO;
	private EvoPopulationContentStoreOption storeOption;
	private EvoPopulationSelection populationSelection;
	private final boolean autoSave;

	private final ReflectionProvider> chromosomeRF;
	private GeneticAlgorithmBOAutoSaveHandler autoSaveHandler;

	private int generation = 0;
	private Collection testSamples;
	private List chromosomes = new ArrayList();
	private List newGenerationChromosomes = new ArrayList();

	private EvoRun evoRun;
	private Population lastLocalPopulation;

    private final Log log = LogFactory.getLog(getClass());

	protected GeneticAlgorithmBO(
		EvoRunTask evoRunTask,
		EvoTaskBO evoTaskBO,
		ReflectionProvider> chromosomeRF
	) {
		final EvoTask evoTask = evoRunTask.getEvoTask();
		this.chromosomeRF = chromosomeRF;
		this.gaSetting = evoTask.getGaSetting();
		this.storeOption = evoRunTask.getPopulationContentStoreOption();
		this.populationSelection = evoRunTask.getPopulationSelection();
		this.autoSave = evoRunTask.isAutoSave();
		this.evoTaskBO = evoTaskBO;
		if (evoRunTask.hasInitChromosomes()) {
			for (Chromosome chromosome : evoRunTask.getInitChromosomes()) {
				chromosomes.add((H) chromosome);
			}
			sortChromosomesByScore();
		}

		// initialize evo run
		initEvoRun(evoRunTask);
	}

	private void initEvoRun(EvoRunTask evoRunTask) {
		final EvoTask evoTask = evoRunTask.getEvoTask();
		if (!evoRunTask.isEvoRunDefined()) {
			// initialize evo run
			evoRun = new EvoRun();
			User user = new User();
			user.setId(1l);
			evoRun.setCreatedBy(user);
			evoTask.addEvolutionRun(evoRun);
			if (evoRunTask.hasInitChromosome()) {
				evoRun.setInitChromosome((C) evoRunTask.getInitChromosome());
			}
		} else {
			evoRun = (EvoRun) evoRunTask.getEvoRun();
			Population lastPopulation = ObjectUtil.getLast(evoRun.getPopulations());
			generation = lastPopulation.getGeneration();
			lastLocalPopulation = createPopulationWithSpecifiedContent();
			if (lastLocalPopulation.hasBestChromosome()) {
				lastLocalPopulation.setBestChromosome(
						chromosomeRF.clone(lastLocalPopulation.getBestChromosome()));	
			}
		}
	}

	/**
	 * Evolves the chromosomes for defined number of steps (generations).
	 */
	public void evolve() {
		// generate the first generation if no chromosomes available, otherwise create the next generation
		if (generation == 0 && chromosomes.isEmpty())
			initFirstGenerationChromosomes();
		else
			createNextGeneration();

		// main loop
		while (generation < gaSetting.getGenerationLimit()) {
			evolveOneGeneration();
			log.info("Generation '" + generation + "' evolved." + "Best score/fitness is " + getBestChromosome().getScore() + "/" + getBestChromosome().getFitness() + ".");
		}

		// needed for the last generation
		evaluateChromosomesWithSamples();
		storeResults();
		if (autoSave) {
			replaceOrDeleteLastAutoSave();
		}
	}

	/**
	 * Evolves the next generation of chromosomes.
	 */
	private void evolveOneGeneration() {
		evaluateChromosomesWithSamples();
		storeResults();
		createNextGeneration();
	}

	private void createNextGeneration() {
		crossOver();
		mutate();
		refreshChromosomes();
		generation++;		
	}

	public void setAutoSaveHandler(GeneticAlgorithmBOAutoSaveHandler autoSaveHandler) {
		this.autoSaveHandler = autoSaveHandler;
	}

	protected void initFirstGenerationChromosomes() {
		for (int chromosomeIndex = 0; chromosomeIndex < gaSetting.getPopulationSize(); chromosomeIndex++) {
			chromosomes.add(getChromManipulatorBO().generateRandomChromosome());
		}		
	}

	protected void setChromosomes(List chromosomes) {
		this.chromosomes = chromosomes;
	}

	protected List getChromosomes() {
		return chromosomes;
	}

	protected List getNewGenerationChromosomes() {
		return newGenerationChromosomes;
	}

	protected H[] getNewGenerationChromosomesAsArray() {
		return (H[]) newGenerationChromosomes.toArray(new Chromosome[0]);
	}

	/**
	 * Gets the number of chromosomes in new generation.
	 * 
	 * @return The number of new generation chromosomes.
	 */
	protected int getNewGenerationChromosomeNumber() {
		return newGenerationChromosomes.size();
	}

	/**
	 * Gets the number of chromosomes.
	 * 
	 * @return The number of chromosomes.
	 */
	protected int getChromosomeNumber() {
		return chromosomes.size();
	}

	/**
	 * Adds the value to attribute newGenerationChromosomes.
	 *
	 * @param newGenerationChromosome The value to add.
	 */
	protected void addNewGenerationChromosome(H newGenerationChromosome) {
		newGenerationChromosomes.add(newGenerationChromosome);
	}

	/**
	 * Adds the value to attribute newGenerationChromosomes.
	 *
	 * @param newGenerationChromosomes The value to add.
	 */
	protected void addNewGenerationChromosomes(Collection newGenerationChromosomes) {
		for (H chromosome : newGenerationChromosomes) {
			addNewGenerationChromosome(chromosome);
		}
	}

	/**
	 * Gets the actually best chromosome in evolution.
	 * 
	 * @return The domain object of the actually best chromosome in evolution.
	 */
	public H getBestChromosome() {
		if (!chromosomes.isEmpty()) {
			return ObjectUtil.getFirst(chromosomes);
		}
		return null;
	}

	/**
	 * Gets the actually worst chromosome in evolution.
	 * 
	 * @return The domain object of the actually best chromosome in evolution.
	 */
	public H getWorstChromosome() {
		if (!chromosomes.isEmpty()) {
			return ObjectUtil.getLast(chromosomes);
		}
		return null;
	}

	/**
	 * Processes the renormalize fitness operation for all chromosomes with group increasing fitness.
	 */
	protected void renormalizeFitnessesGroupIncreasing() {
		double consecutiveGroupScore = Double.NEGATIVE_INFINITY;
		int order = 0;
		for (H chromosome : chromosomes) {
			if (chromosome.getScore() != consecutiveGroupScore) {
				consecutiveGroupScore = chromosome.getScore();
				order++;
			}
			chromosome.renormalizeFitnessScoreAdvanced(order, chromosomes.size());
		}
	}

	/**
	 * Processes the renormalize fitness operation for all chromosomes with group jump increasing fitness.
	 */
	protected void renormalizeFitnessesGroupJumpIncreasing() {
		double groupValue = Double.NEGATIVE_INFINITY;
		int order = 1;
		int groupOrder = 0;
		for (H chromosome : chromosomes) {
			if (chromosome.getScore() != groupValue) {
				groupValue = chromosome.getScore();
				groupOrder = order;
			}
			chromosome.renormalizeFitnessScoreAdvanced(groupOrder, chromosomes.size());
			order++;
		}
	}

	/**
	 * Processes the renormalize fitness operation for all chromosomes with strictly increasing fitness.
	 */
	protected void renormalizeFitnessesStrictlyIncreasing() {
		int order = 1;
		for (H chromosome : chromosomes) {
			chromosome.renormalizeFitnessScoreAdvanced(order, chromosomes.size());
			order++;
		}
	}

	/**
	 * Gets the sum of all fitnesses of chromosome in a population. 
	 * 
	 * @return The sum of all fitnesses of chromosome in a population.
	 */
	protected double getPopulationFitnessSum() {
		double sum = 0;
		for (H chromosome : chromosomes) {
			sum += chromosome.getFitness();
		}
		return sum;
	}

	/**
	 * Gets the mean score of chromosomes in the current population. 
	 * 
	 * @return The mean score of chromosomes in the current population.
	 */
	protected double getMeanScore() {
		double sum = 0;
		for (H chromosome : chromosomes) {
			sum += chromosome.getScore();
		}
		return sum / chromosomes.size();
	}

	/**
	 * Gets the mean fitness of chromosomes in the current population. 
	 * 
	 * @return The mean fitness of chromosomes in the current population.
	 */
	protected Double getMeanFitness() {
		Double sum = null;
		for (H chromosome : chromosomes) {
			if (chromosome.hasFitness()) {
				if (sum == null) {
					sum = 0d;
				}
				sum += chromosome.getFitness();
			}
		}
		return sum != null ? sum / chromosomes.size() : null;
	}

	/**
	 * Replaces the chromosome collection with newly evolved one.
	 */
	protected void refreshChromosomes() {
		chromosomes.clear();
		chromosomes.addAll(newGenerationChromosomes);
		newGenerationChromosomes.clear();
	}

	/**
	 * Sorts the chromosomes by their scores.
	 */
	protected void sortChromosomesByScore() {
		// shuffle first to make it more random in case all chromosomes have almost the same score
		Collections.shuffle(chromosomes);
		Comparator> chromosomeComparator = new ChromosomeComparator();
		if (gaSetting.isMaxValueFlag()) {
			chromosomeComparator = new ReverseComparator>(chromosomeComparator);
		}
		Collections.sort(chromosomes, chromosomeComparator);
	}

	/**
	 * Renormalizes the fitness of all chromosomes (if needed).
	 */
	private void renormalizeChromosomeFitnesses() {
		if (gaSetting.getFitnessRenormalizationType() == null) {
			return;
		}
		switch (gaSetting.getFitnessRenormalizationType()) {
			case StrictOrderIncreasing: renormalizeFitnessesStrictlyIncreasing(); break;
			case GroupOrderIncreasing: renormalizeFitnessesGroupIncreasing(); break;
			case GroupJumpOrderIncreasing: renormalizeFitnessesGroupJumpIncreasing(); break;
			default: throw new BndMathException("Fitness renormalization type '" + gaSetting.getFitnessRenormalizationType() + "' not recognized."); 
		}
	}

	/**
	 * Crosses over two parent chromosomes creating one or two new offsprings.
	 * 
	 * @param aParent1 The first parent chromosome to crossover.
	 * @param aParent2 The second parent chromosome to crossover.
	 * @param aBothFlag The flag indicating two children offspring.
	 */
	protected void crossOver(H parent1, H parent2, boolean twoOffspringsFlag) {
		Collection offsprings = new ArrayList();
		if (RandomUtil.nextDouble() <= gaSetting.getCrossOverProbability().doubleValue()) {
			offsprings = crossOverByType(parent1, parent2);
			if (!twoOffspringsFlag)
				offsprings = Collections.singleton(ObjectUtil.getFirst(offsprings));
			if (gaSetting.isConditionalCrossOverFlag()) {
				int offspringIndex = 1;
				evaluateChromosomes(offsprings);
				for (H offspring : offsprings) {
					H parent = offspringIndex == 1 ? parent1 : parent2;
					evaluateChromosome(offspring);
					if (isWorseScore(offspring, parent)) {
						offsprings.remove(offspring);
						offsprings.add(cloneChromosome(parent));
					}					
				}
			}
		} else {
			offsprings.add(cloneChromosome(parent1));
			offsprings.add(cloneChromosome(parent2));
		}
		addNewGenerationChromosomes(offsprings);
	}

	/**
	 *  Crosses over two chromosomes and produce two offsprings
	 */
	protected Collection crossOverByType(H parent1, H parent2) {
		final int parent1Size = parent1.getCodeSize();
		final int parent2Size = parent2.getCodeSize();
		if (parent1Size != parent2Size)
			throw new BndMathException("Size of the first and second parent chromosome is not the same '" + parent1Size + "' vs '" + parent2Size + ".");
		final int crossNum = RandomUtil.nextInt(parent1Size + 1);

		switch (gaSetting.getCrossOverType()) {			
			case Split:
				return getChromManipulatorBO().crossOverSplit(parent1, parent2, crossNum);
			case Shuffle:
				return getChromManipulatorBO().crossOverShuffle(parent1, parent2, crossNum);
			default: throw new BndMathException("Crossover type '" + gaSetting.getCrossOverType() + "' not recognized.");
		}
	}

	protected abstract void crossOver();

	/**
	 * Mutates given chromosome.
	 * 
	 * @param aChromosomeDO The chromosome to mutate.
	 */
	protected void mutate(H chromosome) {
		if (new Random().nextDouble() < gaSetting.getMutationProbability().doubleValue()) {
			if (!gaSetting.isConditionalMutationFlag()) {
				mutateByType(chromosome);	
			} else {
				H originalChromosome = (H) chromosomeRF.clone(chromosome);
				mutateByType(chromosome);
				evaluateChromosome(chromosome);
				if (isWorseScore(chromosome.getScore(), originalChromosome.getScore())) {
					chromosome.setCode(originalChromosome.getCode());
				}						
			}
		}
	}

	/**
	 * Mutates the code of the chromosome by selected mutation type.
	 */
	protected void mutateByType(H chromosome) {
		switch (gaSetting.getMutationType()) {			
			case OneBit:
				getChromManipulatorBO().mutateOneBit(chromosome);
				break;
			case TwoBits:
				getChromManipulatorBO().mutateTwoBits(chromosome);
				break;
			case PerBit:
				getChromManipulatorBO().mutatePerBit(chromosome, gaSetting.getPerBitMutationProbability());
				break;
			case Exchange:
				getChromManipulatorBO().mutateBySwapping(chromosome);
				break;
			default: throw new BndMathException("Mutation type '" + gaSetting.getMutationType() + "' not recognized.");
		}
	}

	protected abstract void mutate();

	private boolean isWorseScore(double score1, double score2) {
		return gaSetting.isMaxValueFlag() ? score1 < score2 : score1 > score2;
	}

	private boolean isWorseScore(Chromosome chromosome1, Chromosome chromosome2) {
		return gaSetting.isMaxValueFlag() ? chromosome1.getScore() < chromosome2.getScore() : chromosome1.getScore() > chromosome2.getScore();
	}

	private H cloneChromosome(H chromosome) {
		return getChromManipulatorBO().cloneChromosome(chromosome);
	}

	public void evaluateChromosome(H chromosome) {
		evaluateChromosomes(Collections.singleton(chromosome));
	}

	public void evaluateChromosomes(Collection chromosomes) {
		getFitnessEvaluatorBO().evaluateScoreAndFitness(chromosomes, testSamples);
	}
	
//	public void evaluateChromosomesInParallel() {
//		Parallelizer parallelizer = new Parallelizer(createEvaluateChromosomeRunnable(), 20, chromosomes);
//		parallelizer.run();
//	}
//
//	private RunnableWith createEvaluateChromosomeRunnable() {
//		return new RunnableWith() {
//
//			@Override
//			public void run(H chromosome) {
//				evaluateChromosome(chromosome);
//			}
//		};
//	}

	private void evaluateChromosomesWithSamples() {
		testSamples = getTestSampleGeneratorBO().createTestSamples();
		evaluateChromosomes(chromosomes);
		sortChromosomesByScore();
		renormalizeChromosomeFitnesses();
	}

	/**
	 * Store results (best chromosomes) and actual population if needed. 
	 */
	public void storeResults() {
		Population localPopulation = createPopulationWithSpecifiedContent();
		if (autoSave) {
			// replace or delete last auto-save first
			replaceOrDeleteLastAutoSave();
			// save a current population with all chromosomes to DB
			persistFullPopulation();
		} else { 
			// save population locally
			if (localPopulation != null) {
				evoRun.addPopulation(localPopulation);
			}
		}
		lastLocalPopulation = localPopulation;
	}

	private void persistFullPopulation() {
		if (evoRun.getId() == null) {
			evoRun = autoSaveHandler.saveEvoRun(evoRun);			
		}
		// save full population
		Population populationToAutoSave = createPopulationWithChromosomes();
		evoRun.addPopulation(populationToAutoSave);

		final Population autoSavedPopulation = (Population) autoSaveHandler.savePopulation(populationToAutoSave);
		evoRun.removePopulation(populationToAutoSave);
		evoRun.addPopulation(autoSavedPopulation);
	}

	/**
	 * Replace or delete previously autosaved population if needed.
	 */
	private void replaceOrDeleteLastAutoSave() {
		if (autoSaveHandler == null) {
			log.warn("Auto save requested, but no handler provided.");
			return;
		}
		Population lastAutoSavedPopulation = ObjectUtil.getLast(evoRun.getPopulations());
		if (lastAutoSavedPopulation == null) {
			// nothing to replace
			return;
		}
		if (lastLocalPopulation != null) {
			if (storeOption != EvoPopulationContentStoreOption.Full) {
				autoSaveHandler.replacePopulation(lastAutoSavedPopulation.getId(), lastLocalPopulation);
			}
		} else {
			autoSaveHandler.removePopulation(lastAutoSavedPopulation.getId());
			evoRun.removePopulation(lastAutoSavedPopulation);
		}
	}

	private Population createPopulationWithSpecifiedContent() {
		if (populationSelection == EvoPopulationSelection.Last && generation < gaSetting.getGenerationLimit()) {
			return null;
		}
		Population population;
		switch (storeOption) {
			case ScoreFitness:
				population = createPopulationWithoutChromosomes();
				break;
			case BestChromosome:
				population = createPopulationWithBestChromosome();
				break;
			case Full:
				population = createPopulationWithChromosomes();
				break;
			default: throw new BndMathException("Population content store option '" + storeOption + "' not recognized.");
		}
		return population;
	}

	private Population createPopulationWithoutChromosomes() {
		Population population = new Population();
		population.setGeneration(generation);

		final H bestChromosome = getBestChromosome();
		final H worstChromosome = getWorstChromosome();

		// explicit score
		population.setMinScore(worstChromosome.getScore());
		population.setMaxScore(bestChromosome.getScore());
		population.setMeanScore(getMeanScore());

		// explicit fitness
		population.setMinFitness(worstChromosome.getFitness());
		population.setMaxFitness(bestChromosome.getFitness());
		population.setMeanFitness(getMeanFitness());

		return population;
	}

	private Population createPopulationWithChromosomes() {
		Population population = new Population();
		population.setGeneration(generation);
		population.addChromosomes((Collection>) chromosomes);
		return population;
	}

	private Population createPopulationWithBestChromosome() {
		Population population = new Population();
		population.setGeneration(generation);

		final H bestChromosome = getBestChromosome();
		final H worstChromosome = getWorstChromosome();

		// explicit score
		population.setMinScore(worstChromosome.getScore());
		population.setMeanScore(getMeanScore());

		// explicit fitness
		population.setMinFitness(worstChromosome.getFitness());
		population.setMeanFitness(getMeanFitness());

		population.setBestChromosome(bestChromosome);

		return population;
	}

	public EvoGaSetting getGaSetting() {
		return gaSetting;
	}

	protected EvoChromManipulatorBO getChromManipulatorBO() {
		return evoTaskBO.getChromManipulator();
	}

	protected EvoFitnessEvaluator getFitnessEvaluatorBO() {
		return evoTaskBO.getFitnessEvaluator();
	}

	protected EvoTestSampleGeneratorBO getTestSampleGeneratorBO() {
		return evoTaskBO.getTestSampleGenerator();
	}

	public EvoRun getEvoRun() {
		return evoRun;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy