StateSpaceDynamics.jl: probabilistic modeling of non-conjugate state-space models

Hi everyone! I’m really happy to announce a new Julia package for modelling multivariate time series data using probabilistic state space models!

State-space models are a core tool for analyzing high-dimensional time series, especially in modern neuroscience where recordings from hundreds or thousands of neurons are common. While Python ecosystems offer mature tools (e.g. ssm, Dynamax), Julia has lacked a general-purpose SSM library that supports non-Gaussian observations, mixed discrete–continuous latent structures, and efficient EM-based inference.

These models are incredibly popular in computational neuroscience for unsupervised analysis of neural dynamics across diverse recording modalities (e.g., electrophysiology, calcium-imaging, fNIRS, LFP, etc.). Specifically, many researchers, as opposed to fitting potentially difficult to interpret black-box models, many now opt to fit models like the switching Linear Dynamical System (SLDS) and its variants thereof.

Our packages primary goal is to allow users to be able to fit these style of models to neural data in an efficient manner so that neuroscientists working in Julia have an equivalent package to those previously mentioned in python.

Inference in StateSpaceDynamics

Given that LDS-style models are latent variable models, one needs a way to perform tractable inference over the posterior distribution of the latent states.
Our package uses a direct optimization approach that has been previously advocated in the neuroscience literature (e.g. Paninski et al., 2010)..

For continuous latent-variable models (e.g. LDS), the package performs inference by directly maximizing the complete-data log posterior with respect to the latent state trajectory. By exploiting the block-tridiagonal structure of the Hessian, inference scales linearly in the number of time steps and recovers the standard Kalman filter and RTS smoother exactly in the Gaussian case.

Importantly, this framework generalizes naturally to non-Gaussian observation models (e.g. Poisson and Bernoulli), requiring only the gradient and Hessian of the observation log-likelihood. For these models, StateSpaceDynamics computes an exact MAP latent trajectory and performs approximate EM using a Laplace approximation to the latent posterior, while maintaining computational efficiency via fast block-tridiagonal solvers.

Learning In StateSpaceDynamics

We currently supports EM-based parameter learning for all LDS-style models. For conjugate Gaussian models, learning reduces exactly to the standard EM updates for linear dynamical systems. For non-conjugate observation models (e.g. Poisson LDS), we use Laplace EM, where the latent posterior is approximated locally by a Gaussian around the MAP trajectory.

For models that mix discrete and continuous latent variables, such as the SLDS, the package implements Variational Laplace EM (vLEM). This approach alternates between:

  • variational inference over discrete state sequences, and
  • Laplace-approximated inference over continuous latent trajectories,

allowing efficient and scalable learning for models that would otherwise could be computationally intractable with generic sampling-based methods.

What’s next?

StateSpaceDynamics is still in active development. Over the coming months, we plan to focus on:

  • Interoperability with HiddenMarkovModels.jl
    Development of StateSpaceDynamics began around the same time as HiddenMarkovModels.jl, and we were not initially aware of how fast and robust that package would become. We are now working toward deprecating our internal HMM implementations in favor of using its backend routines.
  • Additional observation models
    Currently, we support Gaussian and Poisson observations (the most common for neural data). We plan to add additional model classes, including Bernoulli, GP-based observations, and others.
  • Greater reliance on automatic differentiation
    We aim to support AD-based workflows so that new model classes with difficult or intractable derivatives are easy to prototype and integrate.
  • Community-driven development
    We’re very happy to support features and extensions that the community finds useful :slightly_smiling_face:

Final thoughts

Lastly a huge thanks to the reviewers of our Joss publication. They made this a really great experience and over all really made the package better. This was my first major Julia project and I learned a lot from their guidance. Lastly, a big thanks to the whole Julia community. This really is a great place to learn and become a better programmer. Cheers everyone!

You win the 2025 award for best README

Thank you, thats very kind :slight_smile:

This really is a great package and something that I’ve been missing from the Julia eco system for a while.
I was wondering whether there are any plans to include other emission models? For my particular usage, I am trying to model a two-choice behavior, but where I would like to have an option of not making choice. This is mainly to be able to model waiting time between actions.

Thank you for the kind words! Currently, I am in the middle of a big refactor of this package, and we am currently working on expanding the available models.

However, given the amazing functionality of HiddenMarkovModels.jl we are opting to refocus our efforts on LDS models and related variants. Are you looking to model via GLM-HMMs? And if I understand correctly you want the ability to model more than 2 choices? If so, then you want something like a multinomial regression, and how does the ITI data fit in?

Nonetheless, I can point you to two developments: 1.) is the following PR into the HMM package which adds a ControlledEmissionHMM so that one can more easily model with HMMs where the emission model has controls (but other model components are control independent). 2.) This new package I am working on: EmissionModels.jl. The goal being to add a common set of emission models that many might use, with the explicit purpose of being used with HiddenMarkovModels.

So if what I understand correctly is your goal, I can add a multinomial model into the latter package (feel free to open an issue), and then when the former PR lands, you should be good to go. LMK!

Also, if you want to see anything in this package related to LDS models (e.g., observation models/dynamic linear models, etc.) please let me know, as I said, am currently in a big refactor so this feedback is useful for SSD.

I was actually initially looking at HiddenMarkovModels.jl, but I decided on using StateSpaceDynamics.jl since its documentation and tutorials seemed more directly geared towards what I was trying to model. It sounds like the long-term plans are to integrate the two, which I think is good news!
Having a multinomial emission model would indeed serve my purpose; I will look out for that. Thank you!
On another note, I’m curious whether there are plans to incorporate models where the transition probabilities depend on controls? For the behavior I’m trying to model, it looks like the transitions between states are more likely to be driven by external inputs rather than the emissions themselves. Just a thought, and thanks again for the great package!

I’m glad you found the docs and tutorials helpful! Out of curiosity, which parts were most helpful? I am now a collaborator on HiddenMarkovModels.jl so, if there is something you feel which would be beneficial to add, I’d recommend opening an issue for further discussion (feel free to link this discussion!).

Great, I’ll put this on my TODO list. Though if you have the time to work on this as a PR into the EmissionModels package it might happen faster :wink:

I can’t say there is plans as of now, but @gdalle may know better. This should be achievable through the control interface of HiddenMarkovModels. There could be an argument to create another type in the package similar to the ControlledEmissionHMM. In the near future I should have a use case for this here when i add the rSLDS model.

I recommend opening an issue on the HMM package to start a discussion!

This is already possible, see the tutorial on “time dependency”. The tutorial on “control dependency” doesn’t change the transitions matrix but it was just for clarity, not because the package doesn’t allow it.