added firing rate to regimes

This commit is contained in:
Jan Benda 2026-01-29 12:03:32 +01:00
parent 195f419a90
commit b211b42c3f
7 changed files with 142 additions and 78 deletions

View File

@ -501,7 +501,7 @@ When stimulating with both foreign signals simultaneously, additional peaks appe
\begin{figure*}[p]
\includegraphics[width=\columnwidth]{regimes}
\caption{\label{fig:regimes} Linear and nonlinear responses of a model P-unit in a three-fish setting in dependence on stimulus amplitudes. The model P-unit (identifier ``2018-05-08-ad'') was stimulated with two sine waves of equal amplitude (contrast) at difference frequencies $\Delta f_1=40$\,Hz and $\Delta f_2=228$\,Hz relative the receiver's EOD frequency. $\Delta f_2$ was set to match the baseline firing rate $r$ of the P-unit. \figitem{A--D} Top: the stimulus, an amplitude modulation of the receiver's EOD resulting from the stimulation with the two sine waves. The contrasts of both beats increase from \panel{A} to \panel{D} as indicated. Middle: Spike raster of the model P-unit response. Bottom: power spectrum of the firing rate estimated from the spike raster. \figitem{A} At low stimulus contrasts the response is linear. The only peaks in the response spectrum are at the two stimulating beat frequencies (green and purple marker), the latter enhancing the peak at baseline firig rate (blue). The largest peak, however, is always the one atthe EOD frequency of the receiver (black), reflecting the locking of P-unit spikes to the own EOD. The peak at $f_{EOD}$ is also flanked by harmonics of $\Delta f_1$ (small black markers). \figitem{B} At moderately higher stimulus contrast, the peaks in the response spectrum at the two beat frequencies become larger. In addition, a peak at $f_{EOD} - \Delta f_2 $ appears. \figitem{C} At intermediate stimulus contrasts, nonlinear responses start to appear at the sum and the difference of the stimulus frequencies (orange and red). Additional peaks appear at harmonics of $\Delta f_1$ (small green markers) and at $f_{EOD} \Delta f_2 \pm \Delta f_1$ (small purple marker). \figitem{D} At higher stimulus contrasts further peaks appear in the power spectrum at more complex combinations of $f_{EOD}$, $\Delta f_1$, and $\Delta f_2$ as indicated ($k$ is an integer). \figitem{E} Amplitude of the linear (at $\Delta f_1$ and $\Delta f_2$) and nonlinear (at $\Delta f_2 - \Delta f_1$ and $\Delta f_1 + \Delta f_2$) responses of the model P-unit as a function of beat contrast (thick lines). Thin lines indicate the initial linear and quadratic dependence on stimulus amplitude for the linear and nonlinear responses, respectively. In the linear regime, below a stimulus contrast of about 1.2\,\% (left vertical line), the only peaks in the response spectrum are at the stimulus frequencies. In the weakly nonlinear regime up to a contrast of about 3.5\,\% peaks arise at the sum and the difference of the two stimulus frequencies. At stronger stimulation the amplitudes of these nonlinear responses deviate from the quadratic dependency on stimulus contrast.}
\caption{\label{fig:regimes} Linear and nonlinear responses of a model P-unit in a three-fish setting in dependence on stimulus amplitudes. The model P-unit (identifier ``2018-05-08-ad'') was stimulated with two sine waves of equal amplitude (contrast) at difference frequencies $\Delta f_1=40$\,Hz and $\Delta f_2=228$\,Hz relative the receiver's EOD frequency. $\Delta f_2$ was set to match the baseline firing rate $r$ of the P-unit. \figitem{A--D} Top: the stimulus, an amplitude modulation of the receiver's EOD resulting from the stimulation with the two sine waves. The contrasts of both beats increase from \panel{A} to \panel{D} as indicated. Middle: Spike raster of the model P-unit response. Bottom: power spectrum of the firing rate estimated from the spike raster. \figitem{A} At low stimulus contrasts the response is linear. The only peaks in the response spectrum are at the two stimulating beat frequencies (green and purple circles), the latter enhancing the peak at baseline firig rate (blue). The largest peak, however, is always the one at the EOD frequency of the receiver (black), reflecting the locking of P-unit spikes to the own EOD. The peak at $f_{EOD}$ is also flanked by harmonics of $\Delta f_1$ (gray triangles). \figitem{B} At moderately higher stimulus contrast, the peaks in the response spectrum at the two beat frequencies become larger. In addition, a peak at $f_{EOD} - \Delta f_2 $ appears (blue diamond). \figitem{C} At intermediate stimulus contrasts, nonlinear responses start to appear at the sum and the difference of the stimulus frequencies (orange and red). Additional peaks appear at harmonics of $\Delta f_1$ (small green circles) and at $f_{EOD} - \Delta f_2 \pm \Delta f_1$ (purple squares). \figitem{D} At higher stimulus contrasts further peaks appear in the power spectrum. Most of them are interactions of $f_{EOD}$ and $\Delta f_2$ with harmonics of $\Delta f_1$ ($k$ is an integer). \figitem{E} Amplitude of the linear (at $\Delta f_1$ and $\Delta f_2$) and nonlinear (at $\Delta f_2 - \Delta f_1$ and $\Delta f_1 + \Delta f_2$) responses of the model P-unit as a function of beat contrast (thick lines). Thin lines indicate the initial linear and quadratic dependence on stimulus amplitude for the linear and nonlinear responses, respectively. In the linear regime, below a stimulus contrast of about 1.2\,\% (left vertical line), the only peaks in the response spectrum are at the stimulus frequencies. In the weakly nonlinear regime up to a contrast of about 3.5\,\% peaks arise at the sum and the difference of the two stimulus frequencies. At stronger stimulation the amplitudes of these nonlinear responses deviate from the quadratic dependency on stimulus contrast.}
\end{figure*}
The stimuli used in \figref{fig:motivation} had the same not-small amplitude. Whether this stimulus condition falls into the weakly nonlinear regime as in \citet{Voronenko2017} is not clear. In order to illustrate how the responses to two beat frequencies develop over a range of amplitudes we use a stochastic leaky-integrate-and-fire (LIF) based P-unit model fitted to a specific electrophysiologically measured cell \citep{Barayeu2023}.

View File

@ -1,16 +1,38 @@
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from scipy.stats import linregress
from numba import jit
from spectral import rate
from plotstyle import plot_style, lighter, darker
model_cell = '2018-05-08-ad-invivo-1' # 228Hz, CV=0.67
alphas = [0.002, 0.01, 0.03, 0.06]
rmax = 500
amax = 60
cthresh1 = 1.2
cthresh2 = 3.5
model_cell = '2018-05-08-ab-invivo-1' # 116, CV=0.68
alphas = [0.002, 0.008, 0.025, 0.05]
rmax = 400
amax = 50
cthresh1 = 1.2
cthresh2 = 3.0
data_path = Path('data')
sims_path = data_path / 'simulations'
trials = 1000
spec_trials = 100 # set to zero to only recompute firng rates
sigma = 0.002
nfft = 2**18
recompute = False
def load_data(file_path):
data = np.load(file_path)
@ -127,13 +149,13 @@ def punit_spikes(parameter, alpha, beatf1, beatf2, tmax, trials):
return spikes
def plot_am(ax, s, alpha, beatf1, beatf2, tmax):
time = np.arange(0, tmax, 0.0001)
def plot_am(ax, s, alpha, beatf1, beatf2, tmin, tmax):
time = np.arange(tmin, tmax, 0.0001)
am = alpha*np.sin(2*np.pi*beatf1*time)
am += alpha*np.sin(2*np.pi*beatf2*time)
ax.show_spines('l')
ax.plot(1000*time, -100*am, **s.lsAM)
ax.set_xlim(0, 1000*tmax)
ax.plot(1000*(time - tmin), -100*am, **s.lsAM)
ax.set_xlim(0, 1000*(tmax - tmin))
ax.set_ylim(-13, 13)
ax.set_yticks_delta(10)
#ax.set_xlabel('Time', 'ms')
@ -142,22 +164,47 @@ def plot_am(ax, s, alpha, beatf1, beatf2, tmax):
transform=ax.transAxes, ha='right')
def plot_raster(ax, s, spikes, tmax):
spikes_ms = [1000*s[s<tmax] for s in spikes[:16]]
def plot_raster(ax, s, spikes, tmin, tmax):
spikes_ms = [1000*(s[(s >= tmin) & (s <= tmax)] - tmin)
for s in spikes[:16]]
ax.show_spines('')
ax.eventplot(spikes_ms, linelengths=0.9, **s.lsRaster)
ax.set_xlim(0, 1000*tmax)
ax.set_xlim(0, 1000*(tmax - tmin))
#ax.set_xlabel('Time', 'ms')
#ax.set_ylabel('Trials')
def plot_rate(ax, s, path, spikes, tmin, tmax, sigma=0.002):
if recompute or not path.is_file():
print(' compute firing rate')
time = np.arange(0, tmin + tmax, sigma/4)
r, rsd = rate(time, spikes, sigma)
np.savez(path, time=time, rate=r, ratesd=rsd,
sigma=sigma, trials=len(spikes))
else:
print(f' load firing rate from {path}')
data = np.load(path)
time = data['time']
r = data['rate']
rsd = data['ratesd']
mask = (time >= tmin) &(time <= tmax)
time = time[mask] - tmin
r = r[mask]
ax.show_spines('l')
ax.plot(1000*time, r, clip_on=False, **s.lsRate)
ax.set_xlim(0, 1000*(tmax - tmin))
ax.set_ylim(0, rmax)
ax.set_ylabel('Rate', 'Hz')
ax.set_yticks_delta(200)
def compute_power(path, contrast, spikes, nfft, dt):
if not path.exists():
print(f'Compute power spectrum for contrast = {100*contrast:4.1f}%')
def compute_power(path, spikes, nfft, dt):
if spec_trials > 0 and (recompute or not path.is_file()):
print(' compute power spectrum')
psds = []
time = np.arange(nfft + 1)*dt
tmax = nfft*dt
for s in spikes:
for s in spikes[:spec_trials]:
b, _ = np.histogram(s, time)
b = b / dt
fourier = np.fft.rfft(b - np.mean(b))
@ -165,9 +212,9 @@ def compute_power(path, contrast, spikes, nfft, dt):
freqs = np.fft.rfftfreq(nfft, dt)
prr = np.mean(psds, 0)*dt/nfft
np.savez(path, nfft=nfft, deltat=dt, nsegs=len(spikes),
freqs=freqs, prr=prr)
freqs=freqs, prr=prr, trials=len(spikes))
else:
print(f'Load power spectrum for contrast = {100*contrast:4.1f}%')
print(f' load power spectrum from {path}')
data = np.load(path)
freqs = data['freqs']
prr = data['prr']
@ -175,10 +222,12 @@ def compute_power(path, contrast, spikes, nfft, dt):
def decibel(x):
return 10*np.log10(x/1e8)
return 10*np.log10(x/1e8 + 1e-12)
def peak_ampl(freqs, psd, f, df=2):
if f < 0:
f = 5
psd_snippet = psd[(freqs > f - df) & (freqs < f + df)]
return np.max(psd_snippet)
@ -186,67 +235,77 @@ def peak_ampl(freqs, psd, f, df=2):
def plot_psd(ax, s, path, contrast, spikes, nfft, dt, beatf1, beatf2, eodf):
offs = 5
offsm = 3
freqs, psd = compute_power(path, contrast, spikes, nfft, dt)
freqs, psd = compute_power(path, spikes, nfft, dt)
psd /= freqs[1]
ax.plot(freqs, decibel(psd), **s.lsPower)
# mark frequencies:
ax.plot(eodf, decibel(peak_ampl(freqs, psd, eodf)) + offs,
label=r'$f_{EOD}$', clip_on=False, **s.psFEOD)
label=r'$f_{EOD}$', clip_on=False, zorder=50, **s.psFEOD)
ax.plot(beatf2, decibel(peak_ampl(freqs, psd, beatf2)) + offs,
label=r'$r$', clip_on=False, **s.psF0)
label=r'$r$', clip_on=False, zorder=50, **s.psF0)
ax.plot(beatf1, decibel(peak_ampl(freqs, psd, beatf1)) + offs,
label=r'$\Delta f_1$', clip_on=False, **s.psF01)
label=r'$\Delta f_1$', clip_on=False, zorder=50, **s.psF01)
ax.plot(beatf2, decibel(peak_ampl(freqs, psd, beatf2)) + 2*offs + 2,
label=r'$\Delta f_2$', clip_on=False, **s.psF02)
label=r'$\Delta f_2$', clip_on=False, zorder=50, **s.psF02)
ax.plot(beatf2 - beatf1, decibel(peak_ampl(freqs, psd, beatf2 - beatf1)) + offs,
label=r'$\Delta f_2 - \Delta f_1$', clip_on=False, **s.psF01_2)
label=r'$\Delta f_2 - \Delta f_1$', clip_on=False, zorder=50, **s.psF01_2)
ax.plot(beatf1 + beatf2, decibel(peak_ampl(freqs, psd, beatf1 + beatf2)) + offs,
label=r'$\Delta f_1 + \Delta f_2$', clip_on=False, **s.psF012)
label=r'$\Delta f_1 + \Delta f_2$', clip_on=False, zorder=50, **s.psF012)
ax.plot(eodf + beatf1, decibel(peak_ampl(freqs, psd, eodf + beatf1)) + offsm,
label=r'$f_{EOD} \pm k \Delta f_1$', **s.psFEODm)
label=r'$f_{EOD} \pm k \Delta f_1$', zorder=40, **s.psFEODm)
ax.plot(eodf - beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf1)) + offsm, **s.psFEODm)
if contrast > 0.008:
if contrast >= alphas[1]:
ax.plot(eodf - beatf2, decibel(peak_ampl(freqs, psd, eodf - beatf2)) + offsm,
label=r'$f_{EOD} - k \Delta f_2$', **s.psF0m)
if contrast > 0.02:
label=r'$f_{EOD} - k \Delta f_2$', zorder=40, **s.psF0m)
if contrast >= alphas[2]:
ax.plot(2*beatf1, decibel(peak_ampl(freqs, psd, 2*beatf1)) + offsm,
label=r'$k\Delta f_1$', **s.psF01m)
ax.plot(eodf + 2*beatf1, decibel(peak_ampl(freqs, psd, eodf + 2*beatf1)) + offsm, **s.psFEODm)
label=r'$k\Delta f_1$', zorder=40, **s.psF01m)
ax.plot(eodf + 2*beatf1, decibel(peak_ampl(freqs, psd, eodf + 2*beatf1)) + offsm, zorder=40, **s.psFEODm)
ax.plot(eodf - beatf2 + beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf2 + beatf1)) + offsm,
label=r'$f_{EOD} - \Delta f_2 \pm k\Delta f_1$', **s.psF02m)
ax.plot(eodf - beatf2 - beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf2 - beatf1)) + offsm, **s.psF02m)
if contrast > 0.05:
label=r'$f_{EOD} - \Delta f_2 \pm k\Delta f_1$', zorder=40, **s.psF02m)
ax.plot(eodf - beatf2 - beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf2 - beatf1)) + offsm, zorder=40, **s.psF02m)
if contrast >= alphas[3]:
ax.plot(beatf2 + 2*beatf1, decibel(peak_ampl(freqs, psd, beatf2 + 2*beatf1)) + offsm,
label=r'$\Delta f_2 \pm k\Delta f_1$', **s.psF012m)
ax.plot(beatf2 + 3*beatf1, decibel(peak_ampl(freqs, psd, beatf2 + 3*beatf1)) + offsm, **s.psF012m)
ax.plot(beatf2 - 2*beatf1, decibel(peak_ampl(freqs, psd, beatf2 - 2*beatf1)) + offsm, **s.psF012m)
ax.plot(beatf2 - 3*beatf1, decibel(peak_ampl(freqs, psd, beatf2 - 3*beatf1)) + offsm, **s.psF012m)
ax.plot(3*beatf1, decibel(peak_ampl(freqs, psd, 3*beatf1)) + offsm, **s.psF01m)
ax.plot(4*beatf1, decibel(peak_ampl(freqs, psd, 4*beatf1)) + offsm, **s.psF01m)
ax.plot(eodf - 2*beatf1, decibel(peak_ampl(freqs, psd, eodf - 2*beatf1)) + offsm, **s.psFEODm)
ax.plot(eodf - 3*beatf1, decibel(peak_ampl(freqs, psd, eodf - 3*beatf1)) + offsm, **s.psFEODm)
ax.plot(eodf - 4*beatf1, decibel(peak_ampl(freqs, psd, eodf - 4*beatf1)) + offsm, **s.psFEODm)
ax.plot(eodf - 2*beatf2, decibel(peak_ampl(freqs, psd, eodf - 2*beatf2)) + offsm, **s.psF0m)
label=r'$\Delta f_2 \pm k\Delta f_1$', zorder=40, **s.psF012m)
ax.plot(beatf2 + 3*beatf1, decibel(peak_ampl(freqs, psd, beatf2 + 3*beatf1)) + offsm, zorder=40, **s.psF012m)
ax.plot(beatf2 - 2*beatf1, decibel(peak_ampl(freqs, psd, beatf2 - 2*beatf1)) + offsm, zorder=40, **s.psF012m)
ax.plot(beatf2 - 3*beatf1, decibel(peak_ampl(freqs, psd, beatf2 - 3*beatf1)) + offsm, zorder=40, **s.psF012m)
ax.plot(3*beatf1, decibel(peak_ampl(freqs, psd, 3*beatf1)) + offsm, zorder=40, **s.psF01m)
ax.plot(4*beatf1, decibel(peak_ampl(freqs, psd, 4*beatf1)) + offsm, zorder=40, **s.psF01m)
ax.plot(eodf - 2*beatf1, decibel(peak_ampl(freqs, psd, eodf - 2*beatf1)) + offsm, zorder=40, **s.psFEODm)
ax.plot(eodf - 3*beatf1, decibel(peak_ampl(freqs, psd, eodf - 3*beatf1)) + offsm, zorder=40, **s.psFEODm)
ax.plot(eodf - 4*beatf1, decibel(peak_ampl(freqs, psd, eodf - 4*beatf1)) + offsm, zorder=40, **s.psFEODm)
ax.plot(eodf - 2*beatf2, decibel(peak_ampl(freqs, psd, eodf - 2*beatf2)) + offsm, zorder=40, **s.psF0m)
ax.plot(eodf - beatf2 + 2*beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf2 + 2*beatf1)) + offsm,
**s.psF02m)
zorder=40, **s.psF02m)
ax.plot(eodf - beatf2 + 3*beatf1, decibel(peak_ampl(freqs, psd, eodf - beatf2 + 2*beatf1)) + offsm,
**s.psF02m)
zorder=40, **s.psF02m)
ax.set_xlim(0, 750)
ax.set_ylim(-60, 0)
ax.set_xticks_delta(200)
ax.set_yticks_delta(20)
ax.set_xlabel('Frequency', 'Hz')
ax.set_ylabel('Power [dB]')
def plot_example(axs, axr, axp, s, path, cell, alpha, beatf1, beatf2, eodf,
def plot_example(axs, axr, axf, axp, s, path, cell, alpha, beatf1, beatf2, eodf,
nfft, trials):
spec_path = path.with_name(path.stem + f'-contrastspectrum-{1000*alpha:03.0f}.npz')
rate_path = path.with_name(path.stem + f'-contrastrates-{1000*alpha:03.0f}.npz')
dt = 0.0001
tmax = nfft*dt
t1 = 0.1
t0 = 0.112
t1 = 0.212
if not recompute and spec_path.is_file() and rate_path.is_file():
tmax = t0 + t1
trials = 20
else:
print(' compute spike response')
spikes = punit_spikes(cell, alpha, beatf1, beatf2, tmax, trials)
plot_am(axs, s, alpha, beatf1, beatf2, t1)
plot_raster(axr, s, spikes, t1)
plot_psd(axp, s, path, alpha, spikes, nfft, dt, beatf1, beatf2, eodf)
plot_am(axs, s, alpha, beatf1, beatf2, t0, t1)
plot_raster(axr, s, spikes, t0, t1)
plot_rate(axf, s, rate_path, spikes, t0, t1, sigma)
plot_psd(axp, s, spec_path, alpha, spikes, nfft, dt, beatf1, beatf2, eodf)
def amplitude(power):
@ -279,13 +338,13 @@ def plot_peaks(ax, s, alphas, contrasts, powerf1, powerf2, powerfsum,
powerfdiff):
cmax = 10
contrasts *= 100
ax.plot(contrasts, amplitude_linearfit(contrasts, powerf1, 4),
ax.plot(contrasts, amplitude_linearfit(contrasts, powerf1, cthresh1),
**s.lsF01m)
ax.plot(contrasts, amplitude_linearfit(contrasts, powerf2, 2),
ax.plot(contrasts, amplitude_linearfit(contrasts, powerf2, cthresh1),
**s.lsF02m)
ax.plot(contrasts, amplitude_squarefit(contrasts, powerfsum, 4),
ax.plot(contrasts, amplitude_squarefit(contrasts, powerfsum, cthresh2),
**s.lsF012m)
ax.plot(contrasts, amplitude_squarefit(contrasts, powerfdiff, 4),
ax.plot(contrasts, amplitude_squarefit(contrasts, powerfdiff, cthresh2),
**s.lsF01_2m)
ax.plot(contrasts, amplitude(powerf1), **s.lsF01)
ax.plot(contrasts, amplitude(powerf2), **s.lsF02)
@ -294,27 +353,28 @@ def plot_peaks(ax, s, alphas, contrasts, powerf1, powerf2, powerfsum,
clip_on=False, **s.lsF012)
ax.plot(contrasts[mask], amplitude(powerfdiff)[mask],
clip_on=False, **s.lsF01_2)
ymax = 60
for alpha, tag in zip(alphas, ['A', 'B', 'C', 'D']):
ax.plot(100*alpha, ymax*0.95, 'vk', ms=4, clip_on=False)
ax.text(100*alpha, ymax, tag, ha='center')
ax.plot(100*alpha, 1.05*amax, 'vk', ms=4, clip_on=False)
ax.text(100*alpha, 1.1*amax, tag, ha='center')
#ax.axvline(contrast, **s.lsGrid)
#ax.text(contrast, 630, tag, ha='center')
cthresh1 = 1.2
cthresh2 = 3.5
print(f'Linear regime ends at a contrast of about {cthresh1:4.1f}%')
print(f'Weakly non-linear regime ends at a contrast of about {cthresh2:4.1f}%')
ax.axvline(cthresh1, **s.lsLine)
ax.axvline(cthresh2, **s.lsLine)
yoffs = 35
yoffs = 35 if amax == 60 else 31
ax.text(cthresh1/2, yoffs, 'linear\nregime',
ha='center', va='center')
ax.text((cthresh1 + cthresh2)/2, yoffs, 'weakly\nnonlinear\nregime',
ha='center', va='center')
ax.text(5.5, yoffs, 'strongly\nnonlinear\nregime',
ha='center', va='center')
if amax == 60:
ax.text(5.5, yoffs, 'strongly\nnonlinear\nregime',
ha='center', va='center')
else:
ax.text(5.5, 6, 'strongly\nnonlinear\nregime',
ha='center', va='bottom')
ax.set_xlim(0, cmax)
ax.set_ylim(0, ymax)
ax.set_ylim(0, amax)
ax.set_xticks_delta(2)
ax.set_yticks_delta(20)
ax.set_xlabel('Contrast', r'\%')
@ -325,12 +385,10 @@ if __name__ == '__main__':
ratebase, cvbase, beatf1, beatf2, \
contrasts, powerf1, powerf2, powerfsum, powerfdiff = \
load_data(sims_path / f'{model_cell}-contrastpeaks.npz')
alphas = [0.002, 0.01, 0.03, 0.06]
parameters = load_models(data_path / 'punitmodels.csv')
cell = cell_parameters(parameters, model_cell)
eodf = cell['EODf']
nfft = 2**18
print(f'Loaded data for cell {model_cell}: ')
print(f' baseline rate = {ratebase:.0f}Hz, CV = {cvbase:.2f}')
@ -338,23 +396,26 @@ if __name__ == '__main__':
print()
s = plot_style()
fig, (axes, axa) = plt.subplots(2, 1, height_ratios=[4, 3],
cmsize=(s.plot_width, 0.65*s.plot_width))
fig, (axes, axa) = plt.subplots(2, 1, height_ratios=[5, 3],
cmsize=(s.plot_width, 0.7*s.plot_width))
fig.subplots_adjust(leftm=8, rightm=2, topm=2, bottomm=3.5, hspace=0.6)
axe = axes.subplots(3, 4, wspace=0.4, hspace=0.2,
height_ratios=[1, 2, 0.6, 3])
axe = axes.subplots(4, 4, wspace=0.4, hspace=0.2,
height_ratios=[1, 2, 2, 0.6, 3])
fig.show_spines('lb')
# example power spectra:
for c, alpha in enumerate(alphas):
path = sims_path / f'{model_cell}-contrastspectrum-{1000*alpha:03.0f}.npz'
plot_example(axe[0, c], axe[1, c], axe[2, c], s, path,
cell, alpha, beatf1, beatf2, eodf, nfft, 100)
axe[1, 0].xscalebar(1, -0.1, 20, 'ms', ha='right')
axe[2, 3].legend(loc='center right', bbox_to_anchor=(1.05, -0.8),
path = sims_path / f'{model_cell}'
print(f'Example response for contrast {100*alpha:4.1f}%:')
plot_example(axe[0, c], axe[1, c], axe[2, c], axe[3, c], s, path,
cell, alpha, beatf1, beatf2, eodf, nfft, trials)
print()
axe[2, 0].xscalebar(1, -0.1, 20, 'ms', ha='right')
axe[3, 3].legend(loc='center right', bbox_to_anchor=(1.05, -0.9),
ncol=11, columnspacing=0.6, handletextpad=0)
fig.common_yspines(axe[0, :])
fig.common_yticks(axe[2, :])
fig.common_yticks(axe[3, :])
fig.tag(axe[0, :], xoffs=-3, yoffs=1.6)
# contrast dependence:

View File

@ -12,6 +12,8 @@ cell = '2021-08-03-ac-invivo-1'
data_path = Path('data')
sigma = 0.002
def load_spikes(cell_path, f1=797, f2=631):
load = False
@ -205,14 +207,15 @@ def plot_stimulus(ax, s, tmax, eodf, freqs, c=0.1):
ams = {}
f1, f2 = freqs
label = '$f_{EOD}$'
dp = np.pi
if f1 is not None:
eod += c*np.cos(2*np.pi*(eodf + f1)*time)
am += c*np.cos(2*np.pi*f1*time)
eod += c*np.cos(2*np.pi*(eodf + f1)*time + dp)
am += c*np.cos(2*np.pi*f1*time + dp)
ams = s.lsF02
label += r' \& $f_1$'
if f2 is not None:
eod += c*np.cos(2*np.pi*(eodf + f2)*time)
am += c*np.cos(2*np.pi*f2*time)
eod += c*np.cos(2*np.pi*(eodf + f2)*time + dp)
am += c*np.cos(2*np.pi*f2*time + dp)
ams = s.lsF01
label += r' \& $f_2$'
if f1 is not None and f2 is not None:
@ -234,7 +237,7 @@ def plot_raster(ax, s, spikes, tmin, tmax):
def plot_rate(ax, s, spikes, tmin, tmax, sigma=0.002):
time = np.arange(0, tmin + tmax, 0.001)
time = np.arange(0, tmin + tmax, sigma/4)
r, rsd = rate(time, spikes, sigma)
mask = (time >= tmin) & (time <= tmax)
time = time[mask] - tmin
@ -311,7 +314,7 @@ if __name__ == '__main__':
plot_psd(axs[4, i], s, freqs, power, fmax)
sub_spikes = align_spikes(sub_spikes, stim_freqs[i], stim_phases[i])
plot_raster(axs[2, i], s, sub_spikes, tmin, tmax)
plot_rate(axs[3, i], s, sub_spikes, tmin, tmax)
plot_rate(axs[3, i], s, sub_spikes, tmin, tmax, sigma)
mark_freq(axs[4, 0], freqs, powers[0], base_rate, f'$r={base_rate:.0f}$\\,Hz', s.psF0, 30)
mark_freq(axs[4, 1], freqs, powers[1], df2, f'$\\Delta f_1=f_1 - f_{{EOD}}={abs(df2):.0f}$\\,Hz', s.psF02)
mark_freq(axs[4, 1], freqs, powers[1], 2*df2, f'$2\\Delta f_1={abs(2*df2):.0f}$\\,Hz', s.psF02)