Multiple epoch of running in standalone mode

Is there an easy way to do multiple epoch of running on the same input stimuli in standalone mode?
Right now, I need to do the following when running a new epoch:

     # epoch#1 
     ......
     run(10*second, report='text') 
     device.build(directory='code', compile=True, run=True, debug=False)
     #epoch#2
     device.reinit()
     device.activate(build_on_run=False)
     .......
     run(10*second, report='text') 
     device.build(directory='code', compile=True, run=True, debug=False)

Is there a simpler way to do it without reinitialize the whole code?

Thanks
–Wei

Hi, could you give some more detail of what you mean by “multiple epoch of running on the same input stimuli”? Do any parameters change between the runs or are they exactly the same (and the only difference is due to random numbers)?

Hi Marcel

The parameters are exactly the same and the only changes is due to random numbers. For example, I can run the model for 100 seconds in one round or I can run the same model in 10 rounds of 10 seconds.

Thanks
–Wei

If you want to run the same standalone model repeatedly, you can use device.build(run=False) to compile the model, and then call device.run with some parameters (see below). The call to device.run is unfortunately more complicated than it should be, this is something that we want to change (also see github issues #1239 and #1240.

Here’s a little example that runs the same stochastic simulation repeatedly with standalone (note that you need to store the results after every run somewhere, otherwise they will get overwritten by the later runs). In this particular case of course you should rather use a single run with 10 neurons, but I hope it illustrates the idea:

from brian2 import *
set_device('cpp_standalone', build_on_run=False)

tau = 10*ms
G = NeuronGroup(1, 'dv/dt = -v/tau + tau**-0.5*xi : 1')
mon = StateMonitor(G, 'v', record=0)
run(100*ms)

device.build(run=False)
voltages = []

for _ in range(10):
    device.run(device.project_dir, with_output=False, run_args=[])
    voltages.append(mon.v[0])

for idx in range(10):
    plt.plot(mon.t/ms, voltages[idx])
plt.show()

note that you need to store the results after every run somewhere,
otherwise they will get overwritten by the later runs

Does this also apply to the trained synapses. In another word, it is necessary to save and load the trained synapse in between runs? I tried this on the STDP examples for 10 rounds of 10 second run without saving the synapse from earlier runs. The result is very different from 1 round of 100 second run. Each individual run sees to start from scratch.
Here is the code:

 from brian2 import *

 set_device('cpp_standalone', build_on_run=False)
 n_epoch = 10
 N = 1000
 taum = 10*ms
 taupre = 20*ms
 taupost = taupre
 Ee = 0*mV
 vt = -54*mV
 vr = -60*mV
 El = -74*mV
 taue = 5*ms
 F = 15*Hz
 gmax = .01
 dApre = .01
 dApost = -dApre * taupre / taupost * 1.05
 dApost *= gmax
 dApre *= gmax

 eqs_neurons = '''
 dv/dt = (ge * (Ee-v) + El - v) / taum : volt
 dge/dt = -ge / taue : 1
 '''

 input = PoissonGroup(N, rates=F)
 neurons = NeuronGroup(1, eqs_neurons, threshold='v>vt', reset='v = vr',
                       method='euler')
 S = Synapses(input, neurons,
              '''w : 1
                 dApre/dt = -Apre / taupre : 1 (event-driven)
                 dApost/dt = -Apost / taupost : 1 (event-driven)''',
              on_pre='''ge += w
                     Apre += dApre
                     w = clip(w + Apost, 0, gmax)''',
              on_post='''Apost += dApost
                      w = clip(w + Apre, 0, gmax)''',
              )
 S.connect()
 S.w = 'rand() * gmax'
 mon = StateMonitor(S, 'w', record=[0, 1])
 s_mon = SpikeMonitor(input)

 run(10*second, report='text')
 device.build(directory="code", compile=True, run=False, debug=False)
 for i in range(n_epoch):
     device.run(device.project_dir, with_output=False, run_args=[])

     subplot(311)
     plot(S.w / gmax, '.k')
     ylabel('Weight / gmax')
     xlabel('Synapse index')
     subplot(312)
     hist(S.w / gmax, 20)
     xlabel('Weight / gmax')
     subplot(313)
     plot(mon.t/second, mon.w.T/gmax)
     xlabel('Time (s)')
     ylabel('Weight / gmax')
     tight_layout()
     show()

Ok, there was a misunderstanding here. In your original code, you recreate the network from scratch every time and I therefore thought runs are independent and you only run it repeatedly to get different noise instantiations (as in my example). Now you actually don’t want to run the same network repeatedly, but instead you want to run each network with the weights from the previous run, right? But then, why do you want to do multiple runs instead of a single long run? It is possible to do this with multiple runs, but it needs the somewhat cumbersome technique that I detailed in a gist (which I seem to link every day now… this indicates that this is really a thing we should make more straightforward).

I see. Sometime a long run can accumulate lots of spikes. I was hoping to make it a bit more flexible, e.g. to record only spikes from the last run, etc. So it’s more of a issue of convenience. Thanks for the help.

Note that even with a single run you can do something like:

set_device('cpp_standalone', build_on_run=False)
# construct network
run(90*second)
spike_mon = SpikeMonitor(...)
run(10*second)
device.build()

This will only record spikes in the last 10 seconds.

1 Like