DOI: 10.5281/zenodo.7958464
title: Dataset for "Brain-machine interface learning is facilitated by specific patterning of distributed cortical feedback"

description: Dataset for the manuscript entitled: Brain-machine interface learning is facilitated by specific patterning of distributed cortical feedback.

DOI of the manuscript: 10.1126/sciadv.adh1328

Abstract of the manuscript:

Neuroprosthetics offer great hope for motor-impaired patients. One obstacle is that fine motor control requires near-instantaneous, rich somatosensory feedback. Such distributed feedback may be recreated in a brain-machine interface using distributed artificial stimulation across the cortical surface. Here, we hypothesized that neuronal stimulation must be contiguous in its spatiotemporal dynamics in order to be efficiently integrated by sensorimotor circuits. Using a closed-loop brain-machine interface, we trained head-fixed mice to control a virtual cursor by modulating the activity of motor cortex neurons. We provided artificial feedback in real time with distributed optogenetic stimulation patterns in the primary somatosensory cortex. Mice developed a specific motor strategy and succeeded to learn the task only when the optogenetic feedback pattern was spatially and temporally contiguous while it moved across the topography of the somatosensory cortex. These results reveal new properties of sensorimotor cortical integration and set new constraints on the design of neuroprosthetics.

Description of the variables in the data storage dictionary:



cursor_positions: sequence of the virtual cursor position over a session

range_of_rewardable_cursor_position: range of cursor positions that can be rewarded. Upper threshold excluded. Lower threshold included.  

cursor_times: timing of the cursor positions provided by the cursor_positions data, in the same clock as spike and lick times. 

lick_times: timing of all recorded licks.  

reward_times:timing of the opening of the valve that releases the water reward. 

master_spike_times: time of the spikes of the master neurons.

master_spike_shape: spike shape of each spike stored in master_spike_times. The shipe shapes are shown for 3s (30kHz sampling rate), for each 4 electrode of the corresponding tetrode.

neighbor_spike_times': same as master_spike_times for neighbor neurons.

neighbor_spike_shape': same as master_spike_shape for neighbor neurons.   


General structure of the data set:

Each variable is a hierarchical tree of lists: [Protocol][Mouse][Session]. 

Protocol takes one of the following values: 0: Bar feedback | 1: Full shuffle | 2: No Feedback | 5: Playback Structured Feedback | 7: Spontaneous activity | 8: Barrel Shuffle | 9: Frame shuffle.

Mouse and Session iterate through respectively the mice that were involved in the protocole, and the sessions, generally 5 in total.

Python code to load the hdf5 File

The code below relies on libraries available on a standard, mac anaconda install of the jupyter notebook system on the 25/03/2024. It provides with several nested lists of Numpy arrays.

For instance, to access the series of cursor positions of Mouse M during session number S of Protocole P, index as follows: 

CP = cursor_positions_DATA[P][M][S]

# 1 - Load the libraries

    import h5py    import numpy as np    import pylab as pl

    filename = "./data.h5"    f = h5py.File(filename, "r")

# 2 - Extraction of the cursor position, time, lick and reward time, as well as the activity of the master and neighbor neurons. 

    cursor_positions = f['cursor_positions']

    cursor_positions_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[cursor_positions[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    bfr = np.array(S)                    DATA_mouse.append(bfr)                 DATA_protocole.append(DATA_mouse)        cursor_positions_DATA.append(DATA_protocole)

 

    cursor_times = f['cursor_times']

    cursor_times_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[cursor_times[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    bfr = np.array(S)                    DATA_mouse.append(bfr)                 DATA_protocole.append(DATA_mouse)        cursor_times_DATA.append(DATA_protocole)

 

    lick_times = f['lick_times']

    lick_times_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[lick_times[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    bfr = np.array(S)                    DATA_mouse.append(bfr)                 DATA_protocole.append(DATA_mouse)        lick_times_DATA.append(DATA_protocole)

 

    reward_times = f['reward_times']

    reward_times_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[reward_times[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    bfr = np.array(S)                    DATA_mouse.append(bfr)                 DATA_protocole.append(DATA_mouse)        reward_times_DATA.append(DATA_protocole)

 

    master_spike_times = f['master_spike_times']

    master_spike_times_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[master_spike_times[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    DATA_unit = []                    for Unit in range(len(S)):                        if not(list(S) == [0,1]):                            N = f[S[Unit][0]]                            bfr = np.array(N)                            DATA_unit.append(bfr)                     DATA_mouse.append(DATA_unit)                DATA_protocole.append(DATA_mouse)        master_spike_times_DATA.append(DATA_protocole)

 

    neighbor_spike_times = f['neighbor_spike_times']

    neighbor_spike_times_DATA = []    for Protocole in range(9):        DATA_protocole = []        P = f[neighbor_spike_times[Protocole][0]]        for Mouse in range(16):                # Loop through the mice, and collect             M = f[P[Mouse][0]]            if not(list(M) == [0,1]) and (len(M) == 5):                DATA_mouse = []                for Session in range(5):                    S = f[M[Session][0]]                    DATA_unit = []                    for Unit in range(len(S)):                        if not(list(S) == [0,1]):                            N = f[S[Unit][0]]                            bfr = np.array(N)                            DATA_unit.append(bfr)                     DATA_mouse.append(DATA_unit)                DATA_protocole.append(DATA_mouse)        neighbor_spike_times_DATA.append(DATA_protocole)