|
| 1 | +# Dynamic Synapses and Conductance |
| 2 | + |
| 3 | +In this lesson, we will study dynamic synapses, or synaptic cable components in |
| 4 | +ngc-learn that evolve on fast time-scales in response to their pre-synaptic inputs. |
| 5 | +These types of chemical synapse components are useful for modeling time-varying |
| 6 | +conductance which ultimately drives eletrical current input into neuronal units |
| 7 | +(such as spiking cells). |
| 8 | +Here, we will learn how to build two important types of dynamic synapses in |
| 9 | +ngc-learn -- the exponential synapse and the alpha synapse -- and visualize |
| 10 | +the time-course of their resulting conductances. In addition, we will then |
| 11 | +construct and study a small neuronal circuit involving a leaky integrator that |
| 12 | +is driven by exponential synapses relaying pulses from an excitatory and an |
| 13 | +inhibitory population of Poisson input encoding cells. |
| 14 | + |
| 15 | +## Chemical Synapses |
| 16 | + |
| 17 | + |
| 18 | +Building a dynamic synapse can be done by importing |
| 19 | +[ExponentialSynapse](ngclearn.components.synapses.ExponentialSynapse) and |
| 20 | +[AlphaSynapse](ngclearn.components.synapses.AlphaSynapse) |
| 21 | +from ngc-learn's in-built components and setting them up within a model |
| 22 | +context for easy analysis. |
| 23 | +This can be done as follows (using the meta-parameters we provide in the |
| 24 | +code block below to ensure reasonable dynamics): |
| 25 | + |
| 26 | +```python |
| 27 | +from jax import numpy as jnp, random, jit |
| 28 | +from ngcsimlib.context import Context |
| 29 | +import numpy as np |
| 30 | +np.random.seed(42) |
| 31 | +from ngclearn.components import ExponentialSynapse, AlphaSynapse |
| 32 | +from ngclearn.operations import summation |
| 33 | + |
| 34 | +from ngcsimlib.compilers.process import Process |
| 35 | +from ngcsimlib.context import Context |
| 36 | +import ngclearn.utils.weight_distribution as dist |
| 37 | + |
| 38 | + |
| 39 | +dkey = random.PRNGKey(1234) ## creating seeding keys for synapses |
| 40 | +dkey, *subkeys = random.split(dkey, 6) |
| 41 | +dt = 0.1 # ms ## integration time constant |
| 42 | +T = 8. # ms ## total duration time |
| 43 | + |
| 44 | +Tsteps = int(T/dt) + 1 |
| 45 | + |
| 46 | +# ---- build a two-synapse system ---- |
| 47 | +with Context("dual_syn_system") as ctx: |
| 48 | + Wexp = ExponentialSynapse( ## exponential dynamic synapse |
| 49 | + name="Wexp", shape=(1, 1), tau_syn=3., g_syn_bar=1., syn_rest=0., resist_scale=1., |
| 50 | + weight_init=dist.constant(value=1.), key=subkeys[0] |
| 51 | + ) |
| 52 | + Walpha = AlphaSynapse( ## alpha dynamic synapse |
| 53 | + name="Walpha", shape=(1, 1), tau_syn=1., g_syn_bar=1., syn_rest=0., resist_scale=1., |
| 54 | + weight_init=dist.constant(value=1.), key=subkeys[0] |
| 55 | + ) |
| 56 | + |
| 57 | + ## set up basic simulation process calls |
| 58 | + advance_process = (Process("advance_proc") |
| 59 | + >> Wexp.advance_state |
| 60 | + >> Walpha.advance_state) |
| 61 | + ctx.wrap_and_add_command(jit(advance_process.pure), name="run") |
| 62 | + |
| 63 | + reset_process = (Process("reset_proc") |
| 64 | + >> Wexp.reset |
| 65 | + >> Walpha.reset) |
| 66 | + ctx.wrap_and_add_command(jit(reset_process.pure), name="reset") |
| 67 | +``` |
| 68 | + |
| 69 | +where we notice in the above we have instantiated two different kinds of chemical synapse components |
| 70 | +that we will run side-by-side in order to extract their produced conductance values in response to |
| 71 | +the exact same input stream. For both the exponential and the alpha synapse, there are at least three |
| 72 | +important hyper-parameters to configure: |
| 73 | +1. `tau_syn` ($\tau_{\text{syn}}$): the synaptic conductance decay time constant; |
| 74 | +2. `g_syn_bar` ($\bar{g}_{\text{syn}}$): the maximal conductance value produced by each pulse transmitted |
| 75 | + across this synapse; and, |
| 76 | +3. `syn_rest` ($E_{rest}$): the (post-synaptic) reversal potential for this synapse -- note that this value |
| 77 | + determines the direction of current flow through the synapse, yielding a synapse with an |
| 78 | + excitatory nature for non-negative values of `syn_rest` or a synapse with an inhibitory |
| 79 | + nature for negative values of `syn_rest`. |
| 80 | + |
| 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). |
| 83 | +For the exponential synapse, the dynamics adhere to the following ODE: |
| 84 | + |
| 85 | +$$ |
| 86 | +\frac{\partial g_{\text{syn}}(t)}{\partial t} = -g_{\text{syn}}(t)/\tau_{\text{syn}} + \bar{g}_{\text{syn}} \sum_{k} \delta(t - t_{k}) |
| 87 | +$$ |
| 88 | + |
| 89 | +where the conductance (for a post-synaptic unit) output of this synapse is driven by a sum over all of its incoming pre-synaptic spikes; this ODE means that pre-synaptic spikes are filtered via an expoential kernel (i.e., a low-pass filter). |
| 90 | +On the other hand, for the alpha synapse, the dynamics adhere to the following coupled set of ODEs: |
| 91 | + |
| 92 | +$$ |
| 93 | +\frac{\partial h_{\text{syn}}(t)}{\partial t} &= -h_{\text{syn}}(t)/\tau_{\text{syn}} + \bar{g}_{\text{syn}} \sum_{k} \delta(t - t_{k}) \\ |
| 94 | +\frac{\partial g_{\text{syn}}(t)}{\partial t} &= -g_{\text{syn}}(t)/\tau_{\text{syn}} + h_{\text{syn}}(t)/\tau_{\text{syn}} |
| 95 | +$$ |
| 96 | + |
| 97 | +where $h_{\text{syn}}(t)$ is an intermediate variable that operates in service of driving the conductance variable $g_{\text{syn}}(t)$ itself. |
| 98 | + |
| 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)$: |
| 100 | + |
| 101 | +$$ |
| 102 | +j_{\text{syn}}(t) = g_{\text{syn}}(t) (v(t) - E_{\text{rest}}) |
| 103 | +$$ |
| 104 | + |
| 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`). |
| 106 | + |
| 107 | + |
| 108 | +### Examining the Conductances of Dynamic Synapses |
| 109 | + |
| 110 | +We can track and visualize the conductance outputs of our two different dynamic synapses by running a stream of controlled pre-synaptic pulses. Specifically, we will observe the output behavior of each in response to a sparse stream, eight milliseconds in length, where only a single spike is emitted at one millisecond. |
| 111 | +To create the simulation of a single input pulse stream, you can write the following code: |
| 112 | + |
| 113 | +```python |
| 114 | +time_ticks = [] |
| 115 | +time_labs = [] |
| 116 | +for t in range(Tsteps): |
| 117 | + if t % 10 == 0: |
| 118 | + time_ticks.append(t) |
| 119 | + time_labs.append(f"{t * dt:.1f}") |
| 120 | + |
| 121 | +time_span = [] |
| 122 | +g = [] |
| 123 | +ga = [] |
| 124 | +ctx.reset() |
| 125 | +for t in range(Tsteps): |
| 126 | + s_t = jnp.zeros((1, 1)) |
| 127 | + if t * dt == 1.: ## pulse at 1 ms |
| 128 | + s_t = jnp.ones((1, 1)) |
| 129 | + Wexp.inputs.set(s_t) |
| 130 | + Walpha.inputs.set(s_t) |
| 131 | + Wexp.v.set(Wexp.v.value * 0) |
| 132 | + Walpha.v.set(Walpha.v.value * 0) |
| 133 | + ctx.run(t=t * dt, dt=dt) |
| 134 | + |
| 135 | + print(f"\r g = {Wexp.g_syn.value} ga = {Walpha.g_syn.value}", end="") |
| 136 | + g.append(Wexp.g_syn.value) |
| 137 | + ga.append(Walpha.g_syn.value) |
| 138 | + time_span.append(t) #* dt) |
| 139 | +print() |
| 140 | +g = jnp.squeeze(jnp.concatenate(g, axis=1)) |
| 141 | +g = g/jnp.amax(g) |
| 142 | +ga = jnp.squeeze(jnp.concatenate(ga, axis=1)) |
| 143 | +ga = ga/jnp.amax(ga) |
| 144 | +``` |
| 145 | + |
| 146 | +Note that we further normalize the conductance trajectories of both synapses to lie within |
| 147 | +the range of $[0, 1]$, primarily for visualization purposes. |
| 148 | +Finally, to visualize the conductance time-course of both synapses, you can write the |
| 149 | +following: |
| 150 | + |
| 151 | +```python |
| 152 | +import matplotlib #.pyplot as plt |
| 153 | +matplotlib.use('Agg') |
| 154 | +import matplotlib.pyplot as plt |
| 155 | +cmap = plt.cm.jet |
| 156 | + |
| 157 | +## ---- plot the exponential synapse conductance time-course ---- |
| 158 | +fig, ax = plt.subplots() |
| 159 | + |
| 160 | +gvals = ax.plot(time_span, g, '-', color='tab:red') |
| 161 | +#plt.xticks(time_span, time_labs) |
| 162 | +ax.set_xticks(time_ticks, time_labs) |
| 163 | +ax.set(xlabel='Time (ms)', ylabel='Conductance', |
| 164 | + title='Exponential Synapse Conductance Time-Course') |
| 165 | +ax.grid(which="major") |
| 166 | +fig.savefig("exp_syn.jpg") |
| 167 | +plt.close() |
| 168 | + |
| 169 | +## ---- plot the alpha synapse conductance time-course ---- |
| 170 | +fig, ax = plt.subplots() |
| 171 | + |
| 172 | +gvals = ax.plot(time_span, ga, '-', color='tab:blue') |
| 173 | +#plt.xticks(time_span, time_labs) |
| 174 | +ax.set_xticks(time_ticks, time_labs) |
| 175 | +ax.set(xlabel='Time (ms)', ylabel='Conductance', |
| 176 | + title='Alpha Synapse Conductance Time-Course') |
| 177 | +ax.grid(which="major") |
| 178 | +fig.savefig("alpha_syn.jpg") |
| 179 | +plt.close() |
| 180 | +``` |
| 181 | + |
| 182 | +which should produce and save two plots to disk. You can then compare and contrast the plots of the |
| 183 | +expoential and alpha synapse conductance trajectories: |
| 184 | + |
| 185 | +```{eval-rst} |
| 186 | +.. table:: |
| 187 | + :align: center |
| 188 | +
|
| 189 | + +---------------------------------------------------------+-----------------------------------------------------------+ |
| 190 | + | .. image:: ../docs/images/tutorials/neurocog/expsyn.png | .. image:: ../docs/images/tutorials/neurocog/alphasyn.png | |
| 191 | + | :width: 100px | :width: 100px | |
| 192 | + | :align: center | :align: center | |
| 193 | + +---------------------------------------------------------+-----------------------------------------------------------+ |
| 194 | +``` |
| 195 | + |
| 196 | +## Excitatory-Inhibitory Driven Dynamics |
| 197 | + |
| 198 | + |
| 199 | + |
0 commit comments