/*
* Encog(tm) Core v2.5 - Java Version
* http://www.heatonresearch.com/encog/
* http://code.google.com/p/encog-java/
* Copyright 2008-2010 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.networks.logic;
import org.encog.neural.NeuralNetworkError;
import org.encog.neural.data.NeuralData;
import org.encog.neural.data.bipolar.BiPolarNeuralData;
import org.encog.neural.networks.BasicNetwork;
import org.encog.neural.networks.NeuralOutputHolder;
import org.encog.neural.networks.layers.Layer;
import org.encog.neural.networks.synapse.Synapse;
import org.encog.neural.pattern.ART1Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides the neural logic for an ART1 type network. See ART1Pattern for more
* information on this type of network.
*/
public class ART1Logic extends ARTLogic {
/**
* The serial id.
*/
private static final long serialVersionUID = -8430698735871301528L;
/**
* The logging object.
*/
private static final transient Logger LOGGER = LoggerFactory
.getLogger(ART1Logic.class);
/**
* The first layer, basically, the input layer.
*/
private Layer layerF1;
/**
* The second layer, basically, the output layer.
*/
private Layer layerF2;
/**
* The connection from F1 to F2.
*/
private Synapse synapseF1toF2;
/**
* The connection from F2 to F1.
*/
private Synapse synapseF2toF1;
/**
* last winner in F2 layer.
*/
private int winner;
/**
* A parameter for F1 layer.
*/
private double a1 = 1;
/**
* B parameter for F1 layer.
*/
private double b1 = 1.5;
/**
* C parameter for F1 layer.
*/
private double c1 = 5;
/**
* D parameter for F1 layer.
*/
private double d1 = 0.9;
/**
* L parameter for net.
*/
private double l = 3;
/**
* The vigilance parameter.
*/
private double vigilance = 0.9;
/**
* Allows members of the F2 layer to be inhibited.
*/
private boolean[] inhibitF2;
/**
* Tracks if there was no winner.
*/
private int noWinner;
/**
* The output from the F1 layer.
*/
private BiPolarNeuralData outputF1;
/**
* The output from the F2 layer.
*/
private BiPolarNeuralData outputF2;
/**
* Adjust the weights for the pattern just presented.
*/
public void adjustWeights() {
double magnitudeInput;
for (int i = 0; i < this.layerF1.getNeuronCount(); i++) {
if (this.outputF1.getBoolean(i)) {
magnitudeInput = magnitude(this.outputF1);
this.synapseF1toF2.getMatrix().set(i, this.winner, 1);
this.synapseF2toF1.getMatrix().set(this.winner, i,
this.l / (this.l - 1 + magnitudeInput));
} else {
this.synapseF1toF2.getMatrix().set(i, this.winner, 0);
this.synapseF2toF1.getMatrix().set(this.winner, i, 0);
}
}
}
/**
* Compute the output from the ART1 network. This can be called directly or
* used by the BasicNetwork class. Both input and output should be bipolar
* numbers.
*
* @param input
* The input to the network.
* @param output
* The output from the network.
*/
public void compute(final BiPolarNeuralData input,
final BiPolarNeuralData output) {
int i;
boolean resonance, exhausted;
double magnitudeInput1, magnitudeInput2;
for (i = 0; i < this.layerF2.getNeuronCount(); i++) {
this.inhibitF2[i] = false;
}
resonance = false;
exhausted = false;
do {
setInput(input);
computeF2();
getOutput(output);
if (this.winner != this.noWinner) {
computeF1(input);
magnitudeInput1 = magnitude(input);
magnitudeInput2 = magnitude(this.outputF1);
if ((magnitudeInput2 / magnitudeInput1) < this.vigilance) {
this.inhibitF2[this.winner] = true;
} else {
resonance = true;
}
} else {
exhausted = true;
}
} while (!(resonance || exhausted));
if (resonance) {
adjustWeights();
}
}
/**
* Compute the output for the BasicNetwork class.
*
* @param input
* The input to the network.
* @param useHolder
* The NeuralOutputHolder to use.
* @return The output from the network.
*/
public NeuralData compute(final NeuralData input,
final NeuralOutputHolder useHolder) {
if (!(input instanceof BiPolarNeuralData)) {
final String str =
"Input to ART1 logic network must be BiPolarNeuralData.";
if (ART1Logic.LOGGER.isErrorEnabled()) {
ART1Logic.LOGGER.error(str);
}
throw new NeuralNetworkError(str);
}
final BiPolarNeuralData output = new BiPolarNeuralData(this.layerF1
.getNeuronCount());
compute((BiPolarNeuralData) input, output);
return output;
}
/**
* Compute the output from the F1 layer.
*
* @param input
* The input to the F1 layer.
*/
private void computeF1(final BiPolarNeuralData input) {
double sum, activation;
for (int i = 0; i < this.layerF1.getNeuronCount(); i++) {
sum = this.synapseF1toF2.getMatrix().get(i, this.winner)
* (this.outputF2.getBoolean(this.winner) ? 1 : 0);
activation = ((input.getBoolean(i) ? 1 : 0)
+ this.d1 * sum - this.b1)
/ (1 + this.a1
* ((input.getBoolean(i) ? 1 : 0) + this.d1 * sum)
+ this.c1);
this.outputF1.setData(i, activation > 0);
}
}
/**
* Compute the output from the F2 layer.
*/
private void computeF2() {
int i, j;
double sum, maxOut;
maxOut = Double.NEGATIVE_INFINITY;
this.winner = this.noWinner;
for (i = 0; i < this.layerF2.getNeuronCount(); i++) {
if (!this.inhibitF2[i]) {
sum = 0;
for (j = 0; j < this.layerF1.getNeuronCount(); j++) {
sum += this.synapseF2toF1.getMatrix().get(i, j)
* (this.outputF1.getBoolean(j) ? 1 : 0);
}
if (sum > maxOut) {
maxOut = sum;
this.winner = i;
}
}
this.outputF2.setData(i, false);
}
if (this.winner != this.noWinner) {
this.outputF2.setData(this.winner, true);
}
}
/**
* @return The A1 parameter.
*/
public double getA1() {
return this.a1;
}
/**
* @return The B1 parameter.
*/
public double getB1() {
return this.b1;
}
/**
* @return The C1 parameter.
*/
public double getC1() {
return this.c1;
}
/**
* @return The D1 parameter.
*/
public double getD1() {
return this.d1;
}
/**
* @return The L parameter.
*/
public double getL() {
return this.l;
}
/**
* Copy the output from the network to another object.
*
* @param output
* The target object for the output from the network.
*/
private void getOutput(final BiPolarNeuralData output) {
for (int i = 0; i < this.layerF2.getNeuronCount(); i++) {
output.setData(i, this.outputF2.getBoolean(i));
}
}
/**
* @return The vigilance parameter.
*/
public double getVigilance() {
return this.vigilance;
}
/**
* @return The winning neuron.
*/
public int getWinner() {
return this.winner;
}
/**
* @return Does this network have a "winner"?
*/
public boolean hasWinner() {
return this.winner != this.noWinner;
}
/**
* Setup the network logic, read parameters from the network.
*
* @param network
* The network that this logic class belongs to.
*/
@Override
public void init(final BasicNetwork network) {
super.init(network);
this.layerF1 = getNetwork().getLayer(ART1Pattern.TAG_F1);
this.layerF2 = getNetwork().getLayer(ART1Pattern.TAG_F2);
this.inhibitF2 = new boolean[this.layerF2.getNeuronCount()];
this.synapseF1toF2 = getNetwork().getStructure().findSynapse(
this.layerF1, this.layerF2, true);
this.synapseF2toF1 = getNetwork().getStructure().findSynapse(
this.layerF2, this.layerF1, true);
this.outputF1 = new BiPolarNeuralData(this.layerF1.getNeuronCount());
this.outputF2 = new BiPolarNeuralData(this.layerF2.getNeuronCount());
this.a1 = getNetwork().getPropertyDouble(ARTLogic.PROPERTY_A1);
this.b1 = getNetwork().getPropertyDouble(ARTLogic.PROPERTY_B1);
this.c1 = getNetwork().getPropertyDouble(ARTLogic.PROPERTY_C1);
this.d1 = getNetwork().getPropertyDouble(ARTLogic.PROPERTY_D1);
this.l = getNetwork().getPropertyDouble(ARTLogic.PROPERTY_L);
this.vigilance = getNetwork().getPropertyDouble(
ARTLogic.PROPERTY_VIGILANCE);
this.noWinner = this.layerF2.getNeuronCount();
reset();
}
/**
* Get the magnitude of the specified input.
*
* @param input
* The input to calculate the magnitude for.
* @return The magnitude of the specified pattern.
*/
public double magnitude(final BiPolarNeuralData input) {
double result;
result = 0;
for (int i = 0; i < this.layerF1.getNeuronCount(); i++) {
result += input.getBoolean(i) ? 1 : 0;
}
return result;
}
/**
* Reset the weight matrix back to starting values.
*/
public void reset() {
for (int i = 0; i < this.layerF1.getNeuronCount(); i++) {
for (int j = 0; j < this.layerF2.getNeuronCount(); j++) {
this.synapseF1toF2.getMatrix().set(i, j,
(this.b1 - 1) / this.d1 + 0.2);
this.synapseF2toF1.getMatrix().set(
j,
i,
this.l / (this.l - 1 + this.layerF1.getNeuronCount())
- 0.1);
}
}
}
/**
* Set the A1 parameter.
*
* @param a1
* The new value.
*/
public void setA1(final double a1) {
this.a1 = a1;
}
/**
* Set the B1 parameter.
*
* @param b1
* The new value.
*/
public void setB1(final double b1) {
this.b1 = b1;
}
/**
* Set the C1 parameter.
*
* @param c1
* The new value.
*/
public void setC1(final double c1) {
this.c1 = c1;
}
/**
* Set the D1 parameter.
*
* @param d1
* The new value.
*/
public void setD1(final double d1) {
this.d1 = d1;
}
/**
* Set the input to the neural network.
*
* @param input
* The input.
*/
private void setInput(final BiPolarNeuralData input) {
double activation;
for (int i = 0; i < this.layerF1.getNeuronCount(); i++) {
activation = (input.getBoolean(i) ? 1 : 0)
/ (1 + this.a1 * ((input.getBoolean(i) ? 1 : 0)
+ this.b1) + this.c1);
this.outputF1.setData(i, (activation > 0));
}
}
/**
* Set the L parameter.
*
* @param l
* The new value.
*/
public void setL(final double l) {
this.l = l;
}
/**
* Set the vigilance.
*
* @param vigilance
* The new value.
*/
public void setVigilance(final double vigilance) {
this.vigilance = vigilance;
}
}