(Wang & Buzsáki, 1996) Gamma Oscillation
Here we show the implementation of gamma oscillation proposed by Xiao-Jing Wang and György Buzsáki (1996). They demonstrated that the GABA\(_A\) synaptic transmission provides a suitable mechanism for synchronized gamma oscillations in a network of fast-spiking interneurons.
Let’s first import brainpy and set profiles.
[1]:
import brainpy as bp
bp.integrators.set_default_odeint('rk4')
The network is constructed with Hodgkin–Huxley (HH) type neurons and GABA\(_A\) synapses.
The GABA\(_A\) synapse is coded as:
[2]:
class GABAa(bp.TwoEndConn):
target_backend = 'numpy'
def __init__(self, pre, post, conn, delay=0., g_max=0.1, E=-75.,
alpha=12., beta=0.1, T=1.0, T_duration=1.0, **kwargs):
super(GABAa, self).__init__(pre=pre, post=post, conn=conn, **kwargs)
# parameters
self.g_max = g_max
self.E = E
self.alpha = alpha
self.beta = beta
self.T = T
self.T_duration = T_duration
self.delay = delay
# connections
self.conn_mat = self.conn.requires('conn_mat')
self.size = bp.math.shape(self.conn_mat)
# variables
self.s = bp.math.Variable(bp.math.zeros(self.size))
self.t_last_pre_spike = bp.math.Variable(bp.math.ones(self.size) * -1e7)
self.g = self.register_constant_delay('g', size=self.size, delay=delay)
# function
self.integral = bp.odeint(self.derivative)
def derivative(self, s, t, TT):
return self.alpha * TT * (1 - s) - self.beta * s
def update(self, _t, _dt):
for i in range(self.pre.size[0]):
if self.pre.spike[i] > 0:
self.t_last_pre_spike[i] = _t
TT = ((_t - self.t_last_pre_spike) < self.T_duration) * self.T
self.s[:] = self.integral(self.s, _t, TT)
self.g.push(self.g_max * self.s)
g = self.g.pull()
self.post.input[:] -= bp.math.sum(g, axis=0) * (self.post.V - self.E)
The dynamics of the HH type neurons is given by:
where \(I(t)\) is the injected current, the leak current $ I_L = g_L (V - E_L) $, and the transient sodium current
where the activation variable \(m\) is assumed fast and substituted by its steady-state function \(m_{\infty} = \alpha_m / (\alpha_m + \beta_m)\). And the inactivation variable \(h\) obeys a first=order kinetics:
where the activation variable \(n\) also obeys a first=order kinetics:
[3]:
class HH(bp.NeuGroup):
def __init__(self, size, ENa=55., EK=-90., EL=-65, C=1.0,
gNa=35., gK=9., gL=0.1, V_th=20., phi=5.0, **kwargs):
super(HH, self).__init__(size=size, **kwargs)
# parameters
self.ENa = ENa
self.EK = EK
self.EL = EL
self.C = C
self.gNa = gNa
self.gK = gK
self.gL = gL
self.V_th = V_th
self.phi = phi
# variables
self.V = bp.math.Variable(bp.math.ones(size) * -65.)
self.h = bp.math.Variable(bp.math.ones(size) * 0.6)
self.n = bp.math.Variable(bp.math.ones(size) * 0.32)
self.spike = bp.math.Variable(bp.math.zeros(size, dtype=bool))
self.input = bp.math.Variable(bp.math.zeros(size))
# integral
self.integral = bp.odeint(self.derivative)
def derivative(self, V, h, n, t, Iext):
alpha = 0.07 * bp.math.exp(-(V + 58) / 20)
beta = 1 / (bp.math.exp(-0.1 * (V + 28)) + 1)
dhdt = alpha * (1 - h) - beta * h
alpha = -0.01 * (V + 34) / (bp.math.exp(-0.1 * (V + 34)) - 1)
beta = 0.125 * bp.math.exp(-(V + 44) / 80)
dndt = alpha * (1 - n) - beta * n
m_alpha = -0.1 * (V + 35) / (bp.math.exp(-0.1 * (V + 35)) - 1)
m_beta = 4 * bp.math.exp(-(V + 60) / 18)
m = m_alpha / (m_alpha + m_beta)
INa = self.gNa * m ** 3 * h * (V - self.ENa)
IK = self.gK * n ** 4 * (V - self.EK)
IL = self.gL * (V - self.EL)
dVdt = (- INa - IK - IL + Iext) / self.C
return dVdt, self.phi * dhdt, self.phi * dndt
def update(self, _t, _dt):
V, h, n = self.integral(self.V, self.h, self.n, _t, self.input)
self.spike[:] = bp.math.logical_and(self.V < self.V_th, V >= self.V_th)
self.V[:] = V
self.h[:] = h
self.n[:] = n
self.input[:] = 0.
Let’s run a simulation of a network with 100 neurons with constant inputs (1 \(\mu\)A/cm\(^2\)).
[4]:
num = 100
neu = HH(num, monitors=['spike', 'V'], name='X')
neu.V[:] = -70. + bp.math.random.normal(size=num) * 20
syn = GABAa(pre=neu, post=neu, conn=bp.connect.All2All(include_self=False))
syn.g_max = 0.1 / num
net = bp.math.jit(bp.Network(neu, syn))
net.run(duration=500., inputs=['X.input', 1.], report=0.1)
fig, gs = bp.visualize.get_figure(2, 1, 3, 8)
fig.add_subplot(gs[0, 0])
bp.visualize.line_plot(neu.mon.ts, neu.mon.V, ylabel='Membrane potential (N0)')
fig.add_subplot(gs[1, 0])
bp.visualize.raster_plot(neu.mon.ts, neu.mon.spike, show=True)
Compilation used 10.2059 s.
Start running ...
Run 10.0% used 0.151 s.
Run 20.0% used 0.257 s.
Run 30.0% used 0.343 s.
Run 40.0% used 0.434 s.
Run 50.0% used 0.525 s.
Run 60.0% used 0.615 s.
Run 70.0% used 0.712 s.
Run 80.0% used 0.803 s.
Run 90.0% used 0.898 s.
Run 100.0% used 0.992 s.
Simulation is done in 0.992 s.

We can see the result of this simulation that cells starting at random and asynchronous initial conditions quickly become synchronized and their spiking times are perfectly in-phase within 5-6 oscillatory cycles.
Reference:
Wang, Xiao-Jing, and György Buzsáki. “Gamma oscillation by synaptic inhibition in a hippocampal interneuronal network model.” Journal of neuroscience 16.20 (1996): 6402-6413.