/**
* @author Steven L. Moxley
* @version 1.2
*/
package org.futurist.neuralnet.evolve;
import java.util.ArrayList;
import java.util.Random;
import org.apache.commons.math3.distribution.AbstractRealDistribution;
import org.apache.commons.math3.distribution.BetaDistribution;
import org.apache.commons.math3.distribution.CauchyDistribution;
import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.distribution.FDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import org.apache.commons.math3.distribution.WeibullDistribution;
import org.apache.commons.math3.genetics.Chromosome;
import org.apache.commons.math3.genetics.MutationPolicy;
public class NeuralNetworkMutation implements MutationPolicy {
public static final int numParams = 8;
protected Random rng;
protected ArrayList<AbstractRealDistribution> dists;
/**
* Default constructor to create a <code>NeuralNetworkMutation</code>.
*/
public NeuralNetworkMutation() {
rng = new Random();
dists = new ArrayList<AbstractRealDistribution>();
}
/**
* Mutate the given original <code>Chromosome</code> by randomly deciding how many genes to change and setting them to values randomly drawn from a range of valid values or return an error if the given <code>Chromosome</code> is not an instance of <code>NeuralNetworkChromosome</code>.
* @param c the original <code>Chromosome</code>.
* @return the mutated <code>Chromosome</code>.
*/
public Chromosome mutate(Chromosome c) {
if(c instanceof NeuralNetworkChromosome) {
NeuralNetworkChromosome original = (NeuralNetworkChromosome) c;
// define the valid AbstractRealDistributions
dists.add(new BetaDistribution(original.getStartNodes()*original.getNumOutputs(), original.getStartEdges()));
dists.add(new CauchyDistribution());
dists.add(new ChiSquaredDistribution(original.getStartNodes()*original.getNumOutputs()));
dists.add(new ExponentialDistribution(original.getStartNodes()*original.getNumOutputs()));
dists.add(new FDistribution(original.getStartNodes()*original.getNumOutputs(), original.getStartEdges()));
dists.add(new NormalDistribution());
dists.add(new TDistribution(original.getStartNodes()*original.getNumOutputs()));
dists.add(new UniformRealDistribution());
dists.add(new WeibullDistribution(original.getStartNodes()*original.getNumOutputs(), original.getStartEdges()));
// create a mutant representation with placeholder parameters
NeuralNetworkChromosome mutant = original;
// set the mutant's ID to the original's ID concatenated with a random integer
mutant.setID(rng.nextInt());
// randomly decide which gene to mutate
Boolean[] paramsToChange = new Boolean[numParams];
for(int i = 1; i < numParams; i++) {
paramsToChange[i] = rng.nextBoolean();
// mutate a gene if it was selected
if(paramsToChange[i]) {
switch(i) {
case 1: mutant.setDistribution(selectDistribution());
case 2: mutant.setStartNodes(selectStartNodes());
case 3: mutant.setLayerNodes(selectLayerNodes());
case 4: mutant.setStartEdges(selectStartEdges());
case 5: mutant.setLayers(selectLayers());
case 6: mutant.setLearnRate(selectLearnRate());
case 7: mutant.setLearnScale(selectLearnScale());
}
}
}
return mutant;
} else {
//throw new MathIllegalArgumentException(new DummyLocalizable("NeuralNetworkMutation works only with NeuralNetworkChromosome, not "), original);
System.out.println("NeuralNetworkMutation works only with NeuralNetworkChromosome");
return null;
}
}
/**
* Select a randomly chosen <code>AbstractRealDistribution</code>.
* @return the randomly chosen <code>AbstractRealDistribution</code>.
*/
public AbstractRealDistribution selectDistribution() {
return dists.get(rng.nextInt(dists.size()));
}
/**
* Select a randomly chosen number of input neurons from 2 to 100.
* @return the randomly chosen number of input neurons.
*/
public Integer selectStartNodes() {
return 2 + rng.nextInt(99);
}
/**
* Select a randomly chosen number of neurons per hidden layer from 2 to 100.
* @return the randomly chosen number of neurons per hidden layer.
*/
public Integer selectLayerNodes() {
return 2 + rng.nextInt(99);
}
/**
* Select a randomly chosen number of input synapses from 2 to 100.
* @return the randomly chosen number of input synapses.
*/
public Integer selectStartEdges() {
return 2 + rng.nextInt(99);
}
/**
* Select a randomly chosen number of layers from 3 to 100.
* @return the randomly chosen number of layers.
*/
public Integer selectLayers() {
return 3 + rng.nextInt(98);
}
/**
* Select a randomly chosen learning rate from 0.01 to 5.
* @return the randomly chosen learning rate.
*/
public Double selectLearnRate() {
Double rate = 0.0;
while(rate <= 0 || rate > 5) {
rate = 0.01 + rng.nextInt(5) - Math.abs((1/rng.nextDouble()));
}
return rate;
}
/**
* Select a randomly chosen backpropagation learning scale factor from 0.01 to 5.
* @return the randomly chosen backpropagation learning scale factor.
*/
public Double selectLearnScale() {
Double scale = 0.0;
while(scale <= 0 || scale > 5) {
scale = 0.01 + rng.nextInt(5) - Math.abs((1/rng.nextDouble()));
}
return scale;
}
}