Synthetic Data Generation

This module provides functions for generating synthetic neural data with known ground truth properties.

Experiment Generators

driada.experiment.synthetic.generators.generate_tuned_selectivity_exp(population, tuning_defaults=None, duration=600, fps=20, baseline_rate=0.05, peak_rate=2.0, decay_time=2.0, calcium_noise=0.02, calcium_amplitude_range=(0.5, 2.0), n_discrete_features=2, event_active_fraction=0.08, event_avg_duration=0.8, skip_prob=0.0, hurst=0.3, seed=None, verbose=True, reconstruct_spikes=None)[source]

Generate synthetic experiment with principled tuning-based selectivity.

Creates neurons with biologically meaningful tuning to various feature types. Each neuron group can respond to one or more features with configurable combination modes (OR/AND).

Parameters:
  • population (list of dict) –

    Population configuration. Each dict specifies a neuron group with keys:

    • ”name” : str - Group name (e.g., “hd_cells”, “place_cells”)

    • ”count” : int - Number of neurons in this group

    • ”features” : list of str - Feature names this group responds to. Supported: “head_direction” (von Mises), “position_2d” (2D Gaussian), “x”, “y” (1D Gaussian), “speed” (sigmoid), “event_0”/”event_1”/… (binary), “fbm_0”/”fbm_1”/… (FBM continuous).

    • ”combination” : str, optional - How to combine multiple features: “or” (default) or “and”

    • ”tuning_params” : dict, optional - Override default tuning parameters

  • tuning_defaults (dict, optional) – Override module-level tuning defaults. Keys are feature names, values are dicts with parameter names and values.

  • duration (float, optional) – Recording duration in seconds. Default: 600.

  • fps (float, optional) – Sampling rate in Hz. Default: 20.

  • baseline_rate (float, optional) – Baseline firing rate (spikes/frame). Default: 0.05.

  • peak_rate (float, optional) – Peak firing rate during selectivity. Default: 2.0.

  • decay_time (float, optional) – Calcium decay time constant in seconds. Default: 2.0.

  • calcium_noise (float, optional) – Calcium signal noise standard deviation. Default: 0.02.

  • calcium_amplitude_range (tuple of float, optional) – Range for calcium event amplitudes (min, max). Default: (0.5, 2.0).

  • n_discrete_features (int, optional) – Number of discrete event features to generate. Default: 2.

  • event_active_fraction (float, optional) – Fraction of time each event is active. Default: 0.08.

  • event_avg_duration (float, optional) – Average event duration in seconds. Default: 0.8.

  • skip_prob (float, optional) – Probability of skipping an event (for event features). Default: 0.0.

  • hurst (float, optional) – Hurst parameter for FBM features. Default: 0.3.

  • seed (int, optional) – Random seed for reproducibility.

  • verbose (bool, optional) – Print progress messages. Default: True.

  • reconstruct_spikes (str, optional) – Spike reconstruction method to apply after generating calcium traces. If None, no spike reconstruction is performed.

Returns:

exp – DRIADA Experiment object with neural signals and features. Ground truth is accessible via exp.ground_truth containing: - “expected_pairs” : list of (neuron_idx, feature_name) tuples - “neuron_types” : dict mapping neuron_idx to group name - “tuning_parameters” : dict with detailed tuning params per neuron - “population_config” : reference to input population config

Return type:

Experiment

Examples

>>> # Minimal example with default parameters
>>> population = [
...     {"name": "hd_cells", "count": 4, "features": ["head_direction"]},
...     {"name": "nonselective", "count": 2, "features": []},
... ]
>>> exp = generate_tuned_selectivity_exp(population, duration=60, verbose=False)
>>> exp.n_cells
6
>>> len(exp.ground_truth["expected_pairs"])
4
>>> # Advanced example with custom parameters
>>> population = [
...     {"name": "narrow_hd", "count": 2, "features": ["head_direction"],
...      "tuning_params": {"kappa": 4.0}},  # Narrower tuning
...     {"name": "conjunctive", "count": 2, "features": ["x", "y"],
...      "combination": "and"},  # AND combination
... ]
>>> exp = generate_tuned_selectivity_exp(
...     population, tuning_defaults={"x": {"sigma": 0.3}}, verbose=False
... )
driada.experiment.generate_synthetic_exp(n_dfeats=20, n_cfeats=20, nneurons=500, seed=0, fps=20, duration=1200, **kwargs)[source]

Generate a synthetic experiment with neurons selective to discrete and continuous features.

This is a convenience wrapper around generate_tuned_selectivity_exp() that provides a simpler API for basic synthetic data. Ground truth is always available via exp.ground_truth.

Parameters:
  • n_dfeats (int, optional) – Number of discrete event features. Default: 20.

  • n_cfeats (int, optional) – Number of continuous FBM features. Default: 20.

  • nneurons (int, optional) – Total number of neurons. Default: 500.

  • seed (int, optional) – Random seed for reproducibility. Default: 0.

  • fps (float, optional) – Frames per second. Default: 20.

  • duration (int, optional) – Duration of the experiment in seconds. Default: 1200.

  • **kwargs (dict, optional) – Additional parameters passed to generate_tuned_selectivity_exp. Supported: verbose, baseline_rate, peak_rate, decay_time, calcium_noise, hurst.

Returns:

exp – Synthetic experiment object with calcium signals. Ground truth accessible via exp.ground_truth.

Return type:

Experiment

Examples

>>> exp = generate_synthetic_exp(n_dfeats=10, n_cfeats=10, nneurons=100, verbose=False)
>>> exp.n_cells
100
>>> exp.ground_truth is not None
True
driada.experiment.generate_circular_manifold_exp(n_neurons=100, kappa=4.0, step_std=0.1, add_mixed_features=False, seed=None, verbose=True, return_info=False, **kwargs)[source]

Generate complete experiment with circular manifold (head direction cells).

Creates a synthetic experiment with head direction cells arranged on a circular manifold. Neurons have Von Mises tuning curves with uniformly distributed preferred directions.

Parameters:
  • n_neurons (int, optional) – Number of neurons. Must be positive. Default is 100.

  • kappa (float, optional) – Von Mises concentration parameter (tuning width). Higher values give narrower tuning. Must be positive. Typical values: 2-8. Default is 4.0.

  • step_std (float, optional) – Head direction random walk step size in radians. Must be non-negative. Default is 0.1 (~5.7 degrees).

  • add_mixed_features (bool, optional) – Whether to add circular_angle MultiTimeSeries with cos/sin representation of head direction. Useful for algorithms that cannot handle circular variables directly. Default is False.

  • seed (int, optional) – Random seed for reproducibility. Default is None.

  • verbose (bool, optional) – Print progress messages. Default is True.

  • return_info (bool, optional) – If True, return additional information dictionary. Default is False.

  • **kwargs (dict) – Additional parameters from DEFAULT_SYNTHETIC_PARAMS: duration, fps, baseline_rate, peak_rate, firing_noise, decay_time, calcium_noise, amplitude_range.

Returns:

  • exp (Experiment) – DRIADA Experiment object containing: - calcium signals as main data - static features: fps, decay times, manifold parameters - dynamic features: head_direction (and circular_angle if requested)

  • info (dict, optional) – Only returned if return_info=True. Contains: - ‘manifold_type’: “circular” - ‘n_neurons’: number of neurons - ‘head_direction’: trajectory array - ‘preferred_directions’: array of preferred directions - ‘firing_rates’: underlying firing rates - ‘parameters’: dict of all generation parameters

driada.experiment.generate_2d_manifold_exp(n_neurons=100, field_sigma=0.1, step_size=0.02, momentum=0.8, grid_arrangement=True, bounds=(0, 1), seed=None, verbose=True, return_info=False, **kwargs)[source]

Generate complete experiment with 2D spatial manifold (place cells).

Creates a DRIADA Experiment object with synthetic hippocampal place cell data, including calcium imaging signals and behavioral trajectory.

Parameters:
  • n_neurons (int, optional) – Number of neurons. Must be positive. Default is 100.

  • field_sigma (float, optional) – Place field width. Must be positive. Default is 0.1.

  • step_size (float, optional) – Random walk step size. Must be positive. Default is 0.02.

  • momentum (float, optional) – Trajectory smoothness factor. Must be in [0, 1]. Default is 0.8.

  • grid_arrangement (bool, optional) – If True, arrange place fields in grid. Default is True.

  • bounds (tuple, optional) – Spatial bounds (min, max). Default is (0, 1).

  • seed (int, optional) – Random seed for reproducibility.

  • verbose (bool, optional) – Print progress messages. Default is True.

  • return_info (bool, optional) – If True, return (exp, info) tuple with additional information. Default is False.

  • **kwargs (dict) – Additional parameters from DEFAULT_SYNTHETIC_PARAMS: duration, fps, baseline_rate, peak_rate, firing_noise, decay_time, calcium_noise, amplitude_range.

Returns:

  • exp (Experiment) – DRIADA Experiment object with 2D spatial manifold data.

  • info (dict, optional) – If return_info=True, dictionary with manifold info including: manifold_type, n_neurons, positions, place_field_centers, firing_rates, and all parameters.

driada.experiment.generate_mixed_population_exp(n_neurons=100, manifold_fraction=0.6, manifold_type='2d_spatial', n_discrete_features=3, n_continuous_features=3, duration=600, fps=20.0, seed=None, verbose=True, return_info=False, manifold_params=None)[source]

Generate synthetic experiment with mixed population of manifold and feature-selective cells.

This is a convenience wrapper around generate_tuned_selectivity_exp() that provides a simpler API for common mixed population scenarios. Ground truth is always available via exp.ground_truth.

Parameters:
  • n_neurons (int) – Total number of neurons in the population. Default: 100.

  • manifold_fraction (float) – Fraction of neurons that are manifold cells (0.0-1.0). Default: 0.6. Remaining neurons are split between event and FBM cells.

  • manifold_type (str) – Type of manifold: ‘circular’ (head direction) or ‘2d_spatial’ (place cells). Default: ‘2d_spatial’.

  • n_discrete_features (int) – Number of discrete event features. Default: 3.

  • n_continuous_features (int) – Number of continuous FBM features. Default: 3.

  • duration (float) – Duration of experiment in seconds. Default: 600.

  • fps (float) – Sampling rate in Hz. Default: 20.0.

  • seed (int, optional) – Random seed for reproducibility.

  • verbose (bool) – Print progress messages. Default: True.

  • return_info (bool) – If True, return (exp, info) tuple for backward compatibility. Ground truth is always available via exp.ground_truth regardless. Default: False.

  • manifold_params (dict, optional) – Override tuning parameters for manifold cells. Supported keys: - field_sigma: Size of place field (default: 0.15) - baseline_rate: Baseline firing rate (default: 0.05) - peak_rate: Peak firing rate (default: 2.0) - firing_noise: Firing rate noise (default: 0.05) - decay_time: Calcium decay time (default: 2.0) - calcium_noise: Calcium signal noise (default: 0.02)

Returns:

  • exp (Experiment) – Experiment object with mixed population. Ground truth accessible via exp.ground_truth containing expected_pairs, tuning_parameters, etc.

  • info (dict (only if return_info=True)) – Dictionary containing ground_truth for backward compatibility.

Examples

>>> # Generate population with 60% place cells, 40% feature-selective
>>> exp = generate_mixed_population_exp(
...     n_neurons=50,
...     manifold_fraction=0.6,
...     manifold_type='2d_spatial',
...     verbose=False
... )
>>> exp.n_cells
50
>>> len(exp.ground_truth['expected_pairs']) > 0
True

Signal Generators

driada.experiment.synthetic.core.generate_pseudo_calcium_signal(events=None, duration=600, sampling_rate=20.0, event_rate=0.2, amplitude_range=(0.5, 2), decay_time=2, noise_std=0.1, seed=None, rise_time=None, kernel='exponential')[source]

Generate a pseudo-calcium imaging signal with configurable dynamics.

Creates a synthetic calcium fluorescence signal that mimics GCaMP-like dynamics with configurable kernel shapes, random event amplitudes, and Gaussian noise.

Parameters:
  • events (ndarray or None, optional) – Binary array indicating event occurrences at each time point. If None, events are generated randomly using a Poisson process. When provided, indices directly correspond to sample indices (not time in seconds).

  • duration (float, default=600) – Total duration of the signal in seconds. Only used if events is None. Must be positive.

  • sampling_rate (float, default=20.0) – Sampling rate in Hz. Must be positive.

  • event_rate (float, default=0.2) – Average rate of calcium events per second. Only used if events is None. Must be non-negative.

  • amplitude_range (tuple of float, default=(0.5, 2)) – (min, max) range for random calcium event amplitudes. Must have min <= max.

  • decay_time (float, default=2) – Time constant for exponential decay of calcium events in seconds. Typical GCaMP indicators have decay times of 1-2 seconds. Must be positive.

  • noise_std (float, default=0.1) – Standard deviation of additive Gaussian noise. Must be non-negative.

  • seed (int, optional) – Random seed for reproducibility. Default is None.

  • rise_time (float, optional) – Rise time constant in seconds. Only used for ‘double_exponential’ kernel. Default is None, which uses 0.25s (typical for GCaMP indicators).

  • kernel ({'exponential', 'double_exponential', 'step'}, default='exponential') –

    Type of calcium transient kernel:

    • ’exponential’: Simple exponential decay with instantaneous rise. Form: amplitude * exp(-t/decay_time). Fast but less realistic.

    • ’double_exponential’: Physiologically realistic kernel with separate rise and decay phases. Form: (1 - exp(-t/rise)) * exp(-t/decay). Matches real GCaMP dynamics.

    • ’step’: Non-physiological step-like waveform with sharp edges. Useful as negative control for testing model assumptions.

Returns:

1D array representing the pseudo-calcium signal with shape (n_samples,).

Return type:

ndarray

Raises:

ValueError – If duration, sampling_rate, or decay_time <= 0. If event_rate or noise_std < 0. If amplitude_range[0] > amplitude_range[1]. If kernel is not one of the valid options.

Notes

The calcium signal is modeled as a sum of transients triggered at event times, plus additive Gaussian noise. This approximates the dynamics of genetically encoded calcium indicators like GCaMP6.

When events is None: Event times are drawn uniformly in [0, duration) seconds. When events is provided: Non-zero indices are treated as event sample indices.

Multiple overlapping events accumulate additively without saturation modeling.

Examples

>>> # Generate random calcium signal with simple exponential decay
>>> signal = generate_pseudo_calcium_signal(duration=100, event_rate=0.5)
>>> signal.shape
(2000,)
>>> # Generate with realistic double-exponential dynamics
>>> signal = generate_pseudo_calcium_signal(
...     duration=100, event_rate=0.5,
...     kernel='double_exponential', rise_time=0.25
... )
>>> # Generate from specific spike times
>>> spikes = np.zeros(1000)
>>> spikes[[100, 200, 300]] = 1  # 3 spike events at sample indices
>>> signal = generate_pseudo_calcium_signal(events=spikes)
driada.experiment.synthetic.core.generate_pseudo_calcium_multisignal(n, events=None, duration=600, sampling_rate=20, event_rate=0.2, amplitude_range=(0.5, 2), decay_time=2, noise_std=0.1, seed=None, rise_time=None, kernel='exponential')[source]

Generate multiple pseudo calcium signals.

Parameters:
  • n (int) – Number of neurons. Must be non-negative.

  • events (ndarray, optional) – Event array of shape (n_neurons, n_timepoints). If provided, must have n_neurons == n. Each row corresponds to one neuron’s event indices.

  • duration (float, default=600) – Duration in seconds. Only used if events is None.

  • sampling_rate (float, default=20) – Sampling rate in Hz.

  • event_rate (float, default=0.2) – Average rate of calcium events per second. Only used if events is None.

  • amplitude_range (tuple, default=(0.5, 2)) – (min, max) for the amplitude of calcium events.

  • decay_time (float, default=2) – Time constant for the decay of calcium events in seconds.

  • noise_std (float, default=0.1) – Standard deviation of the Gaussian noise.

  • seed (int, optional) – Random seed for reproducibility. Default is None.

  • rise_time (float, optional) – Rise time constant in seconds. Only used for ‘double_exponential’ kernel. Default is None, which uses 0.25s.

  • kernel ({'exponential', 'double_exponential', 'step'}, default='exponential') – Type of calcium transient kernel. See generate_pseudo_calcium_signal for details on each kernel type.

Returns:

Calcium signals of shape (n_neurons, n_timepoints).

Return type:

ndarray

Raises:

ValueError – If n < 0. If events is provided with wrong shape.

Notes

This is a convenience wrapper that calls generate_pseudo_calcium_signal for each neuron independently. Each neuron gets independent random events (if events=None) and independent noise.

Examples

>>> # Generate 5 neurons with random events
>>> signals = generate_pseudo_calcium_multisignal(5, duration=100)
>>> signals.shape
(5, 2000)
>>> # Generate with specific events per neuron
>>> events = np.random.binomial(1, 0.01, size=(3, 1000))
>>> signals = generate_pseudo_calcium_multisignal(3, events=events)

Usage Examples

Basic Synthetic Data

from driada.experiment import generate_synthetic_exp

# Generate basic synthetic experiment
exp = generate_synthetic_exp(
    nneurons=100,      # number of neurons
    n_dfeats=10,       # discrete features
    n_cfeats=10,       # continuous features
    duration=600,      # 10 minutes
    fps=30,           # 30 Hz sampling
    seed=42           # For reproducibility
)

print(f"Generated {exp.n_cells} neurons, {exp.n_frames/exp.fps}s recording")

Head Direction Cells (Circular Manifold)

from driada.experiment import generate_circular_manifold_exp

# Generate head direction cell population
exp = generate_circular_manifold_exp(
    n_neurons=50,
    duration=600,
    fps=30,
    kappa=4.0,           # concentration parameter
    noise_std=0.1,
    step_std=0.1         # angular velocity noise
)

# Access ground truth data if return_info=True
# exp, info = generate_circular_manifold_exp(..., return_info=True)
# true_angle = info['true_angle']
# preferred_directions = info['preferred_directions']

2D Place Cells

from driada.experiment import generate_2d_manifold_exp

# Generate place cell population
exp = generate_2d_manifold_exp(
    n_neurons=50,
    duration=60,          # 1 minute (for demo)
    fps=20,
    field_sigma=0.1,      # receptive field size
    step_size=0.02,       # movement step
    momentum=0.8,         # movement momentum
    noise_std=0.15
)

# Access data if return_info=True
# exp, info = generate_2d_manifold_exp(..., return_info=True)
# position = info['position']  # (2, n_frames)
# place_fields = info['place_fields']  # neuron receptive fields

Mixed Selectivity Population

from driada.experiment import generate_mixed_population_exp

# Generate population with mixed selectivity
exp = generate_mixed_population_exp(
    n_neurons=100,
    manifold_fraction=0.6,    # 60% manifold cells
    manifold_type="2d_spatial",
    n_discrete_features=3,
    n_continuous_features=2,
    duration=1200,
    fps=30
)

# Access info if return_info=True
# exp, info = generate_mixed_population_exp(..., return_info=True)
# manifold_neurons = info['manifold_neurons']
# feature_neurons = info['feature_neurons']

Ground Truth Information

When using return_info=True parameter, synthetic experiments return ground truth data:

# Get ground truth information
exp, info = generate_circular_manifold_exp(
    n_neurons=50,
    duration=600,
    return_info=True
)

# Access ground truth data from info dict
# The exact contents depend on the generator used

Best Practices

  1. Reproducibility: Always set random seed for reproducible results

  2. Realistic Parameters: Use physiologically plausible parameters

  3. Validation: Use ground truth to validate analysis methods

  4. Noise Levels: Start with low noise, increase to test robustness

  5. Duration: Ensure sufficient data for statistical analyses