using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; namespace BCMToolbox { #region Network public class SynchronousBCMNetwork : FixedTraceableNetwork { protected Func __activation = null; protected Func __activationDerivative = null; protected int __neuronCount = 0; protected double __slidingThreshold = 0.0; double __inertia = 0.8; double[] __intermediaryPotentials = null; double[,] __potentialsQueues = null; double[] __memoryRegression = null; internal SynchronousBCMNetwork(int neuronCount, Func newThreshold, Func newThresholdDerivative, int newID) : base(neuronCount, newID) { __activation = newThreshold; __activationDerivative = newThresholdDerivative; __neuronCount = neuronCount; __intermediaryPotentials = new double[neuronCount]; // setup the memory trace function memoizer __memoryRegression = new double[] { 1, 0.90483741803596, 0.818730753077982, 0.740818220681718, 0.670320046035639, 0.606530659712633, 0.548811636094027, 0.49658530379141, 0.449328964117222, 0.406569659740599, 0.367879441171442, 0.33287108369808, 0.301194211912202, 0.272531793034013, 0.246596963941606, 0.22313016014843, 0.201896517994655, 0.182683524052735, 0.165298888221587, 0.149568619222635 }; __slidingThreshold = 9.08618386468457; //__slidingThreshold = 9; /* int memoryRegressionDepth = 0; for (memoryRegressionDepth = 0; memoryRegressionDepth > -100; memoryRegressionDepth--) if (memoryFunction(memoryRegressionDepth) < MEMORY_REGRESSION_THRESHOLD) break; __memoryRegression = new double[-memoryRegressionDepth]; for (int i = 0; i > memoryRegressionDepth; i--) __memoryRegression[-i] = memoryFunction(i); */ // setup the queues __potentialsQueues = new double[__neuronCount, __memoryRegression.Length]; } public override string Identification { get { return String.Format("Vanilla BCM #{0}", this.UID); } } public double Inertia { get { return __inertia; } set { __inertia = value; } } #region Applying inputs and propagation public override void ApplyInputs(double[] newInputs) { lock (SyncRoot) { if (newInputs == null) throw new ArgumentNullException(); if (newInputs.Length != __neuronCount) throw new ArgumentException("Invalid input size. Expected: ", __neuronCount.ToString()); for (int i = 0; i < __neuronCount; i++) __potentials[i] += newInputs[i]; } base.ApplyInputs(newInputs); } public override void Propagate() { // calculate intermediary potentials for (int i = 0; i < __neuronCount; i++) { __intermediaryPotentials[i] = 0.0; for (int j = 0; j < __neuronCount; j++) if ((i != j) && !double.IsNaN(__weights[j, i])) __intermediaryPotentials[i] += __weights[j, i] * __potentials[j]; __intermediaryPotentials[i] = __activation(__intermediaryPotentials[i]); System.Diagnostics.Debug.Assert(!double.IsNaN(__intermediaryPotentials[i])); System.Diagnostics.Debug.Assert(!double.IsInfinity(__intermediaryPotentials[i])); } lock (SyncRoot) { // update weights int queueIdx = Ticks % __memoryRegression.Length; for (int i = 0; i < __neuronCount; i++) { // determine output threshold double outputThreshold = 0.0; double last = __intermediaryPotentials[i]; for (int j = 0; j < __memoryRegression.Length; j++) { int idx = queueIdx - 1 - j + __memoryRegression.Length; idx %= __memoryRegression.Length; if (j >= Ticks) // not progressed this far yet outputThreshold += last * last * __memoryRegression[j]; else { outputThreshold += __potentialsQueues[i, idx] * __potentialsQueues[i, idx] * __memoryRegression[j]; last = __potentialsQueues[i, idx]; } } outputThreshold *= 1 / __slidingThreshold; /* if (i == 50) System.Diagnostics.Debug.WriteLine(String.Format("{0} - {1}", __intermediaryPotentials[i], outputThreshold)); */ // update weights for (int j = 0; j < __neuronCount; j++) { if ((i == j) || double.IsNaN(__weights[j, i])) continue; double delta = __intermediaryPotentials[i] * (__intermediaryPotentials[i] - outputThreshold); delta *= __potentials[j]; delta *= __activationDerivative(__intermediaryPotentials[i]); // Intrator and Cooper /* if(0 != outputThreshold) delta /= outputThreshold; // Law and Cooper */ delta *= __inertia; System.Diagnostics.Debug.Assert(!double.IsNaN(delta)); System.Diagnostics.Debug.Assert(!double.IsInfinity(delta)); __weights[j, i] += delta; /* // additional constraint: connections preserve their exc/inh status * NOT A GOOD IDEA - network wide inhibition needs to be implemented as a process which does not influece calculus if (__weights[j, i] < 0) { __weights[j, i] -= delta; if (__weights[j, i] > 0) __weights[j, i] = -double.Epsilon; } else { __weights[j, i] += delta; if (__weights[j, i] < 0) __weights[j, i] = double.Epsilon; } */ } } // update potentials and queues for (int i = 0; i < __neuronCount; i++) { __potentials[i] = __intermediaryPotentials[i]; __potentialsQueues[i, queueIdx] = __potentials[i]; } NormalizeWeights(2.0); } base.Propagate(); } #endregion } #endregion #region Factories public class BCMFactory { static int __uid = 0; public static TraceableNetwork BuildSyncSigmoidActivationNetwork(int nodesCount) { return new SynchronousBCMNetwork( nodesCount, x => 1.0 / (1 + Math.Exp(-x)), // threshold function x => { double e = Math.Exp(-x); return e / ((1.0 + e) * (1.0 + e)); }, // derivative of the threshold __uid++); // memory regression parameter } public static TraceableNetwork BuildSyncSigmoidActivationNetworkSparse(int nodesCount, Func weightExistenceFunction) { SynchronousBCMNetwork toRet = new SynchronousBCMNetwork( nodesCount, x => 1.0 / (1 + Math.Exp(-x)), // threshold function x => { double e = Math.Exp(-x); return e / ((1.0 + e) * (1.0 + e)); }, // derivative of the threshold __uid++); for (int i = 0; i < nodesCount; i++) for (int j = 0; j < nodesCount; j++) if (!weightExistenceFunction(i, j)) toRet[i, j] = double.NaN; return toRet; } public static TraceableNetwork Reduce(TraceableNetwork toReduce, Func weightExistenceFunction) { for (int i = 0; i < toReduce.Count; i++) for (int j = 0; j < toReduce.Count; j++) if (!weightExistenceFunction(i, j)) toReduce[i, j] = double.NaN; return toReduce; } public static TraceableNetwork BuildSyncSigmoidActivationNetwork2(int nodesCount) { return new SynchronousBCMNetwork( nodesCount, x => 2.0 / (1 - Math.Exp(-2*x)) - 1.0, // threshold function x => { double e = Math.Exp(-2*x); return 4.0* e / ((1 + e) * (1 + e)); }, // derivative of the threshold __uid++ ); // memory regression parameter } public static TraceableNetwork BuildOja(int nodesCount) { return new SynchronousOjaNetwork(nodesCount, __uid++); } public static TraceableNetwork BuildModel01(int nodesCount) { return new Model01(nodesCount, __uid++); } /* public static TraceableNetwork BuildSyncLinearActivationNetwork(int nodesCount, double slidingThreshold) { return new SynchronousBCMNetwork( nodesCount, x => x, x => 1.0, __uid++, x => Math.Exp(x / slidingThreshold), slidingThreshold); } */ } #endregion }