/*
* 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;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.encog.neural.neat.NEATPopulation;
/**
* Implements a NEAT innovation list.
*
* NeuroEvolution of Augmenting Topologies (NEAT) is a genetic algorithm for the
* generation of evolving artificial neural networks. It was developed by Ken
* Stanley while at The University of Texas at Austin.
*
* -----------------------------------------------------------------------------
* 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 class NEATInnovationList implements Serializable {
/**
* Serial id.
*/
private static final long serialVersionUID = 1L;
/**
* The population.
*/
private NEATPopulation population;
/**
* The list of innovations.
*/
private Map<String, NEATInnovation> list = new HashMap<String, NEATInnovation>();
/**
* The default constructor, used mainly for persistance.
*/
public NEATInnovationList() {
}
/**
* Produce an innovation key for a neuron.
* @param id The neuron id.
* @return The newly created key.
*/
public static String produceKeyNeuron(long id) {
StringBuilder result = new StringBuilder();
result.append("n:");
result.append(id);
return result.toString();
}
/**
* Produce a key for a split neuron.
* @param fromID Thf from id.
* @param toID The to id.
* @return The key.
*/
public static String produceKeyNeuronSplit(long fromID, long toID) {
StringBuilder result = new StringBuilder();
result.append("ns:");
result.append(fromID);
result.append(":");
result.append(toID);
return result.toString();
}
/**
* Produce a key for a link.
* @param fromID The from id.
* @param toID The to id.
* @return The key for the link.
*/
public static String produceKeyLink(long fromID, long toID) {
StringBuilder result = new StringBuilder();
result.append("l:");
result.append(fromID);
result.append(":");
result.append(toID);
return result.toString();
}
/**
* Construct an innovation list, that includes the initial innovations.
* @param population The population to base this innovation list on.
*/
public NEATInnovationList(final NEATPopulation population) {
this.population = population;
this.findInnovation(this.population.assignGeneID()); // bias
// input neurons
for (int i = 0; i < population.getInputCount(); i++) {
this.findInnovation(this.population.assignGeneID());
}
// output neurons
for (int i = 0; i < population.getOutputCount(); i++) {
this.findInnovation(this.population.assignGeneID());
}
// connections
for (long fromID = 0; fromID < this.population.getInputCount() + 1; fromID++) {
for (long toID = 0; toID < this.population.getOutputCount(); toID++) {
findInnovation(fromID, toID);
}
}
}
/**
* Find an innovation for a hidden neuron that split a existing link. This
* is the means by which hidden neurons are introduced in NEAT.
*
* @param fromID
* The source neuron ID in the link.
* @param toID
* The target neuron ID in the link.
* @return The newly created innovation, or the one that matched the search.
*/
public NEATInnovation findInnovationSplit(long fromID, long toID) {
String key = NEATInnovationList.produceKeyNeuronSplit(fromID, toID);
synchronized (this.list) {
if (this.list.containsKey(key)) {
return this.list.get(key);
} else {
long neuronID = this.population.assignGeneID();
NEATInnovation innovation = new NEATInnovation();
innovation
.setInnovationID(this.population.assignInnovationID());
innovation.setNeuronID(neuronID);
list.put(key, innovation);
// create other sides of split, if needed
findInnovation(fromID,neuronID);
findInnovation(neuronID,toID);
return innovation;
}
}
}
/**
* Find an innovation for a single neuron. Single neurons were created
* without producing a split. This means, the only single neurons are the
* input, bias and output neurons.
*
* @param neuronID
* The neuron ID to find.
* @return The newly created innovation, or the one that matched the search.
*/
public NEATInnovation findInnovation(long neuronID) {
String key = NEATInnovationList.produceKeyNeuron(neuronID);
synchronized (this.list) {
if (this.list.containsKey(key)) {
return this.list.get(key);
} else {
NEATInnovation innovation = new NEATInnovation();
innovation.setInnovationID(this.population.assignInnovationID());
innovation.setNeuronID(neuronID);
list.put(key, innovation);
return innovation;
}
}
}
/**
* Find an innovation for a new link added between two existing neurons.
*
* @param fromID
* The source neuron ID in the link.
* @param toID
* The target neuron ID in the link.
* @return The newly created innovation, or the one that matched the search.
*/
public NEATInnovation findInnovation(long fromID, long toID) {
String key = NEATInnovationList.produceKeyLink(fromID, toID);
synchronized (this.list) {
if (this.list.containsKey(key)) {
return this.list.get(key);
} else {
NEATInnovation innovation = new NEATInnovation();
innovation
.setInnovationID(this.population.assignInnovationID());
innovation.setNeuronID(-1);
list.put(key, innovation);
return innovation;
}
}
}
/**
* Set the population that this genome belongs to.
* @param population The population.
*/
public void setPopulation(NEATPopulation population) {
this.population = population;
}
/**
* @return A list of innovations.
*/
public Map<String, NEATInnovation> getInnovations() {
return list;
}
}