/*
* Encog(tm) Core v3.3 - Java Version
* http://www.heatonresearch.com/encog/
* https://github.com/encog/encog-java-core
* Copyright 2008-2014 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.thermal;
import org.encog.mathutil.BoundMath;
import org.encog.mathutil.randomize.RangeRandomizer;
import org.encog.ml.data.MLData;
import org.encog.ml.data.specific.BiPolarNeuralData;
import org.encog.util.EngineArray;
/**
* Implements a Boltzmann machine.
*
*/
public class BoltzmannMachine extends ThermalNetwork {
/**
* Serial id.
*/
private static final long serialVersionUID = 1L;
/**
* The property for run cycles.
*/
public static final String RUN_CYCLES = "runCycles";
/**
* The property for anneal cycles.
*/
public static final String ANNEAL_CYCLES = "annealCycles";
/**
* The current temperature of the neural network. The higher the
* temperature, the more random the network will behave.
*/
private double temperature;
/**
* The thresholds.
*/
private double[] threshold;
/**
* Count used to internally determine if a neuron is "on".
*/
private transient int[] on;
/**
* Count used to internally determine if a neuron is "off".
*/
private transient int[] off;
/**
* The number of cycles to anneal for.
*/
private int annealCycles = 100;
/**
* The number of cycles to run the network through before annealing.
*/
private int runCycles = 1000;
/**
* Default constructors.
*/
public BoltzmannMachine() {
super();
}
/**
* Construct a Boltzmann machine with the specified number of neurons.
* @param neuronCount The number of neurons.
*/
public BoltzmannMachine(final int neuronCount) {
super(neuronCount);
this.threshold = new double[neuronCount];
}
/**
* Note: for Boltzmann networks, you will usually want to call the "run"
* method to compute the output.
*
* This method can be used to copy the input data to the current state. A
* single iteration is then run, and the new current state is returned.
*
* @param input
* The input pattern.
* @return The new current state.
*/
@Override
public MLData compute(final MLData input) {
final BiPolarNeuralData result = new BiPolarNeuralData(input.size());
EngineArray.arrayCopy(input.getData(), getCurrentState().getData());
run();
EngineArray.arrayCopy(getCurrentState().getData(), result.getData());
return result;
}
/**
* Decrease the temperature by the specified amount.
*
* @param d
* The amount to decrease by.
*/
public void decreaseTemperature(final double d) {
this.temperature *= d;
}
/**
* Run the network until thermal equilibrium is established.
*/
public void establishEquilibrium() {
final int count = getNeuronCount();
if (this.on == null) {
this.on = new int[count];
this.off = new int[count];
}
for (int i = 0; i < count; i++) {
this.on[i] = 0;
this.off[i] = 0;
}
for (int n = 0; n < this.runCycles * count; n++) {
run((int) RangeRandomizer.randomize(0, count - 1));
}
for (int n = 0; n < this.annealCycles * count; n++) {
final int i = (int) RangeRandomizer.randomize(0, count - 1);
run(i);
if (getCurrentState().getBoolean(i)) {
this.on[i]++;
} else {
this.off[i]++;
}
}
for (int i = 0; i < count; i++) {
getCurrentState().setData(i, this.on[i] > this.off[i]);
}
}
/**
* @return the annealCycles
*/
public int getAnnealCycles() {
return this.annealCycles;
}
/**
* {@inheritDoc}
*/
@Override
public int getInputCount() {
return getNeuronCount();
}
/**
* {@inheritDoc}
*/
@Override
public int getOutputCount() {
return getNeuronCount();
}
/**
* @return the runCycles
*/
public int getRunCycles() {
return this.runCycles;
}
/**
* @return The temperature the network is currently operating at.
*/
public double getTemperature() {
return this.temperature;
}
/**
* @return the threshold
*/
public double[] getThreshold() {
return this.threshold;
}
/**
* Run the network for all neurons present.
*/
public void run() {
final int count = getNeuronCount();
for (int i = 0; i < count; i++) {
run(i);
}
}
/**
* Run the network for the specified neuron.
*
* @param i
* The neuron to run for.
*/
public void run(final int i) {
int j;
double sum, probability;
final int count = getNeuronCount();
sum = 0;
for (j = 0; j < count; j++) {
sum += getWeight(i, j) * (getCurrentState().getBoolean(j) ? 1 : 0);
}
sum -= this.threshold[i];
probability = 1 / (1 + BoundMath.exp(-sum / this.temperature));
if (RangeRandomizer.randomize(0, 1) <= probability) {
getCurrentState().setData(i, true);
} else {
getCurrentState().setData(i, false);
}
}
/**
* @param annealCycles
* the annealCycles to set
*/
public void setAnnealCycles(final int annealCycles) {
this.annealCycles = annealCycles;
}
/**
* @param runCycles
* the runCycles to set
*/
public void setRunCycles(final int runCycles) {
this.runCycles = runCycles;
}
/**
* Set the network temperature.
*
* @param temperature
* The temperature to operate the network at.
*/
public void setTemperature(final double temperature) {
this.temperature = temperature;
}
/**
* Set the thresholds.
* @param t The thresholds.
*/
public void setThreshold(final double[] t) {
this.threshold = t;
}
/**
* {@inheritDoc}
*/
@Override
public void updateProperties() {
// nothing needed here
}
}