/* * Encog(tm) Core v3.4 - Java Version * http://www.heatonresearch.com/encog/ * https://github.com/encog/encog-java-core * Copyright 2008-2016 Heaton Research, Inc. * * Licensed 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. * * For more information on Heaton Research copyrights, licenses * and trademarks visit: * http://www.heatonresearch.com/copyright */ package org.encog.neural.neat.training.opp; import org.encog.mathutil.randomize.RangeRandomizer; import org.encog.ml.ea.genome.Genome; import org.encog.ml.ea.opp.EvolutionaryOperator; import org.encog.ml.ea.train.EvolutionaryAlgorithm; import org.encog.neural.neat.NEATNeuronType; import org.encog.neural.neat.NEATPopulation; import org.encog.neural.neat.training.NEATGenome; import org.encog.neural.neat.training.NEATInnovation; import org.encog.neural.neat.training.NEATLinkGene; import org.encog.neural.neat.training.NEATNeuronGene; import java.io.Serializable; /** * This class represents a NEAT mutation. NEAT supports several different types * of mutations. This class provides common utility needed by any sort of a NEAT * mutation. This class is abstract and cannot be used by itself. * * ----------------------------------------------------------------------------- * http://www.cs.ucf.edu/~kstanley/ Encog's NEAT implementation was drawn from * the following three Journal Articles. For more complete BibTeX sources, see * NEATNetwork.java. * * Evolving Neural Networks Through Augmenting Topologies * * Generating Large-Scale Neural Networks Through Discovering Geometric * Regularities * * Automatic feature selection in neuroevolution */ public abstract class NEATMutation implements EvolutionaryOperator, Serializable { /** * The trainer that owns this class. */ private EvolutionaryAlgorithm owner; /** * Choose a random neuron. * * @param target * The target genome. Should the input and bias neurons be * included. * @param choosingFrom * True if we are chosing from all neurons, false if we exclude * the input and bias. * @return The random neuron. */ public NEATNeuronGene chooseRandomNeuron(final NEATGenome target, final boolean choosingFrom) { int start; if (choosingFrom) { start = 0; } else { start = target.getInputCount() + 1; } // if this network will not "cycle" then output neurons cannot be source // neurons if (!choosingFrom) { final int ac = ((NEATPopulation) target.getPopulation()) .getActivationCycles(); if (ac == 1) { start += target.getOutputCount(); } } final int end = target.getNeuronsChromosome().size() - 1; // no neurons to pick! if (start > end) { return null; } final int neuronPos = RangeRandomizer.randomInt(start, end); final NEATNeuronGene neuronGene = target.getNeuronsChromosome().get( neuronPos); return neuronGene; } /** * Create a link between two neuron id's. Create or find any necessary * innovation records. * * @param target * The target genome. * @param neuron1ID * The id of the source neuron. * @param neuron2ID * The id of the target neuron. * @param weight * The weight of this new link. */ public void createLink(final NEATGenome target, final long neuron1ID, final long neuron2ID, final double weight) { // first, does this link exist? (and if so, hopefully disabled, // otherwise we have a problem) for (final NEATLinkGene linkGene : target.getLinksChromosome()) { if ((linkGene.getFromNeuronID() == neuron1ID) && (linkGene.getToNeuronID() == neuron2ID)) { // bring the link back, at the new weight linkGene.setEnabled(true); linkGene.setWeight(weight); return; } } // check to see if this innovation has already been tried final NEATInnovation innovation = ((NEATPopulation) target .getPopulation()).getInnovations().findInnovation(neuron1ID, neuron2ID); // now create this link final NEATLinkGene linkGene = new NEATLinkGene(neuron1ID, neuron2ID, true, innovation.getInnovationID(), weight); target.getLinksChromosome().add(linkGene); } /** * Get the specified neuron's index. * @param target The target genome. * @param neuronID * The neuron id to check for. * @return The index. */ public int getElementPos(final NEATGenome target, final long neuronID) { for (int i = 0; i < target.getNeuronsChromosome().size(); i++) { final NEATNeuronGene neuronGene = target.getNeuronsChromosome() .get(i); if (neuronGene.getId() == neuronID) { return i; } } return -1; } /** * @return the owner */ public EvolutionaryAlgorithm getOwner() { return this.owner; } /** * {@inheritDoc} */ @Override public void init(final EvolutionaryAlgorithm theOwner) { this.owner = theOwner; } /** * Determine if this is a duplicate link. * @param target The target. * @param fromNeuronID * The from neuron id. * @param toNeuronID * The to neuron id. * @return True if this is a duplicate link. */ public boolean isDuplicateLink(final NEATGenome target, final long fromNeuronID, final long toNeuronID) { for (final NEATLinkGene linkGene : target.getLinksChromosome()) { if ((linkGene.isEnabled()) && (linkGene.getFromNeuronID() == fromNeuronID) && (linkGene.getToNeuronID() == toNeuronID)) { return true; } } return false; } /** * Determines if a neuron is still needed. If all links to/from a neuron * have been removed, then the neuron is no longer needed. * * @param target * The target genome. * @param neuronID * The neuron id to check for. * @return Returns true, if the neuron is still needed. */ public boolean isNeuronNeeded(final NEATGenome target, final long neuronID) { // do not remove bias or input neurons or output for (final NEATNeuronGene gene : target.getNeuronsChromosome()) { if (gene.getId() == neuronID) { final NEATNeuronGene neuron = gene; if ((neuron.getNeuronType() == NEATNeuronType.Input) || (neuron.getNeuronType() == NEATNeuronType.Bias) || (neuron.getNeuronType() == NEATNeuronType.Output)) { return true; } } } // Now check to see if the neuron is used in any links for (final NEATLinkGene gene : target.getLinksChromosome()) { final NEATLinkGene linkGene = gene; if (linkGene.getFromNeuronID() == neuronID) { return true; } if (linkGene.getToNeuronID() == neuronID) { return true; } } return false; } /** * Obtain the NEATGenome that we will mutate. NEAT mutates the genomes in * place. So the parent and child genome must be the same literal object. * Throw an exception, if this is not the case. * * @param parents * The parents. * @param parentIndex * The parent index. * @param offspring * The offspring. * @param offspringIndex * The offspring index. * @return The genome that we will mutate. */ public NEATGenome obtainGenome(final Genome[] parents, final int parentIndex, final Genome[] offspring, final int offspringIndex) { offspring[offspringIndex] = this.getOwner().getPopulation() .getGenomeFactory().factor(parents[0]); return (NEATGenome) offspring[offspringIndex]; } /** * @return Returns 1, as NEAT mutations only produce one child. */ @Override public int offspringProduced() { return 1; } /** * @return Returns 1, as mutations typically are asexual and only require a * single parent. */ @Override public int parentsNeeded() { return 1; } /** * Remove the specified neuron. * * @param target * The target genome. * @param neuronID * The neuron to remove. */ public void removeNeuron(final NEATGenome target, final long neuronID) { for (final NEATNeuronGene gene : target.getNeuronsChromosome()) { if (gene.getId() == neuronID) { target.getNeuronsChromosome().remove(gene); return; } } } }