Added Genetic Algorithm implementation.

JIRA: MATH-207
Contributed by David Stefka

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@784604 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Phil Steitz 2009-06-14 19:04:32 +00:00
parent 496d85cf17
commit e082a039f8
38 changed files with 2642 additions and 110 deletions

View File

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Chromosome represented by an immutable list of a fixed length.
*
* @param <T> type of the representation list
* @version $Revision:$ $Date:$
* @since 2.0
*/
public abstract class AbstractListChromosome<T> extends Chromosome {
/** List representing the chromosome */
private final List<T> representation;
/**
* Constructor.
* @param representation inner representation of the chromosome
*/
public AbstractListChromosome(final List<T> representation) {
try {
checkValidity(representation);
} catch (InvalidRepresentationException e) {
throw new IllegalArgumentException(String.format("Invalid representation for %s", getClass().getSimpleName()), e);
}
this.representation = Collections.unmodifiableList(new ArrayList<T> (representation));
}
/**
* Constructor.
* @param representation inner representation of the chromosome
*/
public AbstractListChromosome(final T[] representation) {
this(Arrays.asList(representation));
}
/**
*
* Asserts that <code>representation</code> can represent a valid chromosome.
* @param representation representation of the chromosome
* @throws InvalidRepresentationException iff the <code>representation</code> can not represent
* a valid chromosome
*/
protected abstract void checkValidity(List<T> representation) throws InvalidRepresentationException;
/**
* Returns the (immutable) inner representation of the chromosome.
* @return the representation of the chromosome
*/
protected List<T> getRepresentation() {
return representation;
}
/**
* Returns the length of the chromosome.
* @return the length of the chromosome
*/
public int getLength() {
return getRepresentation().size();
}
/**
* Creates a new instance of the same class as <code>this</code> is, with a
* given <code>arrayRepresentation</code>. This is needed in crossover and
* mutation operators, where we need a new instance of the same class, but
* with different array representation.
*
* Usually, this method just calls a constructor of the class.
*
* @param representation
* the inner array representation of the new chromosome.
* @return new instance extended from FixedLengthChromosome with the given
* arrayRepresentation
*/
public abstract AbstractListChromosome<T> newFixedLengthChromosome(final List<T> representation);
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return String.format("(f=%s %s)", getFitness(), getRepresentation());
}
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.List;
/**
* Chromosome represented by a vector of 0s and 1s.
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public abstract class BinaryChromosome extends AbstractListChromosome<Integer> {
/**
* Constructor.
* @param representation list of {0,1} values representing the chromosome
*/
public BinaryChromosome(List<Integer> representation) {
super(representation);
}
/**
* Constructor.
* @param representation array of {0,1} values representing the chromosome
*/
public BinaryChromosome(Integer[] representation) {
super(representation);
}
/**
* {@inheritDoc}
*/
@Override
protected void checkValidity(List<Integer> representation) throws InvalidRepresentationException {
for (int i : representation) {
if (i < 0 || i >1)
throw new InvalidRepresentationException("Elements can be only 0 or 1.");
}
}
/**
* Returns a representation of a random binary array of length <code>length</code>.
* @param length length of the array
* @return a random binary array of length <code>length</code>
*/
public static List<Integer> randomBinaryRepresentation(int length) {
// random binary list
List<Integer> rList= new ArrayList<Integer> (length);
for (int j=0; j<length; j++) {
rList.add(GeneticAlgorithm.getRandomGenerator().nextInt(2));
}
return rList;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isSame(Chromosome another) {
// type check
if (! (another instanceof BinaryChromosome))
return false;
BinaryChromosome anotherBc = (BinaryChromosome) another;
// size check
if (getLength() != anotherBc.getLength())
return false;
for (int i=0; i< getRepresentation().size(); i++) {
if (!(getRepresentation().get(i).equals(anotherBc.getRepresentation().get(i))))
return false;
}
// all is ok
return true;
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.List;
/**
* Mutation for {@link BinaryChromosome}s. Randomly changes one gene.
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public class BinaryMutation implements MutationPolicy {
/**
* Mutate the given chromosome. Randomly changes one gene.
* @param original the original chromosome.
* @return the mutated chromomsome.
*/
public Chromosome mutate(Chromosome original) {
if (!(original instanceof BinaryChromosome)) {
throw new IllegalArgumentException("Binary mutation works on BinaryChromosome only.");
}
BinaryChromosome origChrom = (BinaryChromosome) original;
List<Integer> newRepr = new ArrayList<Integer>(origChrom.getRepresentation());
// randomly select a gene
int geneIndex = GeneticAlgorithm.getRandomGenerator().nextInt(origChrom.getLength());
// and change it
newRepr.set(geneIndex, origChrom.getRepresentation().get(geneIndex) == 0 ? 1 : 0);
Chromosome newChrom = origChrom.newFixedLengthChromosome(newRepr);
return newChrom;
}
}

View File

@ -18,13 +18,94 @@ package org.apache.commons.math.genetics;
/**
* Individual in a population. Chromosomes are compared based on their fitness.
* @version $Revision$ $Date$
*
* The chromosomes are IMMUTABLE, and so their fitness is also immutable and
* therefore it can be cached.
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public interface Chromosome {
public abstract class Chromosome implements Comparable<Chromosome>,Fitness {
/**
* Access the fitness of this chromosome.
* Cached value of the fitness of this chromosome.
*/
private double fitness = Double.MIN_VALUE;
/**
* Access the fitness of this chromosome. The bigger the fitness, the better
* the chromosome.
*
* Computation of fitness is usually very time-consuming task, therefore the
* fitness is cached.
*
* @return the fitness.
*/
Fitness getFitness();
public double getFitness() {
if (this.fitness == Double.MIN_VALUE) {
// no cache - compute the fitness
this.fitness = fitness();
}
return this.fitness;
}
/**
* Compares two chromosomes based on their fitness. The bigger the fitness,
* the better the chromosome.
*
* @param another another chromosome to compare
* @return
* <ul>
* <li>-1 if <code>another</code> is better than <code>this</code></li>
* <li>1 if <code>another</code> is worse than <code>this</code></li>
* <li>0 if the two chromosomes have the same fitness</li>
* </ul>
*/
public int compareTo(Chromosome another) {
return ((Double)this.getFitness()).compareTo(another.getFitness());
}
/**
* Returns <code>true<code> iff <code>another</code> has the same
* representation and therefore the same fitness. By default, it returns
* false -- override it in your implementation if you need it.
* @param another chromosome to compare
* @return true if <code>another</code> is equivalent to this chromosome
*/
protected boolean isSame(Chromosome another) {
return false;
}
/**
* Searches the <code>population</code> for another chromosome with the same
* representation. If such chromosome is found, it is returned, if no such
* chromosome exists, returns <code>null</code>.
*
* @param population
* Population to search
* @return Chromosome with the same representation, or <code>null</code> if
* no such chromosome exists.
*/
protected Chromosome findSameChromosome(Population population) {
for (Chromosome anotherChr : population) {
if (this.isSame(anotherChr))
return anotherChr;
}
return null;
}
/**
* Searches the population for a chromosome representing the same solution,
* and if it finds one, updates the fitness to its value.
*
* @param population
* Population to search
*/
public void searchForFitnessUpdate(Population population) {
Chromosome sameChromosome = findSameChromosome(population);
if (sameChromosome != null) {
fitness = sameChromosome.getFitness();
}
}
}

View File

@ -18,14 +18,16 @@ package org.apache.commons.math.genetics;
/**
* A pair of {@link Chromosome} objects.
* @since 2.0
*
* @version $Revision$ $Date$
*/
public class ChromosomePair {
/** the first chromosome in the pair. */
private Chromosome first;
private final Chromosome first;
/** the second chromosome in the pair. */
private Chromosome second;
private final Chromosome second;
/**
* Create a chromosome pair.
@ -33,7 +35,7 @@ public class ChromosomePair {
* @param c1 the first chromosome.
* @param c2 the second chromosome.
*/
public ChromosomePair(Chromosome c1, Chromosome c2) {
public ChromosomePair(final Chromosome c1, final Chromosome c2) {
super();
first = c1;
second = c2;
@ -56,4 +58,12 @@ public class ChromosomePair {
public Chromosome getSecond() {
return second;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return String.format("(%s,%s)", getFirst(), getSecond());
}
}

View File

@ -19,6 +19,8 @@ package org.apache.commons.math.genetics;
/**
* Policy used to create a pair of new chromosomes by performing a crossover
* operation on a source pair of chromosomes.
*
* @since 2.0
* @version $Revision$ $Date$
*/
public interface CrossoverPolicy {

View File

@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.Collections;
import java.util.List;
/**
* Population of chromosomes which uses elitism (certain percentace of the best
* chromosomes is directly copied to the next generation).
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public class ElitisticListPopulation extends ListPopulation {
/** percentage of chromosomes copied to the next generation */
private double elitismRate = 0.9;
/**
* Creates a new ElitisticListPopulation instance.
*
* @param chromosomes
* list of chromosomes in the population
* @param populationLimit
* maximal size of the population
* @param elitismRate
* how many best chromosomes will be directly transferred to the
* next generation [in %]
*/
public ElitisticListPopulation(List<Chromosome> chromosomes, int populationLimit, double elitismRate) {
super(chromosomes, populationLimit);
this.elitismRate = elitismRate;
}
/**
* Creates a new ListPopulation instance and initializes its inner
* chromosome list.
*
* @param populationLimit maximal size of the population
* @param elitismRate
* how many best chromosomes will be directly transferred to the
* next generation [in %]
*/
public ElitisticListPopulation(int populationLimit, double elitismRate) {
super(populationLimit);
this.elitismRate = elitismRate;
}
/**
* Start the population for the next generation. The
* <code>{@link #elitismRate}<code> percents of the best
* chromosomes are directly copied to the next generation.
*
* @return the beginnings of the next generation.
*/
public Population nextGeneration() {
// initialize a new generation with the same parameters
ElitisticListPopulation nextGeneration = new ElitisticListPopulation(this.getPopulationLimit(), this.getElitismRate());
List<Chromosome> oldChromosomes = this.getChromosomes();
Collections.sort(oldChromosomes);
// index of the last "not good enough" chromosome
int boundIndex = (int) Math.ceil((1.0 - this.getElitismRate()) * oldChromosomes.size());
for (int i=boundIndex; i<oldChromosomes.size(); i++) {
nextGeneration.addChromosome(oldChromosomes.get(i));
}
return nextGeneration;
}
/**
* Sets the elitism rate, i.e. how many best chromosomes will be directly
* transferred to the next generation [in %].
*
* @param elitismRate
* how many best chromosomes will be directly transferred to the
* next generation [in %]
*/
public void setElitismRate(double elitismRate) {
if (elitismRate < 0 || elitismRate > 1)
throw new IllegalArgumentException("Elitism rate has to be in [0,1]");
this.elitismRate = elitismRate;
}
/**
* Access the elitism rate.
* @return the elitism rate
*/
public double getElitismRate() {
return this.elitismRate;
}
}

View File

@ -17,9 +17,17 @@
package org.apache.commons.math.genetics;
/**
* Interface used to compare chromosomes.
* @version $Revision$ $Date$
* Fitness of a chromosome.
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public interface Fitness extends Comparable<Fitness> {
public interface Fitness {
/**
* Compute the fitness. This is usually very time-consuming, so the value
* should be cached.
*
* @return fitness
*/
public double fitness();
}

View File

@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
/**
* Stops after a fixed number of generations. Each time
* {@link #isSatisfied(Population)} is invoked, a generation counter is
* incremented. Once the counter reaches the configured
* <code>maxGenerations</code> value, {@link #isSatisfied(Population)} returns
* true.
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public class FixedGenerationCount implements StoppingCondition {
/** Number of generations that have passed */
private int numGenerations = 0;
/** Maximum number of generations (stopping criteria) */
private final int maxGenerations;
/**
* Create a new FixedGenerationCount instance.
*
* @param maxGenerations number of generations to evolve
*/
public FixedGenerationCount(int maxGenerations) {
if (maxGenerations <= 0)
throw new IllegalArgumentException("The number of generations has to be >= 0");
this.maxGenerations = maxGenerations;
}
/**
* Determine whether or not the given number of generations have passed.
* Increments the number of generations counter if the maximum has not
* been reached.
*
* @param population ignored (no impact on result)
* @return <code>true</code> IFF the maximum number of generations has been exceeded
*/
public boolean isSatisfied(Population population) {
if (this.numGenerations < this.maxGenerations) {
numGenerations++;
return false;
}
return true;
}
/**
* @return the number of generations that have passed
*/
public int getNumGenerations() {
return numGenerations;
}
}

View File

@ -16,28 +16,83 @@
*/
package org.apache.commons.math.genetics;
import org.apache.commons.math.random.RandomGenerator;
import org.apache.commons.math.random.JDKRandomGenerator;
/**
* Implementation of a genetic algorithm. All factors that govern the operation
* of the algorithm can be configured for a specific problem.
*
* @version $Revision$ $Date$
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public class GeneticAlgorithm {
/**
* Static random number generator shared by GA implementation classes.
* Set the randomGenerator seed to get reproducible results.
* Use {@link #setRandomGenerator(RandomGenerator)} to supply an alternative
* to the default JDK-provided PRNG.
*/
private static RandomGenerator randomGenerator = new JDKRandomGenerator();
/**
* Set the (static) random generator.
*
* @param random random generator
*/
public synchronized static void setRandomGenerator(RandomGenerator random) {
randomGenerator = random;
}
/**
* Returns the (static) random generator.
*
* @return the static random generator shared by GA implementation classes
*/
public synchronized static RandomGenerator getRandomGenerator() {
return randomGenerator;
}
/** the crossover policy used by the algorithm. */
private CrossoverPolicy crossoverPolicy;
protected final CrossoverPolicy crossoverPolicy;
/** the rate of crossover for the algorithm. */
private double crossoverRate;
protected final double crossoverRate;
/** the mutation policy used by the algorithm. */
private MutationPolicy mutationPolicy;
protected final MutationPolicy mutationPolicy;
/** the rate of mutation for the algorithm. */
private double mutationRate;
protected final double mutationRate;
/** the selection policy used by the algorithm. */
private SelectionPolicy selectionPolicy;
protected final SelectionPolicy selectionPolicy;
/**
* @param crossoverPolicy The {@link CrossoverPolicy}
* @param crossoverRate The crossover rate as a percentage (0-1 inclusive)
* @param mutationPolicy The {@link MutationPolicy}
* @param mutationRate The mutation rate as a percentage (0-1 inclusive)
* @param selectionPolicy The {@link selectionPolicy}
*/
public GeneticAlgorithm(
CrossoverPolicy crossoverPolicy, double crossoverRate,
MutationPolicy mutationPolicy, double mutationRate,
SelectionPolicy selectionPolicy) {
if (crossoverRate < 0 || crossoverRate > 1) {
throw new IllegalArgumentException("crossoverRate must be between 0 and 1");
}
if (mutationRate < 0 || mutationRate > 1) {
throw new IllegalArgumentException("mutationRate must be between 0 and 1");
}
this.crossoverPolicy = crossoverPolicy;
this.crossoverRate = crossoverRate;
this.mutationPolicy = mutationPolicy;
this.mutationRate = mutationRate;
this.selectionPolicy = selectionPolicy;
}
/**
* Evolve the given population. Evolution stops when the stopping condition
* is satisfied.
@ -54,51 +109,6 @@ public class GeneticAlgorithm {
return current;
}
/**
* Access the crossover policy.
*
* @return the crossover policy.
*/
private CrossoverPolicy getCrossoverPolicy() {
return crossoverPolicy;
}
/**
* Access the crossover rate.
*
* @return the crossover rate.
*/
private double getCrossoverRate() {
return crossoverRate;
}
/**
* Access the mutation policy.
*
* @return the mutation policy.
*/
private MutationPolicy getMutationPolicy() {
return mutationPolicy;
}
/**
* Access the mutation rate.
*
* @return the mutation rate.
*/
private double getMutationRate() {
return mutationRate;
}
/**
* Access the selection policy.
*
* @return the selection policy.
*/
private SelectionPolicy getSelectionPolicy() {
return selectionPolicy;
}
/**
* <p>Evolve the given population into the next generation.</p>
* <p><ol>
@ -118,89 +128,80 @@ public class GeneticAlgorithm {
* </ol>
* </p>
*
*
* @param current the current population.
* @return the population for the next generation.
*/
private Population nextGeneration(Population current) {
public Population nextGeneration(Population current) {
Population nextGeneration = current.nextGeneration();
while (nextGeneration.getPopulationSize() < nextGeneration
.getPopulationLimit()) {
while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
// select parent chromosomes
ChromosomePair pair = getSelectionPolicy().select(current);
// crossover?
if (Math.random() < getCrossoverRate()) {
if (randomGenerator.nextDouble() < getCrossoverRate()) {
// apply crossover policy to create two offspring
pair = getCrossoverPolicy().crossover(pair.getFirst(),
pair.getSecond());
pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond());
}
// mutation?
if (Math.random() < getMutationRate()) {
if (randomGenerator.nextDouble() < getMutationRate()) {
// apply mutation policy to the chromosomes
pair = new ChromosomePair(
getMutationPolicy().mutate(pair.getFirst()),
getMutationPolicy().mutate(pair.getSecond())
);
getMutationPolicy().mutate(pair.getFirst()),
getMutationPolicy().mutate(pair.getSecond()));
}
// add the first chromosome to the population
nextGeneration.addChromosome(pair.getFirst());
// is there still a place for the second chromosome?
if (nextGeneration.getPopulationSize() < nextGeneration
.getPopulationLimit()) {
if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
// add the second chromosome to the population
nextGeneration.addChromosome(pair.getSecond());
}
}
return nextGeneration;
}
/**
* Returns the crossover policy.
* @return crossover policy
*/
public CrossoverPolicy getCrossoverPolicy() {
return crossoverPolicy;
}
/**
* Modify the crossover policy.
*
* @param value the new crossover policy.
* Returns the crossover rate.
* @return crossover rate
*/
public void setCrossoverPolicy(CrossoverPolicy value) {
this.crossoverPolicy = value;
public double getCrossoverRate() {
return crossoverRate;
}
/**
* Modify the crossover rate.
*
* @param value the new crossover rate.
* Returns the mutation policy.
* @return mutation policy
*/
public void setCrossoverRate(double value) {
this.crossoverRate = value;
public MutationPolicy getMutationPolicy() {
return mutationPolicy;
}
/**
* Modify the mutation policy.
*
* @param value the new mutation policy.
* Returns the mutation rate.
* @return mutation rate
*/
public void setMutationPolicy(MutationPolicy value) {
this.mutationPolicy = value;
public double getMutationRate() {
return mutationRate;
}
/**
* Modify the mutation rate.
*
* @param value the new mutation rate.
* Returns the selection policy.
* @return selection policy
*/
public void setMutationRate(double value) {
this.mutationRate = value;
}
/**
* Modify the selection policy.
*
* @param value the new selection policy.
*/
public void setSelectionPolicy(SelectionPolicy value) {
this.selectionPolicy = value;
public SelectionPolicy getSelectionPolicy() {
return selectionPolicy;
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
/**
* Exception indicating that the representation of a chromosome is not valid.
*
* @version $Revision:$ $Date:$
* @since 2.0
*/
public class InvalidRepresentationException extends Exception {
/** Serialization version id */
private static final long serialVersionUID = 1L;
/**
* Constructor
*/
public InvalidRepresentationException() {
super();
}
/**
* Construct an InvalidRepresentationException
* @param arg0 exception message
*/
public InvalidRepresentationException(String arg0) {
super(arg0);
}
/**
* Construct an InvalidRepresentationException
* @param arg0 cause
*/
public InvalidRepresentationException(Throwable arg0) {
super(arg0);
}
/**
* Construct an InvalidRepresentationException
*
* @param arg0 exception message
* @param arg1 cause
*/
public InvalidRepresentationException(String arg0, Throwable arg1) {
super(arg0, arg1);
}
}

View File

@ -0,0 +1,150 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Population of chromosomes represented by a {@link List}.
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public abstract class ListPopulation implements Population {
/** List of chromosomes */
private List<Chromosome> chromosomes;
/** maximial size of the population */
private int populationLimit;
/**
* Creates a new ListPopulation instance.
*
* @param chromosomes list of chromosomes in the population
* @param populationLimit maximal size of the population
*/
public ListPopulation (List<Chromosome> chromosomes, int populationLimit) {
if (chromosomes.size() > populationLimit) {
throw new IllegalArgumentException("List of chromosomes bigger than maxPopulationSize.");
}
if (populationLimit < 0) {
throw new IllegalArgumentException("Population limit has to be >= 0");
}
this.chromosomes = chromosomes;
this.populationLimit = populationLimit;
}
/**
* Creates a new ListPopulation instance and initializes its inner
* chromosome list.
*
* @param populationLimit maximal size of the population
*/
public ListPopulation (int populationLimit) {
if (populationLimit < 0) {
throw new IllegalArgumentException("Population limit has to be >= 0");
}
this.populationLimit = populationLimit;
this.chromosomes = new ArrayList<Chromosome>(populationLimit);
}
/**
* Sets the list of chromosomes.
* @param chromosomes the list of chromosomes
*/
public void setChromosomes(List<Chromosome> chromosomes) {
this.chromosomes = chromosomes;
}
/**
* Access the list of chromosomes.
* @return the list of chromosomes
*/
public List<Chromosome> getChromosomes() {
return chromosomes;
}
/**
* Add the given chromosome to the population.
* @param chromosome the chromosome to add.
*/
public void addChromosome(Chromosome chromosome) {
this.chromosomes.add(chromosome);
}
/**
* Access the fittest chromosome in this population.
* @return the fittest chromosome.
*/
public Chromosome getFittestChromosome() {
// best so far
Chromosome bestChromosome = this.chromosomes.get(0);
for (Chromosome chromosome : this.chromosomes) {
if (chromosome.compareTo(bestChromosome) > 0) {
// better chromosome found
bestChromosome = chromosome;
}
}
return bestChromosome;
}
/**
* Access the maximum population size.
* @return the maximum population size.
*/
public int getPopulationLimit() {
return this.populationLimit;
}
/**
* Sets the maximal population size.
* @param populationLimit maximal population size.
*/
public void setPopulationLimit(int populationLimit) {
this.populationLimit = populationLimit;
}
/**
* Access the current population size.
* @return the current population size.
*/
public int getPopulationSize() {
return this.chromosomes.size();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return this.chromosomes.toString();
}
/**
* Chromosome list iterator
*
* @return chromosome iterator
*/
public Iterator<Chromosome> iterator() {
return chromosomes.iterator();
}
}

View File

@ -18,6 +18,8 @@ package org.apache.commons.math.genetics;
/**
* Algorithm used to mutate a chrommosome.
*
* @since 2.0
* @version $Revision$ $Date$
*/
public interface MutationPolicy {

View File

@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.List;
/**
* One point crossover policy. A random crossover point is selected and the
* first part from each parent is copied to the corresponding child, and the
* second parts are copied crosswise.
*
* Example:
* <pre>
* -C- denotes a crossover point
* -C- -C-
* p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
* \------------/ \-----/ \------------/ \-----/
* || (*) || (**)
* VV (**) VV (*)
* /------------\ /-----\ /------------\ /-----\
* c1 = (1 0 1 0 0 1 | 1 1 1) X p2 = (0 1 1 0 1 0 | 0 1 1)
* </pre>
*
* This policy works only on {@link AbstractListChromosome}, and therefore it
* is parametrized by T. Moreover, the chromosomes must have same lengths.
*
* @param <T> generic type of the {@link AbstractListChromosome}s for crossover
* @since 2.0
* @version $Revision:$ $Date:$
*
*/
public class OnePointCrossover<T> implements CrossoverPolicy {
/**
* Performs one point crossover. A random crossover point is selected and the
* first part from each parent is copied to the corresponding child, and the
* second parts are copied crosswise.
*
* Example:
* -C- denotes a crossover point
* -C- -C-
* p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
* \------------/ \-----/ \------------/ \-----/
* || (*) || (**)
* VV (**) VV (*)
* /------------\ /-----\ /------------\ /-----\
* c1 = (1 0 1 0 0 1 | 1 1 1) X p2 = (0 1 1 0 1 0 | 0 1 1)
*
* @param first first parent (p1)
* @param second second parent (p2)
* @return pair of two children (c1,c2)
*/
@SuppressWarnings("unchecked")
public ChromosomePair crossover(Chromosome first, Chromosome second) {
if (! (first instanceof AbstractListChromosome && second instanceof AbstractListChromosome)) {
throw new IllegalArgumentException("One point crossover works on FixedLengthChromosomes only.");
}
return crossover((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
}
/**
* Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
*
* @param first the first chromosome.
* @param second the second chromosome.
* @return the pair of new chromosomes that resulted from the crossover.
*/
private ChromosomePair crossover(AbstractListChromosome<T> first, AbstractListChromosome<T> second) {
int length = first.getLength();
if (length != second.getLength())
throw new IllegalArgumentException("Both chromosomes must have same lengths.");
// array representations of the parents
List<T> parent1Rep = first.getRepresentation();
List<T> parent2Rep = second.getRepresentation();
// and of the children
ArrayList<T> child1Rep = new ArrayList<T> (first.getLength());
ArrayList<T> child2Rep = new ArrayList<T> (second.getLength());
// select a crossover point at random (0 and length makes no sense)
int crossoverIndex = 1 + (GeneticAlgorithm.getRandomGenerator().nextInt(length-2));
// copy the first part
for (int i = 0; i < crossoverIndex; i++) {
child1Rep.add(parent1Rep.get(i));
child2Rep.add(parent2Rep.get(i));
}
// and switch the second part
for (int i = crossoverIndex; i < length; i++) {
child1Rep.add(parent2Rep.get(i));
child2Rep.add(parent1Rep.get(i));
}
return new ChromosomePair(
first.newFixedLengthChromosome(child1Rep),
second.newFixedLengthChromosome(child2Rep)
);
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.List;
/**
* Interface indicating that the chromosome represents a permutation of objects.
*
* @param <T>
* type of the permuted objects
* @since 2.0
* @version $Revision:$ $Date:$
*/
public interface PermutationChromosome<T> {
/**
* Permutes the <code>sequence</code> of objects of type T according to the
* permutation this chromosome represents. For example, if this chromosome
* represents a permutation (3,0,1,2), and the unpermuted sequence is
* (a,b,c,d), this yields (d,a,b,c).
*
* @param sequence
* the unpermuted (original) sequence of objects
* @return permutation of <code>sequence</code> represented by this
* permutation
*/
public List<T> decode(List<T> sequence);
}

View File

@ -18,9 +18,11 @@ package org.apache.commons.math.genetics;
/**
* A collection of chromosomes that facilitates generational evolution.
* @version $Revision$ $Date$
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public interface Population {
public interface Population extends Iterable<Chromosome> {
/**
* Access the current population size.
* @return the current population size.

View File

@ -0,0 +1,290 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* <p>
* Random Key chromosome is used for permutation representation. It is a vector
* of a fixed length of real numbers in [0,1] interval. The index of the i-th
* smallest value in the vector represents an i-th member of the permutation.
* </p>
*
* <p>
* For example, the random key [0.2, 0.3, 0.8, 0.1] corresponds to the
* permutation of indices (3,0,1,2). If the original (unpermuted) sequence would
* be (a,b,c,d), this would mean the sequence (d,a,b,c).
* </p>
*
* <p>
* With this representation, common operators like n-point crossover can be
* used, because any such chromosome represents a valid permutation.
* </p>
*
* <p>
* Since the chromosome (and thus its arrayRepresentation) is immutable, the
* array representation is sorted only once in the constructor.
* </p>
*
* <p>
* For details, see:
* <ul>
* <li>Bean, J.C.: Genetic algorithms and random keys for sequencing and
* optimization. ORSA Journal on Computing 6 (1994) 154160</li>
* <li>Rothlauf, F.: Representations for Genetic and Evolutionary Algorithms.
* Volume 104 of Studies in Fuzziness and Soft Computing. Physica-Verlag,
* Heidelberg (2002)</li>
* </ul>
* </p>
*
* @param <T>
* type of the permuted objects
* @since 2.0
* @version $Revision:$ $Date:$
*/
public abstract class RandomKey<T> extends AbstractListChromosome<Double> implements PermutationChromosome<T> {
/**
* Cache of sorted representation (unmodifiable).
*/
private final List<Double> sortedRepresentation;
/**
* Base sequence [0,1,...,n-1], permuted accorting to the representation (unmodifiable).
*/
private final List<Integer> baseSeqPermutation;
/**
* Constructor.
*
* @param representation list of [0,1] values representing the permutation
*/
public RandomKey(List<Double> representation) {
super(representation);
// store the sorted representation
List<Double> sortedRepr = new ArrayList<Double> (getRepresentation());
Collections.sort(sortedRepr);
sortedRepresentation = Collections.unmodifiableList(sortedRepr);
// store the permutation of [0,1,...,n-1] list for toString() and isSame() methods
baseSeqPermutation = Collections.unmodifiableList(
decodeGeneric(baseSequence(getLength()), getRepresentation(), sortedRepresentation)
);
}
/**
* Constructor.
*
* @param representation array of [0,1] values representing the permutation
*/
public RandomKey(Double[] representation) {
this(Arrays.asList(representation));
}
/**
* {@inheritDoc}
*/
public List<T> decode(List<T> sequence) {
return decodeGeneric(sequence, getRepresentation(), sortedRepresentation);
}
/**
* Decodes a permutation represented by <code>representation</code> and
* returns a (generic) list with the permuted values.
*
* @param <S> generic type of the sequence values
* @param sequence the unpermuted sequence
* @param representation representation of the permutation ([0,1] vector)
* @param sortedRepr sorted <code>representation</code>
* @return list with the sequence values permuted according to the representation
*/
private static <S> List<S> decodeGeneric(List<S> sequence, List<Double> representation, List<Double> sortedRepr) {
int l = sequence.size();
if (representation.size() != l) {
throw new IllegalArgumentException(String.format("Length of sequence for decoding (%s) has to be equal to the length of the RandomKey (%s)", l, representation.size()));
}
if (representation.size() != sortedRepr.size()) {
throw new IllegalArgumentException(String.format("Representation and sortedRepr must have same sizes, %d != %d", representation.size(), sortedRepr.size()));
}
List<Double> reprCopy = new ArrayList<Double> (representation);// do not modify the orig. representation
// now find the indices in the original repr and use them for permuting
List<S> res = new ArrayList<S> (l);
for (int i=0; i<l; i++) {
int index = reprCopy.indexOf(sortedRepr.get(i));
res.add(sequence.get(index));
reprCopy.set(index, null);
}
return res;
}
/**
* Returns <code>true</code> iff <code>another</code> is a RandomKey and
* encodes the same permutation.
*
* @param another chromosome to compare
* @return true iff chromosomes encode the same permutation
*/
@Override
protected boolean isSame(Chromosome another) {
// type check
if (! (another instanceof RandomKey))
return false;
RandomKey<?> anotherRk = (RandomKey<?>) another;
// size check
if (getLength() != anotherRk.getLength())
return false;
// two different representations can still encode the same permutation
// the ordering is what counts
List<Integer> thisPerm = this.baseSeqPermutation;
List<Integer> anotherPerm = anotherRk.baseSeqPermutation;
for (int i=0; i<getLength(); i++) {
if (thisPerm.get(i) != anotherPerm.get(i))
return false;
}
// the permutations are the same
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected void checkValidity(java.util.List<Double> representation) throws InvalidRepresentationException {
for (double val : representation) {
if (val < 0 || val > 1) {
throw new InvalidRepresentationException("Values of representation must be in [0,1] interval");
}
}
};
/**
* Generates a representation corresponding to a random permutation of
* length l which can be passed to the RandomKey constructor.
*
* @param l
* length of the permutation
* @return representation of a random permutation
*/
public static final List<Double> randomPermutation(int l) {
List<Double> repr = new ArrayList<Double>(l);
for (int i=0; i<l; i++) {
repr.add(GeneticAlgorithm.getRandomGenerator().nextDouble());
}
return repr;
}
/**
* Generates a representation corresponding to an identity permutation of
* length l which can be passed to the RandomKey constructor.
*
* @param l
* length of the permutation
* @return representation of an identity permutation
*/
public static final List<Double> identityPermutation(int l) {
List<Double> repr = new ArrayList<Double>(l);
for (int i=0; i<l; i++) {
repr.add((double)i/l);
}
return repr;
}
/**
* Generates a representation of a permutation corresponding to the
* <code>data</code> sorted by <code>comparator</code>. The
* <code>data</code> is not modified during the process.
*
* This is useful if you want to inject some permutations to the initial
* population.
*
* @param <S> type of the data
* @param data list of data determining the order
* @param comparator how the data will be compared
* @return list representation of the permutation corresponding to the parameters
*/
public static <S> List<Double> comparatorPermutation(List<S> data, Comparator<S> comparator) {
List<S> sortedData = new ArrayList<S> (data);
Collections.sort(sortedData, comparator);
return inducedPermutation(data, sortedData);
}
/**
* Generates a representation of a permutation corresponding to a
* permutation which yields <code>permutedData</code> when applied to
* <code>originalData</code>.
*
* This method can be viewed as an inverse to {@link #decode(List)}.
*
* @param <S> type of the data
* @param originalData the original, unpermuted data
* @param permutedData the data, somehow permuted
* @return representation of a permutation corresponding to the permutation <code>originalData -> permutedData</code>
* @throws IllegalArgumentException iff the <code>permutedData</code> and <code>originalData</code> contains different data
*/
public static <S> List<Double> inducedPermutation(List<S> originalData, List<S> permutedData) throws IllegalArgumentException {
if (originalData.size() != permutedData.size()) {
throw new IllegalArgumentException("originalData and permutedData must have same length");
}
int l = originalData.size();
List<S> origDataCopy = new ArrayList<S> (originalData);
Double[] res = new Double[l];
for (int i=0; i<l; i++) {
int index = origDataCopy.indexOf(permutedData.get(i));
if (index == -1) {
throw new IllegalArgumentException("originalData and permutedData must contain the same objects.");
}
res[index] = (double) i / l;
origDataCopy.set(index, null);
}
return Arrays.asList(res);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return String.format("(f=%s pi=(%s))", getFitness(), baseSeqPermutation);
}
/**
* Helper for constructor. Generates a list of natural numbers (0,1,...,l-1).
*
* @param l length of list to generate
* @return list of integers from 0 to l-1
*/
private static List<Integer> baseSequence(int l) {
List<Integer> baseSequence = new ArrayList<Integer> (l);
for (int i=0; i<l; i++) {
baseSequence.add(i);
}
return baseSequence;
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math.MathRuntimeException;
/**
* Mutation operator for {@link RandomKey}s. Changes a randomly chosen element
* of the array representation to a random value uniformly distributed in [0,1].
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public class RandomKeyMutation implements MutationPolicy {
/**
* {@inheritDoc}
*
* @throws IllegalArgumentException if <code>original</code> is not a
* {@link RandomKeys} instance
*/
public Chromosome mutate(Chromosome original) {
if (!(original instanceof RandomKey)) {
throw MathRuntimeException.createIllegalArgumentException(
"RandomKeyMutation works only with RandomKeys, got " +
original.getClass().getSimpleName());
}
RandomKey<?> originalRk = (RandomKey<?>) original;
List<Double> repr = originalRk.getRepresentation();
int rInd = GeneticAlgorithm.getRandomGenerator().nextInt(repr.size());
List<Double> newRepr = new ArrayList<Double> (repr);
newRepr.set(rInd, GeneticAlgorithm.getRandomGenerator().nextDouble());
return originalRk.newFixedLengthChromosome(newRepr);
}
}

View File

@ -18,7 +18,9 @@ package org.apache.commons.math.genetics;
/**
* Algorithm used to select a chromosome pair from a population.
* @version $Revision$ $Date$
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public interface SelectionPolicy {
/**

View File

@ -18,7 +18,9 @@ package org.apache.commons.math.genetics;
/**
* Algorithm used to determine when to stop evolution.
* @version $Revision$ $Date$
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public interface StoppingCondition {
/**

View File

@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.ArrayList;
import java.util.List;
/**
* Tournament selection scheme. Each of the two selected chromosomes is selected
* based on n-ary tournament -- this is done by drawing {@link #arity} random
* chromosomes without replacement from the population, and then selecting the
* fittest chromosome among them.
*
* @since 2.0
* @version $Revision:$ $Date:$
*/
public class TournamentSelection implements SelectionPolicy {
/** number of chromosomes included in the tournament selections */
private int arity;
/**
* Creates a new TournamentSelection instance.
*
* @param arity
* how many chromosomes will be drawn to the tournament
*/
public TournamentSelection(int arity) {
this.arity = arity;
}
/**
* Select two chromosomes from the population. Each of the two selected
* chromosomes is selected based on n-ary tournament -- this is done by
* drawing {@link #arity} random chromosomes without replacement from the
* population, and then selecting the fittest chromosome among them.
*
* @param population
* the population from which the chromosomes are choosen.
* @return the selected chromosomes.
*/
public ChromosomePair select(Population population) {
return new ChromosomePair(
tournament((ListPopulation) population),
tournament((ListPopulation)population)
);
}
/**
* Helper for {@link #select(Population)}. Draw {@link #arity} random
* chromosomes without replacement from the population, and then select the
* fittest chromosome among them.
*
* @param population
* the population from which the chromosomes are choosen.
* @return the selected chromosome.
*/
private Chromosome tournament(ListPopulation population) {
if (population.getPopulationSize() < this.arity)
throw new IllegalArgumentException("Tournament arity cannot be bigger than population size.");
// auxiliary population
ListPopulation tournamentPopulation = new ListPopulation(this.arity) {
public Population nextGeneration() {
// not useful here
return null;
}
};
// create a copy of the chromosome list
List<Chromosome> chromosomes = new ArrayList<Chromosome> (population.getChromosomes());
for (int i=0; i<this.arity; i++) {
// select a random individual and add it to the tournament
int rind = GeneticAlgorithm.getRandomGenerator().nextInt(chromosomes.size());
tournamentPopulation.addChromosome(chromosomes.get(rind));
// do not select it again
chromosomes.remove(rind);
}
// the winner takes it all
return tournamentPopulation.getFittestChromosome();
}
/**
* Gets the arity (number of chromosomes drawn to the tournament).
*
* @return arity of the tournament
*/
public int getArity() {
return arity;
}
/**
* Sets the arity (number of chromosomes drawn to the tournament).
*
* @param arity arity of the tournament
*/
public void setArity(int arity) {
this.arity = arity;
}
}

View File

@ -18,7 +18,7 @@
<!-- $Revision$ -->
<body>
<p>
This package provides basic genetic algorithms components.
This package provides Genetic Algorithms components and implementations.
</p>
</body>
</html>

View File

@ -39,6 +39,9 @@ The <action> type attribute can be add,update,fix,remove.
</properties>
<body>
<release version="2.0" date="TBD" description="TBD">
<action dev="luc" type="fix" issue="MATH-207" due-to="David Stefka">
Added Genetic Algorithm implementation.
</action>
<action dev="luc" type="fix" issue="MATH-274" >
Fixed detection of not positive definite matrices in Cholesky decomposition
</action>

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
public class BinaryChromosomeTest {
@Test
public void testInvalidConstructor() {
Integer[][] reprs = new Integer[][] {
new Integer[] {0,1,0,1,2},
new Integer[] {0,1,0,1,-1}
};
for (Integer[] repr : reprs) {
try {
new DummyBinaryChromosome(repr);
fail("Exception not caught");
} catch (IllegalArgumentException e) {
}
}
}
@Test
public void testRandomConstructor() {
for (int i=0; i<20; i++) {
new DummyBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(10));
}
}
@Test
public void testIsSame() {
Chromosome c1 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1});
Chromosome c2 = new DummyBinaryChromosome(new Integer[] {0,1,1,0,1});
Chromosome c3 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1,1});
Chromosome c4 = new DummyBinaryChromosome(new Integer[] {1,1,0,1,0,1});
Chromosome c5 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,0});
Chromosome c6 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1});
assertFalse(c1.isSame(c2));
assertFalse(c1.isSame(c3));
assertFalse(c1.isSame(c4));
assertFalse(c1.isSame(c5));
assertTrue(c1.isSame(c6));
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import org.junit.Test;
public class BinaryMutationTest {
@Test
public void testMutate() {
BinaryMutation mutation = new BinaryMutation();
// stochastic testing :)
for (int i=0; i<20; i++) {
DummyBinaryChromosome original = new DummyBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(10));
DummyBinaryChromosome mutated = (DummyBinaryChromosome) mutation.mutate(original);
// one gene should be different
int numDifferent = 0;
for (int j=0; j<original.getRepresentation().size(); j++) {
if (original.getRepresentation().get(j) != mutated.getRepresentation().get(j))
numDifferent++;
}
assertEquals(1, numDifferent);
}
}
}

View File

@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class ChromosomeTest {
@Test
public void testCompareTo() {
Chromosome c1 = new Chromosome() {
public double fitness() {
return 0;
}
};
Chromosome c2 = new Chromosome() {
public double fitness() {
return 10;
}
};
Chromosome c3 = new Chromosome() {
public double fitness() {
return 10;
}
};
assertTrue(c1.compareTo(c2) < 0);
assertTrue(c2.compareTo(c1) > 0);
assertEquals(0,c3.compareTo(c2));
assertEquals(0,c2.compareTo(c3));
}
private abstract static class DummyChromosome extends Chromosome {
private final int repr;
public DummyChromosome(final int repr) {
this.repr = repr;
}
@Override
protected boolean isSame(Chromosome another) {
return ((DummyChromosome) another).repr == repr;
}
}
@Test
public void testFindSameChromosome() {
Chromosome c1 = new DummyChromosome(1) {
public double fitness() {
return 1;
}
};
Chromosome c2 = new DummyChromosome(2) {
public double fitness() {
return 2;
}
};
Chromosome c3 = new DummyChromosome(3) {
public double fitness() {
return 3;
}
};
Chromosome c4 = new DummyChromosome(1) {
public double fitness() {
return 5;
}
};
Chromosome c5 = new DummyChromosome(15) {
public double fitness() {
return 15;
}
};
List<Chromosome> popChr = new ArrayList<Chromosome>();
popChr.add(c1);
popChr.add(c2);
popChr.add(c3);
Population pop = new ListPopulation(popChr,3) {
public Population nextGeneration() {
// not important
return null;
}
};
assertNull(c5.findSameChromosome(pop));
assertEquals(c1, c4.findSameChromosome(pop));
c4.searchForFitnessUpdate(pop);
assertEquals(1, c4.getFitness(),0);
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.List;
/**
* Implementation of BinaryChromosome for testing purposes
*/
public class DummyBinaryChromosome extends BinaryChromosome {
public DummyBinaryChromosome(List<Integer> representation) {
super(representation);
}
public DummyBinaryChromosome(Integer[] representation) {
super(representation);
}
@Override
public AbstractListChromosome<Integer> newFixedLengthChromosome(List<Integer> representation) {
return new DummyBinaryChromosome(representation);
}
public double fitness() {
// uninteresting
return 0;
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import java.util.List;
/**
* Implementation of RandomKey for testing purposes
*/
public class DummyRandomKey extends RandomKey<String> {
public DummyRandomKey(List<Double> representation) {
super(representation);
}
public DummyRandomKey(Double[] representation) {
super(representation);
}
@Override
public AbstractListChromosome<Double> newFixedLengthChromosome(List<Double> representation) {
return new DummyRandomKey(representation);
}
public double fitness() {
// unimportant
return 0;
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import org.junit.Test;
public class ElitisticListPopulationTest {
private static int counter = 0;
@Test
public void testNextGeneration() {
ElitisticListPopulation pop = new ElitisticListPopulation(100, 0.203);
for (int i=0; i<pop.getPopulationLimit(); i++) {
pop.addChromosome(new DummyChromosome());
}
Population nextGeneration = pop.nextGeneration();
assertEquals(20, nextGeneration.getPopulationSize());
}
private static class DummyChromosome extends Chromosome {
private final int fitness;
public DummyChromosome() {
this.fitness = counter;
counter++;
}
public double fitness() {
return this.fitness;
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;
public class FitnessCachingTest {
// parameters for the GA
private static final int DIMENSION = 50;
private static final double CROSSOVER_RATE = 1;
private static final double MUTATION_RATE = 0.1;
private static final int TOURNAMENT_ARITY = 5;
private static final int POPULATION_SIZE = 10;
private static final int NUM_GENERATIONS = 50;
private static final double ELITISM_RATE = 0.2;
// how many times was the fitness computed
public static int fitnessCalls = 0;
@Test
public void testFitnessCaching() {
// initialize a new genetic algorithm
GeneticAlgorithm ga = new GeneticAlgorithm(
new OnePointCrossover<Integer>(),
CROSSOVER_RATE, // all selected chromosomes will be recombined (=crosssover)
new BinaryMutation(),
MUTATION_RATE, // no mutation
new TournamentSelection(TOURNAMENT_ARITY)
);
// initial population
Population initial = randomPopulation();
// stopping conditions
StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
// run the algorithm
ga.evolve(initial, stopCond);
int neededCalls =
POPULATION_SIZE /*initial population*/ +
(NUM_GENERATIONS - 1) /*for each population*/ * (int)(POPULATION_SIZE * (1.0 - ELITISM_RATE)) /*some chromosomes are copied*/
;
assertTrue(fitnessCalls <= neededCalls); // some chromosomes after crossover may be the same os old ones
}
/**
* Initializes a random population.
*/
private static ElitisticListPopulation randomPopulation() {
List<Chromosome> popList = new LinkedList<Chromosome>();
for (int i=0; i<POPULATION_SIZE; i++) {
BinaryChromosome randChrom = new DummyCountingBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(DIMENSION));
popList.add(randChrom);
}
return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
}
private static class DummyCountingBinaryChromosome extends DummyBinaryChromosome {
public DummyCountingBinaryChromosome(List<Integer> representation) {
super(representation);
}
@Override
public double fitness() {
fitnessCalls++;
return 0;
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.Iterator;
import org.junit.Test;
public class FixedGenerationCountTest {
@Test
public void testIsSatisfied() {
FixedGenerationCount fgc = new FixedGenerationCount(20);
int cnt = 0;
Population pop = new Population() {
public void addChromosome(Chromosome chromosome) {
// unimportant
}
public Chromosome getFittestChromosome() {
// unimportant
return null;
}
public int getPopulationLimit() {
// unimportant
return 0;
}
public int getPopulationSize() {
// unimportant
return 0;
}
public Population nextGeneration() {
// unimportant
return null;
}
public Iterator<Chromosome> iterator() {
// unimportant
return null;
}
};
while (!fgc.isSatisfied(pop))
cnt++;
assertEquals(20, cnt);
}
}

View File

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;
/**
* This is also an example of usage.
*/
public class GeneticAlgorithmTestBinary {
// parameters for the GA
private static final int DIMENSION = 50;
private static final int POPULATION_SIZE = 50;
private static final int NUM_GENERATIONS = 50;
private static final double ELITISM_RATE = 0.2;
private static final double CROSSOVER_RATE = 1;
private static final double MUTATION_RATE = 0.1;
private static final int TOURNAMENT_ARITY = 2;
@Test
public void test() {
// to test a stochastic algorithm is hard, so this will rather be an usage example
// initialize a new genetic algorithm
GeneticAlgorithm ga = new GeneticAlgorithm(
new OnePointCrossover<Integer>(),
CROSSOVER_RATE, // all selected chromosomes will be recombined (=crosssover)
new BinaryMutation(),
MUTATION_RATE,
new TournamentSelection(TOURNAMENT_ARITY)
);
// initial population
Population initial = randomPopulation();
// stopping conditions
StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
// best initial chromosome
Chromosome bestInitial = initial.getFittestChromosome();
// run the algorithm
Population finalPopulation = ga.evolve(initial, stopCond);
// best chromosome from the final population
Chromosome bestFinal = finalPopulation.getFittestChromosome();
// the only thing we can test is whether the final solution is not worse than the initial one
// however, for some implementations of GA, this need not be true :)
assertTrue(bestFinal.compareTo(bestInitial) > 0);
//System.out.println(bestInitial);
//System.out.println(bestFinal);
}
/**
* Initializes a random population.
*/
private static ElitisticListPopulation randomPopulation() {
List<Chromosome> popList = new LinkedList<Chromosome>();
for (int i=0; i<POPULATION_SIZE; i++) {
BinaryChromosome randChrom = new FindOnes(BinaryChromosome.randomBinaryRepresentation(DIMENSION));
popList.add(randChrom);
}
return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
}
/**
* Chromosomes represented by a binary chromosome.
*
* The goal is to set all bits (genes) to 1.
*/
private static class FindOnes extends BinaryChromosome {
public FindOnes(List<Integer> representation) {
super(representation);
}
/**
* Returns number of elements != 0
*/
public double fitness() {
int num = 0;
for (int val : this.getRepresentation()) {
if (val != 0)
num++;
}
// number of elements >= 0
return num;
}
@Override
public AbstractListChromosome<Integer> newFixedLengthChromosome(List<Integer> representation) {
return new FindOnes(representation);
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/**
* This is also an example of usage.
*
* This algorithm does "stochastic sorting" of a sequence 0,...,N.
*
*/
public class GeneticAlgorithmTestPermutations {
// parameters for the GA
private static final int DIMENSION = 20;
private static final int POPULATION_SIZE = 80;
private static final int NUM_GENERATIONS = 200;
private static final double ELITISM_RATE = 0.2;
private static final double CROSSOVER_RATE = 1;
private static final double MUTATION_RATE = 0.08;
private static final int TOURNAMENT_ARITY = 2;
// numbers from 0 to N-1
private static List<Integer> sequence = new ArrayList<Integer>();
static {
for (int i=0; i<DIMENSION; i++) {
sequence.add(i);
}
}
@Test
public void test() {
// to test a stochastic algorithm is hard, so this will rather be an usage example
// initialize a new genetic algorithm
GeneticAlgorithm ga = new GeneticAlgorithm(
new OnePointCrossover<Integer>(),
CROSSOVER_RATE,
new RandomKeyMutation(),
MUTATION_RATE,
new TournamentSelection(TOURNAMENT_ARITY)
);
// initial population
Population initial = randomPopulation();
// stopping conditions
StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
// best initial chromosome
Chromosome bestInitial = initial.getFittestChromosome();
// run the algorithm
Population finalPopulation = ga.evolve(initial, stopCond);
// best chromosome from the final population
Chromosome bestFinal = finalPopulation.getFittestChromosome();
// the only thing we can test is whether the final solution is not worse than the initial one
// however, for some implementations of GA, this need not be true :)
assertTrue(bestFinal.compareTo(bestInitial) > 0);
//System.out.println(bestInitial);
//System.out.println(bestFinal);
}
/**
* Initializes a random population
*/
private static ElitisticListPopulation randomPopulation() {
List<Chromosome> popList = new ArrayList<Chromosome>();
for (int i=0; i<POPULATION_SIZE; i++) {
Chromosome randChrom = new MinPermutations(RandomKey.randomPermutation(DIMENSION));
popList.add(randChrom);
}
return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
}
/**
* Chromosomes representing a permutation of (0,1,2,...,DIMENSION-1).
*
* The goal is to sort the sequence.
*/
private static class MinPermutations extends RandomKey<Integer> {
public MinPermutations(List<Double> representation) {
super(representation);
// TODO Auto-generated constructor stub
}
public double fitness() {
int res = 0;
List<Integer> decoded = decode(sequence);
for (int i=0; i<decoded.size(); i++) {
int value = (Integer) decoded.get(i);
if (value != i) {
// bad position found
res += Math.abs(value - i);
}
}
// the most fitted chromosome is the one with minimal error
// therefore we must return negative value
return -res;
}
@Override
public AbstractListChromosome<Double> newFixedLengthChromosome(List<Double> representation) {
return new MinPermutations(representation);
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.ArrayList;
import org.junit.Test;
public class ListPopulationTest {
@Test
public void testGetFittestChromosome() {
Chromosome c1 = new Chromosome() {
public double fitness() {
return 0;
}
};
Chromosome c2 = new Chromosome() {
public double fitness() {
return 10;
}
};
Chromosome c3 = new Chromosome() {
public double fitness() {
return 15;
}
};
ArrayList<Chromosome> chromosomes = new ArrayList<Chromosome> ();
chromosomes.add(c1);
chromosomes.add(c2);
chromosomes.add(c3);
ListPopulation population = new ListPopulation(chromosomes,10) {
public Population nextGeneration() {
// not important
return null;
}
};
assertEquals(c3, population.getFittestChromosome());
}
}

View File

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import org.junit.Test;
public class OnePointCrossoverTest {
@Test
public void testCrossover() {
Integer[] p1 = new Integer[] {1,0,1,0,0,1,0,1,1};
Integer[] p2 = new Integer[] {0,1,1,0,1,0,1,1,1};
BinaryChromosome p1c = new DummyBinaryChromosome(p1);
BinaryChromosome p2c = new DummyBinaryChromosome(p2);
OnePointCrossover<Integer> opc = new OnePointCrossover<Integer>();
// how to test a stochastic method?
for (int i=0; i<20; i++) {
ChromosomePair pair = opc.crossover(p1c,p2c);
Integer[] c1 = new Integer[p1.length];
Integer[] c2 = new Integer[p2.length];
c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation().toArray(c1);
c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation().toArray(c2);
// first and last values will be the same
assertEquals((int) p1[0], (int) c1[0]);
assertEquals((int) p2[0], (int) c2[0]);
assertEquals((int) p1[p1.length-1], (int) c1[c1.length-1]);
assertEquals((int) p2[p2.length-1], (int) c2[c2.length-1]);
// moreover, in the above setting, the 2nd, 3rd and 7th values will be the same
assertEquals((int) p1[2], (int) c1[2]);
assertEquals((int) p2[2], (int) c2[2]);
assertEquals((int) p1[3], (int) c1[3]);
assertEquals((int) p2[3], (int) c2[3]);
assertEquals((int) p1[7], (int) c1[7]);
assertEquals((int) p2[7], (int) c2[7]);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import org.junit.Test;
public class RandomKeyMutationTest {
@Test
public void testMutate() {
MutationPolicy mutation = new RandomKeyMutation();
int l=10;
for (int i=0; i<20; i++) {
DummyRandomKey origRk = new DummyRandomKey(RandomKey.randomPermutation(l));
Chromosome mutated = mutation.mutate(origRk);
DummyRandomKey mutatedRk = (DummyRandomKey) mutated;
int changes = 0;
for (int j=0; j<origRk.getLength(); j++) {
if (origRk.getRepresentation().get(j) != mutatedRk.getRepresentation().get(j)) {
changes++;
}
}
assertEquals(1,changes);
}
}
}

View File

@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.junit.Test;
public class RandomKeyTest {
@Test(expected=IllegalArgumentException.class)
public void testConstructor1() {
@SuppressWarnings("unused")
DummyRandomKey drk = new DummyRandomKey(new Double[] {0.2, 0.3, 1.2});
}
@Test(expected=IllegalArgumentException.class)
public void testConstructor2() {
@SuppressWarnings("unused")
DummyRandomKey drk = new DummyRandomKey(new Double[] {0.2, 0.3, -0.2});
}
@Test
public void testIsSame() {
DummyRandomKey drk1 = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
DummyRandomKey drk2 = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
DummyRandomKey drk3 = new DummyRandomKey(new Double[] {0.4, 0.15, 0.5, 0.8, 0.2});
DummyRandomKey drk4 = new DummyRandomKey(new Double[] {0.4, 0.25, 0.5, 0.8, 0.2});
DummyRandomKey drk5 = new DummyRandomKey(new Double[] {0.4, 0.25, 0.5, 0.8, 0.2, 0.5});
assertTrue(drk1.isSame(drk2));
assertTrue(drk2.isSame(drk3));
assertFalse(drk3.isSame(drk4));
assertFalse(drk4.isSame(drk5));
}
@Test
public void testDecode() {
DummyRandomKey drk = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
List<String> decoded = drk.decode(Arrays.asList(new String[] {"a", "b", "c", "d", "e"}));
assertEquals("b", decoded.get(0));
assertEquals("e", decoded.get(1));
assertEquals("a", decoded.get(2));
assertEquals("c", decoded.get(3));
assertEquals("d", decoded.get(4));
}
@Test
public void testRandomPermutation() {
// never generate an invalid one
for (int i=0; i<10; i++) {
@SuppressWarnings("unused")
DummyRandomKey drk = new DummyRandomKey(RandomKey.randomPermutation(20));
}
}
@Test
public void testIdentityPermutation() {
DummyRandomKey drk = new DummyRandomKey(RandomKey.identityPermutation(5));
List<String> decoded = drk.decode(Arrays.asList(new String[] {"a", "b", "c", "d", "e"}));
assertEquals("a", decoded.get(0));
assertEquals("b", decoded.get(1));
assertEquals("c", decoded.get(2));
assertEquals("d", decoded.get(3));
assertEquals("e", decoded.get(4));
}
@Test
public void testComparatorPermutation() {
List<String> data = Arrays.asList(new String[] {"x", "b", "c", "z", "b"});
List<Double> permutation = RandomKey.comparatorPermutation(data, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
Double[] permArr = new Double[data.size()];
permArr = permutation.toArray(permArr);
assertArrayEquals(new Double[] {0.6,0.0,0.4,0.8,0.2}, permArr);
List<String> decodedData = new DummyRandomKey(permutation).decode(data);
assertEquals("b", decodedData.get(0));
assertEquals("b", decodedData.get(1));
assertEquals("c", decodedData.get(2));
assertEquals("x", decodedData.get(3));
assertEquals("z", decodedData.get(4));
permutation = RandomKey.comparatorPermutation(data, new Comparator<String>() {
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
permArr = new Double[data.size()];
permArr = permutation.toArray(permArr);
assertArrayEquals(new Double[] {0.2,0.6,0.4,0.0,0.8}, permArr);
decodedData = new DummyRandomKey(permutation).decode(data);
assertEquals("z", decodedData.get(0));
assertEquals("x", decodedData.get(1));
assertEquals("c", decodedData.get(2));
assertEquals("b", decodedData.get(3));
assertEquals("b", decodedData.get(4));
}
@Test
public void testInducedPermutation() {
List<String> origData = Arrays.asList(new String[] {"a", "b", "c", "d", "d"});
List<String> permutedData = Arrays.asList(new String[] {"d", "b", "c", "a", "d"});
DummyRandomKey drk = new DummyRandomKey(RandomKey.inducedPermutation(origData, permutedData));
List<String> decoded = drk.decode(origData);
assertEquals("d", decoded.get(0));
assertEquals("b", decoded.get(1));
assertEquals("c", decoded.get(2));
assertEquals("a", decoded.get(3));
assertEquals("d", decoded.get(4));
try {
RandomKey.inducedPermutation(
Arrays.asList(new String[] {"a", "b", "c", "d", "d"}),
Arrays.asList(new String[] {"a", "b", "c", "d"})
);
fail("Uncaught exception");
} catch (IllegalArgumentException e) {
// no-op
}
try {
RandomKey.inducedPermutation(
Arrays.asList(new String[] {"a", "b", "c", "d", "d"}),
Arrays.asList(new String[] {"a", "b", "c", "d", "f"})
);
fail("Uncaught exception");
} catch (IllegalArgumentException e) {
// no-op
}
}
@Test
public void testEqualRepr() {
DummyRandomKey drk = new DummyRandomKey(new Double[] {0.2, 0.2, 0.5});
List<String> decodedData = drk.decode(Arrays.asList(new String[] {"a", "b", "c"}));
assertEquals("a", decodedData.get(0));
assertEquals("b", decodedData.get(1));
assertEquals("c", decodedData.get(2));
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.genetics;
import static org.junit.Assert.*;
import org.junit.Test;
public class TournamentSelectionTest {
private static int counter = 0;
@Test
public void testSelect() {
TournamentSelection ts = new TournamentSelection(2);
ElitisticListPopulation pop = new ElitisticListPopulation(100, 0.203);
for (int i=0; i<pop.getPopulationLimit(); i++) {
pop.addChromosome(new DummyChromosome());
}
// how to write a test for stochastic method?
for (int i=0; i<20; i++) {
ChromosomePair pair = ts.select(pop);
// the worst chromosome should NEVER be selected
assertTrue(pair.getFirst().getFitness() > 0);
assertTrue(pair.getSecond().getFitness() > 0);
}
}
private static class DummyChromosome extends Chromosome {
private final int fitness;
public DummyChromosome() {
this.fitness = counter;
counter++;
}
public double fitness() {
return this.fitness;
}
}
}