Configuring NDS

The scripts provided with NDS are configured using a settings.yaml file. By default, this file is stored in $HOME/.nds and created when you run the nds_post_install_config script. However, you can specify custom locations by passing the --settings_path=/path/to/settings.yaml argument to most NDS scripts. You can customize the behavior of the NDS components by editing the settings.yaml as described below.

Encoder

The encoder transforms behavioral data into spike rates using a pretrained model. It is comprised of three sub-components, the preprocessor, the encoder model, and the postprocessor. Each of these sub-components can be configured using the setttings.yaml file. Additionally, the input and output and system behavior can be configured.

Encoder period

The time interval between encoder iterations can be configured by changing the loop_time configuration:

timer:
  loop_time: 0.02 # seconds

Input

Configure one of the 2 possible behavior data input sources for the encoder.

Behavior LSL stream

To enable LSL as input for the encoder apply the following configuration:

encoder:
  input:
    type: "LSL"

The available settings for this input source are:

encoder:
  input:
    lsl:
      stream_name: "NDS-Behavior"
      # maximum time for attempting a connection to an LSL input stream
      connection_timeout: 60.0 # seconds

Prerecorded behavior file

To enable file as input for the encoder change the following configuration:

encoder:
  input:
    type: "file"

The available customizations for this input source are:

encoder:
  input:
    file:
      # paths can be absolute or relative to the current work directory
      path: "sample_data/session_4_behavior_standardized.npz"
      sampling_rate: 50 # Hz
      timestamps_array_name: "timestamps_train"
      data_array_name: "vel_train"

The npz file is expected to include two arrays, one with timestamps and another one with behavior data. The name of each array has to be defined in the configuration file, this information is required by the encoder in order to read the data correctly. Export standardized model for streaming contains an example of how to save a file in the correct format.

Plugins

Preprocessor

Enable the encoder to use your custom preprocessor by changing the path:

encoder:
  # set a path to your custom preprocessor plugin
  preprocessor: 'plugins/preprocessor.py'

Postprocessor

Enable the encoder to use your custom postprocessor by changing the path:

encoder:
  # set a path to your custom postprocessor plugin
  postprocessor: 'plugins/postprocessor.py'

Model

To integrate your customized model set the following configuration:

encoder:
  # path to the model entrypoint
  model: 'plugins/model.py'

Output

The encoder can be configured to output to a file or to an LSL stream. Each output type allows its own customization.

Spike rates LSL output

To enable LSL output apply the following configuration:

encoder:
  output:
    type: "LSL"

The available customizations for this output type are:

encoder:
  output:
    lsl:
      # possible values are "float32", "double64", "int8", "int16", "int32", "int64"
      channel_format: "int16"
      stream_name: "NDS-SpikeRates"
      stream_type: "SpikingRates"
      source_id: "SimulatedSpikeRates"
      instrument:
        manufacturer: "Hardware Company"
        model: "Simulated"
        id: 0

stream_name is the most relevant in this context as it is used by the ephys generator script to discover and connect to the LSL source.

Save spike rates to file

To set the encoder output to a file apply the following configuration:

encoder:
  output:
    type: "file"

The available customizations for this output type are:

encoder:
  output:
    file: "output.csv"

The format of the output file is CSV without header. The first column is the timestamp and the following columns are the spike rates for each unit.

Ephys Generator

The electrophysiology (ephys) generator is responsible for synthesizing spikes based on input spike rate data that it receives. The ephys generator produces three outputs that are continuously pushed to 3 LSL streams. The system properties and streams can be configured using the settings.yaml file. The available general customizations for ephys generator are:

ephys_generator:
  random_seed: 12332323

  resolution: 0.25 # uV per count

  raw_data_frequency: 30_000 # Hz
  n_units_per_channel: 1 # number of distinct waveforms in each channel

  refractory_time: 0.001 # seconds

  lsl_chunk_frequency: 1_000 # Hz

Random seed

random_seed is used to initialize NumPy’s underlying random number generator which is useful for reproducibility between runs.

Resolution

resolution is the resolution of the electrophysiology data in microvolts per count.

Raw data frequency

raw_data_frequency represents the sampling rate of the raw data output. This can have an impact on the performance of the simulation. While a typical value is 30kHz, on machines with less-capable CPUs this should be lowered to 10kHz.

Units per channel

n_units_per_channel defines how many neurons are being captured per channel.

Refractory time

refractory_time is the minimum time between two consecutive spikes for a unit.

LSL chuck frequency

lsl_chunk_frequency determines how often data is being pushed to the LSL streams.

Input

Ephys generator typically consumes the output from the encoder, in which case the input type should be LSL.

Spike rates LSL input

To use an LSL input apply the following configuration:

ephys_generator:
  input:
    type: "LSL"

The available customizations for this input type are:

ephys_generator:
  input:
    lsl:
      stream_name: "NDS-SpikeRates"
      # maximum time for attempting a connection to an LSL input stream
      connection_timeout: 60.0 # seconds

If ephys generator should read the rates from the encoder via LSL, then the stream_name in this configuration should match the stream_name from the encoder output.

Testing input

Like the name suggests, this input is meant for testing. To use it, apply the following configuration:

ephys_generator:
  input:
    type: "testing"

The available customizations for this input type are:

ephys_generator:
  input:
    testing:
      n_channels: 50

When the testing input is configured, the ephys generator will output spikes based on constant spike rates for each channel/unit. The rates are uniformly distributed in the interval [0, 100). This means that, if n_channels is set to 50, the first unit will have a spike rate of 0, while the last unit will have a spike rate of 99 spikes/s.

Output

Following are the configurations for each of the 3 LSL outputs.

Raw

The available customizations for this output type are:

ephys_generator:
  output:
    raw:
      lsl:
        # possible values are "float32", "double64", "int8", "int16", "int32", "int64"
        channel_format: "int16"
        stream_name: "NDS-RawData"
        stream_type: "Ephys"
        source_id: "SimulatedRawData"
        instrument:
          manufacturer: "Hardware Company"
          model: "Simulated"
          id: 0

LFP

The available customizations for this output type are:

ephys_generator:
  output:
    lfp:
      data_frequency: 1_000 # Hz
      filter_cutoff: 300 # Hz
      filter_order: 4 # number
      lsl:
        # possible values are "float32", "double64", "int8", "int16", "int32", "int64"
        channel_format: "int16"
        stream_name: "NDS-LFPData"
        stream_type: 'LFP'
        source_id: "SimulatedLFPData"
        instrument:
          manufacturer: "Hardware Company"
          model: "Simulated"
          id: 0

Spike Events

The available customizations for this output type are:

ephys_generator:
  output:
    spike_events:
      lsl:
        # possible values are "float32", "double64", "int8", "int16", "int32", "int64"
        channel_format: "int16"
        stream_name: "NDS-SpikeEvents"
        stream_type: "SpikeEvents"
        source_id: "SimulatedSpikeEvents"
        instrument:
          manufacturer: "Hardware Company"
          model: "Simulated"
          id: 0

Noise

Noise is being generated with the help of the colorednoise library. The following noise characteristics can be adjusted:

ephys_generator:
  # Gaussian (1/f)**beta noise settings
  noise:
    # exponent
    beta: 1.5
    # noise standard deviation
    standard_deviation: 40.0
    # low-frequency cutoff. The power-spectrum below fmin is flat.
    fmin: 0.0001
    # unique noise samples to generate per channel
    # the noise is repeated if the samples are all used
    samples: 262144

For example here’s how changing the beta can impact the output raw data:

diagram

Loading custom spike waveforms

It is possible to configure the waveforms for the spikes synthesized by the ephys generator by adjusting the following configuration:

ephys_generator:
  waveforms:
    n_samples: 48
    prototypes:
      1: "[0.0, 8.5, 17.0, 24.0, 30.75, 47.75, 64.5, 72.75, 81.25, 72.75, 64.5, 28.0, -8.25, -99.25, -190.0, -194.25, -198.5, -172.0, -145.25, -95.0, -44.75, 1.5, 47.75, 68.75, 89.5, 92.5, 95.25, 82.5, 70.0, 57.5, 44.75, 29.5, 14.0, 7.0, 0.0, -2.75, -5.5, -7.0, -8.25, -4.0, 0.0]"
      2: "[0.0, -4.0, -8.25, -16.75, -25.0, -74.0, -123.0, -111.75, -100.5, 8.5, 117.5, 137.25, 156.75, 117.5, 78.5, 22.5, -33.5, -41.75, -50.25, -47.5, -44.75, -40.5, -36.25, -25.0, -14.0, -5.5, 3.0, 10.0, 17.0, 18.25, 19.75, 11.25, 3.0, -1.25, -5.5, -5.5, -5.5, -4.0, -2.75, -1.25, 0.0]"
      3: "[0.0, -5.5, -11.0, -25.0, -39.0, -95.0, -151.0, -151.0, -151.0, -99.25, -47.5, 8.5, 64.5, 106.5, 148.25, 175.0, 201.5, 169.25, 137.25, 89.5, 42.0, 22.5, 3.0, -5.5, -14.0, -16.75, -19.5, -21.0, -22.25, -22.25, -22.25, -22.25, -22.25, -21.0, -19.5, -15.25, -11.0, -9.75, -8.25, -4.0, 0.0]"


    unit_prototype_mapping:
      default: 1
      1: 3

A new custom waveform should be added first to the prototypes dictionary, where the key can be any integer serving as the prototype ID and the value is the JSON list of the waveform samples. In the next step, the unit_prototype_mapping dictionary should be updated to include the new prototype ID for the unit that is expected to use it. The default key is used for all units that are not explicitly defined in the mapping. So in the example above, unit 1 will use waveform prototype 3, and units 0 and 2 will both use waveform prototype 1.