0

I am trying to create a Neural Network class made up of Neuron objects wired together.

My Neuron class has

  1. Dendrites
    The number of dendrites is specified in the parameters when the class is initialised. The Dendrites are stored in a list whose index stores the voltages of each Dendrite. eg: neuron1.dendrites[2]=0.12 volts.
  2. Activation (Threshold) Potential The sum of all the dendrite potentials provides the neuron potential. If this potential exceeds the Threshold Potential, my Neuron will fire. There are other neurons connected to the axon of my neuron. The Axon of my Neuron connects to the Dendrites of other Neuron objects. Several Dendrites from other Neurons may connect to the Axon of my Neuron. They will all receive a fixed voltage (outputVoltage) when my Neuron fires. It fires in an All-or-Nothing manner.
  3. Firing state
    When the the Activation Potential is reached, the firing state = on (True)
  4. My Neuron class also has a setConnections() method. This method receives a python list of Dendrites. In this method, I wish to iterate through my internal list of external Dendrites and reset their voltage values. This is not working. I cannot figure out why and so seek assistance here.

I provide a stripped down version of my code below:

import threading

 
 class Neuron:
      def __init__(self, dendrites, activation_Pot=0.24):
      """
      Create a dendrites[] array.
      Each element of this array represents the voltage of that dendrite.
      We can then loop through the array to sum up the signal strength.  

      If the signal strength exceeds the Activation potential, then the all-or-nothing threshold has been breached and
      we can transmit our signal along the axon.
      """
      self.dendrites = [0]*dendrites
      self.InputPotential = 0  # This variable will store the sum of all the dendrite voltages.  It is being initialised here.
      self.activation_Pot = activation_Pot
      self.on = True
      self.off = False
      self.voltsOut = 0.12      # This constant dictates the potential of the axon when the neuron is firing.
      self.outputPotential = 0  # This variable  SETS the potential of the single axon when the threshold activation potential of the  neuron has been reached and the neuron is firing.
      self.firing = self.off
      self.axonConnections = []
      
      # Launch a thread to check on a timer the sum of all dendrite inputs and fire when output > Activation Potential.
      t1 = threading.Thread(target = self.start, args=[])
      t1.start()
      
      
      def fire(self):
          self.outputPotential = self.voltsOut
          self.firing = self.on
          print("Neuron is firing!")
          
          for outputDendrites in self.axonConnections:
              outputDendrites = self.outputPotential
              
      def stopFiring(self):
          self.outputPotential = 0
          self.firing = self.off
          print("Neuron has STOPPED firing!")
          
          
      def setActivation_Pot(self, activation_Pot):
          if (activation_Pot >= 0) and (activation_Pot <=1):
              self.activation_Pot = activation_Pot
          else:
              print("activation_Pot value needs to be between 0 and 1")
              print("activation_Pot has not been reset.")
              print("Please consider re-inputting a valid value.")        
              
              
      def getActivation_Pot(self):
          return self.activation_Pot
          
          
      def setAxonConnections(self, axonConnections):
          self.axonConnections = axonConnections
          
      def getAxonConnections(self):
          return self.axonConnections
      
      
      def start(self):
          while True:
              while True:
                  self.InputPotential = 0  
                  for dendrite in self.dendrites:
                      self.InputPotential+=dendrite
                      
                  if self.InputPotential >= self.activation_Pot:
                      self.fire()
                      break
              while True:
                  self.InputPotential = 0
                  for dendrite in self.dendrites:
                      self.InputPotential+=dendrite
                  
                  if self.InputPotential < self.activation_Pot:
                      self.stopFiring()
                      break

Here is the relevant code from the from the main.py script which is testing the Neuron class:

from neuron import Neuron

# Instantiate transmitting neurone...
n0 = Neuron(3, 0.36)

# Instantiate receiving neurones...
n1 = Neuron(3, 0.36)
n2 = Neuron(3, 0.36)
n3 = Neuron(3, 0.36)

# Make the connections: I do this by creating storing my external Dendrites into a list 
# and passing that list to the transmitting neuron for it to update the voltages 
# of each of these neurons.  BUT THESE LIST VARIABLES ARE NOT GETTING UPDATED...
axonConns = [n1.dendrites[0], n2.dendrites[1], n3.dendrites[2]]
n0.setAxonConnections(axonConns) # THE LIST VARIABLES OF THE axonConns LIST ARE NOT GETTING UPDATED!!

n0.fire()  # THE LIST VARIABLES OF THE axonConns LIST ARE NOT GETTING UPDATED by this fire() method!!

I hope that makes sense. In summary: I am passing a list of variables from my main.py script at the line n0.setAxonConnections(axonConns). The variables in this list are not getting updated by the neuron.fire() method of my Neuron class. Can someone please explain why? Forgive me, I am a python newbe!

IqbalHamid
  • 2,324
  • 1
  • 18
  • 24
  • 2
    `axonConns` is *not* a "list of variables". It is list of values derived from a group of variables. Further, the line (I presume) you think should be updating these variables (`outputDendrites = self.outputPotential`) is just assigning to a local variable which happened to previously have a value from your list. – Scott Hunter Jun 29 '21 at 00:54
  • @ScottHunter, I understand what you are saying. But I've only just started to learn python (literally days ago). Is there a solution? In certain languages such as VB6.0 it is possible to pass arguments by reference. Is this possible in python? – IqbalHamid Jun 29 '21 at 01:05
  • One quick way to make this work is: `axonConns = [(n1.dendrites, 0), (n2.dendrites, 1), (n3.dendrites, 3)]` and handle it with the index in mind with `fire()`. You need to pass the list itself to reflect the changes in the way you intend to. This is assuming you want n1, n2 and n3 to get updated. But if not, then the answer that just came in from @aebange should be enough. – Brian Destura Jun 29 '21 at 01:10
  • No, python does not support call by reference. – juanpa.arrivillaga Jun 29 '21 at 02:16

3 Answers3

1

Might not be the best solution, but just my 2c. If you want the other neuron's dendrites to be updated as well, you can declare the connections like so:

axonConns = [(n1.dendrites, 0), (n2.dendrites, 1), (n3.dendrites, 2)]

You need to pass the list of the dendrites itself, and define which of the dendrites are to be considered in the connection by index. Then change the fire method to take the index into account:

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    
    for dendrites, index in self.axonConnections:
        dendrites[index] = self.outputPotential

EDIT:

To demonstrate why OP's answer is not enough to update neurons outside fire()

In [1]: x = [1, 2, 3]
   ...:
   ...: def foo(val):
   ...:     potential = 100
   ...:     for i in range(len(val)):
   ...:         val[i] = potential
   ...:     return val
   ...:
   ...: print(x)
   ...: print(foo([x[0], x[1], x[2]]))
   ...: print(x)
   ...:
[1, 2, 3]
[100, 100, 100]
[1, 2, 3]
Brian Destura
  • 11,487
  • 3
  • 18
  • 34
  • @ bdbd In response to your comment under my own solution which I have now deleted: Weird! axonConns list got updated (leading me to believe it worked) BUT the individual elements did not. In other words, you are correct. the dendrites themselves did not get updated. I will have another look at this later in the day. – IqbalHamid Jun 29 '21 at 03:29
  • Yes in that approach `axonConns` will be updated but only on the context of the neuron firing, which is `n0`. But on the context of other neurons, nothing will be changed. – Brian Destura Jun 29 '21 at 03:31
1

By playing around with the following code in python, I thought I had found a solution:

def changeListVars(n):
    for i in range(len(n)):
        n[i] = n[i]+5
    print()
    print(n)

x=1
y=2
z=3
m = [x,y,z]
print(len(m))

for i in range(len(m)):
    print (m[i])
    
changeListVars(m)
print()
print(m)

The output of above was:

3
1
2
3

[6, 7, 8]

[6, 7, 8]

Initially, this appears to work. Therefore to fix my code, I amended the two lines:

        for i in range(len(self.axonConnections)):
            self.axonConnections[i] = self.outputPotential

so my new fire() method reads as follows:

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    
    for i in range(len(self.axonConnections)):
        self.axonConnections[i] = self.outputPotential

The results of the above experiment CLEARLY indicate that python DOES indeed pass variables of a list, by reference, by default when passed in an argument.

HOWEVER (thanks to @bdbd) I discovered that the elements of the list remained unchanged. So for example,

print(x, y, z)

yields

1 2 3

It also means that in my code, while the axonConn array (which stores a list of the external dendrite voltages) was getting updated, the dendrites themselves were not getting updated.

After some research and experimentation, I have finally solved the conundrum. First some necessary background reading:

All Python objects have:

  1. a unique identity (an integer, returned by id(x))
  2. a type (returned by type(x))
  3. some content

You cannot change the identity!

You cannot change the type!

Some objects allow you to change their content (without changing the identity or the type, that is). These are mutable data types and include 'list's for example

Most objects do not (eg: string, integer, tuple, etc...)

I finally conclude:

  1. python ALWAYS passes by reference!

  2. However, certain data types are immutable (strings, integers, etc..)

  3. When references to those are passed to a function; any manipulation of those variables will necessitate a duplicate being made of the variable to store the result of the manipulation.

  4. Any duplication of a variable leads to a new reference being created to point to the new value. This is explained well here.

  5. As such, when this happens, the calling function no longer sees the manipulated result. This is because there is no way to re-assign the reference of the argument of the calling function to point to the the new reference. Once the contents of a variable have had to be duplicated, the calling function no longer sees it as the new contents has a new reference.

  6. This means mutables such as lists can be appended and retain the same reference -hence it appears as if they had been passed by reference.

  7. But immutables such as strings and integers will lose their references following a manipulation because a new object (string or integer for example) has to be created with that new value. The new object will always have a new reference! Hence, in these scenarios, the function call will behave as if it has been passed by value!.

I provide some code below which illustrates this well:

'''
The results of this block of code suggest that python passes by value.
However, in truth, the output of the code PROVES a reference is passed!
But because the variable's data type is immutable, a new object is created within the function with a NEW reference.
The upshot of this being, behaviour which seems to show that the argument is passed by value, when this is not strictly the case.
'''
# EXAMPLE 1:
class PassByReference:
    def __init__(self):
        self.variable = 1
        print("BEFORE function call, self.variable = " + str(self.variable))
        print("BEFORE function call, id(self.variable) = " + str(id(self.variable)))
        self.change(self.variable)
        print("AFTER function call, self.variable = " + str(self.variable))
        print("AFTER function call, id(self.variable) = " + str(id(self.variable)))

    def change(self, var):
        print("INSIDE modifying function, var TO BE incremented!  id(var) BEFORE incrementing = " + str(id(var)))
        var = var + 1
        print("INSIDE modifying function, var incremented!  Now, var = " + str(var))
        print("INSIDE modifying function, AFTER var incremented!  id(var) AFTER incrementing = " + str(id(var)))

p = PassByReference()

Here are further examples which demonstrate the same finding:

'''
The results of all the examples below suggest that python passes by value.
However, in truth, the output of each block of code PROVES a reference is passed!

Where the variable data type is immutable, a new object is created within the function with a NEW reference.  The upshot of this being, behaviour which seems to show that the argument is passed by value, when this is not strictly the case.

Where the data type is mutable, the output clearly identifies that the arguments have been passed by reference and have been successfully mutated without losing their object reference so that "By Reference" behaviour manifests.

'''

# EXAMPLE 2:
def changeListVars2(n):
    print("From inside the function, BEFORE incrementing n, id(n) = " + str(id(n)))
    n = n + 5
    print("From inside the function, n = " + str(n))
    print("From inside the function, AFTER incrementing n, id(n) = " + str(id(n)))

m = 1
print("Before function call, m = " + str(m))
print("id(m) = " + str(id(m)))


changeListVars2(m)
print("After function call, m = " + str(m))
print("id(m) = " + str(id(m)))


# EXAMPLE 3:
def changeListVars(n):
    for i in range(len(n)):
        n[i] = n[i]+5
    print("From inside the function, n = " + str(n))

x = 1
y = 2
z = 3
m = [x, y, z]
print("Before function call, m = " + str(m))
print("id(x) = " + str(id(x)))
print("id(m) = " + str(id(m)))


changeListVars(m)
print("After function call, m = " + str(m))
print("After function call, id(m) = " + str(id(m)))
print("After function call:  [x=" + str(x) + ", y=" + str(y) + ", z=" + str(z) + "]")
print("After function call, id(x) = " + str(id(x)))


print() #a new line to seperate output from the two blocks of code
print() #a new line to seperate output from the two blocks of code
print("ChangeListVars2:")


# EXAMPLE 4:
def swap(a, b):
    x = a
    print ("id(x) = " + str(id(x)))
    print ("id(a) = " + str(id(a)))
    print ("id(b) = " + str(id(b)))
    a = b

    print ("id(a) = " + str(id(a)))
    b = x
    print ("id(b) = " + str(id(b)))
    a[0]= '20'




var1 = ['1','2','3','4']
var2 = ['5','6','7','8','9']
print ("id(var1) = " + str(id(var1)))
print ("id(var2) = " + str(id(var2)))
print()

swap(var1, var2)
print()

print ("id(var1 = )" + str(id(var1)))
print ("id(var2 = )" + str(id(var2)))
print ("var1 = " + str(var1))
print ("var2 = " + str(var2))


IqbalHamid
  • 2,324
  • 1
  • 18
  • 24
  • 1
    "I can therefore conclude from the output of this code that python DOES indeed pass variables of a list, by reference, by default when passed in an argument." No, *it absolutely does not*. See the linked duplicate for a thorough discussion of Python's evaluation strategy – juanpa.arrivillaga Jun 29 '21 at 02:19
  • @juanpa.arrivillaga, If you run the code at the top of my answer, which starts 'def changeListVars(n)', & examine the output, it would suggest that m in 'changeListVars(m)' is passed by reference. The function 'def changeListVars(n)' does not return a result. It simply changes the argument passed to it and the value of m outside the function still gets altered. This indicates that the argument is being passed by reference. Do please try it. Also, you are correct that the link you provided suggests otherwise, but there are some differences. I am looking into this and will revert shortly. – IqbalHamid Jun 29 '21 at 02:31
  • No, that *absolutely does not imply call by reference*. If you want to demonstrate call by reference, it is simple. Create a function, `foo` such that for `foo(variable)`, variable will be rebound in the *caller*. That is what call by reference means. E.g `y = 0; x = 0; foo(x); foo(y); print(x, y)` shoud print `1 1` instead of `0 0`. – juanpa.arrivillaga Jun 29 '21 at 02:32
  • With all due respect, is that not what my code is also demonstrating? Your foo(x) is like my changeListVars(m). You set x=1. I set m=[1] (effectively). Your x outside foo() is changed after calling foo(x). My m outside changeListVars() is changed after calling changeListVars(m). – IqbalHamid Jun 29 '21 at 03:06
  • @IqbalHamid Try to see whether n1, n2 and n3's dendrites got updated. – Brian Destura Jun 29 '21 at 03:07
  • I meant checking for example, `n1.dendrites[0]` outside `fire()` is updated? – Brian Destura Jun 29 '21 at 03:13
  • @bdbd Weird! axonConns list got updated (leading me to believe it worked) BUT the individual elements did not. In other words, you are correct. the dendrites themselves did not get updated. – IqbalHamid Jun 29 '21 at 03:25
0

You aren't properly reassigning the values of outputDendrites in your class method.

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    # Store the axonConnections into a temporary list for parsing since we'll be changing the values WHILE interating
    initial_axonConnections_list = self.axonConnections
    for index, outputDendrites in enumerate(initial_axonConnections_list):
        outputDendrites = self.outputPotential
        # We have stored the value in outputDendrites, but we're not doing anything with it, we have to assign it
        self.axonConnections[index] = outputDendrites
aebange
  • 35
  • 5
  • It's worth pointing out that this is an issue you probably would've noticed right away if you were using an IDE. Pycharm flagged "outputDendrites" right away as an unused variable which is what allowed me to quickly debug your code. I would strongly suggest checking that out. – aebange Jun 29 '21 at 01:13