Shallow recurrent decoders for neural and behavioural dynamics.
The 5 matches
- [1] § Model systems › Mouse › SHRED reconstruction of neural response to visual stimuli ↔ mice/shred_mice_expB_i_flash.ipynb, lines 560–629 · score 0.59 · SCig, MB, POL, VISp, APN, POST
- [2] § Model systems › Mouse › SHRED reconstruction of neural response to visual stimuli ↔ mice/shred_mice_expB_i_dg.ipynb, lines 589–658 · score 0.59 · SCig, MB, POL, VISp, APN, POST
- [3] § Model systems › Mouse › SHRED reconstruction of local field potential from pupil area response to a visual stimulus ↔ mice/shred_mice_expB_iv.ipynb, lines 125–150 · score 0.56 · cubic spline, pupil area, stimuli
- [4] § Model systems › Control comparison ↔ mice/shred_mice_expB_i_dg.ipynb, lines 756–826 · score 0.55 · power spectral density, linear regression, PSD, neurons, SHRED
- [5] § Model systems › Control comparison ↔ mice/shred_mice_expB_i_dg.ipynb, lines 756–826 · score 0.52 · Power spectral densities, linear regression, ground truth, PSDs, neurons, SHRED
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,010 lines · 35 KB · no license · 3 matches
- # %% [markdown]
- # # Applying SHRED to the Mice Dataset
- # %% [markdown]
- # Dataset obtained from Allen Brain Atlas https://allensdk.readthedocs.io/en/latest/visual_coding_neuropixels.html
- # %% [markdown]
- # SHRED (SHallow REcurrent Decoder) models are a network architecture that merges a recurrent layer (LSTM) with a shallow decoder network (SDN) to reconstruct high-dimensional spatio-temporal fields from a trajectory of sensor measurements of the field. More formally, the SHRED architecture can be written as
- # $$ \mathcal {H} \left( \{ y_i \} _{i=t-k}^t \right) = \mathcal {F} \left( \mathcal {G} \left( \{ y_i \} _{i=t-k}^t \right) ; W_{RN}) ; W_{SD} \right)$$
- # where $\mathcal F$ is a feed forward network parameterized by weights $W_{SD}$, $\mathcal G$ is a LSTM network parameterized by weights $W_{RN}$, and $\{ y_i \} _{i=t-k}^t$ is a trajectory of sensor measurements of a high-dimensional spatio-temporal field $\{ x_i \} _{i=t-k}^t$.
- # %% [markdown]
- # Goal: Reconstruct LFP potential of different brain regions from only one session 756029989 and 180 degree orientation drifting gratings
- #
- # LFP band:
- # - ~2.5 kHz original sample rate
- # - 1000 Hz analog lo-pass
- # - 625 Hz digital lo-pass
- # - NWB includes every 2nd sample and every 4th channel
- # %%
- # Importing all dependencies
- import numpy as np
- import torch
- import subprocess
- import os
- import torch
- import matplotlib.pyplot as plt
- from sklearn.preprocessing import MinMaxScaler
- import random
- import mne
- # %%
- import pandas as pd
- from scipy.ndimage import gaussian_filter
- from pathlib import Path
- import json
- from IPython.display import display
- from PIL import Image
- from allensdk.brain_observatory.ecephys.visualization import plot_mean_waveforms, plot_spike_counts, raster_plot
- from allensdk.brain_observatory.visualization import plot_running_speed
- # tell pandas to show all columns when we display a DataFrame
- pd.set_option("display.max_columns", None)
- # %%
- #Set up data cache
- from allensdk.brain_observatory.ecephys.ecephys_project_cache import EcephysProjectCache
- HOME_DIR = "/Users/amyrude/Downloads/Kutz_Research/SHRED_neuro/neuralSHRED/"
- data_directory = os.path.join(HOME_DIR, "mice/data") #where data will be stored
- manifest_path = os.path.join(data_directory, 'manifest.json')
- cache = EcephysProjectCache.from_warehouse(manifest = manifest_path)
- # %%
- sessions = cache.get_session_table()
- print('Total number of sessions: ' + str(len(sessions)))
- sessions.head()
- # Can also filter which mice to select
- filtered_sessions = sessions[(sessions.index == 756029989)]
- filtered_sessions.head()
- # %%
- #Loading dataset for specific session
- session_id = 756029989 # for example
- session = cache.get_session_data(session_id)
- print([attr_or_method for attr_or_method in dir(session) if attr_or_method[0] != '_'])
- # %% [markdown]
- # **Importing local field potential (LFP) data**
- # %%
- # list the probes recorded from in this session
- session.probes.head()
- # %%
- {session.probes.loc[probe_id].description :
- list(session.channels[session.channels.probe_id == probe_id].ecephys_structure_acronym.unique())
- for probe_id in session.probes.index.values}
- # %%
- probe_id = session.probes.index.values[2] ### Select 0 to probe A, 2 for probe C
- lfp = session.get_lfp(probe_id)
- # %%
- print(lfp)
- # %%
- # now use a utility to associate intervals of /rows with structures
- ## For probe C, mismatch between channels in lfp["channel"] and session.channels.index
- lfp_channels = lfp["channel"].values.tolist()
- valid_channels = [ch for ch in lfp_channels if ch in session.channels.index]
- structure_acronyms, intervals = session.channel_structure_intervals(valid_channels)
- interval_midpoints = [aa + (bb - aa) / 2 for aa, bb in zip(intervals[:-1], intervals[1:])]
- print(structure_acronyms)
- print(intervals)
- print(interval_midpoints)
- # %% [markdown]
- # Aligning the LFP data to a particular stimulus
- # %%
- stim_table = session.get_stimulus_table('drifting_gratings')
- stim_table[stim_table['stimulus_condition_id'] == 246].head(10)
- # %%
- # presentation_table = session.stimulus_presentations[session.stimulus_presentations.stimulus_name == 'drifting_gratings']
- presentation_table = session.stimulus_presentations[session.stimulus_presentations.stimulus_condition_id == 246]
- presentation_times = presentation_table.start_time.values
- presentation_ids = presentation_table.index.values
- # %%
- print(presentation_table)
- # %%
- # %%
- ### Probe A and C
- dt = 1/500
- sr = 1/dt
- trial_window = np.arange(0, 2, dt)
- time_selection = np.concatenate([trial_window + t for t in presentation_times])
- inds = pd.MultiIndex.from_product((presentation_ids, trial_window),
- names=('presentation_id', 'time_from_presentation_onset'))
- ds = lfp.sel(time = time_selection, method='nearest').to_dataset(name = 'aligned_lfp')
- ds = ds.assign(time=inds).unstack('time')
- aligned_lfp = ds['aligned_lfp']
- # %%
- print(aligned_lfp.shape)
- # %%
- fig, ax = plt.subplots()
- im = ax.imshow(aligned_lfp.mean(dim='presentation_id'), aspect='auto', origin='lower', vmin=-1e-4, vmax=1e-4)
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- # time_labels = [ f"{val:1.3}" for val in lfp["time"].values[window][time_label_indices]]
- # ax.set_xticks(time_label_indices + 0.5)
- # ax.set_xticklabels(time_labels)
- ax.set_xlabel("Sample Number", fontsize=20)
- plt.colorbar(im, fraction=0.036, pad=0.04)
- plt.show()
- ## just one
- fig, ax = plt.subplots()
- im = ax.imshow(aligned_lfp[:,0,:], aspect='auto', origin='lower', vmin=-0.5e-3, vmax=0.5e-3)
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- # time_labels = [ f"{val:1.3}" for val in lfp["time"].values[window][time_label_indices]]
- # ax.set_xticks(time_label_indices + 0.5)
- # ax.set_xticklabels(time_labels)
- ax.set_xlabel("Sample Number", fontsize=20)
- plt.colorbar(im, fraction=0.036, pad=0.04)
- plt.show()
- # %%
- ### save data as np arrays
- sens_max = 76 #74
- probe = 'c'
- lfp_np = aligned_lfp.data
- data = np.hstack((lfp_np[:sens_max,0,:], lfp_np[:sens_max,1,:], lfp_np[:sens_max,2,:]))
- print(data.shape)
- n_t = data.shape[1]
- n_s = data.shape[0]
- ## Plot the numpy array
- fig, ax = plt.subplots(figsize = (5,6))
- im = ax.imshow(data, aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'Reds')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- for y in [0,1000, 2000]:
- plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- ax.set_xlim((-15,data.shape[1]))
- ax.set_ylim((0,sens_max))
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=20)
- plt.colorbar(im, fraction=0.036, pad=0.04)
- plt.show()
- # %% [markdown]
- # **Applying SHRED on Dataset**
- # %%
- #Importing packages
- os.environ["CUDA_VISIBLE_DEVICES"]="0"
- SHRED_DIR = os.path.join(HOME_DIR, "neuralSHRED/sindy-shred-main")
- import sys
- sys.path.append(SHRED_DIR)
- from processdata import load_data
- from processdata import TimeSeriesDataset
- import sindy
- import sindy_shred
- os.environ["CUDA_VISIBLE_DEVICES"]="0"
- # %%
- load_X = data.T
- latent_dim = 32
- poly_order = 1
- num_neurons = 3
- lags = 100
- test_val_size = 1000 - lags
- ## Ignore below
- include_sine = False
- library_dim = sindy.library_size(latent_dim, poly_order, include_sine, True)
- train_indices = np.arange(0, n_t - lags - test_val_size)
- mask = np.ones(n_t - lags)
- mask[train_indices] = 0
- valid_test_indices = np.arange(0, n_t - lags)[np.where(mask!=0)[0]]
- valid_indices = valid_test_indices[int(test_val_size/2):test_val_size]
- test_indices = valid_test_indices[:int(test_val_size/2)]
- # %%
- ## Plot the lag
- fig, ax = plt.subplots(figsize = (7,3))
- im = ax.imshow(data[:, :lags], aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'Reds')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- # for y in [0,1000, 2000]:
- # plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- # ax.set_xlim((-15,data.shape[1]))
- ax.set_ylim((0,sens_max))
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=20)
- plt.colorbar(im, fraction=0.036, pad=0.04)
- plt.show()
- # %%
- # Randomly select the sensors from the CA1 region for probe A (35 - 51) and SUB for probe C (31 - 54)
- print(intervals)
- print(structure_acronyms)
- for k in range(4,5):
- neuron_locations = np.random.choice(np.arange(31, 54), size=num_neurons, replace=False)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/neuron_loc_{probe}_{k}.npy")
- np.save(file_path, neuron_locations)
- sc = MinMaxScaler()
- sc = sc.fit(load_X[train_indices])
- transformed_X = sc.transform(load_X)
- ### Generate input sequences to a SHRED model
- all_data_in = np.zeros((n_t - lags, lags, num_neurons))
- for i in range(len(all_data_in)):
- all_data_in[i] = transformed_X[i:i+lags, neuron_locations]
- ### Generate training validation and test datasets both for reconstruction of states and forecasting sensors
- device = 'cuda' if torch.cuda.is_available() else 'cpu'
- train_data_in = torch.tensor(all_data_in[train_indices], dtype=torch.float32).to(device)
- valid_data_in = torch.tensor(all_data_in[valid_indices], dtype=torch.float32).to(device)
- test_data_in = torch.tensor(all_data_in[test_indices], dtype=torch.float32).to(device)
- ### -1 to have output be at the same time as final sensor measurements
- train_data_out = torch.tensor(transformed_X[train_indices + lags - 1], dtype=torch.float32).to(device)
- valid_data_out = torch.tensor(transformed_X[valid_indices + lags - 1], dtype=torch.float32).to(device)
- test_data_out = torch.tensor(transformed_X[test_indices + lags - 1], dtype=torch.float32).to(device)
- train_dataset = TimeSeriesDataset(train_data_in, train_data_out)
- valid_dataset = TimeSeriesDataset(valid_data_in, valid_data_out)
- test_dataset = TimeSeriesDataset(test_data_in, test_data_out)
- shred = sindy_shred.SINDy_SHRED(num_neurons, n_s, hidden_size=latent_dim, hidden_layers=2, l1=350, l2=400, dropout=0.1,
- library_dim=library_dim, poly_order=poly_order,
- include_sine=include_sine, dt=dt, layer_norm=False, sindy = False).to(device)
- validation_errors = sindy_shred.fit(shred, train_dataset, valid_dataset, batch_size=128, num_epochs=200,
- lr=1e-3, verbose=True, threshold=0.25, patience=5, sindy_regularization= 0.0,
- optimizer="AdamW", thres_epoch=100)
- test_recons = sc.inverse_transform(shred(test_dataset.X).detach().cpu().numpy())
- test_ground_truth = sc.inverse_transform(test_dataset.Y.detach().cpu().numpy())
- train_recons = sc.inverse_transform(shred(train_dataset.X).detach().cpu().numpy())
- train_ground_truth = sc.inverse_transform(train_dataset.Y.detach().cpu().numpy())
- mask = np.ones(train_ground_truth.shape[1], dtype=bool)
- mask[neuron_locations] = False
- train_ex = train_recons.T[mask]
- train_gt_ex = train_ground_truth.T[mask]
- mask = np.ones(test_ground_truth.shape[1], dtype=bool)
- mask[neuron_locations] = False
- test_ex = test_recons.T[mask]
- test_gt_ex = test_ground_truth.T[mask]
- mse_train = np.linalg.norm(train_ex - train_gt_ex) / np.linalg.norm(train_gt_ex)
- mse_test = np.linalg.norm(test_ex - test_gt_ex) / np.linalg.norm(test_gt_ex)
- print('mse test', mse_test)
- print('mse train', mse_train)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_recons_{probe}_{k}.npy")
- np.save(file_path, train_recons)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_recons_{probe}_{k}.npy")
- np.save(file_path, test_recons)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_gt_{probe}_{k}.npy")
- np.save(file_path, train_ground_truth)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_gt_{probe}_{k}.npy")
- np.save(file_path, test_ground_truth)
- file_path = os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/mse_{probe}_{k}.npy")
- np.save(file_path, [mse_train, mse_test])
- # %% [markdown]
- # **Plotting the Data**
- # %%
- ## Probe A
- structure_acronyms = ['APN', 'DG', 'CA1', 'VISam', 'nan']
- intervals = [0, 27, 35, 51, 74 ,87]
- interval_midpoints = [13.5, 31.0, 43.0, 62.5, 80.5]
- ### Load the data
- sens_max = 74
- probe = 'a'
- trial = 2
- train_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_recons_a_{trial}.npy"))
- test_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_recons_a_{trial}.npy"))
- data_recon = np.vstack((train_recon, test_recon))
- train_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_gt_a_{trial}.npy"))
- test_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_gt_a_{trial}.npy"))
- data_gt= np.vstack((train_gt, test_gt))
- neuron_loc = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/neuron_loc_a_{trial}.npy"))
- mse = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/mse_a_{trial}.npy"))
- ## Plot the Reconstruction
- fig, ax = plt.subplots(figsize = (6.5,3))
- im = ax.imshow(data_recon.T, aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'YlOrRd')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- for tick in ax.yaxis.get_minor_ticks():
- tick.label1.set_fontsize(12)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- for y in [0,1000, 2000]:
- plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- for k in range(len(neuron_loc)):
- ax.plot(-5, neuron_loc[k], 'o', color='red', zorder = 10, clip_on=False, markersize = 5)
- ax.set_clip_on(False)
- ax.set_xlim((-15,data_recon.shape[0]))
- ax.set_ylim((0,sens_max))
- plt.tick_params(axis='both', which='major', labelsize=13)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=15)
- cbar = plt.colorbar(im, fraction=0.036, pad=0.04)
- cbar.set_label("V", fontsize = 12)
- cbar.ax.tick_params(labelsize=11)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, "mice/data_output/figs/lfp_dg_a_recon.png"), transparent = True, dpi = 400)
- plt.show()
- ## Plot the Ground Truth
- fig, ax = plt.subplots(figsize = (6.5,3))
- im = ax.imshow(data_gt.T, aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'YlOrRd')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- for tick in ax.yaxis.get_minor_ticks():
- tick.label1.set_fontsize(12)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- for y in [0,1000, 2000]:
- plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- for k in range(len(neuron_loc)):
- ax.plot(-5, neuron_loc[k], 'o', color='red', zorder = 10, clip_on=False, markersize = 5)
- ax.set_clip_on(False)
- ax.set_xlim((-15,data_gt.shape[0]))
- ax.set_ylim((0,sens_max))
- plt.tick_params(axis='both', which='major', labelsize=13)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=15)
- cbar = plt.colorbar(im, fraction=0.036, pad=0.04)
- cbar.set_label("V", fontsize = 12)
- cbar.ax.tick_params(labelsize=11)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, "mice/data_output/figs/lfp_dg_a_gt.png"), transparent = True, dpi = 400)
- plt.show()
- print('mse', mse)
- # %%
- structure_acronyms = ['APN', 'DG', 'CA1', 'VISam', 'nan']
- intervals = [0, 27, 35, 51, 74 ,87]
- APN_train_gt = train_gt[0:8,:]
- DG_train_gt = train_gt[27:35,:]
- CA1_train_gt = train_gt[35:43,:]
- VISam_train_gt = train_gt[51:59,:]
- APN_test_gt = test_gt[0:8,:]
- DG_test_gt = test_gt[27:35,:]
- CA1_test_gt = test_gt[35:43,:]
- VISam_test_gt = test_gt[51:59,:]
- APN_train_recon = train_recon[0:8,:]
- DG_train_recon = train_recon[27:35,:]
- CA1_train_recon = train_recon[35:43,:]
- VISam_train_recon = train_recon[51:59,:]
- APN_test_recon = test_recon[0:8,:]
- DG_test_recon = test_recon[27:35,:]
- CA1_test_recon = test_recon[35:43,:]
- VISam_test_recon = test_recon[51:59,:]
- ### CA1 to CA1
- mse_ca1_train = np.linalg.norm(CA1_train_recon - CA1_train_gt) / np.linalg.norm(CA1_train_gt)
- mse_ca1_test = np.linalg.norm(CA1_test_recon - CA1_test_gt) / np.linalg.norm(CA1_test_gt)
- ### CA1 to VISam
- mse_visam_train = np.linalg.norm(VISam_train_recon - VISam_train_gt) / np.linalg.norm(VISam_train_gt)
- mse_visam_test = np.linalg.norm(VISam_test_recon - VISam_test_gt) / np.linalg.norm(VISam_test_gt)
- ### CA1 to DG
- mse_dg_train = np.linalg.norm(DG_train_recon - DG_train_gt) / np.linalg.norm(DG_train_gt)
- mse_dg_test = np.linalg.norm(DG_test_recon - DG_test_gt) / np.linalg.norm(DG_test_gt)
- ### CA1 to APN
- mse_apn_train = np.linalg.norm(APN_train_recon - APN_train_gt) / np.linalg.norm(APN_train_gt)
- mse_apn_test = np.linalg.norm(APN_test_recon - APN_test_gt) / np.linalg.norm(APN_test_gt)
- print('mse visam train', mse_visam_train, mse_visam_test)
- print('mse dg train', mse_dg_train, mse_dg_test)
- print('mse apn train', mse_apn_train, mse_apn_test)
- print('mse ca1 train', mse_ca1_train, mse_ca1_test)
- # %%
- ## Probe C
- structure_acronyms = ['POL', '', '', 'SCig', '', 'SUB', 'VISp', 'nan']
- intervals = [ 0, 5 , 9 ,12 ,29, 31 ,54, 76 ,81]
- interval_midpoints = [2.5, 7.0, 10.5, 20.5, 30.0, 42.5, 65.0, 78.5]
- ### Load the data
- sens_max = 76
- probe = 'c'
- trial = 3 ## trial 4- 64 latent dimension
- train_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_recons_c_{trial}.npy"))
- test_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_recons_c_{trial}.npy"))
- data_recon = np.vstack((train_recon, test_recon))
- train_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_gt_c_{trial}.npy"))
- test_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_gt_c_{trial}.npy"))
- data_gt= np.vstack((train_gt, test_gt))
- neuron_loc = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/neuron_loc_c_{trial}.npy"))
- mse = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/mse_c_{trial}.npy"))
- ## Plot the Reconstruction
- fig, ax = plt.subplots(figsize = (6.5,3))
- im = ax.imshow(data_recon.T, aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'GnBu')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- for tick in ax.yaxis.get_minor_ticks():
- tick.label1.set_fontsize(12)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- for y in [0,1000, 2000]:
- plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- for k in range(len(neuron_loc)):
- ax.plot(-5, neuron_loc[k], 'o', color='red', zorder = 10, clip_on=False, markersize = 5)
- ax.set_clip_on(False)
- ax.set_xlim((-15,data_recon.shape[0]))
- ax.set_ylim((0,sens_max))
- plt.tick_params(axis='both', which='major', labelsize=13)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=15)
- cbar = plt.colorbar(im, fraction=0.036, pad=0.04)
- cbar.ax.tick_params(labelsize=11)
- cbar.set_label("V", fontsize = 12)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, "mice/data_output/figs/lfp_dg_c_recon.png"), transparent = True, dpi = 400)
- plt.show()
- ## Plot the Ground Truth
- fig, ax = plt.subplots(figsize = (6.5,3))
- im = ax.imshow(data_gt.T, aspect='auto', origin='lower', vmin=-0.4e-3, vmax=0.4e-3, cmap = 'GnBu')
- ax.set_yticks(intervals)
- ax.set_yticks(interval_midpoints, minor=True)
- ax.set_yticklabels(structure_acronyms, minor=True)
- for tick in ax.yaxis.get_minor_ticks():
- tick.label1.set_fontsize(12)
- plt.tick_params("y", which="major", labelleft=False, length=40)
- for y in [0,1000, 2000]:
- plt.axvline(y, color='white', linestyle='--', linewidth = '1.25')
- for k in range(len(neuron_loc)):
- ax.plot(-5, neuron_loc[k], 'o', color='red', zorder = 10, clip_on=False, markersize = 5)
- ax.set_clip_on(False)
- ax.set_xlim((-15,data_gt.shape[0]))
- ax.set_ylim((0,sens_max))
- plt.tick_params(axis='both', which='major', labelsize=13)
- num_time_labels = 8
- time_label_indices = np.around(np.linspace(1, len(trial_window), num_time_labels)).astype(int) - 1
- ax.set_xlabel("Sample Number", fontsize=15)
- cbar = plt.colorbar(im, fraction=0.036, pad=0.04)
- cbar.ax.tick_params(labelsize=11)
- cbar.set_label("V", fontsize = 12)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, "mice/data_output/figs/lfp_dg_c_gt.png"), transparent = True, dpi = 400)
- plt.show()
- print('mse', mse)
- # %%
- structure_acronyms = ['POL', 'APN', 'MB', 'SCig', 'POST', 'SUB', 'VISp', 'nan']
- intervals = [ 0, 5 , 9 ,12 ,29, 31 ,54, 76 ,81]
- POL_train_gt = train_gt[0:2,:]
- APN_train_gt = train_gt[5:7,:]
- MB_train_gt = train_gt[9:11,:]
- SCig_train_gt = train_gt[12:14,:]
- POST_train_gt = train_gt[29:31,:]
- SUB_train_gt = train_gt[31:33,:]
- VISp_train_gt = train_gt[54:57,:]
- POL_train_recon = train_recon[0:2,:]
- APN_train_recon = train_recon[5:7,:]
- MB_train_recon = train_recon[9:11,:]
- SCig_train_recon = train_recon[12:14,:]
- POST_train_recon = train_recon[29:31,:]
- SUB_train_recon = train_recon[31:33,:]
- VISp_train_recon = train_recon[54:57,:]
- POL_test_gt = test_gt[0:2,:]
- APN_test_gt = test_gt[5:7,:]
- MB_test_gt = test_gt[9:11,:]
- SCig_test_gt = test_gt[12:14,:]
- POST_test_gt = test_gt[29:31,:]
- SUB_test_gt = test_gt[31:33,:]
- VISp_test_gt = test_gt[54:57,:]
- POL_test_recon = test_recon[0:2,:]
- APN_test_recon = test_recon[5:7,:]
- MB_test_recon = test_recon[9:11,:]
- SCig_test_recon = test_recon[12:14,:]
- POST_test_recon = test_recon[29:31,:]
- SUB_test_recon = test_recon[31:33,:]
- VISp_test_recon = test_recon[54:57,:]
- ### APN
- mse_apn_train = np.linalg.norm(APN_train_recon - APN_train_gt) / np.linalg.norm(APN_train_gt)
- mse_apn_test = np.linalg.norm(APN_test_recon - APN_test_gt) / np.linalg.norm(APN_test_gt)
- print('apn', mse_apn_train, mse_apn_test)
- ### VISp
- mse_visp_train = np.linalg.norm(VISp_train_recon - VISp_train_gt) / np.linalg.norm(VISp_train_gt)
- mse_visp_test = np.linalg.norm(VISp_test_recon - VISp_test_gt) / np.linalg.norm(VISp_test_gt)
- print('visp', mse_visp_train, mse_visp_test)
- ### MB
- mse_mb_train = np.linalg.norm(MB_train_recon - MB_train_gt) / np.linalg.norm(MB_train_gt)
- mse_mb_test = np.linalg.norm(MB_test_recon - MB_test_gt) / np.linalg.norm(MB_test_gt)
- print('mb', mse_mb_train, mse_mb_test)
- ### SCig
- mse_scig_train = np.linalg.norm(SCig_train_recon - SCig_train_gt) / np.linalg.norm(SCig_train_gt)
- mse_scig_test = np.linalg.norm(SCig_test_recon - SCig_test_gt) / np.linalg.norm(SCig_test_gt)
- print('scig', mse_scig_train, mse_scig_test)
- ### POL
- mse_pol_train = np.linalg.norm(POL_train_recon - POL_train_gt) / np.linalg.norm(POL_train_gt)
- mse_pol_test = np.linalg.norm(POL_test_recon - POL_test_gt) / np.linalg.norm(POL_test_gt)
- print('pol', mse_pol_train, mse_pol_test)
- ##SUB
- mse_sub_train = np.linalg.norm(SUB_train_recon - SUB_train_gt) / np.linalg.norm(SUB_train_gt)
- mse_sub_test = np.linalg.norm(SUB_test_recon - SUB_test_gt) / np.linalg.norm(SUB_test_gt)
- print('sub', mse_sub_train, mse_sub_test)
- ## POST
- mse_post_train = np.linalg.norm(POST_train_recon - POST_train_gt) / np.linalg.norm(POST_train_gt)
- mse_post_test = np.linalg.norm(POST_test_recon - POST_test_gt) / np.linalg.norm(POST_test_gt)
- print('post', mse_post_train, mse_post_test)
- # %%
- #### Linear Regression
- ## Probe A
- trial = 2
- train_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_recons_a_{trial}.npy"))
- test_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_recons_a_{trial}.npy"))
- data_recon = np.vstack((train_recon, test_recon))
- train_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_gt_a_{trial}.npy"))
- test_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_gt_a_{trial}.npy"))
- data_gt= np.vstack((train_gt, test_gt))
- neuron_loc = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/neuron_loc_a_{trial}.npy"))
- mse = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/mse_a_{trial}.npy"))
- ##### Linear Regression
- from sklearn.linear_model import LinearRegression
- train_in = train_gt[:, neuron_loc]
- test_in = test_gt[:, neuron_loc]
- data_in = data_gt[:, neuron_loc]
- train_out = train_gt
- model = LinearRegression()
- model.fit(train_in, train_out)
- test_out = model.predict(test_in)
- print('test out shape',test_out.shape)
- data_out = model.predict(data_in)
- print('data out shape',data_out.shape)
- plt.figure(figsize = (6,2))
- plt.plot(test_out)
- plt.title('Linear Regression')
- plt.show()
- plt.figure(figsize = (6,2))
- plt.plot(test_gt)
- plt.title('Ground Truth')
- plt.show()
- ######### Plotting the reconstruction
- fig, ax = plt.subplots(figsize = (6.5,2))
- plt.imshow(data_gt.T, aspect = "auto")
- plt.xlabel('Time (s)', fontsize = 12)
- plt.ylabel('Neuron', fontsize =12)
- # Add red dots for neuron locations on y-axis
- for loc in neuron_loc:
- ax.plot(0.1, loc, 'ro', markersize=5, clip_on=False, zorder=10)
- plt.colorbar()
- plt.tight_layout()
- # plt.savefig(os.path.join(HOME_DIR, f'worms/data_output/figs/{name}_recon.png'), transparent = True, dpi = 400)
- plt.show()
- print('out shape',data_out.shape)
- fig, ax = plt.subplots(figsize = (6.5,2))
- plt.imshow(data_out.T, aspect = "auto")
- plt.xlabel('Time (s)', fontsize = 12)
- plt.ylabel('Neuron', fontsize = 12)
- plt.colorbar()
- # Add red dots for neuron locations on y-axis
- for loc in neuron_loc:
- ax.plot(0.1, loc, 'ro', markersize=5, clip_on=False, zorder=10)
- plt.tight_layout()
- # plt.savefig(os.path.join(HOME_DIR, f'worms/data_output/figs/{name}_linear.png'), transparent = True, dpi = 400)
- plt.show()
- n_train = train_out.shape[0]
- mask = np.ones(train_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- train_ex = data_out[:n_train,:].T[mask]
- train_gt_ex = train_gt.T[mask]
- mask = np.ones(test_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- test_ex = test_out.T[mask]
- test_gt_ex = test_gt.T[mask]
- mse_train = np.linalg.norm(train_ex - train_gt_ex) / np.linalg.norm(train_gt_ex)
- mse_test = np.linalg.norm(test_ex - test_gt_ex) / np.linalg.norm(test_gt_ex)
- print('mse train, test', [mse_train, mse_test])
- mask = np.ones(train_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- data_ex = data_out.T[mask]
- data_gt_ex = data_gt.T[mask]
- mse_whole = np.linalg.norm(data_ex - data_gt_ex) / np.linalg.norm(data_gt_ex)
- print('mse whole', mse_whole)
- # %%
- # Compute power spectral density for each dataset
- from scipy import signal
- import numpy as np
- name = 'expB_i_dg'
- # Parameters for PSD computation
- nperseg = min(256, data_out.shape[0] // 4) # Window length for Welch's method
- noverlap = nperseg // 2
- time = np.linspace(0, 2*data_out.shape[0]/3000, data_out.shape[0])
- # Initialize lists to store PSDs
- psds_linear = []
- psds_gt = []
- psds_shred = []
- freqs = None
- # Compute PSD for each neuron/channel
- for i in range(data_out.shape[1]):
- # Linear regression PSD
- f, psd_linear = signal.welch(data_out[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_linear.append(psd_linear)
- # Ground truth PSD
- f, psd_gt = signal.welch(data_gt[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_gt.append(psd_gt)
- # SHRED PSD
- f, psd_shred = signal.welch(data_recon[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_shred.append(psd_shred)
- if freqs is None:
- freqs = f
- # Convert to arrays
- psds_linear = np.array(psds_linear)
- psds_gt = np.array(psds_gt)
- psds_shred = np.array(psds_shred)
- # Compute mean and standard deviation across neurons
- mean_psd_linear = np.mean(psds_linear, axis=0)
- std_psd_linear = np.std(psds_linear, axis=0)
- mean_psd_gt = np.mean(psds_gt, axis=0)
- std_psd_gt = np.std(psds_gt, axis=0)
- mean_psd_shred = np.mean(psds_shred, axis=0)
- std_psd_shred = np.std(psds_shred, axis=0)
- # Plot the power spectral densities with smooth distributions
- fig, ax = plt.subplots(figsize=(3.5, 2))
- neuron = 10
- # Plot mean with shaded regions for standard deviation
- ax.loglog(freqs, mean_psd_linear, 'b-', linewidth=2, label='Linear Regression', alpha=0.8)
- ax.fill_between(freqs, mean_psd_linear - std_psd_linear, mean_psd_linear + std_psd_linear,
- alpha=0.3, color='blue')
- ax.loglog(freqs, mean_psd_gt, 'k-', linewidth=2, label='Ground Truth', alpha=0.8)
- ax.fill_between(freqs, mean_psd_gt - std_psd_gt, mean_psd_gt + std_psd_gt,
- alpha=0.3, color='black')
- ax.loglog(freqs, mean_psd_shred, 'r-', linewidth=2, label='SHRED', alpha=0.8)
- ax.fill_between(freqs, mean_psd_shred - std_psd_shred, mean_psd_shred + std_psd_shred,
- alpha=0.3, color='red')
- ax.set_xlabel('Frequency (Hz)', fontsize=12)
- ax.set_ylabel('PSD', fontsize=12)
- # ax.legend(fontsize=12)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, f'mice/data_output/figs/{name}_psd.png'), transparent = True, dpi = 400)
- plt.show()
- # %%
- mse_linear = np.linalg.norm(mean_psd_linear - mean_psd_gt) / np.linalg.norm(mean_psd_gt)
- mse_shred = np.linalg.norm(mean_psd_shred - mean_psd_gt) / np.linalg.norm(mean_psd_gt)
- print(mse_shred)
- print(mse_linear)
- # %%
- #### Linear Regression
- ## Probe C
- trial = 3
- train_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_recons_c_{trial}.npy"))
- test_recon = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_recons_c_{trial}.npy"))
- data_recon = np.vstack((train_recon, test_recon))
- train_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/train_gt_c_{trial}.npy"))
- test_gt = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/test_gt_c_{trial}.npy"))
- data_gt= np.vstack((train_gt, test_gt))
- neuron_loc = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/neuron_loc_c_{trial}.npy"))
- mse = np.load(os.path.join(HOME_DIR, f"mice/data_output/exp_Bi/dg/mse_c_{trial}.npy"))
- ##### Linear Regression
- from sklearn.linear_model import LinearRegression
- train_in = train_gt[:, neuron_loc]
- test_in = test_gt[:, neuron_loc]
- data_in = data_gt[:, neuron_loc]
- train_out = train_gt
- model = LinearRegression()
- model.fit(train_in, train_out)
- test_out = model.predict(test_in)
- print('test out shape',test_out.shape)
- data_out = model.predict(data_in)
- print('data out shape',data_out.shape)
- plt.figure(figsize = (6,2))
- plt.plot(test_out)
- plt.title('Linear Regression')
- plt.show()
- plt.figure(figsize = (6,2))
- plt.plot(test_gt)
- plt.title('Ground Truth')
- plt.show()
- ######### Plotting the reconstruction
- fig, ax = plt.subplots(figsize = (6.5,2))
- plt.imshow(data_gt.T, aspect = "auto")
- plt.xlabel('Time (s)', fontsize = 12)
- plt.ylabel('Neuron', fontsize =12)
- # Add red dots for neuron locations on y-axis
- for loc in neuron_loc:
- ax.plot(0.1, loc, 'ro', markersize=5, clip_on=False, zorder=10)
- plt.colorbar()
- plt.tight_layout()
- # plt.savefig(os.path.join(HOME_DIR, f'worms/data_output/figs/{name}_recon.png'), transparent = True, dpi = 400)
- plt.show()
- print('out shape',data_out.shape)
- fig, ax = plt.subplots(figsize = (6.5,2))
- plt.imshow(data_out.T, aspect = "auto")
- plt.xlabel('Time (s)', fontsize = 12)
- plt.ylabel('Neuron', fontsize = 12)
- plt.colorbar()
- # Add red dots for neuron locations on y-axis
- for loc in neuron_loc:
- ax.plot(0.1, loc, 'ro', markersize=5, clip_on=False, zorder=10)
- plt.tight_layout()
- # plt.savefig(os.path.join(HOME_DIR, f'worms/data_output/figs/{name}_linear.png'), transparent = True, dpi = 400)
- plt.show()
- n_train = train_out.shape[0]
- mask = np.ones(train_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- train_ex = data_out[:n_train,:].T[mask]
- train_gt_ex = train_gt.T[mask]
- mask = np.ones(test_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- test_ex = test_out.T[mask]
- test_gt_ex = test_gt.T[mask]
- mse_train = np.linalg.norm(train_ex - train_gt_ex) / np.linalg.norm(train_gt_ex)
- mse_test = np.linalg.norm(test_ex - test_gt_ex) / np.linalg.norm(test_gt_ex)
- print('mse train, test', [mse_train, mse_test])
- mask = np.ones(train_out.shape[1], dtype=bool)
- mask[neuron_loc] = False
- data_ex = data_out.T[mask]
- data_gt_ex = data_gt.T[mask]
- mse_whole = np.linalg.norm(data_ex - data_gt_ex) / np.linalg.norm(data_gt_ex)
- print('mse whole', mse_whole)
- # %%
- #Compute power spectral density for each dataset
- from scipy import signal
- import numpy as np
- name = 'expB_i_dg_c'
- # Parameters for PSD computation
- nperseg = min(256, data_out.shape[0] // 4) # Window length for Welch's method
- noverlap = nperseg // 2
- time = np.linspace(0, 2*data_out.shape[0]/3000, data_out.shape[0])
- # Initialize lists to store PSDs
- psds_linear = []
- psds_gt = []
- psds_shred = []
- freqs = None
- # Compute PSD for each neuron/channel
- for i in range(data_out.shape[1]):
- # Linear regression PSD
- f, psd_linear = signal.welch(data_out[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_linear.append(psd_linear)
- # Ground truth PSD
- f, psd_gt = signal.welch(data_gt[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_gt.append(psd_gt)
- # SHRED PSD
- f, psd_shred = signal.welch(data_recon[:, i], fs=1/(time[1]-time[0]),
- nperseg=nperseg, noverlap=noverlap)
- psds_shred.append(psd_shred)
- if freqs is None:
- freqs = f
- # Convert to arrays
- psds_linear = np.array(psds_linear)
- psds_gt = np.array(psds_gt)
- psds_shred = np.array(psds_shred)
- # Compute mean and standard deviation across neurons
- mean_psd_linear = np.mean(psds_linear, axis=0)
- std_psd_linear = np.std(psds_linear, axis=0)
- mean_psd_gt = np.mean(psds_gt, axis=0)
- std_psd_gt = np.std(psds_gt, axis=0)
- mean_psd_shred = np.mean(psds_shred, axis=0)
- std_psd_shred = np.std(psds_shred, axis=0)
- # Plot the power spectral densities with smooth distributions
- fig, ax = plt.subplots(figsize=(3.5, 2))
- neuron = 10
- # Plot mean with shaded regions for standard deviation
- ax.loglog(freqs, mean_psd_linear, 'b-', linewidth=2, label='Linear Regression', alpha=0.8)
- ax.fill_between(freqs, mean_psd_linear - std_psd_linear, mean_psd_linear + std_psd_linear,
- alpha=0.3, color='blue')
- ax.loglog(freqs, mean_psd_gt, 'k-', linewidth=2, label='Ground Truth', alpha=0.8)
- ax.fill_between(freqs, mean_psd_gt - std_psd_gt, mean_psd_gt + std_psd_gt,
- alpha=0.3, color='black')
- ax.loglog(freqs, mean_psd_shred, 'r-', linewidth=2, label='SHRED', alpha=0.8)
- ax.fill_between(freqs, mean_psd_shred - std_psd_shred, mean_psd_shred + std_psd_shred,
- alpha=0.3, color='red')
- ax.set_xlabel('Frequency (Hz)', fontsize=12)
- ax.set_ylabel('PSD', fontsize=12)
- # ax.legend(fontsize=12)
- plt.tight_layout()
- plt.savefig(os.path.join(HOME_DIR, f'mice/data_output/figs/{name}_psd.png'), transparent = True, dpi = 400)
- plt.show()
- # %%
- mse_linear = np.linalg.norm(mean_psd_linear - mean_psd_gt) / np.linalg.norm(mean_psd_gt)
- mse_shred = np.linalg.norm(mean_psd_shred - mean_psd_gt) / np.linalg.norm(mean_psd_gt)
- print(mse_shred)
- print(mse_linear)
- # %%
shred_mice_expB_i_dg.ipynb at commit 6b86875, no license · at the source
Overview
- Department of Applied Mathematics, University of Washington, Seattle, WA, USA
- Autodesk Research, London, UK
Abstract
Machine learning algorithms are affording new opportunities for building bio-inspired and data-driven models characterizing neural activity. Critical to understanding decision-making and behaviour is quantifying the relationship between the activity of neuronal population codes and individual neurons. We leverage a SHallow REcurrent Decoder (SHRED) architecture for mapping the dynamics of population codes to individual neurons and other proxy measures of neural activity and behaviour. SHRED is constructed from a temporal sequence model, which encodes the temporal dynamics of limited sensor data in multiple scenarios, and a shallow decoder, which reconstructs the corresponding high-dimensional neuronal and/
This article is part of the discussion meeting issue ‘Digital healthcare for the management of functional neurological disorders’.
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 5 matches between paragraphs and lines of code.
amysrude/neuralSHRED
6b8687501a637ec64ba2ffe65313c6acced4c83a, 15 September 2025Availability: 1 check, the latest on 26 September 2026: the link answers
- 26 September 2026: the link answers
21 files
- mice/
shred_mice.ipynb , Jupyter, 398 lines - mice/
shred_mice_exp2.1.ipynb , Jupyter, 291 lines - mice/
shred_mice_expB_i_dg.ipy , Jupyter, 1,010 lines, 3 matchesnb - mice/
shred_mice_expB_i_flash. , Jupyter, 1,001 lines, 1 matchipynb - mice/
shred_mice_expB_ii.ipynb , Jupyter, 799 lines - mice/
shred_mice_expB_iii.ipyn , Jupyter, 522 linesb - mice/
shred_mice_expB_iv.ipynb , Jupyter, 655 lines, 1 match - shred/
models_shred.py , Python, 145 lines - shred/
processdata_shred.py , Python, 46 lines - shred/
reconstructions_shred.py , Python, 107 lines - sindy-shred-main/
processdata.py , Python, 39 lines - sindy-shred-main/
reconstructions.py , Python, 107 lines - sindy-shred-main/
sindy.py , Python, 317 lines - sindy-shred-main/
sindy_shred.py , Python, 182 lines - sindy-shred-main/
sindy_utils.py , Python, 150 lines - sindy-shred-main/
sst_sindy_shred.ipynb , Jupyter, 620 lines - worms/
shred_worms_expA_i.ipynb , Jupyter, 743 lines - worms/
shred_worms_expA_ii.ipyn , Jupyter, 732 linesb - worms/
shred_worms_expA_iii_pt1 , Jupyter, 759 lines.ipynb - worms/
shred_worms_expA_iii_pt2 , Jupyter, 355 lines.ipynb - repository limit reached (2,000 files or 30 MB): the rest is at the source (4 files)
- README.md, Text, 23 lines
meganebers/mobileSHRED
66e85893bea3a463e18aaaf8b087387c64561018, 7 March 2024Availability: 1 check, the latest on 26 September 2026: the link answers
- 26 September 2026: the link answers
6 files
- Detonation/
ARA_mobileSHRED_detonati , Python, 360 lineson_multimodal.py - SHRED_base/
forecasts.py , Python, 120 lines - SHRED_base/
models.py , Python, 145 lines - SHRED_base/
processdata.py , Python, 46 lines - SHRED_base/
reconstructions.py , Python, 107 lines - README.md, Text, 17 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;
- 25 scripts, each with its path and the digest of its content;
- 5 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 accessibility
All code and data are publicly available. Figures and experiments can be reproduced from the following GitHub: http://
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, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 2 authors, 5 keywords, 9 MeSH terms, 2 funders, 58 references.
Cite
This paper
Rude, A., & Kutz, J. N. (2026). Shallow recurrent decoders for neural and behavioural dynamics. Philosophical transactions of the Royal Society of London. Series B, Biological sciences, 381(1958), 20240461. https://
BibTeX
@article{rude2026shallow
author = {Rude, Amy and Kutz, J Nathan},
title = {{Shallow recurrent decoders for neural and behavioural dynamics}},
journal = {Philosophical transactions of the Royal Society of London. Series B, Biological sciences},
year = {2026},
month = sep,
volume = {381},
number = {1958},
pages = {20240461},
publisher = {Royal Society},
issn = {0962-8436},
doi = {10.1098/
url = {https://
pmid = {42750452},
pmcid = {PMC13583487}
}
RIS
TY - JOUR
AU - Rude, Amy
AU - Kutz, J Nathan
TI - Shallow recurrent decoders for neural and behavioural dynamics
T2 - Philosophical transactions of the Royal Society of London. Series B, Biological sciences
J2 - Philos Trans R Soc Lond B Biol Sci
PY - 2026
DA - 2026/
VL - 381
IS - 1958
SP - 20240461
SN - 0962-8436
PB - Royal Society
DO - 10.1098/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1098/
"type": "article-journal",
"title": "Shallow recurrent decoders for neural and behavioural dynamics",
"container-title": "Philosophical transactions of the Royal Society of London. Series B, Biological sciences",
"author": [
{
"family": "Rude",
"given": "Amy"
},
{
"family": "Kutz",
"given": "J Nathan"
}
],
"container-title-short":
"volume": "381",
"issue": "1958",
"page": "20240461",
"DOI": "10.1098/
"PMID": "42750452",
"PMCID": "PMC13583487",
"ISSN": "0962-8436",
"publisher": "Royal Society",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
9,
1
]
]
}
}
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.1038/s41593-026-02257-5 [code]
- Neural sequences underlying directed turning in Caenorhabditis elegans.Journal: Nature neuroscienceIn common: h5py, Pillow, PyTorch, 4 other tools, C. elegans, 3 references
- [2] doi:10.1038/s41467-026-72709-w [code]
- An epifluorescence microscope design for naturalistic behavior and cellular activity in freely moving Caenorhabditis elegans.Journal: Nature communicationsIn common: imageio, h5py, Pillow, 5 other tools, C. elegans, 1 reference
- [3] doi:10.3389/frai.2026.1771088 [code]
- Few-shot deployment of pretrained MRI transformers in brain imaging tasks.Journal: Frontiers in artificial intelligenceIn common: imageio, h5py, Pillow, 6 other tools, 1 reference
- [4] doi:10.1038/s41467-026-76939-w [code]
- HIPPIE: a generative model for electrophysiological analysis across species, technologies, and modalities.Journal: Nature communicationsIn common: AllenSDK, h5py, Pillow, 6 other tools, mouse
- [5] doi:10.3389/fncom.2026.1786996 [code]
- Schumann-anchored golden ratio organization of human neural oscillations.Journal: Frontiers in computational neuroscienceIn common: imageio, MNE-Python, h5py, 6 other tools
- [6] doi:10.1371/journal.pcbi.1014162 [code]
- Exploring neural manifolds across a wide range of intrinsic dimensions.Journal: PLoS computational biologyIn common: h5py, scikit-learn, pandas, 3 other tools, computational, 3 references
- [7] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: xarray, h5py, Pillow, 6 other tools, mouse
- [8] doi:10.1093/bioinformatics/btag540 [code]
- Deciphering spatial heterogeneity by multimodal spatial transcriptomics modelling with SpatialModal.Journal: Bioinformatics (Oxford, England)In common: imageio, h5py, Pillow, 6 other tools, mouse
- [9] doi:10.1038/s41467-026-76098-y [code]
- A single computational objective can produce specialization of streams in visual cortex.Journal: Nature communicationsIn common: xarray, h5py, Pillow, 6 other tools
- [10] doi:10.1371/journal.pcbi.1014152 [code]
- Synchronization properties in C. elegans: Relating behavioral circuits to structural and functional neuronal connectivity.Journal: PLoS computational biologyIn common: pandas, SciPy, Matplotlib, 1 other tool, C. elegans, 4 references
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, 25 scripts, and 5 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:a95511f0e20e32be…
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.
