Ambiguous on_pre operation

I have a problem with setting up a slightly comlicated on_pre operation.

Here’s the code that is supposed to increase the voltage of post-synapse. The set-up is standard: An LIF neuron connected to a spike generating source. However, I noticed a combination of “synaptic equations + spike generator object” leads to the correct behavior while some others don’t. In such a way that it seems unpredictable.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  5 11:43:46 2022

@author: arash
"""

import matplotlib.pyplot as plt
import numpy as np
import brian2 as b2


def plot():
    """to plot results"""
    fig, axs = plt.subplots(4,1, sharex=True)

    for i in range(G.N):
        axs[0].plot(monS.t, monG.v[i,:], label='v')
    
    for id_ in range(len(S)):
        axs[1].plot(monS.t, monS.g[id_,:] ,label='g'+str(id_))
        axs[2].plot(monS.t, monS.h[id_,:] ,label='h'+str(id_))
        axs[3].plot(monS.t, monS.v_post[id_,:] ,label='v_post_'+str(id_))
        
    for ax in axs:
        ax.legend()


b2.start_scope()

G = b2.NeuronGroup(1, 
                   '''
                   dv/dt = -(v+70*mV)/tau : volt (unless refractory)
                   tau = 10*ms : second (shared)
                   ''',
                   'euler', 
                   threshold='v>-50*mV', 
                   refractory=2*b2.ms, 
                   reset='v=-70*mV')

# a convenient way to switch spike generation method
spk_gen='poisson'
if spk_gen =='poisson':
    P = b2.PoissonGroup(1, rates= 50*b2.Hz)
else: 
    indices = np.array([0, 1, 2])
    times = np.array([1, 20, 30])*b2.ms
    P = b2.SpikeGeneratorGroup(3, indices, times)

S = b2.Synapses(P, G, 
                '''
                dg/dt = (-g + h)/tau_s : 1 (clock-driven)
                dh/dt = -h/tau_s : 1 (clock-driven)
                tau_s = 5*ms : second (shared)
                J = 10*mV : volt
                ''',
                on_pre='''
                v_post += J*g
                h = 1
                ''', # here is when the bug might be...
                method='exact')

S.connect()
monG = b2.StateMonitor(G, ['v'],True)
monS = b2.StateMonitor(S, ['h','g','v_post'], record=True)
G.v = -70*b2.mV
b2.run(100*b2.ms)
plot()

Running this leads to the expected result that is depicted below:
correct

However, if I change the spk_gen flag and create spikes from SpikeGeneratorGroup, then the same code leads to the following, suggesting that the post_synapse is not updated at all:
wrong

Interestingly, if I change my on_pre argument using some explicit numerical value, say v_post += 10*mV, ..., or v_post += J (no multiplication by g), It works:
correct_somehow

Why is that? How can I predict the behavior and how can I avoid unwanted responses?
Thank you very much!

Arash

Hi @arashgmn. The problem is that at the beginning of your simulation, g and h are both 0. This means that the very first spike for each synapse does not have any effect. In the PoissonGroup input, you have a single synapse that receives all the spikes, and all but the first one therefore have the expected effect. In the SpikeGeneratorGroup version, you create 3 synapses, and each of them only receives a single spike, so none of them is propagated. If you use a stimulation that is more similar to your PoissonGroup example, e.g.:

    indices = [0, 0, 0]
    times = [1, 20, 30]*b2.ms
    P = b2.SpikeGeneratorGroup(1, indices, times)

you will get the expected result (note that the first spike triggers updates of g and h but no post-synaptic change):
Figure_1

Hope that clears things up!

It makes complete sense! Thank you @mstimberg for your prompt response. Hope this prevents others to make the same silly mistake :smiley:

1 Like