PySINDy: Fitting Sparse Governing Equations to Measured Trajectories
A package for the sparse identification of nonlinear dynamical systems from data
At a glance
- What is it?
- PySINDy implements the SINDy method and related system-identification techniques in Python, returning differential equations rather than black-box predictors. It is a good fit when you can measure states and derivatives and want an interpretable model; it is a poor fit when the dynamics need latent variables or the data are too noisy for numerical differentiation.
- Who is it for?
- Adopt PySINDy if you have time series of state variables and a plausible candidate library, and you need equations you can read, control, or analyze. Do not adopt it if your measurements are noisy and unlabeled, if the dynamics depend on hidden variables you cannot observe, or if a predictive model is all you need.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 97 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem PySINDy addresses
Most machine learning applied to time series produces a function that maps inputs to outputs. You can evaluate it, but you cannot read it. PySINDy targets a different output: the governing differential equations themselves. The README describes system identification as the process of using measurement data to infer the governing dynamics, and notes that the resulting models are interpretable and generalizable. Once you have the equations, the README states you can predict future states, inform control inputs, or study the system analytically.
The intended user is someone who already has trajectories of state variables over time and suspects the dynamics are governed by a small number of terms. A mechanical engineer with position and velocity traces, a chemist with concentration measurements, a biologist with population counts. If you can write down a dictionary of candidate terms (constants, linear terms, products, trigonometric functions) and the true equations are sparse in that dictionary, the method has something to work with. If the true dynamics require terms your dictionary does not contain, no amount of fitting will recover them.
The sparse regression pipeline behind SINDy
The method is a two-stage construction followed by a regression. First, the state measurements are differentiated to obtain derivative estimates. Second, a feature library is built by evaluating candidate functions on the measured states. Third, a sparse regression selects a small subset of library columns that reproduce the derivatives.
The README's opening example makes the pipeline concrete. A particle obeys x' = -2x and y' = y, sampled at 100 points on the interval [0, 1] with initial conditions x_0 = 3 and y_0 = 0.5. The code constructs the state matrix with np.stack, instantiates ps.SINDy(), calls fit with the time vector and feature names, and prints the result. The documented output is x' = -2.000 x and y' = 1.000 y, matching the generating system to three decimal places.
Two design choices are worth noting. The default SINDy() constructor is doing a lot of implicit work: it picks a differentiation method and a library and an optimizer. The README does not spell out those defaults, so you will need the object model documentation to know what you are actually running. And the package's own object model page is linked as the place to understand the types of objects in pysindy, which suggests the defaults are meant as a starting point rather than a complete specification. That is a reasonable API decision, but it means a fit that looks correct may be correct for reasons you have not inspected.
Installation and the optional optimizer extras
The preferred installation is pip or conda: pip install pysindy. The README notes you may need the --user flag depending on your environment.
The base install does not include every optimizer. The package publishes extras, installed as pip install pysindy[miosr], and the README lists three:
cvxpy, for the convex optimizer SR3 and its subclasses. miosr, for the branch-and-bound optimizer for the L0 constraint, MIOSR. sbr, for the Bayesian regression optimizer yielding posteriors, SBR.
That split matters for reproducibility. If a paper or tutorial uses MIOSR and you installed the base package, the import will fail rather than fall back silently, which is the right behavior but easy to miss. The SBR extra is the one with the most different character: it returns posteriors rather than point estimates, which changes what you can say about uncertainty in the discovered coefficients. The README does not describe the posterior format, so treat that as something to check in the documentation before designing an analysis around it.
Derivative estimation is the weak joint
The README lists a related package called derivative, described as a Python package for differentiating (and optionally, smoothing) data, used by pysindy. That single line points at the part of the pipeline most likely to determine whether your fit succeeds.
SINDy regresses derivatives against library terms. If the derivative estimates are wrong, the regression is fitting noise. Finite differences on noisy measurements amplify that noise, and the amplification grows as the sampling interval shrinks. The package exposes alternatives (the separate derivative package handles smoothing as well as differentiation), but the choice is yours to make and the README does not present it as a decision point.
This is where I would push back on the framing in the README. The example uses clean, analytically generated exponentials, where any reasonable differentiation scheme works. Real measurement data rarely looks like that. A user who copies the example pattern onto noisy sensor data and gets a dense, uninterpretable model may conclude the method does not work, when the actual failure was upstream in the derivative step. The package does not appear to warn about this.
When PySINDy is the wrong tool
The method assumes the state is fully observed. If the dynamics depend on a variable you did not measure, the library cannot contain the right terms because it is built from the variables you supplied. You will get a sparse model that fits the observed trajectories and fails to generalize, and nothing in the output flags the missing variable.
It also assumes the dynamics are autonomous in the sense that the right-hand side is a function of the current state. Systems with explicit time dependence, delayed feedback, or external forcing need those inputs folded into the library, which the README does not walk through.
Finally, the method is not a forecaster for chaotic or high-dimensional systems in the way a neural surrogate is. It returns equations, and equations for a chaotic system are still chaotic. If your goal is a fast approximate simulator and interpretability is irrelevant, a black-box model is the simpler path. The README's own framing, that models are interpretable and generalizable, is a statement about what you get, not a claim that this is the right approach for every time series problem.
Dynamic Mode Decomposition as the closest alternative
The README points to PyDMD as a related package and calls Dynamic Mode Decomposition a great alternative to SINDy. The difference in approach is real and worth understanding before you pick.
DMD approximates the dynamics as a linear operator acting on observables, typically via an eigendecomposition of a best-fit linear map between snapshots. It does not require you to supply a library of candidate nonlinear terms, and it does not require numerical differentiation of the data, which sidesteps the weak joint described above. In exchange, you get a linear model in a chosen observable space. If the underlying dynamics are strongly nonlinear and you do not enrich the observables, DMD will fit a linear approximation and stop there.
SINDy makes the opposite trade. You supply the nonlinear structure through the library, and in return the discovered model can be genuinely nonlinear and sparse. If you do not know what terms belong in the library, DMD is the lower-friction starting point. If you do, SINDy can recover structure that DMD would smear into a linear operator. The README lists both without ranking them, which is honest but leaves the choice to you.
Release cadence, licence and what the repository does not state
The repository shows a v2.1.0 release in January 2026, following v2.0.0 in August 2025 and a release candidate a month before that. The v2.0.0 line was a major version, so if you are upgrading from a 1.x installation, expect API changes and read the release notes rather than assuming drop-in compatibility. The last push to the default branch is dated June 2026, so the project is active.
The licence field in the repository metadata is NOASSERTION, which means the automated classifier could not map the licence file to a known identifier. That is not the same as having no licence, but it does mean you should read the LICENSE file in the repository yourself before depending on the package in a product. The README also points academic users to a dedicated page for citation recommendations, which is a signal that the project expects scholarly use and wants citations handled a particular way.
Maintenance cost is mostly the extras. Each optional optimizer (cvxpy, miosr, sbr) is an additional dependency with its own release cycle, and pinning them is your problem, not the package's. If you rely on MIOSR for reproducibility and a dependency bump changes solver behavior, the equations you published may not reproduce exactly. Budget for pinning those versions in whatever environment you use to regenerate results.
Editorial conclusion
Adopt PySINDy if you have time series of state variables and a plausible candidate library, and you need equations you can read, control, or analyze. Do not adopt it if your measurements are noisy and unlabeled, if the dynamics depend on hidden variables you cannot observe, or if a predictive model is all you need. Before committing, verify three things against your own data: how sensitive the discovered coefficients are to your derivative estimator, whether the default polynomial library can express the terms you expect, and which optional extras (cvxpy, miosr, sbr) you need, since those pull in separate dependencies. The package will not tell you that your library is wrong; it will return a sparse model regardless.
Community notes