/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator.features.construction;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import com.rapidminer.example.Attribute;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.example.set.AttributeWeightedExampleSet;
import com.rapidminer.generator.BasicArithmeticOperationGenerator;
import com.rapidminer.generator.FeatureGenerator;
import com.rapidminer.generator.ReciprocalValueGenerator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.OperatorProgress;
import com.rapidminer.operator.ProcessStoppedException;
import com.rapidminer.operator.features.selection.GeneticAlgorithm;
import com.rapidminer.operator.features.selection.SelectionCrossover;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.parameter.conditions.BooleanParameterCondition;
/**
* In contrast to its superclass {@link GeneticAlgorithm}, the {@link GeneratingGeneticAlgorithm}
* generates new attributes and thus can change the length of an individual. Therfore specialized
* mutation and crossover operators are being applied. Generators are chosen at random from a list
* of generators specified by boolean parameters. <br/>
*
* Since this operator does not contain algorithms to extract features from value series, it is
* restricted to example sets with only single attributes. For automatic feature extraction from
* values series the value series plugin for RapidMiner written by Ingo Mierswa should be used. It
* is available at <a href="http://rapidminer.com">http://rapidminer.com</a>
*
* @rapidminer.reference Ritthoff/etal/2001a
*
* @author Ingo Mierswa
*/
public abstract class AbstractGeneratingGeneticAlgorithm extends ExampleSetBasedFeatureOperator {
public static final String[] SELECTION_SCHEMES = { "uniform", "cut", "roulette wheel", "stochastic universal sampling",
"Boltzmann", "rank", "tournament", "non dominated sorting" };
public static final int UNIFORM_SELECTION = 0;
public static final int CUT_SELECTION = 1;
public static final int ROULETTE_WHEEL = 2;
public static final int STOCHASTIC_UNIVERSAL = 3;
public static final int BOLTZMANN_SELECTION = 4;
public static final int RANK_SELECTION = 5;
public static final int TOURNAMENT_SELECTION = 6;
public static final int NON_DOMINATED_SORTING_SELECTION = 7;
/** The parameter name for "Number of individuals per generation." */
public static final String PARAMETER_POPULATION_SIZE = "population_size";
/**
* The parameter name for "Number of generations after which to terminate the
* algorithm."
*/
public static final String PARAMETER_MAXIMUM_NUMBER_OF_GENERATIONS = "maximum_number_of_generations";
public static final String PARAMETER_USE_EARLY_STOPPING = "use_early_stopping";
/**
* The parameter name for "Stop criterion: Stop after n generations without improval of the
* performance (-1: perform all generations)."
*/
public static final String PARAMETER_GENERATIONS_WITHOUT_IMPROVAL = "generations_without_improval";
/**
* The parameter name for "The fraction of the current population which should be used as
* tournament members (only tournament selection)."
*/
public static final String PARAMETER_TOURNAMENT_SIZE = "tournament_size";
/** The parameter name for "The scaling temperature (only Boltzmann selection)." */
public static final String PARAMETER_START_TEMPERATURE = "start_temperature";
/**
* The parameter name for "If set to true the selection pressure is increased to maximum
* during the complete optimization run (only Boltzmann and tournament selection)."
*/
public static final String PARAMETER_DYNAMIC_SELECTION_PRESSURE = "dynamic_selection_pressure";
/**
* The parameter name for "If set to true, the best individual of each generations is
* guaranteed to be selected for the next generation (elitist selection)."
*/
public static final String PARAMETER_KEEP_BEST_INDIVIDUAL = "keep_best_individual";
public static final String PARAMETER_P_INITIALIZE = "p_initialize";
public static final String PARAMETER_P_CROSSOVER = "p_crossover";
public static final String PARAMETER_CROSSOVER_TYPE = "crossover_type";
public static final String PARAMETER_USE_PLUS = "use_plus";
public static final String PARAMETER_USE_DIFF = "use_diff";
public static final String PARAMETER_USE_MULT = "use_mult";
public static final String PARAMETER_USE_DIV = "use_div";
public static final String PARAMETER_RECIPROCAL_VALUE = "reciprocal_value";
/** The size of the population. */
private int numberOfIndividuals;
/** Maximum number of generations. */
private int maxGen;
/**
* Stop criterion: Stop after generationsWithoutImproval generations without an improval of the
* fitness.
*/
private int generationsWithoutImproval;
public AbstractGeneratingGeneticAlgorithm(OperatorDescription description) {
super(description);
}
/**
* Returns a specialized generating mutation, e.g. a <code>AttributeGenerator</code>.
*/
protected abstract ExampleSetBasedPopulationOperator getGeneratingPopulationOperator(ExampleSet exampleSet)
throws OperatorException;
/**
* Returns an operator that performs the mutation. Can be overridden by subclasses.
*/
protected abstract ExampleSetBasedPopulationOperator getMutationPopulationOperator(ExampleSet example)
throws OperatorException;
/** Returns an empty list. */
protected List<ExampleSetBasedPopulationOperator> getPostProcessingPopulationOperators(ExampleSet input)
throws OperatorException {
return new LinkedList<ExampleSetBasedPopulationOperator>();
}
/** Returns the list with pre eval pop ops. */
@Override
public final List<ExampleSetBasedPopulationOperator> getPreEvaluationPopulationOperators(ExampleSet input)
throws OperatorException {
// pre eval ops
List<ExampleSetBasedPopulationOperator> preOp = new LinkedList<ExampleSetBasedPopulationOperator>();
// crossover
ExampleSetBasedPopulationOperator crossover = getCrossoverPopulationOperator(input);
if (crossover != null) {
preOp.add(crossover);
}
// mutation
ExampleSetBasedPopulationOperator mutation = getMutationPopulationOperator(input);
if (mutation != null) {
preOp.add(mutation);
}
// other preevaluation ops
preOp.addAll(getPreProcessingPopulationOperators(input));
return preOp;
}
/** Returns the list with post eval pop ops. */
@Override
public final List<ExampleSetBasedPopulationOperator> getPostEvaluationPopulationOperators(ExampleSet input)
throws OperatorException {
// other post eval ops
List<ExampleSetBasedPopulationOperator> postOp = new LinkedList<ExampleSetBasedPopulationOperator>();
// selection
this.numberOfIndividuals = getParameterAsInt(PARAMETER_POPULATION_SIZE);
this.maxGen = getParameterAsInt(PARAMETER_MAXIMUM_NUMBER_OF_GENERATIONS);
this.generationsWithoutImproval = getParameterAsBoolean(PARAMETER_USE_EARLY_STOPPING)
? getParameterAsInt(PARAMETER_GENERATIONS_WITHOUT_IMPROVAL) : maxGen;
boolean keepBest = getParameterAsBoolean(PARAMETER_KEEP_BEST_INDIVIDUAL);
boolean dynamicSelection = getParameterAsBoolean(PARAMETER_DYNAMIC_SELECTION_PRESSURE);
postOp.add(new ExampleSetBasedTournamentSelection(numberOfIndividuals,
getParameterAsDouble(PARAMETER_TOURNAMENT_SIZE), this.maxGen, dynamicSelection, keepBest, getRandom()));
postOp.addAll(getPostProcessingPopulationOperators(input));
return postOp;
}
/**
* Returns true if generation is >= maximum_number_of_generations or after
* generations_without_improval generations without improval.
*/
@Override
public boolean solutionGoodEnough(ExampleSetBasedPopulation pop) {
return pop.getGeneration() >= maxGen || pop.getGenerationsWithoutImproval() >= generationsWithoutImproval;
}
/**
* Sets up a population of given size and creates ExampleSets with randomly selected attributes
* (the probability to be switched on is controlled by pInitialize).
*/
@Override
public ExampleSetBasedPopulation createInitialPopulation(ExampleSet es) throws UndefinedParameterError {
ExampleSetBasedPopulation initP = new ExampleSetBasedPopulation();
int populationSize = getParameterAsInt(PARAMETER_POPULATION_SIZE);
double p_initialize = getParameterAsDouble(PARAMETER_P_INITIALIZE);
while (initP.getNumberOfIndividuals() < populationSize) {
AttributeWeightedExampleSet nes = new AttributeWeightedExampleSet(es, null);
for (Attribute attribute : nes.getAttributes()) {
if (getRandom().nextDouble() > p_initialize) {
nes.flipAttributeUsed(attribute);
}
}
if (nes.getNumberOfUsedAttributes() > 0) {
initP.add(new ExampleSetBasedIndividual(nes));
}
}
return initP;
}
protected List<ExampleSetBasedPopulationOperator> getPreProcessingPopulationOperators(ExampleSet exampleSet)
throws OperatorException {
List<ExampleSetBasedPopulationOperator> popOps = new LinkedList<ExampleSetBasedPopulationOperator>();
ExampleSetBasedPopulationOperator generator = getGeneratingPopulationOperator(exampleSet);
if (generator != null) {
popOps.add(generator);
}
return popOps;
}
/** Returns an <code>UnbalancedCrossover</code>. */
protected ExampleSetBasedPopulationOperator getCrossoverPopulationOperator(ExampleSet exampleSet)
throws UndefinedParameterError {
double pCrossover = getParameterAsDouble(PARAMETER_P_CROSSOVER);
int crossoverType = getParameterAsInt(PARAMETER_CROSSOVER_TYPE);
return new UnbalancedCrossover(crossoverType, pCrossover, getRandom());
}
/** Returns a list with all generator which should be used. */
public List<FeatureGenerator> getGenerators() {
List<FeatureGenerator> generators = new ArrayList<FeatureGenerator>();
if (getParameterAsBoolean(PARAMETER_USE_PLUS)) {
generators.add(new BasicArithmeticOperationGenerator(BasicArithmeticOperationGenerator.SUM));
}
if (getParameterAsBoolean(PARAMETER_USE_DIFF)) {
generators.add(new BasicArithmeticOperationGenerator(BasicArithmeticOperationGenerator.DIFFERENCE));
}
if (getParameterAsBoolean(PARAMETER_USE_MULT)) {
generators.add(new BasicArithmeticOperationGenerator(BasicArithmeticOperationGenerator.PRODUCT));
}
if (getParameterAsBoolean(PARAMETER_USE_DIV)) {
generators.add(new BasicArithmeticOperationGenerator(BasicArithmeticOperationGenerator.QUOTIENT));
}
if (getParameterAsBoolean(PARAMETER_RECIPROCAL_VALUE)) {
generators.add(new ReciprocalValueGenerator());
}
return generators;
}
@Override
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
ParameterType type = new ParameterTypeInt(PARAMETER_POPULATION_SIZE, "Number of individuals per generation.", 1,
Integer.MAX_VALUE, 5);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_MAXIMUM_NUMBER_OF_GENERATIONS,
"Number of generations after which to terminate the algorithm.", 1, Integer.MAX_VALUE, 30);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_USE_PLUS, "Generate sums.", true);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_USE_DIFF, "Generate differences.", false);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_USE_MULT, "Generate products.", true);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_USE_DIV, "Generate quotients.", false);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_RECIPROCAL_VALUE, "Generate reciprocal values.", true);
type.setExpert(false);
types.add(type);
types.add(new ParameterTypeBoolean(PARAMETER_USE_EARLY_STOPPING,
"Enables early stopping. If unchecked, always the maximum number of generations is performed.", false));
type = new ParameterTypeInt(PARAMETER_GENERATIONS_WITHOUT_IMPROVAL,
"Stop criterion: Stop after n generations without improval of the performance.", 1, Integer.MAX_VALUE, 2);
type.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_USE_EARLY_STOPPING, true, true));
types.add(type);
types.add(new ParameterTypeDouble(PARAMETER_TOURNAMENT_SIZE,
"The fraction of the current population which should be used as tournament members (only tournament selection).",
0.0d, 1.0d, 0.25d));
types.add(new ParameterTypeDouble(PARAMETER_START_TEMPERATURE, "The scaling temperature (only Boltzmann selection).",
0.0d, Double.POSITIVE_INFINITY, 1.0d));
types.add(new ParameterTypeBoolean(PARAMETER_DYNAMIC_SELECTION_PRESSURE,
"If set to true the selection pressure is increased to maximum during the complete optimization run (only Boltzmann and tournament selection).",
true));
types.add(new ParameterTypeBoolean(PARAMETER_KEEP_BEST_INDIVIDUAL,
"If set to true, the best individual of each generations is guaranteed to be selected for the next generation (elitist selection).",
false));
types.add(new ParameterTypeDouble(PARAMETER_P_INITIALIZE, "Initial probability for an attribute to be switched on.",
0, 1, 0.5));
type = new ParameterTypeDouble(PARAMETER_P_CROSSOVER, "Probability for an individual to be selected for crossover.",
0, 1, 0.5);
types.add(type);
types.add(new ParameterTypeCategory(PARAMETER_CROSSOVER_TYPE, "Type of the crossover.",
SelectionCrossover.CROSSOVER_TYPES, SelectionCrossover.UNIFORM));
return types;
}
@Override
protected int getMaxGenerations() {
try {
return getParameterAsInt(AbstractGeneratingGeneticAlgorithm.PARAMETER_MAXIMUM_NUMBER_OF_GENERATIONS);
} catch (UndefinedParameterError e) {
return OperatorProgress.NO_PROGRESS;
}
}
@Override
protected void applyLoopOperations() throws ProcessStoppedException {
super.applyLoopOperations();
getProgress().step();
}
}