/*
* Encog(tm) Core v3.0 - Java Version
* http://www.heatonresearch.com/encog/
* http://code.google.com/p/encog-java/
* Copyright 2008-2011 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;
import java.util.ArrayList;
import java.util.List;
import org.encog.engine.network.activation.ActivationFunction;
import org.encog.engine.network.activation.ActivationSigmoid;
import org.encog.ml.BasicML;
import org.encog.ml.MLContext;
import org.encog.ml.MLError;
import org.encog.ml.MLRegression;
import org.encog.ml.data.MLData;
import org.encog.ml.data.MLDataSet;
import org.encog.ml.data.basic.BasicMLData;
import org.encog.neural.NeuralNetworkError;
import org.encog.util.simple.EncogUtility;
/**
* Implements a NEAT network as a synapse between two layers. In Encog, a NEAT
* network is created by using a NEATSynapse between an input and output layer.
*
* NEAT networks only have an input and an output layer. There are no actual
* hidden layers. Rather this synapse will evolve many hidden neurons that have
* connections that are not easily defined by layers. Connections can be
* feedforward, recurrent, or self-connected.
*
* NEAT networks relieve the programmer of the need to define the hidden layer
* structure of the neural network.
*
* The output from the neural network can be calculated normally or using a
* snapshot. The snapshot mode is slower, but it can be more accurate. The
* snapshot handles recurrent layers better, as it takes the time to loop
* through the network multiple times to "flush out" the recurrent links.
*
* 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/
*
*/
public class NEATNetwork extends BasicML implements MLContext, MLRegression, MLError {
/**
* The serial ID.
*/
private static final long serialVersionUID = 3660295468309926508L;
public static final String PROPERTY_NETWORK_DEPTH = "depth";
public static final String PROPERTY_LINKS = "links";
public static final String PROPERTY_SNAPSHOT = "snapshot";
/**
* The activation function.
*/
private ActivationFunction activationFunction;
private ActivationFunction outputActivationFunction;
/**
* The depth of the network.
*/
private int networkDepth;
/**
* The neurons that make up this network.
*/
private final List<NEATNeuron> neurons = new ArrayList<NEATNeuron>();
/**
* Should snapshot be used to calculate the output of the neural network.
*/
private boolean snapshot = false;
private int inputCount;
private int outputCount;
/**
* Default constructor.
*/
public NEATNetwork() {
}
/**
* Construct a NEAT synapse.
*
* @param inputCount
* The number of input neurons.
* @param outputCount
* The number of output neurons.
* @param neurons
* The neurons in this synapse.
* @param activationFunction
* The activation function to use.
* @param networkDepth
* The depth of the network.
*/
public NEATNetwork(final int inputCount,
final int outputCount,
final List<NEATNeuron> neurons,
final ActivationFunction activationFunction,
final ActivationFunction outputActivationFunction,
final int networkDepth) {
this.inputCount = inputCount;
this.outputCount = outputCount;
this.outputActivationFunction = outputActivationFunction;
this.neurons.addAll(neurons);
this.networkDepth = networkDepth;
this.activationFunction = activationFunction;
}
/**
* Construct a NEAT network.
*
* @param inputCount
* The input count.
* @param outputCount
* The output count.
*/
public NEATNetwork(final int inputCount, final int outputCount) {
this.inputCount = inputCount;
this.outputCount = outputCount;
this.networkDepth = 0;
this.activationFunction = new ActivationSigmoid();
}
/**
* Clear any context from previous runs. This sets the activation of all
* neurons to zero.
*/
public void clearContext() {
for (final NEATNeuron neuron : this.neurons) {
neuron.setOutput(0);
}
}
/**
* Compute the output from this synapse.
*
* @param input
* The input to this synapse.
* @return The output from this synapse.
*/
public MLData compute(final MLData input) {
final MLData result = new BasicMLData(this.outputCount);
if (this.neurons.size() == 0) {
throw new NeuralNetworkError(
"This network has not been evolved yet, it has no neurons in the NEAT synapse.");
}
int flushCount = 1;
if (this.snapshot) {
flushCount = this.networkDepth;
}
// iterate through the network FlushCount times
for (int i = 0; i < flushCount; ++i) {
int outputIndex = 0;
int index = 0;
result.clear();
// populate the input neurons
while (this.neurons.get(index).getNeuronType()
== NEATNeuronType.Input) {
this.neurons.get(index).setOutput(input.getData(index));
index++;
}
// set the bias neuron
this.neurons.get(index++).setOutput(1);
while (index < this.neurons.size()) {
final NEATNeuron currentNeuron = this.neurons.get(index);
double sum = 0;
for (final NEATLink link : currentNeuron.getInboundLinks()) {
final double weight = link.getWeight();
final double neuronOutput = link.getFromNeuron()
.getOutput();
sum += weight * neuronOutput;
}
final double[] d = new double[1];
d[0] = sum / currentNeuron.getActivationResponse();
this.activationFunction.activationFunction(d,0,d.length);
this.neurons.get(index).setOutput(d[0]);
if (currentNeuron.getNeuronType() == NEATNeuronType.Output) {
result.setData(outputIndex++, currentNeuron.getOutput());
}
index++;
}
}
this.outputActivationFunction.activationFunction(result.getData(), 0, result.size());
return result;
}
/**
* @return The activation function.
*/
public ActivationFunction getActivationFunction() {
return this.activationFunction;
}
/**
* @return The network depth.
*/
public int getNetworkDepth() {
return this.networkDepth;
}
/**
* @return The NEAT neurons.
*/
public List<NEATNeuron> getNeurons() {
return this.neurons;
}
/**
* @return True if snapshot is being used.
*/
public boolean isSnapshot() {
return this.snapshot;
}
/**
* Set the activation function.
* @param activationFunction The activation function.
*/
public void setActivationFunction(
final ActivationFunction activationFunction) {
this.activationFunction = activationFunction;
}
/**
* Sets if snapshot is used.
*
* @param snapshot
* True if snapshot is used.
*/
public void setSnapshot(final boolean snapshot) {
this.snapshot = snapshot;
}
@Override
public int getInputCount() {
return this.inputCount;
}
@Override
public int getOutputCount() {
return this.outputCount;
}
@Override
public void updateProperties() {
}
public void setInputCount(int i) {
this.inputCount = i;
}
public void setOutputCount(int i) {
this.outputCount = i;
}
public void setNetworkDepth(int i) {
this.networkDepth = i;
}
/**
* @return the outputActivationFunction
*/
public ActivationFunction getOutputActivationFunction() {
return outputActivationFunction;
}
/**
* @param outputActivationFunction the outputActivationFunction to set
*/
public void setOutputActivationFunction(
ActivationFunction outputActivationFunction) {
this.outputActivationFunction = outputActivationFunction;
}
/**
* Calculate the error for this neural network.
*
* @param data
* The training set.
* @return The error percentage.
*/
public double calculateError(final MLDataSet data) {
return EncogUtility.calculateRegressionError(this,data);
}
}