/*
* Copyright 2007-2013
* Licensed under GNU Lesser General Public License
*
* This file is part of EpochX
*
* EpochX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EpochX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with EpochX. If not, see <http://www.gnu.org/licenses/>.
*
* The latest version is available from: http://www.epochx.org
*/
package org.epochx;
import static org.epochx.RandomSequence.RANDOM_SEQUENCE;
import java.util.List;
import org.epochx.Config.ConfigKey;
import org.epochx.Config.Template;
import org.epochx.event.ConfigEvent;
import org.epochx.event.EventManager;
import org.epochx.event.Listener;
/**
* A <code>BranchedBreeder</code> produces a new <code>Population</code> by
* applying a single genetic operator per individual. The operator that is
* applied at each step is selected at random. The designated
* <code>IndividualSelector</code> is used to choose the individuals that
* undergo the selected operator. The result is that each individual in the new
* population that is produced is the product of just one genetic operator.
*/
public class BranchedBreeder implements Breeder, Listener<ConfigEvent> {
/**
* The key for setting and retrieving the size of the elite.
*/
public static final ConfigKey<Integer> ELITISM = new ConfigKey<Integer>();
/**
* The list of operators to be used to generate new individuals.
*/
private List<Operator> operators;
/**
* The selection strategy used to select the individual that will survive
* a generation.
*/
private IndividualSelector selector;
/**
* The random number generator.
*/
private RandomSequence random;
/**
* The number of individuals to copy to the next generation by elitism.
*/
private int elitism;
/**
* Constructs a <code>BranchedBreeder</code> that configures itself upon
* construction and firing of appropriate <code>ConfigEvents</code>.
*/
public BranchedBreeder() {
setup();
EventManager.getInstance().add(ConfigEvent.class, this);
}
/**
* Applies genetic operators to produce a new population. The available
* operators are repeatedly selected from at random and applied to produce
* new individuals that are added to the next population. The configured
* <code>IndividualSelector</code> is used to select individuals from
* the existing population to supply as inputs to the operator. The number
* of individuals required by the operator is determined by its
* <code>inputSize</code> method. The individuals returned by an operator
* are inserted into the next population. This process is repeated until the
* next population has been filled. If the new population has insufficient
* space for all the new individuals then only those that there
* is space for will be added and all others will be discarded.
*
* @param population the current population of individuals that a new
* population will be produced from
* @return a newly constructed population filled with individuals produced
* by the application of genetic operators
*/
@Override
public Population process(Population population) {
selector.setup(population);
Population newPopulation = new Population();
int size = population.size();
double[] probabilities = new double[operators.size()];
double cumulative = 0.0;
for (int i = 0; i < operators.size(); i++) {
cumulative += operators.get(i).probability();
probabilities[i] = cumulative;
}
if (elitism > 0) {
Individual[] elite = population.elites(elitism);
for (Individual individual: elite) {
newPopulation.add(individual.clone());
size--;
}
}
while (size > 0) {
double r = random.nextDouble() * cumulative;
Operator operator = null;
for (int i = 0; i < probabilities.length; i++) {
if (r <= probabilities[i]) {
operator = operators.get(i);
break;
}
}
Individual[] parents = null;
do {
parents = new Individual[operator.inputSize()];
for (int i = 0; i < parents.length; i++) {
parents[i] = selector.select().clone();
}
parents = operator.apply(parents);
} while (parents == null);
for (int i = 0; (i < parents.length) && (size > 0); i++) {
newPopulation.add(parents[i]);
size--;
}
}
return newPopulation;
}
/**
* Sets up this breeder with the appropriate configuration settings.
* This method is called whenever a <code>ConfigEvent</code> occurs for a
* change in any of the following configuration parameters:
* <ul>
* <li><code>Breeder.OPERATORS</code>
* <li><code>Breeder.SELECTOR</code>
* <li><code>RandomSequence.RANDOM_SEQUENCE</code>
* <li><code>BranchedBreeder.ELITISM</code>
* </ul>
*/
protected void setup() {
operators = Config.getInstance().get(OPERATORS);
selector = Config.getInstance().get(SELECTOR);
random = Config.getInstance().get(RANDOM_SEQUENCE);
elitism = Config.getInstance().get(ELITISM, 0);
}
/**
* Receives configuration events and triggers this breeder to configure its
* parameters if the <code>ConfigEvent</code> is for one of its required
* parameters.
*
* @param event {@inheritDoc}
*/
@Override
public void onEvent(ConfigEvent event) {
if (event.isKindOf(Template.TEMPLATE, OPERATORS, SELECTOR, RANDOM_SEQUENCE, ELITISM)) {
setup();
}
}
}