[171] | 1 | using System; |
---|
| 2 | using System.Collections.Generic; |
---|
| 3 | using System.Collections; |
---|
| 4 | using System.Linq; |
---|
| 5 | using System.Text; |
---|
| 6 | |
---|
| 7 | namespace BCMToolbox |
---|
| 8 | { |
---|
| 9 | |
---|
| 10 | #region Network |
---|
| 11 | public class SynchronousBCMNetwork : FixedTraceableNetwork |
---|
| 12 | { |
---|
| 13 | |
---|
| 14 | protected Func<double, double> __activation = null; |
---|
| 15 | protected Func<double, double> __activationDerivative = null; |
---|
| 16 | protected int __neuronCount = 0; |
---|
| 17 | protected double __slidingThreshold = 0.0; |
---|
| 18 | |
---|
| 19 | double __inertia = 0.8; |
---|
| 20 | double[] __intermediaryPotentials = null; |
---|
| 21 | double[,] __potentialsQueues = null; |
---|
| 22 | double[] __memoryRegression = null; |
---|
| 23 | |
---|
| 24 | |
---|
| 25 | internal SynchronousBCMNetwork(int neuronCount, Func<double, double> newThreshold, Func<double, double> newThresholdDerivative, int newID) |
---|
| 26 | : base(neuronCount, newID) |
---|
| 27 | { |
---|
| 28 | __activation = newThreshold; |
---|
| 29 | __activationDerivative = newThresholdDerivative; |
---|
| 30 | __neuronCount = neuronCount; |
---|
| 31 | __intermediaryPotentials = new double[neuronCount]; |
---|
| 32 | |
---|
| 33 | |
---|
| 34 | |
---|
| 35 | // setup the memory trace function memoizer |
---|
| 36 | __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 }; |
---|
| 37 | __slidingThreshold = 9.08618386468457; |
---|
| 38 | //__slidingThreshold = 9; |
---|
| 39 | |
---|
| 40 | /* |
---|
| 41 | int memoryRegressionDepth = 0; |
---|
| 42 | for (memoryRegressionDepth = 0; memoryRegressionDepth > -100; memoryRegressionDepth--) |
---|
| 43 | if (memoryFunction(memoryRegressionDepth) < MEMORY_REGRESSION_THRESHOLD) |
---|
| 44 | break; |
---|
| 45 | |
---|
| 46 | __memoryRegression = new double[-memoryRegressionDepth]; |
---|
| 47 | for (int i = 0; i > memoryRegressionDepth; i--) |
---|
| 48 | __memoryRegression[-i] = memoryFunction(i); */ |
---|
| 49 | |
---|
| 50 | // setup the queues |
---|
| 51 | __potentialsQueues = new double[__neuronCount, __memoryRegression.Length]; |
---|
| 52 | } |
---|
| 53 | |
---|
| 54 | public override string Identification |
---|
| 55 | { |
---|
| 56 | get |
---|
| 57 | { |
---|
| 58 | return String.Format("Vanilla BCM #{0}", this.UID); |
---|
| 59 | } |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | |
---|
| 63 | |
---|
| 64 | |
---|
| 65 | |
---|
| 66 | public double Inertia |
---|
| 67 | { |
---|
| 68 | get |
---|
| 69 | { |
---|
| 70 | return __inertia; |
---|
| 71 | } |
---|
| 72 | |
---|
| 73 | set |
---|
| 74 | { |
---|
| 75 | __inertia = value; |
---|
| 76 | } |
---|
| 77 | } |
---|
| 78 | |
---|
| 79 | |
---|
| 80 | |
---|
| 81 | |
---|
| 82 | #region Applying inputs and propagation |
---|
| 83 | |
---|
| 84 | |
---|
| 85 | public override void ApplyInputs(double[] newInputs) |
---|
| 86 | { |
---|
| 87 | lock (SyncRoot) |
---|
| 88 | { |
---|
| 89 | if (newInputs == null) |
---|
| 90 | throw new ArgumentNullException(); |
---|
| 91 | |
---|
| 92 | if (newInputs.Length != __neuronCount) |
---|
| 93 | throw new ArgumentException("Invalid input size. Expected: ", __neuronCount.ToString()); |
---|
| 94 | |
---|
| 95 | for (int i = 0; i < __neuronCount; i++) |
---|
| 96 | __potentials[i] += newInputs[i]; |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | base.ApplyInputs(newInputs); |
---|
| 100 | } |
---|
| 101 | |
---|
| 102 | |
---|
| 103 | |
---|
| 104 | public override void Propagate() |
---|
| 105 | { |
---|
| 106 | // calculate intermediary potentials |
---|
| 107 | for (int i = 0; i < __neuronCount; i++) |
---|
| 108 | { |
---|
| 109 | __intermediaryPotentials[i] = 0.0; |
---|
| 110 | for (int j = 0; j < __neuronCount; j++) |
---|
| 111 | if ((i != j) && !double.IsNaN(__weights[j, i])) |
---|
| 112 | __intermediaryPotentials[i] += __weights[j, i] * __potentials[j]; |
---|
| 113 | |
---|
| 114 | |
---|
| 115 | |
---|
| 116 | __intermediaryPotentials[i] = __activation(__intermediaryPotentials[i]); |
---|
| 117 | System.Diagnostics.Debug.Assert(!double.IsNaN(__intermediaryPotentials[i])); |
---|
| 118 | System.Diagnostics.Debug.Assert(!double.IsInfinity(__intermediaryPotentials[i])); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | lock (SyncRoot) |
---|
| 122 | { |
---|
| 123 | // update weights |
---|
| 124 | int queueIdx = Ticks % __memoryRegression.Length; |
---|
| 125 | for (int i = 0; i < __neuronCount; i++) |
---|
| 126 | { |
---|
| 127 | // determine output threshold |
---|
| 128 | double outputThreshold = 0.0; |
---|
| 129 | double last = __intermediaryPotentials[i]; |
---|
| 130 | for (int j = 0; j < __memoryRegression.Length; j++) |
---|
| 131 | { |
---|
| 132 | int idx = queueIdx - 1 - j + __memoryRegression.Length; |
---|
| 133 | idx %= __memoryRegression.Length; |
---|
| 134 | |
---|
| 135 | if (j >= Ticks) // not progressed this far yet |
---|
| 136 | outputThreshold += last * last * __memoryRegression[j]; |
---|
| 137 | else |
---|
| 138 | { |
---|
| 139 | outputThreshold += __potentialsQueues[i, idx] * __potentialsQueues[i, idx] * __memoryRegression[j]; |
---|
| 140 | last = __potentialsQueues[i, idx]; |
---|
| 141 | } |
---|
| 142 | } |
---|
| 143 | outputThreshold *= 1 / __slidingThreshold; |
---|
| 144 | |
---|
| 145 | /* |
---|
| 146 | if (i == 50) |
---|
| 147 | System.Diagnostics.Debug.WriteLine(String.Format("{0} - {1}", __intermediaryPotentials[i], outputThreshold)); */ |
---|
| 148 | |
---|
| 149 | // update weights |
---|
| 150 | for (int j = 0; j < __neuronCount; j++) |
---|
| 151 | { |
---|
| 152 | if ((i == j) || double.IsNaN(__weights[j, i])) continue; |
---|
| 153 | |
---|
| 154 | double delta = __intermediaryPotentials[i] * (__intermediaryPotentials[i] - outputThreshold); |
---|
| 155 | |
---|
| 156 | delta *= __potentials[j]; |
---|
| 157 | delta *= __activationDerivative(__intermediaryPotentials[i]); // Intrator and Cooper |
---|
| 158 | /* |
---|
| 159 | if(0 != outputThreshold) |
---|
| 160 | delta /= outputThreshold; // Law and Cooper */ |
---|
| 161 | delta *= __inertia; |
---|
| 162 | System.Diagnostics.Debug.Assert(!double.IsNaN(delta)); |
---|
| 163 | System.Diagnostics.Debug.Assert(!double.IsInfinity(delta)); |
---|
| 164 | __weights[j, i] += delta; |
---|
| 165 | |
---|
| 166 | /* |
---|
| 167 | // additional constraint: connections preserve their exc/inh status |
---|
| 168 | * NOT A GOOD IDEA - network wide inhibition needs to be implemented as a process which does not influece calculus |
---|
| 169 | if (__weights[j, i] < 0) |
---|
| 170 | { |
---|
| 171 | __weights[j, i] -= delta; |
---|
| 172 | if (__weights[j, i] > 0) |
---|
| 173 | __weights[j, i] = -double.Epsilon; |
---|
| 174 | } |
---|
| 175 | else |
---|
| 176 | { |
---|
| 177 | __weights[j, i] += delta; |
---|
| 178 | if (__weights[j, i] < 0) |
---|
| 179 | __weights[j, i] = double.Epsilon; |
---|
| 180 | } */ |
---|
| 181 | } |
---|
| 182 | } |
---|
| 183 | |
---|
| 184 | |
---|
| 185 | // update potentials and queues |
---|
| 186 | for (int i = 0; i < __neuronCount; i++) |
---|
| 187 | { |
---|
| 188 | __potentials[i] = __intermediaryPotentials[i]; |
---|
| 189 | __potentialsQueues[i, queueIdx] = __potentials[i]; |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | |
---|
| 193 | NormalizeWeights(2.0); |
---|
| 194 | } |
---|
| 195 | |
---|
| 196 | base.Propagate(); |
---|
| 197 | } |
---|
| 198 | |
---|
| 199 | #endregion |
---|
| 200 | |
---|
| 201 | |
---|
| 202 | } |
---|
| 203 | #endregion |
---|
| 204 | |
---|
| 205 | #region Factories |
---|
| 206 | public class BCMFactory |
---|
| 207 | { |
---|
| 208 | static int __uid = 0; |
---|
| 209 | public static TraceableNetwork BuildSyncSigmoidActivationNetwork(int nodesCount) |
---|
| 210 | { |
---|
| 211 | return new SynchronousBCMNetwork( |
---|
| 212 | nodesCount, |
---|
| 213 | x => 1.0 / (1 + Math.Exp(-x)), // threshold function |
---|
| 214 | x => { double e = Math.Exp(-x); return e / ((1.0 + e) * (1.0 + e)); }, // derivative of the threshold |
---|
| 215 | __uid++); // memory regression parameter |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | public static TraceableNetwork BuildSyncSigmoidActivationNetworkSparse(int nodesCount, Func<int, int, bool> weightExistenceFunction) |
---|
| 219 | { |
---|
| 220 | SynchronousBCMNetwork toRet = new SynchronousBCMNetwork( |
---|
| 221 | nodesCount, |
---|
| 222 | x => 1.0 / (1 + Math.Exp(-x)), // threshold function |
---|
| 223 | x => { double e = Math.Exp(-x); return e / ((1.0 + e) * (1.0 + e)); }, // derivative of the threshold |
---|
| 224 | __uid++); |
---|
| 225 | |
---|
| 226 | for (int i = 0; i < nodesCount; i++) |
---|
| 227 | for (int j = 0; j < nodesCount; j++) |
---|
| 228 | if (!weightExistenceFunction(i, j)) |
---|
| 229 | toRet[i, j] = double.NaN; |
---|
| 230 | |
---|
| 231 | return toRet; |
---|
| 232 | } |
---|
| 233 | |
---|
| 234 | public static TraceableNetwork Reduce(TraceableNetwork toReduce, Func<int, int, bool> weightExistenceFunction) |
---|
| 235 | { |
---|
| 236 | for (int i = 0; i < toReduce.Count; i++) |
---|
| 237 | for (int j = 0; j < toReduce.Count; j++) |
---|
| 238 | if (!weightExistenceFunction(i, j)) |
---|
| 239 | toReduce[i, j] = double.NaN; |
---|
| 240 | return toReduce; |
---|
| 241 | } |
---|
| 242 | |
---|
| 243 | |
---|
| 244 | public static TraceableNetwork BuildSyncSigmoidActivationNetwork2(int nodesCount) |
---|
| 245 | { |
---|
| 246 | return new SynchronousBCMNetwork( |
---|
| 247 | nodesCount, |
---|
| 248 | x => 2.0 / (1 - Math.Exp(-2*x)) - 1.0, // threshold function |
---|
| 249 | x => { double e = Math.Exp(-2*x); return 4.0* e / ((1 + e) * (1 + e)); }, // derivative of the threshold |
---|
| 250 | __uid++ |
---|
| 251 | ); // memory regression parameter |
---|
| 252 | } |
---|
| 253 | |
---|
| 254 | public static TraceableNetwork BuildOja(int nodesCount) |
---|
| 255 | { |
---|
| 256 | return new SynchronousOjaNetwork(nodesCount, |
---|
| 257 | __uid++); |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | public static TraceableNetwork BuildModel01(int nodesCount) |
---|
| 261 | { |
---|
| 262 | return new Model01(nodesCount, |
---|
| 263 | __uid++); |
---|
| 264 | } |
---|
| 265 | |
---|
| 266 | /* |
---|
| 267 | public static TraceableNetwork BuildSyncLinearActivationNetwork(int nodesCount, double slidingThreshold) |
---|
| 268 | { |
---|
| 269 | return new SynchronousBCMNetwork( |
---|
| 270 | nodesCount, |
---|
| 271 | x => x, |
---|
| 272 | x => 1.0, |
---|
| 273 | __uid++, |
---|
| 274 | x => Math.Exp(x / slidingThreshold), |
---|
| 275 | slidingThreshold); |
---|
| 276 | } */ |
---|
| 277 | } |
---|
| 278 | #endregion |
---|
| 279 | } |
---|