/* * 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.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 } }