Chapter 7: Modeling the visual field (with FilterNet)

FilterNet is a part of the BMTK that simulates the effects of visual input onto cells in the LGN. It uses LGNModel as a backend, which uses neural-filters to simulate firing rates and spike-trains one may expect given a stimulus on (especially mouse) visual field. FilterNet supports a number of visual stimuli including static-graitings, moving-graiting, full-field flashes, static images and even movies.

Filternet uses a linear-nonlinear-Poisson (lnp) model, using a spatial-temporal linear filter to convert a movie into a series of spike trains.

FilterNet LNP

A simple example

1. Building the network

FilterNet is modeled on the mammalian LGN and expects a two-dimensional network of cells that can be mapped onto the receptive field. The default parameters of the filters are based on experimental recordings from the mouse LGN [Durant et. al, 2016, Billeh et. al, 2019] As our starting case we will have a simple network of 20 ON cells and 20 OFF cells with coordinates that correspond to the 240x120 movie we will be playing against it.

[1]:
import numpy as np
from bmtk.builder import NetworkBuilder


net = NetworkBuilder('lgn')  # Initialize network called 'lgn'

net.add_nodes(  # add 10 simple OFF cells
    N=20,
    model_type='virtual',
    model_template='lgnmodel:tOFF_TF15',
    x=np.random.uniform(0.0, 240.0, 20),
    y=np.random.uniform(0.0, 120.0, 20),
    spatial_size=1.0,
    dynamics_params='tOFF_TF15.json'
)

net.add_nodes(  # add 10 simple ON cells
    N=20,
    model_type='virtual',
    model_template='lgnmodel:tON',
    x=np.random.uniform(0.0, 240.0, 20),
    y=np.random.uniform(0.0, 120.0, 20),
    spatial_size=1.0,
    dynamics_params='tON_TF8.json'
)

# Create and save the network
net.build()
net.save_nodes(output_dir='sim_ch07/network')

2. Setting up the simulation enviornment

The following will create the necessarcy python and configuration files to run a simulation, all placed in the sim_ch07 directory.

[2]:
from bmtk.utils.sim_setup import build_env_filternet

build_env_filternet(
    base_dir='sim_ch07',
    network_dir='sim_ch07/network',
    tstop=3000.0,                   # run the simulation for 3 seconds
    include_examples=True)          # includes example model files which we'll use in this tutorial
WARNING:root:Configuration file /home/ping/bmtk_change/bmtk/docs/tutorial/sim_ch07/config.json already exists, skipping. Please delete existing file, use a different name, or use overwrite_config=True.
WARNING:root:Configuration file /home/ping/bmtk_change/bmtk/docs/tutorial/sim_ch07/circuit_config.json already exists, skipping. Please delete existing file, use a different name, or use overwrite_config=True.
WARNING:root:Configuration file /home/ping/bmtk_change/bmtk/docs/tutorial/sim_ch07/simulation_config.json already exists, skipping. Please delete existing file, use a different name, or use overwrite_config=True.

The most important file that we will want to change is the simulation_config.json which contains information about the simulation.

By default the config file does not contain any stimulus so the firing rate will simply be at the baseline firing rate. To change this and play a certain type of movie/stimulus, which here we’ll use a movie with a bright flash full-screen for an extend period of time. To do so open up simulation_config.json and add the following inputs section:

{
    "inputs": {
        "full_field_flash": {
            "input_type": "movie",
            "module": "full_field_flash",
            "row_size": 120,
            "col_size": 240,
            "t_on": 1000.0,
            "t_off": 2000.0,
            "max_intensity": 20.0,
            "frame_rate": 1000.0
        }
    }
}

This will create a flash on the screen from 1 to 2 seconds. Make sure the frame rate is sufficient for the temporal fluctuations in the LGN filters (e.g., 1000.0).

3 Running the simulation

You can run the simulation from the command line by going to the sim_ch07 directory and running:

$ python run_filternet.py simulation_config.json

And if you have MPI installed with access to a cluster you can parallize much of the process

$ mpirun -np $NCORES python run_filternet.py simulation_config.json

or we can run it directly

[3]:
from bmtk.simulator import filternet

config = filternet.Config.from_json('sim_ch07/config.json')
config.build_env()
net = filternet.FilterNetwork.from_config(config)
sim = filternet.FilterSimulator.from_config(config, net)
sim.run()
2023-02-10 13:38:44,402 [WARNING] Directory /home/ping/bmtk_change/bmtk/docs/tutorial/sim_ch07/output already exists.
WARNING:FilterNetIOUtils:Directory /home/ping/bmtk_change/bmtk/docs/tutorial/sim_ch07/output already exists.
2023-02-10 13:38:44,403 [INFO] Created log file
INFO:FilterNetIOUtils:Created log file
2023-02-10 13:39:03,776 [INFO] Building cells.
INFO:FilterNetIOUtils:Building cells.
2023-02-10 13:39:04,649 [INFO] Evaluating rates.
INFO:FilterNetIOUtils:Evaluating rates.
2023-02-10 13:39:11,702 [INFO] Done.
INFO:FilterNetIOUtils:Done.

This will generate a number of files in the output directory, including rates.csv that contains the firing rate of each cell over the duration of the simulation, and spikes.h5 in which the rates have been converted to spike times using a nonhomogeneous Poisson process.

[4]:
from bmtk.analyzer.spike_trains import plot_raster

_ = plot_raster(config_file='sim_ch07/config.json', group_by='model_template')

_images/tutorial_filter_models_9_0.png

As expected, the flash from 1 to 2 seconds caused the ON cells to temporary increase firing while also supressing the OFF cells.

Running different types of Stimuli

Currently FilterNet allows for a number of different types of custom and pre-alligned type of stimuli. To change the type of stimuli requires updating the inputs section in the simulation_config.json file like above.

movie

Allows playing a custom movie file in the form of a three-dimension matrix saved in a npy file.

example

{
  "inputs": {
    "movie_input": {
      "input_type": "movie",
      "module": "movie",
      "data_file": "/path/to/my/movie.npy",
      "frame_rate": 1000.0
    }
  }
}

parameters * movie - Link to a 3-dimensional (time, x, y) matrix representing a movie * frame_rate - frames per second

grating

Plays a drifting grating across the screen

example

{
  "inputs": {
    "LGN_spikes": {
      "input_type": "movie",
      "module": "graiting",
      "row_size": 120,
      "col_size": 240,
      "gray_screen_dur": 0.5,
      "cpd": 0.04,
      "temporal_f": 4.0,
      "contrast": 0.8,
      "theta": 45.0,
      "phase": 0.0,
      "frame_rate": 1000.0
    }
  }
}

parameters * row_size, col_size - width and heigth dimensions of screen in pixels. * grapy_screen_dur - displays an optional gray screen for a number of seconds before the grating starts. (default: 0) * cpd - spatial frequncy represented as cycles per degree. (default: 0.05) * temporal_f - temporal frequency in Hz. (default: 4.0) * theta: orientation angle, in degrees (default: 45.0) * phase: temporal phase, in degrees (default: 0.0) * contrast: the maximum constrast, must be between 0 and 1.0 (default: 1.0)

full field flash

Creates a bright (or dark) flash on a gray screen for a limited number of seconds

example

{
    "inputs": {
        "full_field_flash": {
            "input_type": "movie",
            "module": "full_field_flash",
            "row_size": 120,
            "col_size": 240,
            "t_on": 1000.0,
            "t_off": 2000.0,
            "max_intensity": 20.0
            "frame_rate": 1000.0
        }
    }
}

parameters * row_size, col_size - width and heigth dimensions of screen in pixels. * t_on - time (ms) from the beginning on when to start the flash * t_off - length (ms) of flash * max_intensity - intensity of screen during flash (>0.0 is brighter, <0.0 is darker) compared to gray screen.

looming

Creates a spreading black field originating from the center.

example

{
  "inputs": {
    "LGN_spikes": {
      "input_type": "movie",
      "module": "looming",
      "row_size": 120,
      "col_size": 240,
      "frame_rate": 1000.0,
      "gray_screen_dur": 0.5,
      "t_looming": 1.0
    }
  }
}

parameters * row_size, col_size - width and heigth dimensions of screen in pixels. * frame_rate - frames per second * gray_screen_dur - durating of initial grey screen (seconds) * t_looming - time of looming movie (seconds).

Units

simple ON/OFF cells

These are cells that respond by either increasing or decreasing firing rates to brightness changes in their receptive fields. They are also the basis for more advanced cell types. When building a network you can set a cell or set of cells with model_template='lgnmodel:tON' or model_template='lgnmodel:tOFF' during the call to add_nodes(...). There are also a number of special types (tON_TF8, sON_TF1, sON_TF2, sON_TF4, sON_TF8, sON_TF15, tOFF_TF1, tOFF_TF2, tOFF_TF4, tOFF_TF8, tOFF_TF15, sOFF_TF1, sOFF_TF2, sOFF_TF4, sOFF_TF8, sOFF_TF15) which have properties that have been pre-calculated using electrophysiological recordings from the mouse LGN (Durant et. al 2016, Billeh et. al 2019).

A combination of a Gaussian spatial filter and double cosine temporal filter are used to filter the receptive field.

013d2eb1d29a4fcb903b31a7047fbaaa

4db37d349258445eba319cb57d1d1709

parameters

The parameters for the cell are by default set in the .json file pointed to by the dynamic_params parameter are calculated from extracellular recordings from the mouse LGN (Durand et. al 2016, Billeh et. al, 2019). These parameters may be over ridden to be unique to each cell by passing in a list of values in the call to add_nodes. For example to given every cell a randomized spontaneous firing rate using the spont_fr variable (see below) you would just add the following:

net.add_nodes(  # add 10 simple ON cells
    N=20,
    model_template='lgnmodel:tON',
    spont_fr=np.random.uniform(1.0, 2.0, 20) # create a rand list of 20 firing rates between 1 to 2 Hz
    ....
)

spatial filter parameters * spatial_size: (float, float) - the row and column spread of the gaussian filter, default: (1.0, 1.0) * spatial_rotation: float - the gaussian rotation in degrees, default: 0.0

image0

temporal filter parameters * weights: (float, float) - used to control amplitude of the the two bumps in cosine filter. The first value is used to set the major bump and must have weights[0] > weights[1] * kpeaks: (float, float) - used to control the spread of the two bumps, the first value for the initial larger peak and the second for the smaller peak. The second peak must have a greater spread than the first. * delays: (float, float) - controls the delays of the first and second bumps

image1

non-linear filter parameters * spont_fr: float - The spontaneous/resting firing rate of the cell. ON/OFF cells use the function \(Heaviside(s+spont\_fr)*(s+spont\_fr)\) to filter the firing rate following the spatialtemporal filter.

Spatial Mixture cells

These types of units combine two spatial filters to create a Mexican hat filter so the spatial filter responds to a bright area surrounded by darkness (or vice-versa). To use set model_template='lgnmodel:LGNOnOFFCell'

parameters * sigma_on: (float, float) - spread of the ON region of the spatial filter. * sigma_off: (float, float) - spread of the OFF region of the spatial filter.

The temporal filter is set using the same parameters as a simple cell.

Dual filter cells (sONtOFF, sONsOFF)

These units combine two simple linear filters into one, and can be set using either model_template='lgnmodel:sONsOFF' or model_template='lgnmodel:sONtOFF', both with a non-dominate spatial-off filter.

parameters

The parameters for the first filter (tOFF or sOFF subunit) is set using the same parameters as a simple cell. You can set non-dominate secondary subunit parameters in two ways: 1. Adding parameter non_dom_params that points to a second dynamics_params .json file. (eg. non_dom_params='sON_TF8.json') 2. add the postfix *_non_dom* to the specified params (eg. weights_non_dom, kpeaks_non_dom, delays_non_dom)

[ ]: