updated figure captions

This commit is contained in:
Jan Benda 2025-05-22 19:03:41 +02:00
parent ee2b8f98b7
commit 3e07684093
12 changed files with 142 additions and 95 deletions

View File

@ -52,15 +52,15 @@ def plot_response_spectrum(ax, s, eodf, rate, freqs, prr):
ax.show_spines('b')
mask = (freqs > 30) & (freqs < 890)
ax.plot(freqs[mask], power_db[mask], **s.lsC1)
ax.plot(freqs[eod_i], power_db[eod_i], **s.psFEOD)
ax.plot(freqs[rate_i], power_db[rate_i], **s.psF0)
ax.plot(freqs[eod_i], power_db[eod_i] + 2, **s.psFEOD)
ax.plot(freqs[rate_i], power_db[rate_i] + 2, **s.psF0)
ax.set_xlim(0, 900)
ax.set_ylim(-25, 5)
ax.set_xticks_delta(300)
ax.set_xlabel('$f$', 'Hz')
ax.text(freqs[eod_i], power_db[eod_i] + 2, '$f_{\\rm EOD}$',
ax.text(freqs[eod_i], power_db[eod_i] + 4, '$f_{\\rm EOD}$',
ha='center')
ax.text(freqs[rate_i], power_db[rate_i] + 2, '$r$',
ax.text(freqs[rate_i], power_db[rate_i] + 4, '$r$',
ha='center')
ax.yscalebar(1.05, 0, 10, 'dB', ha='right')
@ -131,7 +131,7 @@ def plot_diagonals(ax, s, fbase, contrast1, freqs1, chi21, contrast2, freqs2, ch
nlips.append(nlip)
nlifs.append(nlif)
print(f' SI at {100*contrast:.1f}% contrast: {nli:.2f}')
ax.axvline(fbase, **s.lsGrid)
#ax.axvline(fbase, **s.lsGrid)
ax.plot(diags[1][0], 1e-3*diags[1][1], **s.lsC2)
ax.plot(diags[0][0], 1e-3*diags[0][1], **s.lsC1)
ax.plot(nlifs[1], 1e-3*nlips[1], **s.psC2)
@ -148,7 +148,7 @@ def plot_diagonals(ax, s, fbase, contrast1, freqs1, chi21, contrast2, freqs2, ch
ax.text(nlifs[0] - 25, 1e-3*nlips[0], f'{100*contrast1:.0f}\\%',
ha='right')
ax.text(nlifs[0] + 35, 1e-3*nlips[0], f'SI={nlis[0]:.1f}')
ax.text(fbase, 1.75, '$r$', ha='center')
#ax.text(fbase, 1.75, '$r$', ha='center')
if __name__ == '__main__':
@ -181,6 +181,9 @@ if __name__ == '__main__':
*example_cell[0])
fcutoff2, contrast2, freqs2, gain2, chi22 = load_spectra(data_path,
*example_cell[1])
print(f' contrast1: {100*contrast1:4.1f}% contrast2: {100*contrast2:4.1f}%')
print(f' fcutoff1 : {fcutoff1:3.0f}Hz fcutoff2 : {fcutoff2:3.0f}Hz')
print(f' duration1: {time1[-1]:4.1f}s duration2: {time2[-1]:4.1f}s')
s = plot_style()
s.cell_color1 = s.ampul_color1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -86,8 +86,9 @@ def plot_corr(ax, data, xcol, ycol, zcol, zmin, zmax, xpdfmax, cmap, color,
def nli_stats(title, data, column, nli_thresh):
print(title)
print(f' nli threshold: {nli_thresh:.1f}')
nrecs = data.rows()
ncells = len(np.unique(data['cell']))
cells = np.unique(data['cell'])
ncells = len(cells)
nrecs = len(data)
print(f' cells: {ncells}')
print(f' recordings: {nrecs}')
hcells = np.unique(data[data(column) > nli_thresh, 'cell'])
@ -95,7 +96,11 @@ def nli_stats(title, data, column, nli_thresh):
print(f' high nli recordings: n={np.sum(data(column) > nli_thresh):3d}, '
f'{100*np.sum(data(column) > nli_thresh)/nrecs:4.1f}%')
nsegs = data['nsegs']
print(f' number of segments: {np.min(nsegs):4.0f} - {np.max(nsegs):4.0f}, median={np.median(nsegs):4.0f}, mean={np.mean(nsegs):4.0f}, std={np.std(nsegs):4.0f}')
print(f' number of segments: {np.min(nsegs):4.0f} - {np.max(nsegs):4.0f}, median={np.median(nsegs):4.0f}, mean={np.mean(nsegs):4.0f}, std={np.std(nsegs):4.0f}')
nrecs = []
for cell in cells:
nrecs.append(len(data[data["cell"] == cell, :]))
print(f' number of recordings per cell: {np.min(nrecs):4.0f} - {np.max(nrecs):4.0f}, median={np.median(nrecs):4.0f}, mean={np.mean(nrecs):4.0f}, std={np.std(nrecs):4.0f}')
def plot_cvbase_nli_punit(ax, data, ycol, nli_thresh, color):

View File

@ -3,6 +3,7 @@ import matplotlib.pyplot as plt
from scipy.stats import pearsonr, linregress, gaussian_kde
from thunderlab.tabledata import TableData
from pathlib import Path
from spectral import whitenoise, diag_projection, peakedness
from plotstyle import plot_style, labels_params, significance_str
@ -26,24 +27,24 @@ def sort_files(cell_name, all_files, n):
return files, nums
def plot_chi2(ax, s, data_file):
def plot_chi2(ax, s, data_file, rate):
fcutoff = 300
data = np.load(data_file)
n = data['n']
alpha = data['alpha']
freqs = data['freqs']
pss = data['pss']
dt_fix = 1 # 0.0005
prss = np.abs(data['prss'])/dt_fix*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
chi2 = np.abs(data['prss'])*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
ax.set_visible(True)
ax.set_aspect('equal')
i0 = np.argmin(freqs < -300)
i0 = np.argmin(freqs < -fcutoff)
i0 = np.argmin(freqs < 0)
i1 = np.argmax(freqs > 300)
i1 = np.argmax(freqs > fcutoff)
if i1 == 0:
i1 = len(freqs)
freqs = freqs[i0:i1]
prss = prss[i0:i1, i0:i1]
vmax = np.quantile(prss, 0.996)
chi2 = chi2[i0:i1, i0:i1]
vmax = np.quantile(chi2, 0.996)
ten = 10**np.floor(np.log10(vmax))
for fac, delta in zip([1, 2, 3, 4, 6, 8, 10],
[0.5, 1, 1, 2, 3, 4, 5]):
@ -51,18 +52,22 @@ def plot_chi2(ax, s, data_file):
vmax = fac*ten
ten *= delta
break
pc = ax.pcolormesh(freqs, freqs, prss, vmin=0, vmax=vmax,
pc = ax.pcolormesh(freqs, freqs, chi2, vmin=0, vmax=vmax,
rasterized=True)
if 'noise_frac' in data:
ax.set_title('$c$=0\\,\\%', fontsize='medium')
else:
ax.set_title(f'$c$={100*alpha:g}\\,\\%', fontsize='medium')
ax.set_xlim(0, 300)
ax.set_ylim(0, 300)
ax.set_xlim(0, fcutoff)
ax.set_ylim(0, fcutoff)
ax.set_xticks_delta(100)
ax.set_yticks_delta(100)
ax.set_xlabel('$f_1$', 'Hz')
ax.set_ylabel('$f_2$', 'Hz')
dfreqs, diag = diag_projection(freqs, chi2, 2*fcutoff)
nli, nlif = peakedness(dfreqs, diag, rate, median=False)
ax.text(0.95, 0.88, f'SI($r$)={nli:.1f}', ha='right', zorder=50,
color='white', fontsize='medium', transform=ax.transAxes)
cax = ax.inset_axes([1.04, 0, 0.05, 1])
cax.set_spines_outward('lrbt', 0)
if alpha == 0.1:
@ -75,14 +80,18 @@ def plot_chi2(ax, s, data_file):
def plot_chi2_contrasts(axs, s, cell_name):
print(f' {cell_name}')
d = sims_path / f'baseline-{cell_name}.npz'
data = np.load(d)
rate = float(data['rate'])
cv = float(data['cv'])
print(f' {cell_name}: r={rate:3.0f}Hz, CV={cv:4.2f}')
files, nums = sort_files(cell_name,
sims_path.glob(f'chi2-split-{cell_name}-*.npz'), 1)
plot_chi2(axs[0], s, files[-1])
plot_chi2(axs[0], s, files[-1], rate)
for k, alphastr in enumerate(['010', '030', '100']):
files, nums = sort_files(cell_name,
sims_path.glob(f'chi2-noisen-{cell_name}-{alphastr}-*.npz'), 2)
plot_chi2(axs[k + 1], s, files[-1])
plot_chi2(axs[k + 1], s, files[-1], rate)
def plot_nli_cv(ax, s, data, alpha, cells):
@ -173,12 +182,10 @@ def plot_summary_contrasts(axs, s, cells):
if __name__ == '__main__':
s = plot_style()
#labels_params(xlabelloc='right', ylabelloc='top')
fig, axs = plt.subplots(6, 4, cmsize=(s.plot_width, 0.95*s.plot_width),
height_ratios=[1, 1, 1, 1, 0, 1])
fig, axs = plt.subplots(5, 4, cmsize=(s.plot_width, 0.95*s.plot_width),
height_ratios=[3, 3, 3, 3, 0, 3])
fig.subplots_adjust(leftm=7, rightm=9, topm=2, bottomm=3.5,
wspace=1, hspace=0.7)
for ax in axs.flat:
ax.set_visible(False)
wspace=1, hspace=0.5)
print('Example cells:')
for k in range(len(model_cells)):
plot_chi2_contrasts(axs[k], s, model_cells[k])
@ -186,8 +193,8 @@ if __name__ == '__main__':
fig.common_yticks(axs[k, :])
fig.common_xticks(axs[:4, k])
print()
plot_summary_contrasts(axs[5], s, model_cells)
fig.common_yticks(axs[5, :])
plot_summary_contrasts(axs[4], s, model_cells)
fig.common_yticks(axs[4, :])
fig.tag(axs, xoffs=-4.5, yoffs=1.8)
fig.savefig()
print()

View File

@ -3,6 +3,7 @@ import matplotlib.pyplot as plt
from scipy.stats import pearsonr, linregress, gaussian_kde
from thunderlab.tabledata import TableData
from pathlib import Path
from spectral import whitenoise, diag_projection, peakedness
from plotstyle import plot_style, labels_params, significance_str
@ -23,24 +24,24 @@ def sort_files(cell_name, all_files, n):
return files, nums
def plot_chi2(ax, s, data_file):
def plot_chi2(ax, s, data_file, rate):
fcutoff = 300
data = np.load(data_file)
n = data['n']
alpha = data['alpha']
freqs = data['freqs']
pss = data['pss']
dt_fix = 1 # 0.0005
prss = np.abs(data['prss'])/dt_fix*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
chi2 = np.abs(data['prss'])*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
ax.set_visible(True)
ax.set_aspect('equal')
i0 = np.argmin(freqs < -300)
i0 = np.argmin(freqs < -fcutoff)
i0 = np.argmin(freqs < 0)
i1 = np.argmax(freqs > 300)
i1 = np.argmax(freqs > fcutoff)
if i1 == 0:
i1 = len(freqs)
freqs = freqs[i0:i1]
prss = prss[i0:i1, i0:i1]
vmax = np.quantile(prss, 0.996)
chi2 = chi2[i0:i1, i0:i1]
vmax = np.quantile(chi2, 0.996)
ten = 10**np.floor(np.log10(vmax))
for fac, delta in zip([1, 2, 3, 4, 6, 8, 10],
[0.5, 1, 1, 2, 3, 4, 5]):
@ -48,19 +49,23 @@ def plot_chi2(ax, s, data_file):
vmax = fac*ten
ten *= delta
break
pc = ax.pcolormesh(freqs, freqs, prss, vmin=0, vmax=vmax,
pc = ax.pcolormesh(freqs, freqs, chi2, vmin=0, vmax=vmax,
rasterized=True)
ns = f'$N={n}$' if n <= 100 else f'$N=10^{np.log10(n):.0f}$'
if 'noise_frac' in data:
ax.set_title(f'$c$=0\\,\\%, {ns}', fontsize='medium')
else:
ax.set_title(f'$c$={100*alpha:g}\\,\\%, {ns}', fontsize='medium')
ax.set_xlim(0, 300)
ax.set_ylim(0, 300)
ax.set_xlim(0, fcutoff)
ax.set_ylim(0, fcutoff)
ax.set_xticks_delta(100)
ax.set_yticks_delta(100)
ax.set_xlabel('$f_1$', 'Hz')
ax.set_ylabel('$f_2$', 'Hz')
dfreqs, diag = diag_projection(freqs, chi2, 2*fcutoff)
nli, nlif = peakedness(dfreqs, diag, rate, median=False)
ax.text(0.95, 0.88, f'SI($r$)={nli:.1f}', ha='right', zorder=50,
color='white', fontsize='medium', transform=ax.transAxes)
cax = ax.inset_axes([1.04, 0, 0.05, 1])
cax.set_spines_outward('lrbt', 0)
if alpha == 0.1:
@ -73,16 +78,20 @@ def plot_chi2(ax, s, data_file):
def plot_chi2_contrasts(axs, s, cell_name, n=None):
print(f' {cell_name}')
d = sims_path / f'baseline-{cell_name}.npz'
data = np.load(d)
rate = float(data['rate'])
cv = float(data['cv'])
print(f' {cell_name}: r={rate:3.0f}Hz, CV={cv:4.2f}')
files, nums = sort_files(cell_name,
sims_path.glob(f'chi2-split-{cell_name}-*.npz'), 1)
idx = -1 if n is None else nums.index(n)
plot_chi2(axs[0], s, files[idx])
plot_chi2(axs[0], s, files[idx], rate)
for k, alphastr in enumerate(['010', '030', '100']):
files, nums = sort_files(cell_name,
sims_path.glob(f'chi2-noisen-{cell_name}-{alphastr}-*.npz'), 2)
idx = -1 if n is None else nums.index(n)
plot_chi2(axs[k + 1], s, files[idx])
plot_chi2(axs[k + 1], s, files[idx], rate)
def plot_nli_diags(ax, s, data, alphax, alphay, xthresh, ythresh, cell_name):
@ -176,27 +185,28 @@ def plot_summary_diags(axs, s, xthresh, ythresh, cell_name):
if __name__ == '__main__':
nsmall = 100
xthresh = 1.2
ythresh = 1.8
s = plot_style()
fig, axs = plt.subplots(6, 4, cmsize=(s.plot_width, 0.85*s.plot_width),
fig, axs = plt.subplots(4, 4, cmsize=(s.plot_width, 0.85*s.plot_width),
height_ratios=[1, 1, 0, 1, 0, 1])
fig.subplots_adjust(leftm=7, rightm=9, topm=2, bottomm=4,
wspace=1, hspace=1)
wspace=1, hspace=0.8)
for ax in axs.flat:
ax.set_visible(False)
print('Example cells:')
plot_chi2_contrasts(axs[0], s, model_cell)
plot_chi2_contrasts(axs[1], s, model_cell, 10)
plot_chi2_contrasts(axs[1], s, model_cell, nsmall)
for k in range(2):
fig.common_yticks(axs[k, :])
for k in range(4):
fig.common_xticks(axs[:2, k])
print()
plot_summary_contrasts(axs[3], s, xthresh, ythresh, model_cell)
plot_summary_diags(axs[5], s, xthresh, ythresh, model_cell)
plot_summary_contrasts(axs[2], s, xthresh, ythresh, model_cell)
plot_summary_diags(axs[3], s, xthresh, ythresh, model_cell)
fig.common_yticks(axs[2, 1:])
fig.common_yticks(axs[3, 1:])
fig.common_yticks(axs[5, 1:])
fig.tag(axs, xoffs=-4.5, yoffs=1.8)
axs[1, 0].set_visible(False)
fig.savefig()

View File

@ -1,7 +1,7 @@
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from spectral import whitenoise
from spectral import whitenoise, diag_projection, peakedness
from plotstyle import plot_style
@ -25,10 +25,11 @@ def sort_files(cell_name, all_files, n):
return files, nums
def plot_chi2(ax, s, freqs, chi2, nsegs):
def plot_chi2(ax, s, freqs, chi2, nsegs, rate):
fcutoff = 300
ax.set_aspect('equal')
i0 = np.argmin(freqs < 0)
i1 = np.argmax(freqs > 300)
i1 = np.argmax(freqs > fcutoff)
if i1 == 0:
i1 = len(freqs)
freqs = freqs[i0:i1]
@ -45,15 +46,22 @@ def plot_chi2(ax, s, freqs, chi2, nsegs):
prev_delta = delta
pc = ax.pcolormesh(freqs, freqs, chi2, vmin=0, vmax=vmax,
rasterized=True)
ax.set_xlim(0, 300)
ax.set_ylim(0, 300)
ax.set_xlim(0, fcutoff)
ax.set_ylim(0, fcutoff)
ax.set_xticks_delta(100)
ax.set_yticks_delta(100)
ax.set_xlabel('$f_1$', 'Hz')
ax.set_ylabel('$f_2$', 'Hz')
ax.text(1, 1.1, f'$N=10^{{{np.log10(nsegs):.0f}}}$',
ha='right', transform=ax.transAxes)
if nsegs < 10000:
ax.text(1, 1.1, f'$N={nsegs}$',
ha='right', transform=ax.transAxes)
else:
ax.text(1, 1.1, f'$N=10^{{{np.log10(nsegs):.0f}}}$',
ha='right', transform=ax.transAxes)
dfreqs, diag = diag_projection(freqs, chi2, 2*fcutoff)
nli, nlif = peakedness(dfreqs, diag, rate, median=False)
ax.text(0.95, 0.88, f'SI($r$)={nli:.1f}', ha='right', zorder=50,
color='white', fontsize='medium', transform=ax.transAxes)
cax = ax.inset_axes([1.04, 0, 0.05, 1])
cax.set_spines_outward('lrbt', 0)
cb = fig.colorbar(pc, cax=cax)
@ -65,6 +73,7 @@ def plot_chi2(ax, s, freqs, chi2, nsegs):
def plot_overn(ax, s, files, nmax=1e6):
fcutoff = 300
ns = []
stats = []
for fname in files:
@ -79,7 +88,7 @@ def plot_overn(ax, s, files, nmax=1e6):
chi2 = np.abs(data['prss'])/dt_fix*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
ns.append(n)
i0 = np.argmin(freqs < 0)
i1 = np.argmax(freqs > 300)
i1 = np.argmax(freqs > fcutoff)
if i1 == 0:
i1 = len(freqs)
chi2 = chi2[i0:i1, i0:i1]
@ -115,7 +124,7 @@ def plot_overn(ax, s, files, nmax=1e6):
ax.set_ylabel('$|\\chi_2|$ [Hz]')
def plot_chi2_contrast(ax1, ax2, s, files, nums, nsmall, nlarge):
def plot_chi2_contrast(ax1, ax2, s, files, nums, nsmall, nlarge, rate):
for ax, n in zip([ax1, ax2], [nsmall, nlarge]):
i = nums.index(n)
data = np.load(files[i])
@ -124,11 +133,13 @@ def plot_chi2_contrast(ax1, ax2, s, files, nums, nsmall, nlarge):
freqs = data['freqs']
pss = data['pss']
chi2 = np.abs(data['prss'])*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
cax = plot_chi2(ax, s, freqs, chi2, n)
cax = plot_chi2(ax, s, freqs, chi2, n, rate)
cax.set_ylabel('')
print(f'Modeled cell {"-".join(files[i].name.split("-")[2:-2])} at {100*alpha:4.1f}% contrast: noise_frac={1:3.1f}, nsegs={n}')
print()
def plot_chi2_split(ax1, ax2, s, files, nums, nsmall, nlarge):
def plot_chi2_split(ax1, ax2, s, files, nums, nsmall, nlarge, rate):
for ax, n in zip([ax1, ax2], [nsmall, nlarge]):
i = nums.index(n)
data = np.load(files[i])
@ -138,8 +149,10 @@ def plot_chi2_split(ax1, ax2, s, files, nums, nsmall, nlarge):
freqs = data['freqs']
pss = data['pss']
chi2 = np.abs(data['prss'])*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
cax = plot_chi2(ax, s, freqs, chi2, n)
cax = plot_chi2(ax, s, freqs, chi2, n, rate)
cax.set_ylabel('')
print(f'Modeled cell {"-".join(files[i].name.split("-")[2:-1])} at {100*alpha:4.1f}% contrast: noise_frac={noise_frac:3.1f}, nsegs={n}')
print()
return alpha, noise_frac
@ -152,12 +165,15 @@ def plot_chi2_data(ax, s, cell_name, run):
data_file = data_path / f'{cell_name}-spectral-s{run:02d}.npz'
data = np.load(data_file)
n = data['n']
nfft = data['nfft']
deltat = data['deltat']
alpha = data['alpha']
freqs = data['freqs']
pss = data['pss']
chi2 = np.abs(data['prss'])*0.5/np.sqrt(pss.reshape(1, -1)*pss.reshape(-1, 1))
print(f'Measured cell {data_file.name} at {100*alpha:.1f}% contrast: r={ratebase:3.0f}Hz, CV={cvbase:4.2f}')
plot_chi2(ax, s, freqs, chi2, n)
print(f'Measured cell {"-".join(data_file.name.split("-")[:-2])} at {100*alpha:4.1f}% contrast: r={ratebase:3.0f}Hz, CV={cvbase:4.2f}, dt={1000*deltat:4.2f}ms, nfft={nfft}, win={1000*deltat*nfft:6.1f}ms, nsegs={n}')
print()
plot_chi2(ax, s, freqs, chi2, n, ratebase)
return alpha, ratebase, eodf
@ -263,7 +279,7 @@ if __name__ == '__main__':
files, nums = sort_files(example_cell[0], data_files, 2)
axss[1].text(xt, yt, 'P-unit model', fontsize='large',
transform=axs[1, 1].transAxes, color=s.model_color1)
plot_chi2_contrast(axss[1], axss[2], s, files, nums, nsmall, nlarge)
plot_chi2_contrast(axss[1], axss[2], s, files, nums, nsmall, nlarge, ratebase)
axr1 = plot_noise_split(axss[0], data_contrast, 0, 1, wtime, wnoise)
plot_overn(axss[3], s, files, nmax=1e6)
axss[3].legend(loc='lower center', bbox_to_anchor=(0.5, 1.2),
@ -273,7 +289,7 @@ if __name__ == '__main__':
axss = axs[2]
data_files = sims_path.glob(f'chi2-noisen-{example_cell[0]}-{1000*contrast:03.0f}-*.npz')
files, nums = sort_files(example_cell[0], data_files, 2)
plot_chi2_contrast(axss[1], axss[2], s, files, nums, nsmall, nlarge)
plot_chi2_contrast(axss[1], axss[2], s, files, nums, nsmall, nlarge, ratebase)
axr2 = plot_noise_split(axss[0], contrast, 0, 1, wtime, wnoise)
plot_overn(axss[3], s, files, nmax=1e6)
@ -286,7 +302,7 @@ if __name__ == '__main__':
axss[1].text(xt + 0.9, yt, f'(noise split)', fontsize='large',
transform=axss[1].transAxes)
noise_contrast, noise_frac = plot_chi2_split(axss[1], axss[2], s,
files, nums, nsmall, nlarge)
files, nums, nsmall, nlarge, ratebase)
axr3 = plot_noise_split(axss[0], 0, noise_contrast, noise_frac,
wtime, wnoise)
plot_overn(axss[3], s, files, nmax=1e6)
@ -303,4 +319,3 @@ if __name__ == '__main__':
[axr3] + axs[3, 1:].tolist()],
xoffs=[-4.5, 1, 1, -4.5], yoffs=2)
fig.savefig()
print()

View File

@ -420,7 +420,7 @@ We here analyze nonlinear responses in two types of primary electroreceptor affe
\begin{figure*}[t]
\includegraphics[width=\columnwidth]{lifsuscept}
\caption{\label{fig:lifresponse} First- (linear) and second-order response functions of the leaky integrate-and-fire model. \figitem{A} Magnitude of the first-order response function $|\chi_1(f)|$, also known as the ``gain'' function, quantifies the response amplitude relative to the stimulus amplitude, both measured at the stimulus frequency. \figitem{B} Magnitude of the second-order response function $|\chi_2(f_1, f_2)|$ quantifies the response at the sum of two stimulus frequencies. For linear systems, the second-order response function is zero, because linear systems do not create new frequencies and thus there is no response at the sum of the two frequencies. The plots show the analytical solutions from \citep{Lindner2001} and \citep{Voronenko2017} with $\mu = 1.1$ and $D = 0.001$. Note that the leaky integrate-and-fire model is formulated without dimensions, frequencies are given in multiples of the inverse membrane time constant.}
\caption{\label{fig:lifresponse} First- (linear) and second-order response functions of the leaky integrate-and-fire model. \figitem{A} Magnitude of the first-order response function $|\chi_1(f)|$, also known as the ``gain'' function, quantifies the response amplitude relative to the stimulus amplitude, both measured at the same stimulus frequency. \figitem{B} Magnitude of the second-order response function $|\chi_2(f_1, f_2)|$ quantifies the response at the sum of two stimulus frequencies. For linear systems, the second-order response function is zero, because linear systems do not create new frequencies and thus there is no response at the sum of the two frequencies. The plots show the analytical solutions from \citet{Lindner2001} and \citet{Voronenko2017} with $\mu = 1.1$ and $D = 0.001$. Note that the leaky integrate-and-fire model is formulated without dimensions, frequencies are given in multiples of the inverse membrane time constant.}
\end{figure*}
We like to think about signal encoding in terms of linear relations with unique mapping of a given input value to a certain output of the system under consideration. Indeed, such linear methods, for example the transfer function or first-oder susceptibility shown in figure~\ref{fig:lifresponse}, have been widely and successfully applied to describe and predict neuronal responses and are an invaluable tool to characterize neural systems \citep{Borst1999}. Nonlinear mechanisms, on the other hand, are key on different levels of neural processing. Deciding for one action over another is a nonlinear process on the systemic level. On the cellular level, spiking neurons are inherently nonlinear. Whether an action potential is elicited depends on the membrane potential to exceed a threshold \citep{Hodgkin1952, Koch1995}. Because of such nonlinearities, understanding and predicting neuronal responses to sensory stimuli is in general a difficult task.
@ -450,16 +450,16 @@ When stimulating with both foreign signals simultaneously, additional peaks appe
\begin{figure*}[p]
\includegraphics[width=\columnwidth]{regimes}
\caption{\label{fig:nonlin_regime} Linear and nonlinear responses of a model P-unit in a three-fish setting in dependence on increasing stimulus amplitudes. The model P-unit was stimulated with two sinewaves of equal amplitude (contrast) at difference frequencies $\bone=30$\,Hz and $\btwo=130$\,Hz relative the receiver's EOD frequency. \btwo{} was set to match the baseline firing rate \fbase{} of the P-unit. The model used has the cell identifier 2013-01-08-aa (table~\ref{modelparams}). \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 with a Gaussian kernel ($\sigma=1$\,ms). \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) and their amplitudes increase linearly with stimulus contrast (thin lines). \figitem{B} At intermediate stimulus contrasts, nonlinear responses appear at the sum and the difference of the stimulus frequencies (orange and red marker). Their amplitudes grow quadraticlaly with stimulus constrast (thin lines). \figitem{C} At stronger stimulation the amplitudes of these nonlinear repsonses deviate from the quadratic dependency on stimulus contrast. \figitem{D} At higher stimulus contrasts additional peaks appear in the power spectrum. \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.}
\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'', table~\ref{modelparams}) was stimulated with two sinewaves 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). \figitem{B} At moderately higher stimulus contrast, the peaks in the response spectrum at the two beat frequencies are larger. \figitem{C} At intermediate stimulus contrasts, nonlinear responses start to appear at the sum and the difference of the stimulus frequencies (orange and red marker). \figitem{D} At higher stimulus contrasts additional peaks appear in the power spectrum. \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 repsonses 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}.
At very low stimulus contrasts (in the example cell less than approximately 1.5\,\% relative to the receiver's EOD amplitude) the spectrum has small peaks only at the beat frequencies (\subfigref{fig:nonlin_regime}{A}, green and purple). The amplitudes of these peaks initially increase linearly with stimulus amplitude (\subfigref{fig:nonlin_regime}{E}, thin lines), an indication of the linear response at lowest stimulus amplitudes.
At very low stimulus contrasts (in the example cell less than approximately 1.5\,\% relative to the receiver's EOD amplitude) the spectrum has small peaks only at the beat frequencies (\subfigref{fig:regimes}{A}, green and purple). The amplitudes of these peaks initially increase linearly with stimulus amplitude (\subfigref{fig:regimes}{E}, thin lines), an indication of the linear response at lowest stimulus amplitudes.
This linear regime is followed by the weakly nonlinear regime (in the example cell between approximately 1.5\,\% and 4\,\% stimulus contrasts). In addition to the peaks at the stimulus frequencies, peaks at the sum and the difference of the stimulus frequencies appear in the response spectrum (\subfigref{fig:nonlin_regime}{B}, orange and red). The amplitudes of these two peaks initially increase quadratically with stimulus amplitude (\subfigref{fig:nonlin_regime}{E}, thin lines). Note, that we have chosen $\Delta f_2$ to match the baseline firing rate $f_{base}$ of the neuron.
This linear regime is followed by the weakly nonlinear regime (in the example cell between approximately 1.5\,\% and 4\,\% stimulus contrasts). In addition to the peaks at the stimulus frequencies, peaks at the sum and the difference of the stimulus frequencies appear in the response spectrum (\subfigref{fig:regimes}{B}, orange and red). The amplitudes of these two peaks initially increase quadratically with stimulus amplitude (\subfigref{fig:regimes}{E}, thin lines). Note, that we have chosen $\Delta f_2$ to match the baseline firing rate $f_{base}$ of the neuron.
At higher stimulus amplitudes, the linear response and the weakly-nonlinear response begin to deviate from their linear and quadratic dependency on amplitude (\subfigrefb{fig:nonlin_regime}{C \& E}) and additional peaks appear in the response spectrum (\subfigrefb{fig:nonlin_regime}{D}). At high stimulus contrasts, additional nonlinearities in the system, in particular clipping of the firing rate, shape the responses.
At higher stimulus amplitudes, the linear response and the weakly-nonlinear response begin to deviate from their linear and quadratic dependency on amplitude (\subfigrefb{fig:regimes}{C \& E}) and additional peaks appear in the response spectrum (\subfigrefb{fig:regimes}{D}). At high stimulus contrasts, additional nonlinearities in the system, in particular clipping of the firing rate, shape the responses.
For this example, we chose very specific stimulus (beat) frequencies. %One of these matching the P-unit's baseline firing rate.
In the following, however, we are interested in how the nonlinear responses depend on different combinations of stimulus frequencies in the weakly nonlinear regime. For the sake of simplicity we will drop the $\Delta$ notation even though P-unit stimuli are beats.
@ -471,7 +471,7 @@ Weakly nonlinear responses are expected in cells with sufficiently low intrinsic
\begin{figure*}[p]
\includegraphics[width=\columnwidth]{punitexamplecell}
\caption{\label{fig:punit} Linear and nonlinear stimulus encoding in a low-CV P-unit (cell identifier ``2010-06-21-ai"). \figitem{A} Interspike interval (ISI) distribution of the cell's baseline activity, i.e. the cell is driven only by the unperturbed own electric field. The low CV of the ISIs indicates quite regular firing. \figitem{B} Power spectral density of the baseline response with peaks at the cell's baseline firing rate \fbase{} and the fish's EOD frequency \feod{}. \figitem{C} Random amplitude modulation stimulus (top, with cutoff frequency of 300\,Hz) and evoked responses (spike raster, bottom) of the same P-unit. The stimulus contrast (right) measures the strength of the AM. \figitem{D} Gain of the transfer function (first-order susceptibility), \eqnref{linearencoding_methods}, computed from the responses to 10\,\% (light purple) and 20\,\% contrast (dark purple) RAM stimulation of 10\,s duration. \figitem{E, F} Absolute value of the second-order susceptibility, \eqnref{eq:susceptibility}, for both the low and high stimulus contrast. Pink triangles mark vertical, horizontal, and diagonal lines where \fone, \ftwo{} or \fsum{} are equal to \fbase{}. \figitem{G} Second-order susceptibilities projected onto the diagonal (means of all anti-diagonals of the matrices shown in \panel{E, F}). Dots mark \fbase{}, horizontal dashed lines mark medians of the projected susceptibilities.}
\caption{\label{fig:punit} Linear and nonlinear stimulus encoding in example P-units. \figitem{A} Interspike interval (ISI) distribution of a cell's baseline activity, i.e. the cell is driven only by the unperturbed own electric field (cell identifier ``2020-10-27-ag''). This cell has a rather high baseline firing rate $r$ and an intermediate CV$_{\text{base}}$ of its interspike intervals, as indicated. \figitem{B} Power spectral density of the cell's baseline response with peaks at the cell's baseline firing rate $r$ and the fish's EOD frequency $f_{\text{EOD}}$. \figitem{C} Random amplitude modulation stimulus (top, red, with cutoff frequency of 300\,Hz) and evoked responses (spike raster, bottom) of the same P-unit for two different stimulus contrasts (right). The stimulus contrast quantifies the standard deviation of the AM relative to the fish's EOD amplitude. \figitem{D} Gain of the transfer function (first-order susceptibility), \eqnref{linearencoding_methods}, computed from the responses to 10\,\% (light blue) and 20\,\% contrast (dark blue) RAM stimulation of 5\,s duration. \figitem{E} Absolute value of the second-order susceptibility, \eqnref{eq:susceptibility}, for both the low and high stimulus contrast. At the lower stimulus contrast an anti-diagonal where the sum of the two stimulus frequencies equals the neuron's baseline frequency clearly sticks out of the noise floor. \figitem{F} At the higher contrast, the anti-diagonal is weaker. \figitem{G} Second-order susceptibilities projected onto the diagonal (means of all anti-diagonals of the matrices shown in \panel{E, F}). The anti-diagonals from \panel{E} and \panel{F} show up as a peak close to the cell's baseline firing rate $r$. The susceptibility index, SI($r$), quantifies the height of this peak relative to the values in the vicinity \notejb{See equation XXX}. \figitem{H} ISI distributions (top) and second-order susceptibilities (bottom) of four more example P-units (``2021-06-18-ae'', ``2012-03-30-ah'', ``2018-08-24-ak'', ``2018-08-14-ac'') covering the range of baseline firing rates and CV$_{\text{base}}$s as indicated. The first two cells show an anti-diagonal, but not the full expected triangular structure. The second-order susceptibilities of the other two cells are mostly flat and consequently the SI($r$) values are close to one.}
\end{figure*}
Noise stimuli, here random amplitude modulations (RAM) of the EOD (\subfigref{fig:punit}{C}, top trace, red line), are commonly used to characterize stimulus-driven responses of sensory neurons using transfer functions (first-order susceptibility), spike-triggered averages, or stimulus-response coherences. Here, we additionally estimate the second-order susceptibility to quantify nonlinear encoding. P-unit spikes align more or less clearly with fluctuations in the RAM stimulus. A higher stimulus intensity, here a higher contrast of the RAM relative to the EOD amplitude (see methods), entrains the P-unit response more clearly (light and dark purple for low and high contrast stimuli, \subfigrefb{fig:punit}{C}). Linear encoding, quantified by the transfer function, \eqnref{linearencoding_methods}, is similar for the two RAM contrasts in this low-CV P-unit (\subfigrefb{fig:punit}{D}), as expected for a linear system. The first-order susceptibility is low for low frequencies, peaks in the range below 100\,Hz and then falls off again \citep{Benda2005}.
@ -487,7 +487,7 @@ In contrast, a high-CV P-unit (CV$_{\text{base}}=0.4$) does not exhibit pronounc
\begin{figure*}[p]
\includegraphics[width=\columnwidth]{ampullaryexamplecell}
\caption{\label{fig:ampullary} Linear and nonlinear stimulus encoding in an ampullary afferent (cell identifier ``2012-04-26-ae"). \figitem{A} Interspike interval (ISI) distribution of the cell's baseline activity. The very low CV of the ISIs indicates almost perfect periodic spiking. \figitem{B} Power spectral density of baseline activity with peaks at the cell's baseline firing rate and its harmonics. \figitem{C} Bad-limited white noise stimulus (top, with a cutoff frequency of 150\,Hz) added to the fish's self-generated electric field and spike raster of the evoked responses (bottom) for two stimulus contrasts as indicated (right). \figitem{D} Gain of the transfer function, \eqnref{linearencoding_methods}, of the responses to stimulation with 2\,\% (light green) and 20\,\% contrast (dark green) of 10\,s duration. \figitem{E, F} Absolute value of the second-order susceptibility, \eqnref{eq:susceptibility}, for both stimulus contrasts as indicated. Pink triangles indicate the baseline firing rate. \figitem{G} Projections of the second-order susceptibilities in \panel{E, F} onto the diagonal. }
\caption{\label{fig:ampullary} Linear and nonlinear stimulus encoding in example ampullary afferents. \figitem{A} Interspike interval (ISI) distribution of the cell's baseline activity (cell identifier ``2012-04-26-ae''). The very low CV of the ISIs indicates almost perfect periodic spiking. \figitem{B} Power spectral density of baseline activity with peaks at the cell's baseline firing rate and its harmonics. Ampullary afferents do not respond to the fish's EOD frequency, $f_{\text{EOD}}$. \figitem{C} Band-limited white noise stimulus (top, red, with a cutoff frequency of 150\,Hz) added to the fish's self-generated electric field (no amplitude modulation) and spike raster of the evoked responses (bottom) for two stimulus contrasts as indicated (right). \figitem{D} Gain of the transfer function, \eqnref{linearencoding_methods}, of the responses to stimulation with 5\,\% (light green) and 10\,\% contrast (dark green) of 10\,s duration. \figitem{E, F} Absolute value of the second-order susceptibility, \eqnref{eq:susceptibility}, for both stimulus contrasts as indicated. Both show a strong anti-diagonal where the two stimulus frequencies add up to the afferent's baseline firing rate. \figitem{G} Projections of the second-order susceptibilities in \panel{E, F} onto the diagonal. \figitem{H} ISI distributions (top) and second-order susceptibilites (bottom) of four more example afferents (``2010-11-26-an'', ``2011-10-25-ac'', ``2011-02-18-ab'', and ``2014-01-16-aj'').}
\end{figure*}
Electric fish possess an additional electrosensory system, the passive or ampullary electrosensory system, that responds to low-frequency exogenous electric stimuli. The population of ampullary afferents is much less heterogeneous, and known for the much lower CVs of their baseline ISIs (CV$_{\text{base}}=0.06$ to $0.22$, \citealp{Grewe2017}). Ampullary cells do not phase-lock to the EOD and the ISIs are unimodally distributed (\subfigrefb{fig:ampullary}{A}). As a consequence of the high regularity of their baseline spiking activity, the corresponding power spectrum shows distinct peaks at the baseline firing rate \fbase{} and its harmonics. Since the cells do not respond to the self-generated EOD, there is no peak at \feod{} (\subfigrefb{fig:ampullary}{B}). When driven by a low-contrast noise stimulus (note: this is no longer an AM stimulus, \subfigref{fig:ampullary}{C}), ampullary cells exhibit very pronounced bands in the second-order susceptibility, where \fsum{} is equal to \fbase{} or its harmonic (yellow diagonals in \subfigrefb{fig:ampullary}{E}), implying strong nonlinear response components at these frequency combinations (\subfigrefb{fig:ampullary}{G}, top). With higher stimulus contrasts these bands disappear (\subfigrefb{fig:ampullary}{F}), the projection onto the diagonal loses its distinct peak at \fsum{} and its overall level is reduced (\subfigrefb{fig:ampullary}{G}, bottom).
@ -498,7 +498,7 @@ In the example recordings shown above (\figsrefb{fig:punit} and \fref{fig:ampull
\begin{figure*}[p]
\includegraphics[width=\columnwidth]{noisesplit}
\caption{\label{fig:noisesplit} Estimation of second-order susceptibilities in the limit of weak stimuli. \figitem{A} \suscept{} estimated from $N=11$ 0.5\,s long segments of an electrophysiological recording of another low-CV P-unit (cell 2012-07-03-ak, $\fbase=120$\,Hz, CV=0.20) driven with a weak RAM stimulus with contrast 2.5\,\%. Pink edges mark the baseline firing rate where enhanced nonlinear responses are expected. \figitem[i]{B} \textit{Standard condition} of model simulations with intrinsic noise (bottom) and a RAM stimulus (top). \figitem[ii]{B} \suscept{} estimated from simulations of the cell's LIF model counterpart (cell 2012-07-03-ak, table~\ref{modelparams}) based on the same number of FFT segments $N=11$ as in the electrophysiological recording. \figitem[iii]{B} Same as \panel[ii]{B} but using $10^6$ segments. \figitem[i-iii]{C} Same as in \panel[i-iii]{B} but in the \textit{noise split} condition: there is no external RAM signal driving the model. Instead, a large part (90\,\%) of the total intrinsic noise is treated as a signal and is presented as an equivalent amplitude modulation (\signalnoise, center), while the intrinsic noise is reduced to 10\,\% of its original strength (see methods for details). Simulating one million segments, this reveals the full expected trangular structure of the second-order susceptibility.}
\caption{\label{fig:noisesplit} Estimation of second-order susceptibilities. \figitem{A} \suscept{} (right) estimated from $N=198$ 256\,ms long FFT segments of an electrophysiological recording of another P-unit (cell ``2017-07-18-ai'', $r=78$\,Hz, CV$_{\text{base}}=0.22$) driven with a RAM stimulus with contrast 5\,\% (left). \figitem[i]{B} \textit{Standard condition} of model simulations with intrinsic noise (bottom) and a RAM stimulus (top). \figitem[ii]{B} \suscept{} estimated from simulations of the cell's LIF model counterpart (cell ``2017-07-18-ai'', table~\ref{modelparams}) based on a similar number of $N=100$ FFT segments. As in the electrophysiological recording only a weak anti-diagonal is visible. \figitem[iii]{B} Same as \panel[ii]{B} but using $10^6$ FFT segments. Now, the expected triangular structure is revealed. \figitem[iv]{B} Convergence of the \suscept{} estimate as a function of FFTsegements. \figitem{C} At a lower stimulus contrast of 1\,\% the estimate did not converge yet even for $10^6$ FFT segments. \figitem[i]{D} Same as in \panel[i]{B} but in the \textit{noise split} condition: there is no external RAM signal (red) driving the model. Instead, a large part (90\,\%) of the total intrinsic noise is treated as a signal and is presented as an equivalent amplitude modulation ($s_{\xi}(t)$, orange), while the intrinsic noise is reduced to 10\,\% of its original strength (bottom, see methods for details). \figitem[i]{D} 100 FFT segements are still not sufficient for estimating \suscept{}. \figitem[iii]{D} Simulating one million segments reveals the full expected trangular structure of the second-order susceptibility. \figitem[iv]{D} In the noise-split condition, the \suscept{} estimate converges already at about $10^{4}$ FFT Segements.}
\end{figure*}
%\notejb{Since the model overestimated the sensitivity of the real P-unit, we adjusted the RAM contrast to 0.9\,\%, such that the resulting spike trains had the same CV as the electrophysiological recorded P-unit during the 2.5\,\% contrast stimulation (see table~\ref{modelparams} for model parameters).} \notejb{chi2 scale is higher than in real cell}
@ -514,7 +514,7 @@ Note, that the increased number of segments goes along with a substantial reduct
\begin{figure}[p]
\includegraphics[width=\columnwidth]{modelsusceptcontrasts}
\caption{\label{fig:modelsusceptcontrasts}Dependence of second order susceptibility on stimulus contrast. \figitem{A} Second-order susceptibilities estimated for increasing stimulus contrasts $c=0, 1, 3$ and $10$\,\% as indicated ($N=10^7$ FFT segments for $c=1$\,\%, $N=10^6$ segments for all other contrasts, see \subfigrefb{fig:trialnr}{E}). $c=0$\,\% refers to the noise-split configuration (limit to vanishing external RAM signal, see \figrefb{fig:noisesplit}). Shown are simulations of the P-unit model cell ``2017-07-18-ai'' (table~\ref{modelparams}) with a baseline firing rate of $82$\,Hz and CV$=0.23$. The cell shows a clear triangular pattern in its second-order susceptibility even up to a contrast of $10$\,\%. Note, that for $c=1$\,\% (\panel[ii]{D}), the estimate did not converge yet. \figitem{B} Cell ``2012-12-13-ao'' (baseline firing rate of $146$\,Hz, CV$=0.23$) also has strong interactions at its baseline firing rate that survive up to a stimulus contrast of $3$\,\%. \figitem{C} Model cell ``2012-12-20-ac'' (baseline firing rate of $212$\,Hz, CV$=0.26$) shows a weak triangular structure in the second-order susceptibility that vanishes for stimulus contrasts greater than $1$\,\%. \figitem{D} Cell ``2013-01-08-ab'' (baseline firing rate of $218$\,Hz, CV$=0.55$) does not show any triangular pattern in its second-order susceptibility. Nevertheless, interactions between low stimulus frequencies become substantial at higher contrasts. \figitem{E} The presence of an elevated second-order susceptibility where the stimulus frequency add up to the neuron's baseline frequency, can be identified by values of the peakedness of the projected nonlinearity (\nli{}, \eqnref{eq:nli_equation2}) greater than one (horizontal black line). The \nli{} (density to the right) is plotted as a function of the model neuron's baseline CV for all $39$ model cells (table~\ref{modelparams}). Model cells have been visually categorized based on the presence of a triangular pattern in their second-order susceptibility estimated in the noise-split configuration (legend). The cells from \panel{A--D} are marked by a black circle. Pearson's correlation coefficients $r$, the corresponding significance level $p$ and regression line (dashed gray line) are indicated. The higher the stimulus contrast, the less cells show weakly nonlinear interactions as expressed by the triangular structure in the second-order susceptibility.}
\caption{\label{fig:modelsusceptcontrasts}Dependence of second order susceptibility on stimulus contrast. \figitem{A} Second-order susceptibilities estimated for increasing stimulus contrasts $c=0, 1, 3$ and $10$\,\% as indicated ($N=10^7$ FFT segments for $c=1$\,\%, $N=10^6$ segments for all other contrasts). $c=0$\,\% refers to the noise-split configuration (limit to vanishing external RAM signal, see \subfigrefb{fig:noisesplit}{D}). Shown are simulations of the P-unit model cell ``2017-07-18-ai'' (table~\ref{modelparams}) with a baseline firing rate of $82$\,Hz and CV$=0.23$. The cell shows a clear triangular pattern in its second-order susceptibility even up to a contrast of $10$\,\%. Note, that for $c=1$\,\% (\panel[ii]{D}), the estimate did not converge yet. \figitem{B} Cell ``2012-12-13-ao'' (baseline firing rate of $146$\,Hz, CV$=0.23$) also has strong interactions at its baseline firing rate that survive up to a stimulus contrast of $3$\,\%. \figitem{C} Model cell ``2012-12-20-ac'' (baseline firing rate of $212$\,Hz, CV$=0.26$) shows a weak triangular structure in the second-order susceptibility that vanishes for stimulus contrasts larger than $1$\,\%. \figitem{D} Cell ``2013-01-08-ab'' (baseline firing rate of $218$\,Hz, CV$=0.55$) does not show any triangular pattern in its second-order susceptibility. Nevertheless, interactions between low stimulus frequencies become substantial at higher contrasts. \figitem{E} The presence of an elevated second-order susceptibility where the stimulus frequency add up to the neuron's baseline frequency, can be identified by the susceptibility index (SI($r$), \eqnref{eq:nli_equation2}) greater than one (horizontal black line). The SI($r$) (density to the right) is plotted as a function of the model neuron's baseline CV for all $39$ model cells (table~\ref{modelparams}). Model cells have been visually categorized based on the presence of a triangular pattern in their second-order susceptibility estimated in the noise-split configuration (legend). The cells from \panel{A--D} are marked by a black circle. Pearson's correlation coefficients $r$, the corresponding significance level $p$ and regression line (dashed gray line) are indicated. The higher the stimulus contrast, the less cells show weakly nonlinear interactions as expressed by the triangular structure in the second-order susceptibility.}
\end{figure}
\subsection{Weakly nonlinear interactions in many model cells}
@ -526,7 +526,7 @@ The \nli{} correlates with the cell's CV of its baseline interspike intervals ($
\subsection{Weakly nonlinear interactions vanish for higher stimulus contrasts}
As pointed out in the context of stimulation with sine waves (\figrefb{fig:nonlin_regime}), the weakly nonlinear regime can only be observed for sufficiently weak stimulus amplitudes. In the model cells we estimated second-order susceptibilities for RAM stimuli with a contrast of 1, 3, and 10\,\%. For the 10\,\% contrast, $10^5$ FFT segments were sufficient for estimating the susceptibility (\figrefb{fig:trialnr}\,\panel[iv]{E}). The estimate converged similarly as in the noise-split configuration (\figrefb{fig:trialnr}\,\panel[i]{E}). For 3\,\% contrast, one million segments were just sufficient (\figrefb{fig:trialnr}\,\panel[iii]{E}) and for 1\,\% contrast even $10^7$ segments were sometimes not enough for a good estimate (\figrefb{fig:trialnr}\,\panel[ii]{E}). This again demonstrates the detrimental effect of smaller signal-to-noise ratios on the estimate of the second-order susceptibility and the advantage of the noise-split method.
As pointed out in the context of stimulation with sine waves (\figrefb{fig:regimes}), the weakly nonlinear regime can only be observed for sufficiently weak stimulus amplitudes. In the model cells we estimated second-order susceptibilities for RAM stimuli with a contrast of 1, 3, and 10\,\%. For the 10\,\% contrast, $10^5$ FFT segments were sufficient for estimating the susceptibility (\figrefb{fig:trialnr}\,\panel[iv]{E}). The estimate converged similarly as in the noise-split configuration (\figrefb{fig:trialnr}\,\panel[i]{E}). For 3\,\% contrast, one million segments were just sufficient (\figrefb{fig:trialnr}\,\panel[iii]{E}) and for 1\,\% contrast even $10^7$ segments were sometimes not enough for a good estimate (\figrefb{fig:trialnr}\,\panel[ii]{E}). This again demonstrates the detrimental effect of smaller signal-to-noise ratios on the estimate of the second-order susceptibility and the advantage of the noise-split method.
The estimates for 1\,\% contrast (\figrefb{fig:modelsusceptcontrasts}\,\panel[ii]{E}), however, were quite similar to the estimates from the noise-split method, corresponding to a stimulus contrast of 0\,\% ($r=0.92$, $p\ll 0.001$). Thus, RAM stimuli with 1\,\% contrast are sufficiently small to not destroy weakly nonlinear interactions by their linearizing effect. 48\,\% of the model cells have a \nli{} value greater than 1.2.
@ -535,8 +535,7 @@ At a RAM contrast of 3\,\% the \nli{} values become smaller (\figrefb{fig:models
\begin{figure}[tp]
\includegraphics[width=\columnwidth]{modelsusceptlown}
\notejb{We could remove D?}
\notejb{Replace D by C for 100 or 1000 trials?}
\caption{\label{fig:modelsusceptlown}Inferring the triangular structure of the second-order susceptibility from limited data. \figitem{A} Reliably estimating the structure of the second-order susceptibility requires a high number of FFT segements $N$ in the order of one or even ten millions (\figrefb{fig:trialnr}). As an example, susceptibilities of the model cell ``2012-12-21-ak-invivo-1'' (baseline firing rate of 157\,Hz, CV=0.15) are shown for the noise-split configuration ($c=0$\,\%) and RAM stimulus contrasts of $c=1$, $3$, and $10$\,\% as indicated. For contrasts below $10$\,\% this cell shows a nice triangular pattern in its susceptibilities, quite similar to the introductory example of a LIF in \figrefb{fig:lifresponse}. \figitem{B} However, with limited data of $N=10$ trials the susceptibility estimates are noisy and show almost no structure, except for a faint diagonal at the cell's baseline firing rate. \figitem{C} Correlating the estimates of \nli{}, that quantify the height of the ridge where the two stimulus frequencies add up to the neuron's baseline firing rate, based on ten segments (density to the right) with the converged ones based on one or ten million segments at a given stimulus contrast. The black diagonal line is the identity line and the dashed line is a linear regression. The correlation coefficient and corresponding significance level are indicated in the top left corner. The thin vertical line is a threshold at 1.2, the thin horizontal line a threshold at 1.6. The number of cells within each of the resulting four quadrants denote the false positives (top left), true positives (top right), true negatives (bottom left), and false negatives (bottom right). \figitem{D} Relation between the estimates based on ten trials with the one in the noise-split configuration based on one million trials. Cells are categorized as in \subfigref{fig:modelsusceptcontrasts}{E}.}
\caption{\label{fig:modelsusceptlown}Inferring the triangular structure of the second-order susceptibility from limited data. \figitem{A} Reliably estimating the structure of the second-order susceptibility requires a high number of FFT segements $N$ in the order of one or even ten millions. As an example, susceptibilities of the model cell ``2012-12-21-ak-invivo-1'' (baseline firing rate of 157\,Hz, CV=0.15) are shown for the noise-split configuration ($c=0$\,\%) and RAM stimulus contrasts of $c=1$, $3$, and $10$\,\% as indicated. For contrasts below $10$\,\% this cell shows a nice triangular pattern in its susceptibilities, quite similar to the introductory example of a LIF in \figrefb{fig:lifresponse}. \figitem{B} However, with limited data of $N=100$ trials the susceptibility estimates are noisy and show much less structure, except for the anti-diagonal at the cell's baseline firing rate. \figitem{C} Correlating the estimates of SI($r$), that quantify the height of the ridge where the two stimulus frequencies add up to the neuron's baseline firing rate, based on 100 FFT segments (density to the right) with the converged ones based on one or ten million segments at a given stimulus contrast. The black diagonal line is the identity line and the dashed line is a linear regression. The correlation coefficient and corresponding significance level are indicated in the top left corner. The thin vertical line is a threshold at 1.2, the thin horizontal line a threshold at 1.8. The number of cells within each of the resulting four quadrants denote the false positives (top left), true positives (top right), true negatives (bottom left), and false negatives (bottom right) for predicting a triangular structure in the converged susceptibility estimate. \figitem{D} Relation between the estimates based on 100 trials with the one in the noise-split configuration based on one million trials. Cells are categorized as in \subfigref{fig:modelsusceptcontrasts}{E}.}
\end{figure}
\subsection{Weakly nonlinear interactions can be deduced from limited data}
@ -555,7 +554,7 @@ Overall, observing \nli{} values greater than at least 1.6, even for a number of
\begin{figure*}[tp]
\includegraphics[width=\columnwidth]{dataoverview}
\caption{\label{fig:dataoverview} Nonlinear responses in P-units and ampullary cells. The second-order susceptibility is condensed into the peakedness of the nonlinearity, \nli{} \eqnref{eq:nli_equation}, that relates the amplitude of the projected susceptibility at a cell's baseline firing rate to its median (see \subfigrefb{fig:punit}{G}). Each of the recorded neurons contributes at maximum with two stimulus contrasts. Black squares and circles highlight recordings conducted in a single cell. Squares in \panel{A, C, E} correspond to the cell in \figrefb{fig:punit} and circles to the cell in \figrefb{fig:punithighcv}. Squares in \panel{B, D, F} correspond to the cell in \figrefb{fig:ampullary}. \figitem{A, B} There is a negative correlation between the CV during baseline and \nli. \figitem{C, D} There is a negative correlation between the CV during stimulation and \nli. \figitem{E, F} \nli{} is plotted against the response modulation, (see methods), an indicator of the subjective stimulus strength for a cell. There is a negative correlation between response modulation and \nli. Restricting the analysis to the weakest stimulus that was presented to each unique neuron, does not change the results. The number of unique neurons is 221 for P-units and 45 for ampullary cells.
\caption{\label{fig:dataoverview} Nonlinear responses in P-units and ampullary afferents. The second-order susceptibility is condensed into the susceptibility index, SI($r$) \eqnref{eq:nli_equation}, that quantifies the relative amplitude of the projected susceptibility at a cell's baseline firing rate (see \subfigrefb{fig:punit}{G}). Each of the recorded neurons contributes on average two stimulus contrasts. Black squares and circles highlight recordings conducted in a single cell. Squares in \panel{A, C, E} correspond to the cell in \figrefb{fig:punit} and circles to the cell in \figrefb{fig:punithighcv}. Squares in \panel{B, D, F} correspond to the cell in \figrefb{fig:ampullary}. \figitem{A, B} There is a negative correlation between the CV during baseline and \nli. \figitem{C, D} There is a negative correlation between the CV during stimulation and \nli. \figitem{E, F} \nli{} is plotted against the response modulation, (see methods), an indicator of the subjective stimulus strength for a cell. There is a negative correlation between response modulation and \nli. Restricting the analysis to the weakest stimulus that was presented to each unique neuron, does not change the results. The number of unique neurons is 221 for P-units and 45 for ampullary cells.
% The two example P-units shown before are highlighted with dark markers in \subfigrefb{fig:dataoverview}{A, C, E} (squares -- \figrefb{fig:punit}, circles -- \figrefb{fig:punithighcv}).
% Several of the recorded neurons contribute with two samples to the population analysis as their responses have been recorded to two different contrasts of the same RAM stimulus. Higher stimulus contrasts lead to a stronger drive and thus stronger response modulations (see color code bar in \subfigref{fig:dataoverview}{A}, see methods).
% The example cell shown above (\figref{fig:ampullary}) was recorded at two different stimulus intensities and the \nli{} values are highlighted with black squares.

View File

@ -35,7 +35,6 @@ data_path = Path('data') / 'cells'
def load_baseline(path, cell_name):
d = path / f'{cell_name}-baseline.npz'
data = np.load(d)
['eodf', 'isis', 'isih', 'lags', 'corrs', 'freqs', 'prr']
eodf = float(data['eodf'])
rate = float(data['ratebase/Hz'])
cv = float(data['cvbase'])
@ -112,15 +111,15 @@ def plot_response_spectrum(ax, s, eodf, rate, freqs, prr):
ax.show_spines('b')
mask = freqs < 890
ax.plot(freqs[mask], power_db[mask], **s.lsC1)
ax.plot(freqs[eod_i], power_db[eod_i], **s.psFEOD)
ax.plot(freqs[rate_i], power_db[rate_i], **s.psF0)
ax.plot(freqs[eod_i], power_db[eod_i] + 2, **s.psFEOD)
ax.plot(freqs[rate_i], power_db[rate_i] + 2, **s.psF0)
ax.set_xlim(0, 900)
ax.set_ylim(-25, 5)
ax.set_xticks_delta(300)
ax.set_xlabel('$f$', 'Hz')
ax.text(freqs[eod_i], power_db[eod_i] + 2, '$f_{\\rm EOD}$',
ax.text(freqs[eod_i], power_db[eod_i] + 4, '$f_{\\rm EOD}$',
ha='center')
ax.text(freqs[rate_i], power_db[rate_i] + 2, '$r$',
ax.text(freqs[rate_i], power_db[rate_i] + 4, '$r$',
ha='center')
ax.yscalebar(1.05, 0, 10, 'dB', ha='right')
@ -199,7 +198,7 @@ def plot_diagonals(ax, s, fbase, contrast1, freqs1, chi21, contrast2, freqs2, ch
nlips.append(nlip)
nlifs.append(nlif)
print(f' SI at {100*contrast:.1f}% contrast: {nli:.2f}')
ax.axvline(fbase, **s.lsGrid)
#ax.axvline(fbase, **s.lsGrid)
ax.plot(diags[1][0], 1e-3*diags[1][1], **s.lsC2)
ax.plot(diags[0][0], 1e-3*diags[0][1], **s.lsC1)
ax.plot(nlifs[1], 1e-3*nlips[1], **s.psC2)
@ -216,7 +215,7 @@ def plot_diagonals(ax, s, fbase, contrast1, freqs1, chi21, contrast2, freqs2, ch
ax.text(nlifs[0] - 50, 1e-3*nlips[0], f'{100*contrast1:.0f}\\%',
ha='right')
ax.text(nlifs[0] + 70, 1e-3*nlips[0], f'SI={nlis[0]:.1f}')
ax.text(fbase, 4.3, '$r$', ha='center')
#ax.text(fbase, 4.3, '$r$', ha='center')
if __name__ == '__main__':
@ -250,6 +249,9 @@ if __name__ == '__main__':
*example_cell[0])
fcutoff2, contrast2, freqs2, gain2, chi22 = load_spectra(data_path,
*example_cell[1])
print(f' contrast1: {100*contrast1:4.1f}% contrast2: {100*contrast2:4.1f}%')
print(f' fcutoff1 : {fcutoff1:3.0f}Hz fcutoff2 : {fcutoff2:3.0f}Hz')
print(f' duration1: {time1[-1]:4.1f}s duration2: {time2[-1]:4.1f}s')
s = plot_style()
s.cell_color1 = s.punit_color1

View File

@ -153,7 +153,7 @@ def plot_raster(ax, s, spikes, tmax):
def compute_power(path, contrast, spikes, nfft, dt):
if not path.exists():
print(f' Compute power spectrum for contrast = {100*contrast:4.1f}%')
print(f'Compute power spectrum for contrast = {100*contrast:4.1f}%')
psds = []
time = np.arange(nfft + 1)*dt
tmax = nfft*dt
@ -167,7 +167,7 @@ def compute_power(path, contrast, spikes, nfft, dt):
np.savez(path, nfft=nfft, deltat=dt, nsegs=len(spikes),
freqs=freqs, prr=prr)
else:
print(f' Load power spectrum for contrast = {100*contrast:4.1f}%')
print(f'Load power spectrum for contrast = {100*contrast:4.1f}%')
data = np.load(path)
freqs = data['freqs']
prr = data['prr']
@ -267,12 +267,16 @@ def plot_peaks(ax, s, alphas, contrasts, powerf1, powerf2, powerfsum,
ax.text(100*alpha, ymax, tag, ha='center')
#ax.axvline(contrast, **s.lsGrid)
#ax.text(contrast, 630, tag, ha='center')
ax.axvline(1.2, **s.lsLine)
ax.axvline(3.5, **s.lsLine)
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
ax.text(1.2/2, yoffs, 'linear\nregime',
ax.text(cthresh1/2, yoffs, 'linear\nregime',
ha='center', va='center')
ax.text((1.2 + 3.5)/2, yoffs, 'weakly\nnonlinear\nregime',
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')
@ -294,8 +298,10 @@ if __name__ == '__main__':
cell = cell_parameters(parameters, model_cell)
nfft = 2**18
print(f'Loaded data for cell {model_cell}: '
f'baseline rate = {ratebase:.0f}Hz, CV = {cvbase:.2f}')
print(f'Loaded data for cell {model_cell}: ')
print(f' baseline rate = {ratebase:.0f}Hz, CV = {cvbase:.2f}')
print(f' f1 = {beatf1:.0f}Hz, f2 = {beatf2:.0f}Hz')
print()
s = plot_style()
fig, (axes, axa) = plt.subplots(2, 1, height_ratios=[4, 3],