Description of problem
Dear all,
I am new on Brian, and want to implement my network in a class and test it, but it provides no results.
Did I do something wrong here?
Thanks for your time and help.
BR Binh
Minimal code to reproduce problem
class myNeuronClass :
def __init__(self):
eqsM = '''
dv/dt = (g+1)/(1*ms): 1 (unless refractory)
g : 1
'''
self.G = NeuronGroup(1, eqsM, threshold='v>5', reset='v=0; g = 0', refractory=5*ms, method='Euler')
# we send spikes at time 2, and 32ms to the neuron group G for changing the value of its variable "g" to 2 and 4 accordingly
nlayers = 2
self.clocks = []
self.clock_synapses = []
for l in range(nlayers):
spike_times = np.array([l*30+2])*ms
indices = np.array([0]*len(spike_times))
clock = SpikeGeneratorGroup(1, indices, spike_times)
self.clocks.append(clock)
clock_synapse = Synapses(clock, self.G, on_pre='g =' + str((l+1)*2))
clock_synapse.connect()
self.clock_synapses.append(clock_synapse)
def main():
start_scope()
duration = 100*ms
net = myNeuronClass()
M = StateMonitor(net.G, 'v', record=True)
P = StateMonitor(net.G, 'g', record=True)
run(duration)
subplot(211)
plot(M.t/ms, M.v[0])
ylabel('v')
subplot(212)
plot(P.t/ms, P.g[0])
xlabel('Time (ms)')
ylabel('g')
show()
What you have already tried
I want to monitor the value “g” and “v”, but they seem to be empty.
Without using class, it works well.
Expected output (if relevant)
For v: I should see a saw-tooth signal over time
For g: it should be 0 until t= 2, then 2 until t=32, and after that 4
Actual output (if relevant)
empty output
Full traceback of error (if relevant)
Hi @Thanh-Binh – welcome to the Brian community
The issue you are facing is unfortunately quite common, we do not document the “structured program” use case very well, i.e. constructing things in classes and functions as opposed to “flat scripts” as in most of our examples and tutorials.
In your code, the main
function calls run(...)
. At this point, Brian will look for “Brian objects” in the current scope and run them. In your case, it will only find the two StateMonitor
s. There is a way to make it work with custom classes, but in cases like this, I would rather recommend to explicitly construct a Network
object containing everything needed (see Running a simulation — Brian 2 2.7.1 documentation). In your example, you could for example replace your run
call by:
network = Network(collect()) # include the monitors
network.add(net.G) # add the NeuronGroup
network.add(net.clocks) # add the SpikeGeneratorGroups
network.add(net.clock_synapses) # add the Synapses
network.run(duration)
(The first line with the collect
will basically do what the run(...)
call did previously, i.e. collect everything “visible” in the current scope, in your case the two monitors.)
Another option would be to create and store the Network
object as part of the class, or have a general .objects
attribute containing all the objects that need to be added to the network so that the main
function is more independent of the internals of your class.
Hope that gets you going!
PS: I edited your post to display the code more nicely. You can use triple backticks like this for code blocks:
```
# Python code
print("Hello, world!)
```
PPS: A very minor thing, but you can use a single StateMonitor
for both variables:
M = StateMonitor(net.G, ['v', 'g'], record=True)
@mstimberg thanks for your comments!
Even in the Case I put all in a flatt script in main()
I found the code
nlayers = 2
self.clocks = []
self.clock_synapses = []
for l in range(nlayers):
spike_times = np.array([l*30+2])*ms
indices = np.array([0]*len(spike_times))
clock = SpikeGeneratorGroup(1, indices, spike_times)
self.clocks.append(clock)
clock_synapse = Synapses(clock, self.G, on_pre='g =' + str((l+1)*2))
clock_synapse.connect()
self.clock_synapses.append(clock_synapse)
does not work correctly. Clocks emit only 1 spike at 32ms. No Spike is emitted by the 1st Spike time at 2ms !
Why?
Thanks
Hi again. This is the issue that is briefly discussed in the link I sent earlier : the run
call does not “see” the objects that are stored in containers like lists, it only automatically collects objects that are directly stored in variables in the current scope. After the loop, the clock
and clock_synapse
variables contain the values from the second iteration and are automatically included in the network. The objects from the first iteration are only in self.clocks
and self.clock_synapses
, and Brian will therefore ignore them.
@mstimberg thank you very much. I understood this concept.
1 Like