Source code for brainpy._src.measure.lfp
# -*- coding: utf-8 -*-
from jax import numpy as jnp
import brainpy._src.math as bm
__all__ = [
'unitary_LFP',
]
[docs]
def unitary_LFP(times, spikes, spike_type,
xmax=0.2, ymax=0.2, va=200., lambda_=0.2,
sig_i=2.1, sig_e=2.1 * 1.5, location='soma layer', seed=None):
"""A kernel-based method to calculate unitary local field potentials (uLFP)
from a network of spiking neurons [1]_.
.. note::
This method calculates LFP only from the neuronal spikes. It does not consider
the subthreshold synaptic events, or the dendritic voltage-dependent ion channels.
Examples
--------
If you have spike data of excitatory and inhibtiory neurons, you can get the LFP
by the following methods:
>>> import brainpy as bp
>>> n_time = 1000
>>> n_exc = 100
>>> n_inh = 25
>>> times = bm.arange(n_time) * 0.1
>>> exc_sps = bp.math.random.random((n_time, n_exc)) < 0.3
>>> inh_sps = bp.math.random.random((n_time, n_inh)) < 0.4
>>> lfp = bp.measure.unitary_LFP(times, exc_sps, 'exc')
>>> lfp += bp.measure.unitary_LFP(times, inh_sps, 'inh')
Parameters
----------
times: ndarray
The times of the recording points.
spikes: ndarray
The spikes of excitatory neurons recorded by brainpy monitors.
spike_type: str
The neuron type of the spike trains. It can be "exc" or "inh".
location: str
The location of the spikes recorded. It can be "soma layer", "deep layer",
"superficial layer" and "surface".
xmax: float
Size of the array (in mm).
ymax: float
Size of the array (in mm).
va: int, float
The axon velocity (mm/sec).
lambda_: float
The space constant (mm).
sig_i: float
The std-dev of inhibition (in ms)
sig_e: float
The std-dev for excitation (in ms).
seed: int
The random seed.
References
----------
.. [1] Telenczuk, Bartosz, Maria Telenczuk, and Alain Destexhe. "A kernel-based
method to calculate local field potentials from networks of spiking
neurons." Journal of Neuroscience Methods 344 (2020): 108871.
"""
times = bm.as_jax(times)
spikes = bm.as_jax(spikes)
if spike_type not in ['exc', 'inh']:
raise ValueError('"spike_type" should be "exc or ""inh". ')
if spikes.ndim != 2:
raise ValueError('"E_spikes" should be a matrix with shape of (num_time, num_neuron). '
f'But we got {spikes.shape}')
if times.shape[0] != spikes.shape[0]:
raise ValueError('times and spikes should be consistent at the firs axis. '
f'Bug we got {times.shape[0]} != {spikes.shape}.')
# Distributing cells in a 2D grid
rng = bm.random.RandomState(seed)
num_neuron = spikes.shape[1]
pos_xs, pos_ys = rng.rand(2, num_neuron).value * jnp.array([[xmax], [ymax]])
pos_xs, pos_ys = jnp.asarray(pos_xs), jnp.asarray(pos_ys)
# distance/coordinates
xe, ye = xmax / 2, ymax / 2 # coordinates of electrode
dist = jnp.sqrt((pos_xs - xe) ** 2 + (pos_ys - ye) ** 2) # distance to electrode in mm
# amplitude
if location == 'soma layer':
amp_e, amp_i = 0.48, 3. # exc/inh uLFP amplitude (soma layer)
elif location == 'deep layer':
amp_e, amp_i = -0.16, -0.2 # exc/inh uLFP amplitude (deep layer)
elif location == 'superficial layer':
amp_e, amp_i = 0.24, -1.2 # exc/inh uLFP amplitude (superficial layer)
elif location == 'surface layer':
amp_e, amp_i = -0.08, 0.3 # exc/inh uLFP amplitude (surface)
else:
raise NotImplementedError
A = jnp.exp(-dist / lambda_) * (amp_e if spike_type == 'exc' else amp_i)
# delay
delay = 10.4 + dist / va # delay to peak (in ms)
# LFP Calculation
iis, ids = jnp.where(spikes)
tts = times[iis] + delay[ids]
exc_amp = A[ids]
tau = (2 * sig_e * sig_e) if spike_type == 'exc' else (2 * sig_i * sig_i)
return bm.for_loop(lambda t: jnp.sum(exc_amp * jnp.exp(-(t - tts) ** 2 / tau)), times)