Skip to content

Commit 7b6aed2

Browse files
author
Alexander Ororbia
committed
revised dyn-syn lesson further
1 parent a1d3000 commit 7b6aed2

File tree

2 files changed

+183
-9
lines changed

2 files changed

+183
-9
lines changed
Loading

docs/tutorials/neurocog/dynamic_synapses.md

+183-9
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,22 @@ construct and study a small neuronal circuit involving a leaky integrator that
1212
is driven by exponential synapses relaying pulses from an excitatory and an
1313
inhibitory population of Poisson input encoding cells.
1414

15-
## Chemical Synapses
15+
## Synaptic Conductance Modeling
1616

17+
Synapse models are typically used to model the post-synaptic response produced by
18+
action potentials (or pulses) at a pre-synaptic terminal. Assuming an electrical
19+
response (as opposed to a chemical one, e.g., an influx of calcium), such models seek
20+
to emulate the time-course of what is known as post-synaptic receptor conductance. Note
21+
that these dynamic synapse models will end being a bit more sophisticated than the strength
22+
value matrices we might initially employ (as in synapse components such as the
23+
[DenseSynapse](ngclearn.components.synapses.DenseSynapse)).
1724

1825
Building a dynamic synapse can be done by importing
19-
[ExponentialSynapse](ngclearn.components.synapses.ExponentialSynapse) and
26+
[ExponentialSynapse](ngclearn.components.synapses.ExponentialSynapse) or
2027
[AlphaSynapse](ngclearn.components.synapses.AlphaSynapse)
2128
from ngc-learn's in-built components and setting them up within a model
22-
context for easy analysis.
29+
context for easy analysis. For the first part of this lesson, we will import
30+
both of these and compare their behavior.
2331
This can be done as follows (using the meta-parameters we provide in the
2432
code block below to ensure reasonable dynamics):
2533

@@ -43,7 +51,7 @@ T = 8. # ms ## total duration time
4351

4452
Tsteps = int(T/dt) + 1
4553

46-
# ---- build a two-synapse system ----
54+
## ---- build a dual-synapse system ----
4755
with Context("dual_syn_system") as ctx:
4856
Wexp = ExponentialSynapse( ## exponential dynamic synapse
4957
name="Wexp", shape=(1, 1), tau_syn=3., g_syn_bar=1., syn_rest=0., resist_scale=1.,
@@ -78,8 +86,7 @@ important hyper-parameters to configure:
7886
excitatory nature for non-negative values of `syn_rest` or a synapse with an inhibitory
7987
nature for negative values of `syn_rest`.
8088

81-
82-
The flow of electrical current from a pre-synaptic neuron to a post-synaptic one is often modeled under the assumption that pre-synaptic pulses result in impermanent (transient; lasting for a short period of time) changes in the conductance of a post-synaptic neuron. As a result, the resulting conductance dynamics $g_{\text{syn}}(t)$ of each of the two synapses that you have built above can be simulated in ngc-learn according to one or more ordinary differential equations (ODEs).
89+
The flow of electrical current from a pre-synaptic neuron to a post-synaptic one is often modeled under the assumption that pre-synaptic pulses result in impermanent (transient; lasting for a short period of time) changes in the conductance of a post-synaptic neuron. As a result, the resulting conductance dynamics $g_{\text{syn}}(t)$ -- or the effect (conductance changes in the post-synaptic membrane) of a transmitter binding to and opening post-synaptic receptors -- of each of the two synapses that you have built above can be simulated in ngc-learn according to one or more ordinary differential equations (ODEs), which themselves iteratively model different waveform equations of the time-course of synaptic conductance.
8390
For the exponential synapse, the dynamics adhere to the following ODE:
8491

8592
$$
@@ -96,13 +103,14 @@ $$
96103

97104
where $h_{\text{syn}}(t)$ is an intermediate variable that operates in service of driving the conductance variable $g_{\text{syn}}(t)$ itself.
98105

99-
For both the exponential and the alpha synapse, the changes in conductance are finally converted (via Ohm's law) to electrical current to produce the final derived variable $j_{\text{syn}}(t)$:
106+
Finally, we seek model the electrical current that results from some amount of neurotransmitter in previous time steps.
107+
Thus, for both the exponential and the alpha synapse, the changes in conductance are finally converted (via Ohm's law) to electrical current to produce the final derived variable $j_{\text{syn}}(t)$:
100108

101109
$$
102110
j_{\text{syn}}(t) = g_{\text{syn}}(t) (v(t) - E_{\text{rest}})
103111
$$
104112

105-
where $v_{\text{rest}$ (or $E_{\text{rest}}$) is the post-synaptic reverse potential of the synapse; this is typically set to $E_{\text{rest}} = 0$ (millivolts; mV)for the case of excitatory changes and $E_{\text{rest}} = -75$ (mV) for the case of inhibitory changes. $v(t)$ is the voltage/membrane potential of the post-synaptic the synaptic cable wires to, meaning that the conductance models above are voltage-dependent (in ngc-learn, if one wants voltage-independent conductance, then `syn_rest` must be set to `None`).
113+
where $v_{\text{rest}$ (or $E_{\text{rest}}$) is the post-synaptic reverse potential of the ion channels that mediate the synaptic current; this is typically set to $E_{\text{rest}} = 0$ (millivolts; mV)for the case of excitatory changes and $E_{\text{rest}} = -75$ (mV) for the case of inhibitory changes. $v(t)$ is the voltage/membrane potential of the post-synaptic the synaptic cable wires to, meaning that the conductance models above are voltage-dependent (in ngc-learn, if you want voltage-independent conductance, then set `syn_rest = None`).
106114

107115

108116
### Examining the Conductances of Dynamic Synapses
@@ -187,13 +195,179 @@ expoential and alpha synapse conductance trajectories:
187195
:align: center
188196
189197
+---------------------------------------------------------+-----------------------------------------------------------+
190-
| .. image:: ../docs/images/tutorials/neurocog/expsyn.png | .. image:: ../docs/images/tutorials/neurocog/alphasyn.png |
198+
| .. image:: ../docs/images/tutorials/neurocog/expsyn.jpg | .. image:: ../docs/images/tutorials/neurocog/alphasyn.jpg |
191199
| :width: 100px | :width: 100px |
192200
| :align: center | :align: center |
193201
+---------------------------------------------------------+-----------------------------------------------------------+
194202
```
195203

204+
Note that the alpha synapse (right figure) would produce a more realistic fit to recorded synaptic currents (as it attempts to model
205+
the rise and fall of current in a less simplified manner) at the cost of extra compute, given it uses two ODEs to
206+
emulate condutance, as opposed to the faster yet less-biophysically-realistic exponential synapse (left figure).
207+
196208
## Excitatory-Inhibitory Driven Dynamics
197209

210+
Let's next examine a more interesting use-case of the above dynamic synapses -- modeling excitatory and inhibitory
211+
pressures produced by different groups of pre-synaptic spike trains. This allows us to examine a very common
212+
and often-used conductance model that is paired with spiking cells such as the leaky integrate-and-fire (LIF). Specifically,
213+
we seek to simulate the following post-synaptic conductance dynamics for a single LIF unit:
214+
215+
$$
216+
\tau_{m} \frac{\partial v(t)}{\partial t} = -(v(t) - E_{L}) - \frac{g_{E}(t)}{g_{L}} (v(t) - E_{E}) - \frac{g_{I}(t)}{g_{L}} (v(t) - E_{I})
217+
$$
218+
219+
where $g_{L}$ is the leak conductance value for the post-synaptic LIF, $g_{E}(t)$ is the post-synaptic conductance produced by excitatory pre-synaptic spike trains (with excitatory synaptic reverse potential $E_{E}$), and $g_{I}(t)$ is the post-synaptic conductance produced by inhibitory pre-synaptic spike trains (with inhibitory synaptic reverse potential $E_{I}$). Note that the first term of the above ODE is the normal leak portion of the LIF's standard dynamics (scaled by conductance factor $g_{L}$) and the last two terms of the above ODE can be modeled each separately with a dynamic synapse. To differentiate between excitatory and inhibitory conductance changes, we will just configure a different reverse potential for each to induce either excitatory (i.e., $E_{\text{syn}} = E_{E} = 0$ mV) or inhibitory (i.e., $E_{\text{syn}} = E_{I} = -80$ mV) pressure/drive.
220+
221+
We will specifically model the excitatory and inhibitory conductance changes using exponential synapses and the input spike trains for each with Poisson encoding cells; in other words, two different groups of Poisson cells will be wired to a single LIF cell via exponential dynamic synapses. The code for doing this is as follows:
222+
223+
```python
224+
from jax import numpy as jnp, random, jit
225+
from ngcsimlib.context import Context
226+
import numpy as np
227+
np.random.seed(42)
228+
from ngclearn.components import ExponentialSynapse, PoissonCell, LIFCell
229+
from ngclearn.operations import summation
230+
231+
from ngcsimlib.compilers.process import Process
232+
from ngcsimlib.context import Context
233+
import ngclearn.utils.weight_distribution as dist
234+
235+
## create seeding keys
236+
dkey = random.PRNGKey(1234)
237+
dkey, *subkeys = random.split(dkey, 6)
238+
239+
## simulation properties
240+
dt = 0.1 # ms
241+
T = 1000. # ms ## total duration time
242+
243+
## post-syn LIF cell properties
244+
tau_m = 10.
245+
g_L = 10.
246+
v_rest = -75.
247+
v_thr = -55.
248+
249+
## excitatory group properties
250+
exc_freq = 10. # Hz
251+
n_exc = 80
252+
g_e_bar = 2.4
253+
tau_syn_exc = 2.
254+
E_rest_exc = 0.
255+
256+
## inhibitory group properties
257+
inh_freq = 10. # Hz
258+
n_inh = 20
259+
g_i_bar = 2.4
260+
tau_syn_inh = 5.
261+
E_rest_inh = -80.
262+
263+
Tsteps = int(T/dt)
264+
265+
## ---- build a simple E-I spiking circuit ----
266+
with Context("ei_snn") as ctx:
267+
pre_exc = PoissonCell("pre_exc", n_units=n_exc, target_freq=exc_freq, key=subkeys[0]) ## pre-syn excitatory group
268+
pre_inh = PoissonCell("pre_inh", n_units=n_inh, target_freq=inh_freq, key=subkeys[1]) ## pre-syn inhibitory group
269+
Wexc = ExponentialSynapse( ## dynamic synapse between excitatory group and LIF
270+
name="Wexc", shape=(n_exc,1), tau_syn=tau_syn_exc, g_syn_bar=g_e_bar, syn_rest=E_rest_exc, resist_scale=1./g_L,
271+
weight_init=dist.constant(value=1.), key=subkeys[2]
272+
)
273+
Winh = ExponentialSynapse( ## dynamic synapse between inhibitory group and LIF
274+
name="Winh", shape=(n_inh, 1), tau_syn=tau_syn_inh, g_syn_bar=g_i_bar, syn_rest=E_rest_inh, resist_scale=1./g_L,
275+
weight_init=dist.constant(value=1.), key=subkeys[2]
276+
)
277+
post_exc = LIFCell( ## post-syn LIF cell
278+
"post_exc", n_units=1, tau_m=tau_m, resist_m=1., thr=v_thr, v_rest=v_rest, conduct_leak=1., v_reset=-75.,
279+
tau_theta=0., theta_plus=0., refract_time=2., key=subkeys[3]
280+
)
281+
282+
Wexc.inputs << pre_exc.outputs
283+
Winh.inputs << pre_inh.outputs
284+
Wexc.v << post_exc.v ## couple voltage to exc synapse
285+
Winh.v << post_exc.v ## couple voltage to inh synapse
286+
post_exc.j << summation(Wexc.i_syn, Winh.i_syn) ## sum together excitatory & inhibitory pressures
287+
288+
advance_process = (Process("advance_proc")
289+
>> pre_exc.advance_state
290+
>> pre_inh.advance_state
291+
>> Wexc.advance_state
292+
>> Winh.advance_state
293+
>> post_exc.advance_state)
294+
# ctx.wrap_and_add_command(advance_process.pure, name="run")
295+
ctx.wrap_and_add_command(jit(advance_process.pure), name="run")
296+
297+
reset_process = (Process("reset_proc")
298+
>> pre_exc.reset
299+
>> pre_inh.reset
300+
>> Wexc.reset
301+
>> Winh.reset
302+
>> post_exc.reset)
303+
ctx.wrap_and_add_command(jit(reset_process.pure), name="reset")
304+
```
305+
306+
### Examining the Simple Spiking Circuit's Behavior
307+
308+
To run the above spiking circuit, we then write the next block of code (making sure to track/store the resulting membrane potential and pulse values emitted by the post-synaptic LIF):
309+
310+
```python
311+
volts = []
312+
time_span = []
313+
spikes = []
314+
315+
ctx.reset()
316+
pre_exc.inputs.set(jnp.ones((1, n_exc)))
317+
pre_inh.inputs.set(jnp.ones((1, n_inh)))
318+
post_exc.v.set(post_exc.v.value * 0 - 65.) ## initial condition for LIF is -65 mV
319+
volts.append(post_exc.v.value)
320+
time_span.append(0.)
321+
Tsteps = int(T/dt)
322+
for t in range(1, Tsteps):
323+
ctx.run(t=t * dt, dt=dt)
324+
print(f"\r v {post_exc.v.value}", end="")
325+
volts.append(post_exc.v.value)
326+
spikes.append(post_exc.s.value)
327+
time_span.append(t) #* dt)
328+
print()
329+
volts = jnp.squeeze(jnp.concatenate(volts, axis=1))
330+
spikes = jnp.squeeze(jnp.concatenate(spikes, axis=1))
331+
```
332+
333+
from which we may then write the following plotting code to visualize the post-synaptic LIF unit's membrane potential time-course along with any spikes it might have produced in response to the pre-synaptic spike trains:
334+
335+
```python
336+
import matplotlib
337+
matplotlib.use('Agg')
338+
import matplotlib.pyplot as plt
339+
cmap = plt.cm.jet
340+
341+
fig, ax = plt.subplots()
342+
343+
volt_vals = ax.plot(time_span, volts, '-.', color='tab:red')
344+
stat = jnp.where(spikes > 0.)
345+
indx = (stat[0] * 1. - 1.).tolist()
346+
v_thr_below = -0.75
347+
v_thr_above = 2.
348+
spk = ax.vlines(x=indx, ymin=v_thr-v_thr_below, ymax=v_thr+v_thr_above, colors='black', ls='-', lw=2)
349+
_v_thr = v_thr
350+
ax.hlines(y=_v_thr, xmin=0., xmax=time_span[-1], colors='blue', ls='-.', lw=2)
351+
352+
ax.set(xlabel='Time (ms)', ylabel='Voltage',
353+
title='Exponential Synapse LIF')
354+
ax.grid()
355+
fig.savefig("ei_circuit_dynamics.jpg")
356+
```
198357

358+
which should produce a figure depicting dynamics similar to the one below. Black tick
359+
marks indicate post-synaptic pulses whereas the horizontal dashed blue shows the LIF unit's
360+
voltage threshold.
361+
362+
363+
```{eval-rst}
364+
.. table::
365+
:align: center
366+
367+
+----------------------------------------------------------------------+
368+
| .. image:: ../docs/images/tutorials/neurocog/ei_circuit_dynamics.jpg |
369+
| :width: 100px |
370+
| :align: center |
371+
+----------------------------------------------------------------------+
372+
```
199373

0 commit comments

Comments
 (0)