Spatial Adaptation of Primate Retinal Ganglion Cells Between Artificial and Natural Stimuli.
The 6 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
- [1] § Methods › Training ↔ training/cross_stimulus_training.py, the whole file · a weak match · score 0.67 · ReduceLROnPlateau, learning rate scheduler, Adam, patience, epochs, optimizer
- [2] § Methods › Training ↔ ln_model_factorized_marmoset_nm.py, lines 272–350 · score 0.66 · ReduceLROnPlateau, learning rate scheduler, Adam, patience, epochs, optimizer
- [3] § Methods › Power spectra calculation ↔ notebooks/adaptation_paper_figures.ipynb, lines 848–884 · score 0.61 · Fourier Transform, frequency components, FFT, spectra, filtered
- [4] § Methods › Receptive field size and surround amplitude estimation ↔ notebooks/atick_redlich.ipynb, lines 89–194 · score 0.57 · whitening filter, NM spectra, styles, Power, noise, midget
- [5] § Methods › Data acquisition and stimuli ↔ datasets/natural_stimuli/create_data.py, lines 148–205 · score 0.54 · refresh rate, movements, naturalistic stimuli, pixels
- [6] § Methods › Receptive field size and surround amplitude estimation ↔ notebooks/adaptation_paper_figures.ipynb, lines 276–347 · score 0.53 · natural movies, white noise, boxes, styles, parasol, midget
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 1,089 lines · 51 KB · no license · 2 matches
- # %%
- %load_ext autoreload
- # %%
- %autoreload
- # %%
- import matplotlib.pyplot as plt
- import numpy as np
- import torch
- import seaborn as sns
- import pickle
- import pandas as pd
- import matplotlib
- from evaluations.sizes_estimations import handle_filter, get_filter_sign, fit_dog, center_filter
- from scipy.signal import find_peaks
- from tqdm import tqdm
- from scipy.stats import wilcoxon
- from scipy.stats import binned_statistic
- # %% [markdown]
- # ### Cell types
- # %% [markdown]
- # | | **SN** | **SS** |
- # | ------------| ------ | ------ |
- # | Midget OFF | 0 | 0 |
- # | Parasol OFF | 1 | 1,10 |
- # | Large OFF | 2,3,6 | 2 |
- # | Midget ON | 29 | 11 |
- # | Parasol ON | 28 | 9 |
- # %%
- cell_types_r1 = np.load('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/SN_cell_classification.npy')
- cell_types_r2 = np.load('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/SS_cell_classification.npy')
- cell_types_r4 = np.load('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/ON_DS_cell_classification.npy')
- # %%
- def translate_cell_types(cell_types, translation_dict):
- for i in range(cell_types.shape[0]):
- if cell_types[i][1].item() in translation_dict.keys():
- cell_types[i, 3] = int(translation_dict[cell_types[i][1].item()])
- print(f'specific type: {cell_types[i][1].item()} general type: {translation_dict[cell_types[i][1].item()]}')
- else:
- cell_types[i, 3] = cell_types[i][1]
- return cell_types
- # %%
- r1_translation_dict = {0:0, 2:2, 3:2, 4:24, 6:2, 29:3, 28:4}
- r4_translation_dict = {0:1, 1:4, 2:-1, 5:0, 3:-1, 4:-1}
- # %%
- g_cell_types_r4 = np.zeros((cell_types_r4.shape[0], cell_types_r4.shape[1]+1))
- g_cell_types_r4[:, :-1] = cell_types_r4
- # %%
- g_cell_types_r1 = np.zeros((cell_types_r1.shape[0], cell_types_r1.shape[1]+1))
- g_cell_types_r1[:, :-1] = cell_types_r1
- # %%
- cell_types_r1 = translate_cell_types(g_cell_types_r1, r1_translation_dict)
- # %%
- cell_types_r4 = translate_cell_types(g_cell_types_r4, r4_translation_dict)
- # %% [markdown]
- # # Loading performances
- #
- # #### Rank 1 LN model
- # %%
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/test_correlations_r1_r2_r4.pkl', 'rb') as file:
- performances = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/nm_for_wn_test_correlations_r1_r4_finetuned_temp.pkl', 'rb') as f:
- nm_for_wn_performances = pickle.load(f)
- # %%
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/nm_test_correlations_r1_r2_r4.pkl', 'rb') as file:
- nm_performances = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/wn_for_nm_test_correlations_r1_r4_finetuned_temp.pkl', 'rb') as f:
- wn_for_nm_performances = pickle.load(f)
- # %% [markdown]
- # #### Rank 2 LN model
- # %%
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/cs_nm_test_correlations_r1_r4.pkl', 'rb') as file:
- cs_nm_performances = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/cs_nm_for_wn_test_correlations_r1_r4_finetuned_temp.pkl', 'rb') as file:
- cs_nm_for_wn_performances = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/cs_wn_test_correlations_r1_r4.pkl', 'rb') as file:
- cs_wn_performances = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/ln_model_performances/cs_wn_for_nm_test_correlations_r1_r4_finetuned_temp.pkl', 'rb') as file:
- cs_wn_for_nm_performances = pickle.load(file)
- # %%
- df = pd.DataFrame()
- for retina, r_dict in performances.items():
- if retina == '01':
- cell_types = cell_types_r1
- elif retina == '04':
- cell_types = cell_types_r4
- elif retina == '02':
- cell_types = None
- for cell, performance in r_dict.items():
- if cell in nm_performances[retina].keys():
- row = {'retina': retina, 'cell_name': cell,
- 'wn_cc': performance,
- 'nm_cc': nm_performances[retina][cell],
- 'wn_for_nm_cc': wn_for_nm_performances[retina][cell] if retina != '02' else None,
- "nm_for_wn_cc": nm_for_wn_performances[retina][cell] if retina != '02' else None,
- 'cs_wn_cc': cs_wn_performances[retina][cell] if retina != '02' else None,
- 'cs_nm_cc': cs_nm_performances[retina][cell] if retina != '02' else None,
- 'cs_wn_for_nm_cc': cs_wn_for_nm_performances[retina][cell] if retina != '02' else None,
- "cs_nm_for_wn_cc": cs_nm_for_wn_performances[retina][cell] if retina != '02' else None,
- 'cell_class_r': None if cell_types is None else int(cell_types[cell][1]), 'cell_class_g': None if cell_types is None else int(cell_types[cell][3])}
- row_df = pd.DataFrame([row])
- df = pd.concat([df, row_df])
- df = df.reset_index()
- # %% [markdown]
- # # Learned filters
- # %%
- class Filter:
- def __init__(self, f, kind='spat'):
- self.f = f
- self.type = kind
- # %%
- def add_filters(retina, filter_dict, df, spat_col, temp_col, center_col_x, center_col_y):
- if spat_col not in df.columns:
- df[spat_col] = pd.Series(dtype='object')
- df[temp_col] = pd.Series(dtype='object')
- for key in filter_dict.keys():
- spat_filter = filter_dict[key][0]
- if center_col_x is not None:
- x = df[(df['retina'] == retina) & (df['cell_name'] == key)][center_col_x].tolist()
- y = df[(df['retina'] == retina) & (df['cell_name'] == key)][center_col_y].tolist()
- if len(x) > 0:
- x = x[0]
- y = y[0]
- else:
- continue
- spat_filter = center_filter(spat_filter, mu=(x, y))
- spat = Filter(spat_filter, kind='spat')
- temp = Filter(filter_dict[key][1], kind='temp')
- df.loc[(df['retina'] == retina) & (df['cell_name'] == key), spat_col] = spat
- df.loc[(df['retina'] == retina) & (df['cell_name'] == key), temp_col] = temp
- def add_size_or_amp(retina, value_dict, df, size_col):
- for key in value_dict.keys():
- f = value_dict[key]
- df.loc[(df['retina'] == retina) & (df['cell_name'] == key),size_col] = f
- # %%
- def temp_sizes_for_cells(filter_dict, plot=False):
- s, a = {}, {}
- for key in tqdm(filter_dict.keys()):
- s[key], a[key] = handle_filter(filter_dict[key][0].reshape((15,15)), plot=plot)
- # t_s[key], t_a[key] = handle_temp_filter(filter_dict[key][1])
- return s, a
- # %%
- def spatial_info(filter_dict, plot=False):
- s, a, c = {}, {}, {}
- for key in filter_dict.keys():
- s[key], a[key], c[key] = handle_filter(filter_dict[key][0].reshape((15,15)), plot=plot)
- return s, a, c
- # %% [markdown]
- # ### Loading saved rank 1 LN model weights
- #
- # #### White noise filters
- # %%
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/sc_wn_r1_v2', 'rb') as file:
- wn_sc_r1 = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/sc_wn_r4_v2', 'rb') as file:
- wn_sc_r4 = pickle.load(file)
- # %% [markdown]
- # #### Natural movie filters
- # %%
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/sc_nm_r1_v2', 'rb') as file:
- nm_sc_r1 = pickle.load(file)
- with open('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/benchmark_paper/results/sc_nm_r4_v2', 'rb') as file:
- nm_sc_r4 = pickle.load(file)
- # %% [markdown]
- # #### Adding sizes and amplitudes to DataFrame
- # %%
- s, a, c = spatial_info(wn_sc_r1, plot=True)
- c_x = {key: np.round(v[0]) for key, v in c.items()}
- c_y = {key: np.round(v[1]) for key, v in c.items()}
- add_size_or_amp(retina='01', value_dict=s, df=df, size_col='wn_size')
- add_size_or_amp(retina='01', value_dict=a, df=df, size_col='wn_amp')
- add_size_or_amp(retina='01', value_dict=c_x, df=df, size_col='wn_center_x')
- add_size_or_amp(retina='01', value_dict=c_y, df=df, size_col='wn_center_y')
- s, a, c = spatial_info(wn_sc_r4, plot=True)
- c_x = {key: np.round(v[0]) for key, v in c.items()}
- c_y = {key: np.round(v[1]) for key, v in c.items()}
- add_size_or_amp(retina='04', value_dict=s, df=df, size_col='wn_size')
- add_size_or_amp(retina='04', value_dict=a, df=df, size_col='wn_amp')
- add_size_or_amp(retina='04', value_dict=c_x, df=df, size_col='wn_center_x')
- add_size_or_amp(retina='04', value_dict=c_y, df=df, size_col='wn_center_y')
- # %%
- s, a,c = spatial_info(nm_sc_r1, plot=True)
- c_x = {key: np.round(v[0]) for key, v in c.items()}
- c_y = {key: np.round(v[1]) for key, v in c.items()}
- add_size_or_amp(retina='01', value_dict=s, df=df, size_col='nm_size')
- add_size_or_amp(retina='01', value_dict=a, df=df, size_col='nm_amp')
- add_size_or_amp(retina='01', value_dict=c_x, df=df, size_col='nm_center_x')
- add_size_or_amp(retina='01', value_dict=c_y, df=df, size_col='nm_center_y')
- s, a, c = spatial_info(nm_sc_r4, plot=True)
- c_x = {key: np.round(v[0]) for key, v in c.items()}
- c_y = {key: np.round(v[1]) for key, v in c.items()}
- add_size_or_amp(retina='04', value_dict=s, df=df, size_col='nm_size')
- add_size_or_amp(retina='04', value_dict=a, df=df, size_col='nm_amp')
- add_size_or_amp(retina='04', value_dict=c_x, df=df, size_col='nm_center_x')
- add_size_or_amp(retina='04', value_dict=c_y, df=df, size_col='nm_center_y')
- # %%
- df
- # %% [markdown]
- # #### Adding actual filters to DataFrame
- # %%
- add_filters(retina='01', filter_dict= nm_sc_r1, df=df, spat_col='nm_spat', temp_col='nm_temp', center_col_x='nm_center_x', center_col_y='nm_center_y')
- add_filters(retina='04', filter_dict= nm_sc_r4, df=df, spat_col='nm_spat', temp_col='nm_temp', center_col_x='nm_center_x', center_col_y='nm_center_y')
- # %%
- add_filters(retina='01', filter_dict= wn_sc_r1, df=df, spat_col='wn_spat', temp_col='wn_temp', center_col_x='wn_center_x', center_col_y='wn_center_y')
- add_filters(retina='04', filter_dict= wn_sc_r4, df=df, spat_col='wn_spat', temp_col='wn_temp', center_col_x='wn_center_x', center_col_y='wn_center_y')
- # %%
- df = df.reset_index()
- # %%
- colors = ['#F25764', '#FCC9BD', '#39B4E4', '#0871CD', '#011126']
- light_colors = ['#FF5C69', '#DFB2A7', '#39B3E3', '#0871CC']
- dark_colors = ['gray']*4
- # %% [markdown]
- # # Performance figure
- # %%
- from matplotlib.patches import Patch
- def plot_boxplot(cell_values, labels, save_file):
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- sns.set_style("whitegrid")
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none' # This keeps text as text in SVG files
- fig, ax = plt.subplots(figsize=(3., 1.7))
- bar_types = ['WN', 'NM', 'WN', 'NM', 'WN', 'NM', 'WN', 'NM']
- new_order = ['Midget OFF', 'Parasol OFF', 'Large OFF', 'Parasol ON']
- data = None
- for i, values in enumerate(cell_values):
- for value in values:
- if value == 'N/A':
- continue
- if data is None:
- data = pd.DataFrame({'model_type': labels[i], 'correlation': value, 'type': bar_types[i]}, index=[0])
- else:
- data = pd.concat([data, pd.DataFrame({'model_type': labels[i], 'correlation': value, 'type': bar_types[i]}, index=[0])], ignore_index= True)
- data['Group'] = data['type'] + ' ' + data['model_type']
- data['Hue'] = list(zip(data['model_type'], data['type']))
- data['model_type'] = data['model_type'].astype('category')
- palette = colors
- flierprops = dict(marker='o', markerfacecolor='silver', markeredgecolor='None', markersize=3, linestyle='none')
- box = sns.boxplot(x='model_type', y='correlation', data=data, hue='type', palette=palette, dodge=True, order=new_order, flierprops=flierprops)
- sns.stripplot(x='model_type', y='correlation', data=data, hue='type', size=1, color="black", dodge=True, label='_nolegend_', order=new_order)
- for i, artist in enumerate(ax.artists):
- # Fully filled boxes for the wn value in each pair
- if i % 2 == 0:
- artist.set_facecolor(palette[i//2])
- artist.set_edgecolor('black')
- else:
- # Striped boxes for the nm value in each pair
- artist.set_facecolor(palette[i//2])
- artist.set_hatch('/////')
- artist.set_linewidth(0.5)
- artist.set_edgecolor('black')
- ax.grid(False)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['left'].set_color('black')
- ax.spines['bottom'].set_color('black')
- ax.tick_params(bottom=True, top=False, left=True, right=False)
- legend_patches = [
- Patch(facecolor='white', edgecolor='black', label='White noise'),
- Patch(facecolor='white', edgecolor='black', hatch='/////', label='Natural movies')
- ]
- plt.legend(handles=legend_patches)
- sns.despine(offset=5)
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg',
- transparent=True, bbox_inches='tight', dpi=300)
- plt.show()
- return data
- # %% [markdown]
- # ### Loading rank-1 model cell-type specific performances
- # %%
- wn_midget_off = df[(df['cell_class_g'] == 0)]['wn_cc'].tolist()
- wn_parasol_off = df[(df['cell_class_g'] == 1)]['wn_cc'].tolist()
- wn_large_off = df[(df['cell_class_g'] == 2)]['wn_cc'].tolist()
- wn_midget_on = df[(df['cell_class_g'] == 3)]['wn_cc'].tolist()
- wn_parasol_on = df[(df['cell_class_g'] == 4)]['wn_cc'].tolist()
- nm_midget_off = df[(df['cell_class_g'] == 0)]['nm_cc'].tolist()
- nm_parasol_off = df[(df['cell_class_g'] == 1)]['nm_cc'].tolist()
- nm_large_off = df[(df['cell_class_g'] == 2)]['nm_cc'].tolist()
- nm_midget_on = df[(df['cell_class_g'] == 3)]['nm_cc'].tolist()
- nm_parasol_on = df[(df['cell_class_g'] == 4)]['nm_cc'].tolist()
- wn_for_nm_midget_off = df[(df['cell_class_g'] == 0)]['wn_for_nm_cc'].tolist()
- wn_for_nm_parasol_off = df[(df['cell_class_g'] == 1)]['wn_for_nm_cc'].tolist()
- wn_for_nm_large_off = df[(df['cell_class_g'] == 2)]['wn_for_nm_cc'].tolist()
- wn_for_nm_midget_on = df[(df['cell_class_g'] == 3)]['wn_for_nm_cc'].tolist()
- wn_for_nm_parasol_on = df[(df['cell_class_g'] == 4)]['wn_for_nm_cc'].tolist()
- nm_for_wn_midget_off = df[(df['cell_class_g'] == 0)]['nm_for_wn_cc'].tolist()
- nm_for_wn_parasol_off = df[(df['cell_class_g'] == 1)]['nm_for_wn_cc'].tolist()
- nm_for_wn_large_off = df[(df['cell_class_g'] == 2)]['nm_for_wn_cc'].tolist()
- nm_for_wn_midget_on = df[(df['cell_class_g'] == 3)]['nm_for_wn_cc'].tolist()
- nm_for_wn_parasol_on = df[(df['cell_class_g'] == 4)]['nm_for_wn_cc'].tolist()
- # %% [markdown]
- # #### Loading rank-2 cell-specific performances
- # %%
- cs_wn_midget_off = df[(df['cell_class_g'] == 0)]['cs_wn_cc'].tolist()
- cs_wn_parasol_off = df[(df['cell_class_g'] == 1)]['cs_wn_cc'].tolist()
- cs_wn_large_off = df[(df['cell_class_g'] == 2)]['cs_wn_cc'].tolist()
- cs_wn_midget_on = df[(df['cell_class_g'] == 3)]['cs_wn_cc'].tolist()
- cs_wn_parasol_on = df[(df['cell_class_g'] == 4)]['cs_wn_cc'].tolist()
- cs_nm_midget_off = df[(df['cell_class_g'] == 0)]['cs_nm_cc'].tolist()
- cs_nm_parasol_off = df[(df['cell_class_g'] == 1)]['cs_nm_cc'].tolist()
- cs_nm_large_off = df[(df['cell_class_g'] == 2)]['cs_nm_cc'].tolist()
- cs_nm_midget_on = df[(df['cell_class_g'] == 3)]['cs_nm_cc'].tolist()
- cs_nm_parasol_on = df[(df['cell_class_g'] == 4)]['cs_nm_cc'].tolist()
- cs_wn_for_nm_midget_off = df[(df['cell_class_g'] == 0)]['cs_wn_for_nm_cc'].tolist()
- cs_wn_for_nm_parasol_off = df[(df['cell_class_g'] == 1)]['cs_wn_for_nm_cc'].tolist()
- cs_wn_for_nm_large_off = df[(df['cell_class_g'] == 2)]['cs_wn_for_nm_cc'].tolist()
- cs_wn_for_nm_midget_on = df[(df['cell_class_g'] == 3)]['cs_wn_for_nm_cc'].tolist()
- cs_wn_for_nm_parasol_on = df[(df['cell_class_g'] == 4)]['cs_wn_for_nm_cc'].tolist()
- cs_nm_for_wn_midget_off = df[(df['cell_class_g'] == 0)]['cs_nm_for_wn_cc'].tolist()
- cs_nm_for_wn_parasol_off = df[(df['cell_class_g'] == 1)]['cs_nm_for_wn_cc'].tolist()
- cs_nm_for_wn_large_off = df[(df['cell_class_g'] == 2)]['cs_nm_for_wn_cc'].tolist()
- cs_nm_for_wn_midget_on = df[(df['cell_class_g'] == 3)]['cs_nm_for_wn_cc'].tolist()
- cs_nm_for_wn_parasol_on = df[(df['cell_class_g'] == 4)]['cs_nm_for_wn_cc'].tolist()
- # %%
- data = plot_boxplot([wn_midget_off, nm_midget_off, wn_parasol_off, nm_parasol_off, wn_large_off, nm_large_off, wn_parasol_on, nm_parasol_on],
- labels=['Midget OFF', 'Midget OFF', 'Parasol OFF', 'Parasol OFF', 'Large OFF', 'Large OFF', 'Parasol ON', 'Parasol ON'], save_file='ln_perfs_boxplot')
- # %%
- data = plot_boxplot([cs_wn_midget_off, cs_nm_midget_off, cs_wn_parasol_off, cs_nm_parasol_off, cs_wn_large_off, cs_nm_large_off, cs_wn_parasol_on, cs_nm_parasol_on],
- labels=['Midget OFF', 'Midget OFF', 'Parasol OFF', 'Parasol OFF', 'Large OFF', 'Large OFF', 'Parasol ON', 'Parasol ON'], save_file='ln_perfs_boxplot')
- # %%
- def plot_single_wn_nm(ax, wn, nm, title, color):
- ax.grid(False)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['bottom'].set_color('black')
- ax.spines['left'].set_color('black')
- ax.tick_params(bottom=True, top=False, left=True, right=False)
- ax.scatter(nm, wn, facecolor=color, edgecolor='None', s=15, alpha=0.6,)
- ax.plot([0,1], [0,1], linestyle='--', color='black', linewidth=0.5)
- plt.title(title)
- def plot_wn_nm(wn, nm, title, color, save_file=None, xlabel='Correlation NM', ylabel='Correlation WN', xlim=0.15, ylim=0.15,
- figsize=(1.7, 1.7)):
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- sns.set_style("whitegrid")
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none' # This keeps text as text in SVG files
- fig, ax = plt.subplots(figsize=figsize)
- plot_single_wn_nm(ax, wn, nm, title=title, color=color)
- plt.xlabel(xlabel)
- plt.ylabel(ylabel)
- plt.xlim(xlim)
- plt.ylim(ylim)
- sns.despine(offset=5)
- plt.tight_layout()
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg', dpi=300, transparent=True,
- bbox_inches='tight')
- plt.show()
- def plot_shared_wn_nm(wns, nms, figsize, colors, title, save_file=None):
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- sns.set_style("whitegrid")
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none' # This keeps text as text in SVG files
- fig, ax = plt.subplots(figsize=figsize)
- for i, (wn, nm) in enumerate(zip(wns, nms)):
- clean_nm = [x for x,y in zip(nm, wn) if x!= 'N/A' and y != 'N/A']
- clean_wn = [y for x,y in zip(nm, wn) if x!= 'N/A' and y != 'N/A']
- plot_single_wn_nm(ax, clean_wn, clean_nm, color=colors[i], title='')
- for i, (wn, nm) in enumerate(zip(wns, nms)):
- clean_nm = [x for x,y in zip(nm, wn) if x!= 'N/A' and y != 'N/A']
- clean_wn = [y for x,y in zip(nm, wn) if x!= 'N/A' and y != 'N/A']
- ax.scatter(np.nanmean(clean_nm), np.nanmean(clean_wn), facecolor=colors[i], marker='X', s=20, edgecolor='black', linewidth=0.5)
- plt.xlabel('CC in domain')
- plt.ylabel('CC out of domain')
- plt.xlim(0.1, 1)
- plt.ylim(0.1, 1)
- plt.title(title)
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg', dpi=300, transparent=True)
- plt.show()
- # %%
- plot_shared_wn_nm([nm_for_wn_midget_off, nm_for_wn_parasol_off, nm_for_wn_large_off, nm_for_wn_parasol_on], [wn_midget_off, wn_parasol_off, wn_large_off, wn_parasol_on],
- (1.5,1.5), colors,
- title='Performance on WN', save_file='WN_scatter_plot')
- plot_shared_wn_nm([wn_for_nm_midget_off, wn_for_nm_parasol_off, wn_for_nm_large_off, wn_for_nm_parasol_on], [nm_midget_off, nm_parasol_off, nm_large_off, nm_parasol_on],
- (1.5,1.5), colors,
- title='Performance on NM', save_file='NM_scatter_plot')
- # %%
- plot_shared_wn_nm([cs_nm_for_wn_midget_off, cs_nm_for_wn_parasol_off, cs_nm_for_wn_large_off, cs_nm_for_wn_parasol_on], [cs_wn_midget_off, cs_wn_parasol_off, cs_wn_large_off, cs_wn_parasol_on],
- (1.5,1.5), colors,
- title='Performance on WN', save_file='WN_scatter_plot')
- plot_shared_wn_nm([cs_wn_for_nm_midget_off, cs_wn_for_nm_parasol_off, cs_wn_for_nm_large_off, cs_wn_for_nm_parasol_on], [cs_nm_midget_off, cs_nm_parasol_off, cs_nm_large_off, cs_nm_parasol_on],
- (1.5,1.5), colors,
- title='Performance on NM', save_file='NM_scatter_plot')
- # %%
- plot_wn_nm(wn_midget_off, nm_midget_off, title='Midget OFF', color=colors[0], save_file='scatter_perf_midged_off')
- plot_wn_nm(wn_parasol_off, nm_parasol_off, title='Parasol OFF', color=colors[1], save_file='scatter_perf_parasol_off')
- plot_wn_nm(wn_large_off, nm_large_off, title='Large OFF', color=colors[2], save_file='scatter_perf_large_off')
- plot_wn_nm(wn_parasol_on, nm_parasol_on, title='Parasol ON', color=colors[3], save_file='scatter_perf_parasol_on')
- # %%
- limit = 0.15
- plot_wn_nm(nm=wn_midget_off, wn=nm_for_wn_midget_off, title='Midget OFF', color=colors[0], save_file='wn_vs_nm_for_wn_perf_midged_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=wn_parasol_off, wn=nm_for_wn_parasol_off, title='Parasol OFF', color=colors[1], save_file='wn_vs_nm_for_wn_perf_parasol_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=wn_large_off, wn=nm_for_wn_large_off, title='Large OFF', color=colors[2], save_file='wn_vs_nm_for_wn_perf_large_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=wn_parasol_on, wn=nm_for_wn_parasol_on, title='Parasol ON', color=colors[3], save_file='wn_vs_nm_for_wn_perf_parasol_on',
- xlabel='CC in', ylabel='CC out ', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- # %%
- plot_wn_nm(nm=nm_midget_off, wn=wn_for_nm_midget_off, title='Midget OFF', color=colors[0], save_file='nm_vs_wn_for_nm_perf_midged_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=nm_parasol_off, wn=wn_for_nm_parasol_off, title='Parasol OFF', color=colors[1], save_file='nm_vs_wn_for_nm_perf_parasol_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=nm_large_off, wn=wn_for_nm_large_off, title='Large OFF', color=colors[2], save_file='nm_vs_wn_for_nm_perf_large_off',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- plot_wn_nm(nm=nm_parasol_on, wn=wn_for_nm_parasol_on, title='Parasol ON', color=colors[3], save_file='nm_vs_wn_for_nm_perf_parasol_on',
- xlabel='CC in', ylabel='CC out', xlim=limit, ylim=limit,
- figsize=(1.2,1.2))
- # %%
- def plot_traces(responses, outputs, start_index, length, color, dark_color=None, title=None, save_file=None):
- out_domain = None
- if isinstance(responses, list):
- out_domain = responses[1]
- responses = responses[0]
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- sns.set_style("whitegrid")
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none'
- fig, ax = plt.subplots(figsize=(3.2,0.6))
- ax.tick_params(bottom=True, top=False, left=True, right=False)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['bottom'].set_color('black')
- ax.spines['left'].set_color('black')
- ax.grid(False)
- plt.plot(np.linspace(0, length/85, length), outputs[start_index:start_index+length]*85, label='Responses', color='black', linewidth=0.5, linestyle='--')
- if out_domain is not None:
- plt.plot(np.linspace(0, length/85, length), out_domain[start_index:start_index+length]*85, label='Predictions out of domain', color=dark_color, linewidth=0.5)
- plt.plot(np.linspace(0, length/85, length), responses[start_index:start_index+length]*85, label='Predictions' if out_domain is None else 'Predictions in domain', color=color, linewidth=0.5)
- plt.xlabel('Time (s)')
- plt.ylabel('Hz')
- plt.ylim(0,170)
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures//{save_file}.svg', transparent=True, dpi=300, bbox_inches='tight')
- plt.show()
- def get_outputs_and_responses(path, setting_str=''):
- outputs = np.load(f'{path}/{setting_str}test_outputs.npy')
- responses = np.load(f'{path}/{setting_str}test_responses.npy')
- return outputs, responses
- # %%
- # midget cell
- c_67_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina01/cell_67/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/stats/seed_8/')
- c_67_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina01/cell_67/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/stats/seed_8/')
- c_67_wn_for_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina01/cell_67/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/wn_for_nm/ff_0_pn_1/stats/seed_8/',
- setting_str='wn_for_nm_')
- c_67_nm_for_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina01/cell_67/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/nm_for_wn/ff_0_pn_1/stats/seed_8/',
- setting_str='nm_for_wn_')
- # parasol off cell
- c_93_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina04/cell_93/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/stats/seed_8/')
- c_93_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina04/cell_93/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/stats/seed_8/')
- c_93_wn_for_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina04/cell_93/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/wn_for_nm/ff_0_pn_1/stats/seed_8/',
- setting_str='wn_for_nm_')
- c_93_nm_for_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina04/cell_93/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/nm_for_wn/ff_0_pn_1/stats/seed_8/',
- setting_str='nm_for_wn_')
- # large off cell
- c_294_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina01/cell_294/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0//stats/seed_8/')
- c_294_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina01/cell_294/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/stats/seed_8/')
- c_294_wn_for_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina01/cell_294/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/wn_for_nm/ff_0_pn_1/stats/seed_8/',
- setting_str='wn_for_nm_')
- c_294_nm_for_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina01/cell_294/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/nm_for_wn/ff_0_pn_1/stats/seed_8/',
- setting_str='nm_for_wn_')
- # parason ON cell
- c_2_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina04/cell_2/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/stats/seed_8/')
- c_2_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina04/cell_2/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/stats/seed_8/')
- c_2_wn_for_nm = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized__ete/marmoset/retina04/cell_2/lr_0.005_whole_rf_15_ch_30_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_fn_1_do_n_1_dog_0/wn_for_nm/ff_0_pn_1/stats/seed_8/',
- setting_str='wn_for_nm_')
- c_2_nm_for_wn = get_outputs_and_responses('/usr/users/vystrcilova/retinal_circuit_modeling/models/ln_models/ln_models_factorized_4_ete_nm/marmoset/retina04/cell_2/lr_0.005_whole_rf_15_ch_30_l1_0.0_l2_0.0_g_0.0_bs_32_tr_250_s_1_c_0_n_0_fn_1_do_n_1_flipped_gf_0_dog_0/nm_for_wn/ff_0_pn_1/stats/seed_8/',
- setting_str='nm_for_wn_')
- # %%
- plot_traces([c_67_nm[0], c_67_wn_for_nm[0]], c_67_nm[1], start_index=960, length=100, color=colors[0], dark_color=dark_colors[0], save_file='NM_Midget_OFF')
- plot_traces([c_67_wn[0], c_67_nm_for_wn[0]], c_67_wn[1], start_index=100, length=100, color=colors[0], dark_color=dark_colors[0], save_file='WN_Midget_OFF')
- plot_traces([c_93_nm[0], c_93_nm_for_wn[0]], c_93_nm[1], start_index=400, length=100, color=colors[1], dark_color=dark_colors[1], save_file='NM_Parasol_OFF')
- plot_traces([c_93_wn[0], c_93_wn_for_nm[0]], c_93_wn[1], start_index=200, length=100, color=colors[1], dark_color=dark_colors[1], save_file='WN_Parasol_OFF')
- plot_traces([c_294_nm[0], c_294_wn_for_nm[0]], c_294_nm[1], start_index=930, length=100, color=colors[2], dark_color=dark_colors[2], save_file='NM_Large_OFF')
- plot_traces([c_294_wn[0], c_294_nm_for_wn[0]], c_294_wn[1], start_index=420, length=100, color=colors[2], dark_color=dark_colors[2], save_file='WN_Large_OFF')
- plot_traces([c_2_nm[0], c_2_wn_for_nm[0]], c_2_nm[1], start_index=820, length=100, color=colors[3], dark_color=dark_colors[3], save_file='NM_Parasol_ON')
- plot_traces([c_2_wn[0], c_2_nm_for_wn[0]], c_2_wn[1], start_index=100, length=100, color=colors[3], dark_color=dark_colors[3], save_file='WN_Parasol_ON')
- # %% [markdown]
- # #### Calculating mean cell specific performances
- # %%
- print("WN")
- print('midget off', np.mean(wn_midget_off), np.std(wn_midget_off))
- print('parasol off', np.mean(wn_parasol_off), np.std(wn_parasol_off))
- print('large off', np.mean(wn_large_off), np.std(wn_large_off))
- print('parasol on', np.mean(wn_parasol_on), np.std(wn_parasol_on))
- print("NM")
- print('midget off', np.mean(nm_midget_off), np.std(nm_midget_off))
- print('parasol off', np.mean(nm_parasol_off), np.std(nm_parasol_off))
- print('large off', np.mean(nm_large_off), np.std(nm_large_off))
- print('parasol on', np.mean(nm_parasol_on), np.std(nm_parasol_on))
- # %%
- print("WN")
- print('midget off', np.mean(cs_wn_midget_off), np.std(cs_wn_midget_off))
- print('parasol off', np.mean(cs_wn_parasol_off), np.std(cs_wn_parasol_off))
- print('large off', np.mean(cs_wn_large_off), np.std(cs_wn_large_off))
- print('parasol on', np.mean(cs_wn_parasol_on), np.std(cs_wn_parasol_on))
- print("NM")
- print('midget off', np.mean(cs_nm_midget_off), np.std(cs_nm_midget_off))
- print('parasol off', np.mean(cs_nm_parasol_off), np.std(cs_nm_parasol_off))
- print('large off', np.mean(cs_nm_large_off), np.std(cs_nm_large_off))
- print('parasol on', np.mean(cs_nm_parasol_on), np.std(cs_nm_parasol_on))
- # %%
- print("WN for NM")
- print('midget off', np.mean(wn_for_nm_midget_off), np.std(wn_for_nm_midget_off))
- print('parasol off', np.mean(wn_for_nm_parasol_off), np.std(wn_for_nm_parasol_off))
- print('large off', np.mean(wn_for_nm_large_off), np.std(wn_for_nm_large_off))
- print('parasol on', np.mean(wn_for_nm_parasol_on), np.std(wn_for_nm_parasol_on))
- print("NM for WN")
- print('midget off', np.mean(nm_for_wn_midget_off), np.std(nm_for_wn_midget_off))
- print('parasol off', np.mean(nm_for_wn_parasol_off), np.std(nm_for_wn_parasol_off))
- print('large off', np.mean(nm_for_wn_large_off), np.std(nm_for_wn_large_off))
- print('parasol on', np.mean(nm_for_wn_parasol_on), np.std(nm_for_wn_parasol_on))
- # %% [markdown]
- # ## Spatial filter figure
- # %%
- import os
- def get_spectrum(path):
- s = np.load(path)
- s = np.mean(s, axis=0)
- s = clean_peaks(s)
- s = s/s[0]
- return s
- def load_sc_spectra(path, cell_type, retinas, cell_stimulus, stimulus):
- spectra = []
- for retina in retinas:
- files = os.listdir(f'{path}/retina_{int(retina)}')
- for file in files:
- if f'stim_{stimulus}_f_F_{cell_stimulus}_{cell_type}' in file:
- print(file)
- try:
- s = get_spectrum(f'{path}/retina_{int(retina)}/{file}/spectra.npy')
- except Exception as e:
- print('file', file, 'failed')
- continue
- spectra.append(s)
- return spectra
- def clean_peaks(spectrum):
- peaks, _ = find_peaks(spectrum, height=0.000003)
- x = np.arange(len(spectrum))
- clean_spectrum = spectrum.copy()
- for peak in peaks:
- clean_spectrum[peak] = np.nan
- clean_spectrum = np.interp(x, x[~np.isnan(clean_spectrum)], clean_spectrum[~np.isnan(clean_spectrum)])
- return clean_spectrum
- # %%
- nm_spectrum = np.load('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/spectra/spectra_nm.npy')
- wn_spectrum = np.load('/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/spectra/spectra_wn.npy')
- # %%
- def get_kvals(frame_width, frame_height):
- kfreq_x = np.fft.fftshift(np.fft.fftfreq(frame_width)) * frame_width
- kfreq_y = np.fft.fftshift(np.fft.fftfreq(frame_height)) * frame_height
- kfreq_2d = np.meshgrid(kfreq_x, kfreq_y)
- k_norm = np.sqrt(kfreq_2d[0] ** 2 + kfreq_2d[1] ** 2)
- kbins = np.arange(0.5, max(frame_height, frame_width) // 2 + 1)
- kvals = 0.5 * (kbins[1:] + kbins[:-1])
- return kvals
- # %%
- def get_class_filters(df, cell_class, col, class_type='OFF', retina_index=None):
- sub_df = df[df['cell_class_g'] == cell_class]
- all_filters = None
- idx = 0
- for i, row in sub_df.iterrows():
- f = sub_df.loc[i, col]
- working_filter = f.f.copy()
- if all_filters is None:
- all_filters = np.zeros((sub_df.shape[0], 60, 60))
- sign = get_filter_sign(f.f)
- if (class_type == 'OFF') and (sign > 0):
- print(f'flipping OFF cell {i}')
- working_filter = -working_filter
- elif (class_type == 'ON') and (sign < 0):
- working_filter = -working_filter
- working_filter = torch.nn.functional.upsample_nearest(torch.tensor(working_filter).unsqueeze(0).unsqueeze(0), scale_factor=4).numpy()[0,0]
- working_filter = working_filter/np.sum(np.abs(working_filter))
- max_value = np.max(np.abs(working_filter))
- plt.imshow(working_filter, cmap='coolwarm', vmin=-0.5*max_value, vmax=0.5*max_value)
- plt.title(f'Cell {row["cell_name"]}, retina {row["retina"]}, wn cor {row["wn_cc"]}, nm_cor {row["nm_cc"]}')
- plt.show()
- all_filters[idx] = working_filter
- idx+=1
- return all_filters
- def get_ft_filters(class_filters, norm_value=None):
- ft_filters = np.zeros((class_filters.shape[0], 300))
- for i in range(class_filters.shape[0]):
- ft_f = get_spatial_ft(class_filters[i], norm_value=norm_value)
- ft_filters[i] = ft_f
- return ft_filters
- def radial_average(data, center=None):
- y, x = np.indices((data.shape))
- if center is None:
- center = np.array([x.max() / 2, y.max() / 2])
- # Compute radial distances
- r = np.sqrt((x - center[0])**2 + (y - center[1])**2)
- r = r.astype(np.int)
- radial_mean = np.bincount(r.ravel(), data.ravel()) / np.bincount(r.ravel())
- return radial_mean
- def get_power_spectrum(image, norm_ks, k_bins):
- fft_amp = np.abs(np.fft.fftshift(np.fft.fft2(image)))
- plt.imshow(fft_amp)
- plt.show()
- amps = binned_statistic(
- norm_ks.flatten(), fft_amp.flatten(),
- statistic="mean",
- bins=k_bins,
- )[0]
- amps /= amps.sum()
- return amps
- def get_spatial_ft(spat_filter, norm_value=None, plot=False):
- spat_filter = np.pad(spat_filter, 270, mode='constant')
- frame_width = spat_filter.shape[0]
- frame_height = spat_filter.shape[1]
- kfreq_x = np.fft.fftshift(np.fft.fftfreq(frame_width)) * frame_width
- kfreq_y = np.fft.fftshift(np.fft.fftfreq(frame_height)) * frame_height
- kfreq_2d = np.meshgrid(kfreq_x, kfreq_y)
- k_norm = np.sqrt(kfreq_2d[0] ** 2 + kfreq_2d[1] ** 2)
- kbins = np.arange(0.5, max(frame_height, frame_width) // 2 + 1)
- kvals = 0.5 * (kbins[1:] + kbins[:-1])
- if plot:
- plt.imshow(spat_filter)
- if norm_value is not None:
- spat_filter /= np.linalg.norm(spat_filter, ord=2)
- spectrum = get_power_spectrum(spat_filter, k_norm, kbins)
- return spectrum
- # %%
- print('m off WN')
- m_off = get_class_filters(df, 0, col='wn_spat', class_type='OFF')
- nm_m_off = get_class_filters(df, 0, col='nm_spat', class_type='OFF')
- print('p off NM')
- nm_p_off = get_class_filters(df, 1, col='nm_spat', class_type='OFF')
- print('p off WN')
- p_off = get_class_filters(df, 1, col='wn_spat', class_type='OFF',)
- print('l off WN')
- l_off = get_class_filters(df, 2, col='wn_spat', class_type='OFF')
- print('l off NM')
- nm_l_off = get_class_filters(df, 2, col='nm_spat', class_type='OFF')
- print('p on NM')
- nm_p_on = get_class_filters(df, 4, col='nm_spat', class_type='ON')
- print('p on WN')
- p_on = get_class_filters(df, 4, col='wn_spat', class_type='ON')
- max_value_wn = max([np.max(np.abs(m_off)), np.max(np.abs(p_off)), np.max(np.abs(p_on)), np.max(np.abs(l_off))])
- max_value_nm = max([np.max(np.abs(nm_m_off)), np.max(np.abs(nm_p_off)), np.max(np.abs(nm_p_on)), np.max(np.abs(nm_l_off))])
- max_value = max(max_value_nm, max_value_wn)
- # %%
- m_off_ft = get_ft_filters(m_off, norm_value=max_value)
- nm_m_off_ft = get_ft_filters(nm_m_off, norm_value=max_value)
- p_off_ft = get_ft_filters(p_off)
- nm_p_off_ft = get_ft_filters(nm_p_off)
- l_off_ft = get_ft_filters(l_off)
- nm_l_off_ft = get_ft_filters(nm_l_off)
- p_on_ft = get_ft_filters(p_on)
- nm_p_on_ft = get_ft_filters(nm_p_on)
- # %%
- def normalize_spat_filter(spatial_filter):
- # Compute the 2D Fourier Transform
- fft_result = np.fft.fft2(spatial_filter)
- # Calculate the amplitude (magnitude) spectrum
- amplitude_spectrum = np.abs(fft_result)
- # Extract the amplitude of the first frequency component (DC component)
- first_freq_amplitude = amplitude_spectrum[0, 0]
- # Avoid division by zero
- if first_freq_amplitude == 0:
- raise ValueError("The amplitude of the first frequency component is zero, cannot normalize.")
- # Normalize the entire frequency response
- normalized_fft = fft_result / first_freq_amplitude
- # Compute the inverse 2D FFT to get the normalized spatial filter
- normalized_spatial_filter = np.fft.ifft2(normalized_fft)
- # Since the inverse FFT may return complex numbers due to numerical errors,
- # take the real part if you expect a real-valued filter
- normalized_spatial_filter = np.real(normalized_spatial_filter)
- # Optional: Verification
- # Compute the FFT of the normalized spatial filter
- fft_normalized_filter = np.fft.fft2(normalized_spatial_filter)
- # Calculate the amplitude spectrum
- amplitude_spectrum_normalized = np.abs(fft_normalized_filter)
- # Verify the amplitude of the first frequency component
- normalized_first_freq_amplitude = amplitude_spectrum_normalized[0, 0]
- print("Amplitude of the first frequency component after normalization:", normalized_first_freq_amplitude)
- return normalized_spatial_filter/np.max(np.abs(normalized_spatial_filter))
- # %%
- def plot_paper_spat_filters(wn_filters, nm_filters, wn_ft_filters, nm_ft_filters, color, light_color, max_scalar=0.9, save_file=None, filter_file=None, max_value_nm=None, max_value_wn=None, xticks=None):
- wn_mean = np.mean(wn_filters, axis=0)
- nm_mean = np.mean(nm_filters, axis=0)
- wn_mean = normalize_spat_filter(wn_mean)
- nm_mean = normalize_spat_filter(nm_mean)
- wn_ft_filters = wn_ft_filters/wn_ft_filters[:, 0][:, None]
- nm_ft_filters = nm_ft_filters/nm_ft_filters[:, 0][:, None]
- wn_ft_mean = np.mean(wn_ft_filters, axis=0)
- nm_ft_mean = np.mean(nm_ft_filters, axis=0)
- wn_ft_std = np.std(wn_ft_filters, axis=0)
- nm_ft_std = np.std(nm_ft_filters, axis=0)
- if filter_file is not None:
- np.save(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/wn_mean_{filter_file}.npy', wn_mean)
- np.save(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/nm_mean_{filter_file}.npy', nm_mean)
- np.save(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/wn_ft_mean_{filter_file}.npy', wn_ft_mean)
- np.save(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/nm_ft_mean_{filter_file}.npy', nm_ft_mean)
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none'
- fig, ax = plt.subplots(3,1, figsize=(0.8, 2.4))
- cax0 = ax[0].imshow(np.flip(wn_mean, axis=1), cmap='gray', vmin=-max_scalar*max_value_wn, vmax=max_scalar*max_value_wn)
- ax[0].grid(False)
- ax[1].grid(False)
- ax[0].get_xaxis().set_visible(False)
- ax[0].get_yaxis().set_visible(False)
- ax[1].get_xaxis().set_visible(False)
- ax[1].get_yaxis().set_visible(False)
- ax[0].spines['bottom'].set_visible(False)
- ax[0].spines['left'].set_visible(False)
- ax[0].spines['right'].set_visible(False)
- ax[0].spines['top'].set_visible(False)
- ax[1].spines['bottom'].set_visible(False)
- ax[1].spines['left'].set_visible(False)
- ax[1].spines['right'].set_visible(False)
- ax[1].spines['top'].set_visible(False)
- plt.grid(False)
- cax1 = ax[1].imshow(nm_mean, cmap='gray', vmin=-max_scalar*max_value_nm, vmax=max_scalar*max_value_nm)
- ax[2].plot(np.arange(len(nm_ft_mean))/800/7.5, wn_ft_mean, label='WN ft', linewidth=0.5, color=color)
- ax[2].fill_between(np.arange(len(wn_ft_mean))/800/7.5, wn_ft_mean - wn_ft_std, wn_ft_mean + wn_ft_std, color=color, alpha=0.2, label='WN std')
- ax[2].plot(np.arange(len(nm_ft_mean))/800/7.5, nm_ft_mean, label='NM ft', linewidth=0.5, color=color, linestyle='--')
- ax[2].fill_between(np.arange(len(nm_ft_mean))/800/7.5, nm_ft_mean - nm_ft_std, nm_ft_mean + nm_ft_std, color=light_color, alpha=0.2, label='NM std')
- ax[2].set_xlabel('Cycle per \u03bcm')
- ax[2].set_ylabel('Magnitude')
- ax[2].set_xlim(0.00, 0.012)
- ax[2].tick_params(bottom=True, top=False, left=True, right=False)
- sns.despine(offset=5)
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg', dpi=300, transparent=True)
- plt.show()
- return wn_ft_mean, nm_ft_mean
- # %%
- wn_m_off_ft_mean, nm_m_off_ft_mean = plot_paper_spat_filters(m_off, nm_m_off, m_off_ft, nm_m_off_ft, save_file='spat_mean_off_midget', max_value_nm=1, max_value_wn=1, filter_file='m_off', xticks=[0.4, 0.8], color=colors[0], light_color=light_colors[0])
- wn_p_off_ft_mean, nm_p_off_ft_mean = plot_paper_spat_filters(p_off, nm_p_off, p_off_ft, nm_p_off_ft,save_file='spat_mean_off_parasol', max_value_nm=1, max_value_wn=1, filter_file='p_off', xticks=[0.4, 0.8], color=colors[1], light_color=light_colors[1])
- wn_l_off_ft_mean, nm_l_off_ft_mean = plot_paper_spat_filters(l_off, nm_l_off, l_off_ft, nm_l_off_ft,save_file='spat_mean_off_large', max_value_nm=1, max_value_wn=1, filter_file='l_off', xticks=[0.4, 0.8], color=colors[2], light_color=light_colors[2])
- wn_p_on_ft_mean, nm_p_on_ft_mean = plot_paper_spat_filters(p_on, nm_p_on, p_on_ft, nm_p_on_ft,save_file='spat_mean_on_parasol', max_value_nm=1, max_value_wn=1, filter_file='p_on', xticks=[0.4, 0.8], color=colors[3], light_color=light_colors[3])
- # %%
- midget = df[df['cell_class_g']==0]['nm_size'], df[df['cell_class_g']==0]['wn_size']
- parasol = df[df['cell_class_g']==1]['nm_size'], df[df['cell_class_g']==1]['wn_size']
- large = df[df['cell_class_g']==2]['nm_size'], df[df['cell_class_g']==2]['wn_size']
- on = df[df['cell_class_g']==4]['nm_size'], df[df['cell_class_g']==4]['wn_size']
- # %%
- midget_a = df[df['cell_class_g']==0]['nm_amp'], df[df['cell_class_g']==0]['wn_amp']
- parasol_a = df[df['cell_class_g']==1]['nm_amp'], df[df['cell_class_g']==1]['wn_amp']
- large_a = df[df['cell_class_g']==2]['nm_amp'], df[df['cell_class_g']==2]['wn_amp']
- on_a = df[df['cell_class_g']==4]['nm_amp'], df[df['cell_class_g']==4]['wn_amp']
- # %% [markdown]
- # ### Significance testing
- # %% [markdown]
- # | Midget OFF | Parasol OFF | Large OFFF | Parasol ON |
- # | -- | -- | -- | -- |
- # | NM > WN ** | NM > WN ** | NM < WN ** | NM, WN not significantly different |
- # | NM amp > WN amp ** | NM amp > WN amp ** | NM amp > WN amp ** | NM amp, WN amp not significantl different |
- # %%
- def test_significance(wn_cells, nm_cells, alternatives='greater'):
- statistic, p_value = wilcoxon(wn_cells, nm_cells, zero_method='wilcox', correction=False, alternative=alternatives, mode='auto')
- print('Wilcoxon Signed-Rank Test Results:')
- print('Statistic:', statistic)
- print('p-value:', p_value)
- # %%
- test_significance(midget[0], midget[1], alternatives='greater')
- test_significance(parasol[0], parasol[1], alternatives='greater')
- test_significance(large[0], large[1], alternatives='greater')
- test_significance(on[0], on[1], alternatives='greater')
- # %%
- # %%
- test_significance(midget_a[0], midget_a[1], alternatives='greater')
- test_significance(parasol_a[0], parasol_a[1], alternatives='greater')
- test_significance(large_a[0], large_a[1], alternatives='greater')
- test_significance(on_a[0], on_a[1], alternatives='greater')
- # %%
- def plot_pretty_size_plot(df, lw=0, s=15, save_file=None, a=0.75):
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none'
- fig, ax = plt.subplots(figsize=(2.1,2.1))
- ax.tick_params(bottom=True, top=False, left=True, right=False)
- plt.grid(False)
- midget = df[df['cell_class_g']==0]['nm_size'], df[df['cell_class_g']==0]['wn_size']
- parasol = df[df['cell_class_g']==1]['nm_size'], df[df['cell_class_g']==1]['wn_size']
- large = df[df['cell_class_g']==2]['nm_size'], df[df['cell_class_g']==2]['wn_size']
- on = df[df['cell_class_g']==4]['nm_size'], df[df['cell_class_g']==4]['wn_size']
- plt.scatter(*midget, color=colors[0], label='Midget OFF', s=s, linewidth=lw ,alpha=a)
- plt.scatter(*parasol, color=colors[1], label='Parasol OFF', s=s, linewidth=lw, alpha=a)
- plt.scatter(*large, color=colors[2], label='Large OFF', s=s, linewidth=lw, alpha=a)
- plt.scatter(*on, color=colors[3], label='Parasol ON', s=s, linewidth=lw, alpha=a)
- plt.scatter(np.mean(on[0]), np.mean(on[1]), facecolor=colors[3], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(midget[0]), np.mean(midget[1]), facecolor=colors[0], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(parasol[0]), np.mean(parasol[1]), facecolor=colors[1], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(large[0]), np.mean(large[1]), facecolor=colors[2], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.xlim(25*4*30, 1450*4*30)
- plt.ylim(25*4*30, 1450*4*30)
- plt.xscale('log')
- plt.yscale('log')
- plt.plot([10*2*30 ,1450*4* 30] , [10*2*30, 1450*4*30], color='black', linewidth=1, linestyle='--')
- sns.despine(offset=5)
- plt.xlabel('NM center size')
- plt.ylabel('WN center size')
- # plt.legend()
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg', transparent=True, bbox_inches='tight', dpi=300)
- plt.show()
- def plot_pretty_amplitude_plot(df, lw=0, s=15, save_file=None, a=0.75):
- sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
- plt.rcParams['lines.linewidth'] = 0.5
- plt.rcParams['font.size'] = 8
- matplotlib.rcParams['svg.fonttype'] = 'none'
- fig, ax = plt.subplots(figsize=(2.1,2.1))
- ax.tick_params(bottom=True, top=False, left=True, right=False)
- plt.grid(False)
- midget = df[df['cell_class_g']==0]['nm_amp'], df[df['cell_class_g']==0]['wn_amp']
- parasol = df[df['cell_class_g']==1]['nm_amp'], df[df['cell_class_g']==1]['wn_amp']
- large = df[df['cell_class_g']==2]['nm_amp'], df[df['cell_class_g']==2]['wn_amp']
- on = df[df['cell_class_g'] == 4]['nm_amp'], df[df['cell_class_g']==4]['wn_amp']
- plt.scatter(*midget, color=colors[0], label='Midget OFF', s=s, linewidth=lw, alpha=a)
- plt.scatter(*parasol, color=colors[1], label='Parasol OFF', s=s, linewidth=lw, alpha=a)
- plt.scatter(*large, color=colors[2], label='Large OFF', s=s, linewidth=lw, alpha=a)
- plt.scatter(*on, color=colors[3], label='Parasol ON', s=s, linewidth=lw, alpha=a)
- plt.scatter(np.mean(midget[0]), np.mean(midget[1]), facecolor=colors[0], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(parasol[0]), np.mean(parasol[1]), facecolor=colors[1], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(large[0]), np.mean(large[1]), facecolor=colors[2], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.scatter(np.mean(on[0]), np.mean(on[1]), facecolor=colors[3], edgecolor='black', marker='X', linewidth=0.5, s=s*2, alpha=1)
- plt.plot([0, 0.15], [0, 0.15], color='black', linewidth=1, linestyle='--')
- sns.despine(offset=5)
- plt.xlabel('NM surround amplitude')
- plt.ylabel('WN surround amplitude')
- plt.xlim(-0.01, 0.15)
- plt.ylim(-0.01, 0.15)
- # plt.legend()
- if save_file is not None:
- plt.savefig(f'/usr/users/vystrcilova/retinal_circuit_modeling/evaluations/visualizations/adaptation_paper/figures/{save_file}.svg', transparent=True, bbox_inches='tight', dpi=300)
- plt.show()
- # %%
- plot_pretty_size_plot(df, save_file='size_scatter', s=25)
- plot_pretty_amplitude_plot(df, save_file='amps_scatter', a=0.6, s=25)
adaptation_paper_figures.ipynb at commit 0d48996, no license · at the source
Overview
- University of Göttingen, Institute of Computer Science and Campus Institute Data Science, Göttingen 37037, Germany
- Department of Ophthalmology, University Medical Center Göttingen, Göttingen 37073, Germany
- Bernstein Center for Computational Neuroscience Göttingen, Göttingen 14195, Germany
- International Max Planck Research School for Intelligent Systems, Tübingen 72076, Germany
- Tübingen AI Center, University of Tübingen, Tübingen 72076, Germany
- International Max Planck Research School for Neurosciences, Göttingen 72074, Germany
- Laboratory Animal Science Unit, German Primate Center, Göttingen 37077, Germany
- German Center for Cardiovascular Research, Partner Site Göttingen, Göttingen 37073, Germany
- Cluster of Excellence “Multiscale Bioimaging: from Molecular Machines to Networks of Excitable Cells” (MBExC), University of Göttingen, Göttingen 37075, Germany
- Max Planck Institutefor Dynamics and Self-Organization, Göttingen 37077, Germany
Abstract
The retina encodes a broad range of stimuli, adapting its computations to features like brightness, contrast, and motion. However, it is unclear whether it also adapts when switching between natural scenes and white noise (WN). To address this, we analyzed the neural activity of male marmoset retinal ganglion cells (RGCs) in response to WN and naturalistic movies. We trained linear–nonlinear models on both stimuli, evaluated their performance, and compared their receptive fields across stimulus domains. We found that models with spatial filters trained on one stimulus ensemble were less accurate when predicting neural activity on the other compared to models trained directly on the target stimulus. This suggests that spatial processing adapts to stimulus statistics. Different RGC types exhibited distinct changes: The OFF midget cells’ receptive fields became enlarged under natural movies (NMs), resulting in a lower cutoff frequency. Parasol cells and large OFF cells did not significantly change their receptive field sizes. All cell types exhibited stronger surrounds under NMs, resembling the whitening filters predicted by efficient coding for stimulus decorrelation, prompting us to test whether these changes were related to the different spectral content of the two stimulus types. Quantifying the effects of the filters’ enhanced surrounds on the stimulus power spectrum showed a significant contribution toward whitening only in ON parasol cells, where a whitening effect emerged regardless of the training stimulus. These results suggest that while RGCs adapt to the differences between WN and NM stimuli, efficient coding can only partially account for this adaptation.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.
doi.gin.g-node.org/10.12751/g-node.t43ph1
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
ecker-lab/retina-adaptation
0d489964e2c9efd3fb04c815e33b2a67a1051c08, 16 February 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
31 files
- datasets/
FrameWiseDataset.py , Python, 253 lines - datasets/
TrialWiseDataset.py , Python, 447 lines - datasets/
__init__.py , Python, 2 lines - datasets/
natural_stimuli/ , Python, 599 lines, 1 matchcreate_data.py - datasets/
natural_stimuli/ , Python, 21 linescreate_dataset.py - datasets/
naturalmovie_marmoset_lo , Python, 430 linesader.py - datasets/
retina_reliability.py , Python, 186 lines - datasets/
stas.py , Python, 236 lines - datasets/
whitenoise_salamander_lo , Python, 428 linesaders.py - evaluations/
ln_model_performance.py , Python, 866 lines - evaluations/
sizes_estimations.py , Python, 277 lines - ln_model_factorized.py, Python, 372 lines
- ln_model_factorized_marm
oset_nm.py , Python, 351 lines, 1 match - models/
__init__.py , Python, 1 line - models/
ln_model.py , Python, 291 lines - models/
ln_models/ , MATLAB, not shown hereln_models_factorized__et e/ marmoset/ retina04/ cell_2/ cs_lr_0.005_whole_rf_20_ ch_25_l2_0.0_g_0.0_bs_12 _tr_250_s_1_c_0_fn_1_do_ n_1_dog_0/ weights/ seed_8/ best_model.m - models/
ln_models/ , MATLAB, not shown hereln_models_factorized__et e/ marmoset/ retina04/ cell_2/ cs_lr_0.005_whole_rf_20_ ch_25_l2_0.0_g_0.0_bs_12 _tr_250_s_1_c_0_fn_1_do_ n_1_dog_0/ weights/ seed_8/ initial_model.m - notebooks/
adaptation_paper_figures , Jupyter, 1,089 lines, 2 matches.ipynb - notebooks/
atick_redlich.ipynb , Jupyter, 313 lines, 1 match - notebooks/
rank2models.ipynb , Jupyter, 191 lines - power_spectrum_3d.py, Python, 142 lines
- power_spectrum_analysis.
py , Python, 261 lines - train_multiretinal_ln_nm
_for_wn.py , Python, 93 lines - train_multiretinal_ln_wn
_for_nm.py , Python, 89 lines - training/
cross_stimulus_training. , Python, 150 lines, 1 matchpy - training/
measures.py , Python, 94 lines - training/
regularizers.py , Python, 26 lines - training/
train_steps.py , Python, 100 lines - training/
trainers.py , Python, 403 lines - utils/
global_functions.py , Python, 140 lines - ReadME.md, Text, 78 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 30 scripts, each with its path and the digest of its content;
- 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data and code availability
The data is available on GIN (https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 12 authors, 4 keywords, 11 MeSH terms, 2 funders, 45 references.
Cite
This paper
Vystrčilová, M., Sridhar, S., Burg, M. F., Khani, M. H., Karamanlis, D., Schreyer, H. M., Ramakrishna, V., Krüppel, S., Zapp, S. J., Mietsch, M., Gollisch, T., & Ecker, A. S. (2026). Spatial Adaptation of Primate Retinal Ganglion Cells Between Artificial and Natural Stimuli. eNeuro, 13(5), ENEURO.0060-26.2026. https://
BibTeX
@article{vystrcilova2026
author = {Vystrčilová, Michaela and Sridhar, Shashwat and Burg, Max F and Khani, Mohammad H and Karamanlis, Dimokratis and Schreyer, Helene M and Ramakrishna, Varsha and Krüppel, Steffen and Zapp, Sören J and Mietsch, Matthias and Gollisch, Tim and Ecker, Alexander S},
title = {{Spatial Adaptation of Primate Retinal Ganglion Cells Between Artificial and Natural Stimuli}},
journal = {eNeuro},
year = {2026},
month = may,
volume = {13},
number = {5},
pages = {ENEURO.0060--26.2026},
publisher = {Society for Neuroscience},
issn = {2373-2822},
doi = {10.1523/
url = {https://
pmid = {41856791},
pmcid = {PMC13138850}
}
RIS
TY - JOUR
AU - Vystrčilová, Michaela
AU - Sridhar, Shashwat
AU - Burg, Max F
AU - Khani, Mohammad H
AU - Karamanlis, Dimokratis
AU - Schreyer, Helene M
AU - Ramakrishna, Varsha
AU - Krüppel, Steffen
AU - Zapp, Sören J
AU - Mietsch, Matthias
AU - Gollisch, Tim
AU - Ecker, Alexander S
TI - Spatial Adaptation of Primate Retinal Ganglion Cells Between Artificial and Natural Stimuli
T2 - eNeuro
J2 - eNeuro
PY - 2026
DA - 2026/
VL - 13
IS - 5
SP - ENEURO.0060
EP - 26.2026
SN - 2373-2822
PB - Society for Neuroscience
DO - 10.1523/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1523/
"type": "article-journal",
"title": "Spatial Adaptation of Primate Retinal Ganglion Cells Between Artificial and Natural Stimuli",
"container-title": "eNeuro",
"author": [
{
"family": "Vystrčilová",
"given": "Michaela"
},
{
"family": "Sridhar",
"given": "Shashwat"
},
{
"family": "Burg",
"given": "Max F"
},
{
"family": "Khani",
"given": "Mohammad H"
},
{
"family": "Karamanlis",
"given": "Dimokratis"
},
{
"family": "Schreyer",
"given": "Helene M"
},
{
"family": "Ramakrishna",
"given": "Varsha"
},
{
"family": "Krüppel",
"given": "Steffen"
},
{
"family": "Zapp",
"given": "Sören J"
},
{
"family": "Mietsch",
"given": "Matthias"
},
{
"family": "Gollisch",
"given": "Tim"
},
{
"family": "Ecker",
"given": "Alexander S"
}
],
"container-title-short":
"volume": "13",
"issue": "5",
"page": "ENEURO.0060-26.2026",
"DOI": "10.1523/
"PMID": "41856791",
"PMCID": "PMC13138850",
"ISSN": "2373-2822",
"publisher": "Society for Neuroscience",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
4
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1371/journal.pcbi.1014157 [code]
- Modeling spatial contrast sensitivity in responses of primate retinal ganglion cells to natural movies.Journal: PLoS computational biologyIn common: PyTorch, pandas, SciPy, 1 other tool, 9 references, 5 authors
- [2] doi:10.1126/sciadv.aed4172 [code]
- Locomotion optimizes sensory representations through a computational principle shared by rodents and primates.Journal: Science advancesIn common: OpenCV, PyTorch, pandas, 3 other tools, 2 references
- [3] doi:10.7554/elife.105953 [code]
- Top-down feedback in deep neural networks leads to functional differences during audiovisual integration.Journal: eLifeIn common: Pillow, PyTorch, seaborn, 4 other tools, 1 reference
- [4] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: OpenCV, Pillow, PyTorch, 5 other tools
- [5] doi:10.1038/s43856-026-01817-x [code]
- Visual prompt engineering for multimodal and irregularly sampled medical data.Journal: Communications medicineIn common: OpenCV, Pillow, PyTorch, 5 other tools
- [6] doi:10.1038/s41467-026-76837-1 [code]
- Drug screen and machine learning predict neuroprotective agents in a preclinical human model of childhood dementia.Journal: Nature communicationsIn common: OpenCV, Pillow, PyTorch, 5 other tools
- [7] doi:10.1038/s41593-026-02388-9 [code]
- Hippocampal CA3 connectomics reveals a gradient of mossy fiber inputs and selective feedforward inhibition onto pyramidal cells.Journal: Nature neuroscienceIn common: OpenCV, Pillow, PyTorch, 5 other tools
- [8] doi:10.1371/journal.pcbi.1014571 [code]
- SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.Journal: PLoS computational biologyIn common: OpenCV, Pillow, PyTorch, 5 other tools
- [9] doi:10.1162/imag.a.1309 [code]
- Probing the content of semantic representations in body-selective regions.Journal: Imaging neuroscience (Cambridge, Mass.)In common: OpenCV, Pillow, PyTorch, 5 other tools
- [10] doi:10.1016/j.isci.2026.116825 [code]
- Social hierarchy shapes behavioral and transcriptional responses to chronic stress and ketamine in male mice.Journal: iScienceIn common: OpenCV, Pillow, PyTorch, 5 other tools
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 30 scripts, and 6 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:5454c48784594d2e…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
