Skip to content

Model / Maths

Notation

SIMPL tries to stick to the following notations:

  • Latent trajectory: \(X \in \mathbb{R}^{T \times D}\) in maths, X / model.X_ in code. \(X_t\) is the inferred latent at time bin \(t\).
  • Latent-space coordinate: \(x\) in maths, a grid point in model.xF_ in code. This is a possible position/location, not a whole trajectory.
  • Behavioral initialisation: \(X_b\) in maths, Xb in code. This starts the fit and can optionally tether the latent through behavior_prior. \(X_t\)/Xt is ground truth (if known).
  • Spike counts: \(Y \in \mathbb{R}^{T \times N}\). \(y_t\) is one time-bin vector, and \(y_{t,n}\) is one neuron's count in one time bin.
  • Receptive fields: \(F \in \mathbb{R}^{N \times N_{\textrm{bins}}}\). F / model.F_ in code are reshaped to the environment size, e.g. F.shape = (N, N_x_bins, N_y_bins, ...). \(F_n(x)\) is neuron \(n\)'s expected spike count at latent-space point \(x\). Thus \(F_n(X_t)\) is neuron \(n\)'s expected spike count at the decoded latent position and is the Poisson rate parameter.

Full model

This is only a summary, see ICLR paper for full details.

At its heart SIMPL approximately optimises a latent trajectory \(X_{1:T}\) and receptive fields \(F(x)\) under:

\[ p(X_{1:T}, Y \mid F) \;\propto\; \prod_t \underbrace{{\color{A92E5E}p(y_t \mid X_t, F)}}_{{\color{A92E5E}\mathrm{observation\ model}}}\, \underbrace{{\color{1D5C84}p(X_t \mid X_{t-\Delta t})}}_{{\color{1D5C84}\mathrm{dynamics\ model}}} \]

Dynamics model

The temporal prior is a Gaussian random-walk model controlled by \(\sigma_v\) (speed_prior):

\[ {\color{1D5C84}p(X_t \mid X_{t-\Delta t})} \approx {\color{1D5C84}\mathcal{N}\!\left(X_t; X_{t-\Delta t}, (\sigma_v\,\Delta t)^2 I\right)} \]

Smaller speed_prior values enforce smoother decoded trajectories; larger values let each time bin follow the spike likelihood more freely. Set speed_prior=None disables Kalman smoothing. For data without meaningful temporal structure, see Temporal vs. non-temporal datasets.

Optional (behavior_prior)
SIMPL can also include a soft Gaussian tether to whatever the latent was initialised to (typically behavior), controlled by \(\sigma_b\) (behavior_prior):

\[ {\color{1D5C84}p(X_t \mid X_{t-\Delta t})} \propto \underbrace{{\color{1D5C84}\mathcal{N}\!\left(X_t; X_{t-\Delta t}, (\sigma_v\,\Delta t)^2 I\right)}}_{{\color{1D5C84}\mathrm{latent\ close\ to\ previous\ latent}}} \, \cdot \underbrace{{\color{A3CC90}\mathcal{N}\!\left(X_t; X_t^{(0)}, \sigma_b^2 I\right)}}_{{\color{A3CC90}\mathrm{latent\ close\ to\ initialisation}}} \]

Smaller behavior_prior values enforce decoded trajectory to be closer to the behavior passed into Xb; larger values let each time bin follow the spike likelihood more freely (potentially moving far from behavior). Set behavior_prior=None to strictly disable behavior tether.

Once fields are estimated an approximation (see paper) converts Poisson-nonlinear observations to linear observations, allowing these dynamics to be inferred with fast Kalman smoothing.

Observation model

The spike likelihood comes from the fitted tuning curves:

\[ {\color{A92E5E}p(y_t \mid X_t, F)} = {\color{A92E5E}\prod_n \mathrm{Poisson}\!\left(y_{t,n}; F_n(X_t)\right)} \]

where, for neuron \(n\), \(F_n(X_t)\) is the expected spike count in that time bin, i.e. its tuning curve evaluated at the decoded latent position. The tuning curve itself is estimated by the standard KDE equation from the current latent:

\[ {\color{A92E5E}F_n(x) = \frac{\sum_t y_{t,n}\,K(x, X_t)}{\sum_t K(x, X_t)}} \]

\(K\) is a Gaussian kernel with bandwidth kernel_bandwidth. The denominator corrects for non-uniform occupancy. Receptive fields are evaluated on a spatial grid with bin size \(\Delta x\), but decoded positions are not restricted to those grid points.

Units and Discretisation

All hyperparameters (e.g. speed_prior, kernel_bandwidth, bin_size etc.) are defined in data units (e.g. typically [m/s], [m], [m] but these depend on your data of course), not arbitrary time/spatial-bin units.