Last active
August 12, 2017 20:19
-
-
Save redwrasse/a2e718408b467546ecf5105179261804 to your computer and use it in GitHub Desktop.
McCulloch-Pitts neural network
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class McCullochPitts( | |
val numExcitatoryInputs: Int, | |
val numInhibitoryInputs: Int, | |
val threshold: Int, | |
val inhibitoryInputIndices: Option[Seq[Int]] = None) | |
extends NeuralNetwork { | |
val numImmediateInputs: Int = (numExcitatoryInputs | |
+ numInhibitoryInputs) | |
var children: Map[Int, NeuralNetwork] = Map.empty | |
// variable to decide logic for activation function | |
var isInhibited: Boolean = false | |
/** | |
* Returns the integration value. If at least one inhibitory input | |
* exists and at least one such input is 1, the result is 0. | |
* Otherwise the sum of the excitatory inputs is returned. | |
* The input must be given as a single array containing | |
* the inhibitory inputs followed by the excitatory inputs, | |
* unless the inhibitory indices were explicitly specified | |
* in the class constructor. | |
*/ | |
def integration(immediateInput: Array[Double]): Double = { | |
require (immediateInput.size == (numExcitatoryInputs | |
+ numInhibitoryInputs)) | |
if (inhibitoryInputIndices.isDefined) { | |
require (inhibitoryInputIndices.get.size == | |
numInhibitoryInputs) | |
} | |
for (e <- immediateInput) { | |
require (e == 0.0 || e == 1.0) | |
} | |
var inhibitoryInputs: Array[Double] = null | |
var excitatoryInputs: Array[Double] = null | |
if (inhibitoryInputIndices.isDefined) { | |
inhibitoryInputs = inhibitoryInputIndices.get | |
.map(i => immediateInput(i)).toArray | |
excitatoryInputs = (0 to immediateInput.size-1) | |
.filter(i => !inhibitoryInputIndices.get.contains(i)) | |
.map(j => immediateInput(j)).toArray | |
} else { | |
inhibitoryInputs = immediateInput | |
.slice(0, numInhibitoryInputs) | |
excitatoryInputs = immediateInput | |
.slice(numInhibitoryInputs, immediateInput.size) | |
} | |
isInhibited = inhibitoryInputs | |
.filter(_ == 1.0).size > 0 | |
isInhibited match { | |
case true => 0.0 | |
case false => excitatoryInputs.sum | |
} | |
} | |
/** | |
* Returns 0 if the input is inhibited, else: | |
* Returns 1 if the threshold is matched or exceeded, else 0 | |
*/ | |
def activation(e: Double): Double = { | |
if (isInhibited || e < threshold) 0.0 else 1.0 | |
} | |
} | |
abstract class NeuralNetwork { | |
val numImmediateInputs: Int | |
var children: Map[Int, NeuralNetwork] | |
/** | |
* The integration function contains implicitly the weights | |
* for this compute unit | |
*/ | |
def integration(immediateInput: Array[Double]): Double | |
/** | |
* The activation function | |
*/ | |
def activation(e: Double): Double | |
/** | |
* Compute the output given the specified input | |
*/ | |
def compute(input: Array[Double]): Double = { | |
// require (input.size == getNumInputs()) | |
val numInputs = getNumInputs() | |
var inputsToPeelOff: Array[Double] = input | |
var immediateInputs: Array[Double] = Array.empty | |
var childNetwork: Option[NeuralNetwork] = None | |
var childInput: Array[Double] = null | |
var childOutput: Double = 0.0 | |
var j: Int = 0 | |
for (i <- (0 to numImmediateInputs-1)) { | |
childNetwork = child(i) | |
if (childNetwork.isDefined) { | |
j = childNetwork.get.getNumInputs() | |
childInput = inputsToPeelOff.slice(0, j) | |
inputsToPeelOff = inputsToPeelOff.slice(j, numInputs) | |
childOutput = childNetwork.get.compute(childInput) | |
immediateInputs = immediateInputs :+ childOutput | |
} else { | |
immediateInputs = immediateInputs :+ inputsToPeelOff.head | |
inputsToPeelOff = inputsToPeelOff.tail | |
} | |
} | |
// require (inputsToPeelOff.size == 0) | |
activation(integration(immediateInputs)) | |
} | |
/** | |
* Add child nodes at the specified input indices. | |
* Children are added at immediate input indices, *not* | |
* leaf indices | |
*/ | |
def addChildren(m: Map[Int, NeuralNetwork]) = { | |
children = children ++ m | |
} | |
/** | |
* Returns a potential child at the given index; | |
* potential children can exist at any of 0 to numImmediateInputs-1 | |
* indices. | |
*/ | |
def child(i: Int): Option[NeuralNetwork] = { | |
children.get(i) | |
} | |
/** | |
* Returns the number of children for this compute unit | |
*/ | |
def getNumChildren(): Int = { | |
(0 to numImmediateInputs-1).filter(i => child(i).isDefined).size | |
} | |
/** | |
* Returns the number of incoming vertices for this compute unit | |
*/ | |
def getNumImmediateInputs(): Int = { | |
numImmediateInputs | |
} | |
/** | |
* Returns the number of leaf incoming vertices for | |
* the composite neural network | |
*/ | |
def getNumInputs(): Int = { | |
(0 to numImmediateInputs-1) | |
.map(i => child(i)) | |
.map { c => c match { | |
case None => 1 | |
case _ => c.get.getNumInputs() | |
} | |
}.sum | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment