Skip to main content

SiNDAE

SiNDAE — A Simultaneous Approach for Training Neural Differential-Algebraic Equations

PyPI License Python arXiv CI

SiNDAE is a Python package for hybrid modeling of dynamical systems. It learns unknown nonlinear terms in ODE and DAE systems directly from data by embedding a neural network inside the governing equations and training it as a single nonlinear program (NLP). Because the mechanistic equations are kept as hard constraints, the learned model stays physically consistent, including when predicting new operating conditions never seen during training.

SiNDAE is the companion code to A simultaneous approach for training neural differential-algebraic systems of equations (Lueg et al., 2026).

Authors:

Features

  • A scikit-learn-style interface: HybridDAE(...) runs the whole pipeline behind fit(problem) / predict(new_problem), with every stage still configurable.
  • Two training backends behind a symmetric API: either the simultaneous approach or the decomposition approach.
  • ODEs and high-index DAEs, discretized with Pyomo collocation.
  • Bring your own data: fit to measured time series, including the partially observed case where only some states are recorded.
  • Custom neural architectures through a grey-box interface, in addition to the built-in SimpleMLP.
  • Inference under new conditions: embed a trained model in a fresh problem and predict, with the mechanistic structure keeping the result physically feasible.
  • Binary-free install: the pure-Rust POUNCE and FERAL solvers replace HSL/MA27, so no licensed binaries are required.
  • Trained model distribution: export your trained neural network as a JAX serialized .eqx file, an ONNX file, an OMLT NetworkDefinition, or a JSON.

Installation

SiNDAE can be installed via pip (recommended):

pip install sindae            # core: full POUNCE/FERAL workflow (simultaneous, decomposition, inference)
pip install "sindae[full]"    # adds mpi4py (MPI) and cyipopt (optional alternative NLP backend)

The core install is pure pip wheels with no system libraries or licenses, and runs the entire pipeline (simultaneous, decomposition, grey-box, inference) on POUNCE and FERAL. The full extra adds mpi4py (for MPI-parallel decomposition) and cyipopt (an optional alternative NLP backend), whose wheels are platform-dependent; if they do not build, install them from conda-forge and pip install sindae into the same environment. See docs/installation.md for the conda route, GPU/Apple Silicon, and troubleshooting.

For a development install from source:

git clone https://github.com/llueg/SiNDAE.git
cd SiNDAE
pip install -e ".[full,test]"

Quickstart

Generate noisy data from a built-in example, fit the hybrid model, and predict under new conditions with the HybridDAE wrapper:

import jax
import numpy as np
import sindae as sd

jax.config.update("jax_enable_x64", True)

problem = sd.LeslieGowerProblem(nfe=40, ncp=3)      # or define your own problem (see below)
sd.generate_data(problem, noise_std=[0.05, 0.05])   # or load your own measurements

mlp = sd.SimpleMLP(in_size=2, out_size=1, widths=[16, 16],
                   activations=[jax.nn.softplus] * 2)

model = sd.HybridDAE(
    method="simultaneous",              # or "decomposition"
    net=mlp,
    train=sd.SimultaneousConfig(reg_coef=1e-3),
    smoother=sd.SmootherConfig(smooth_coef=10.0),
    pretrain=sd.PretrainConfig(epochs=200, batch_size=32, reg_coef=1e-3),
    solver_options=sd.SolverConfig(tol=1e-6, max_iter=1000, hessian_approximation='exact'),
)
model.fit(problem)                      # smoother -> pretrain -> train

new_problem = sd.LeslieGowerProblem(ics=np.array([[1.2, 0.15]]), nfe=40, ncp=3)
pred = model.predict(new_problem, slack_coef=1e-5)   # inference on new conditions

Change the method to decomposition and use train=sd.DecompConfig(...) to use the decomposition approach.

See the Quickstart guide for the full walkthrough.

How it works

A typical workflow has four stages: build a problem, solve a smoother to get smooth warm-start trajectories and normalization statistics, pre-train the network on those, then train the hybrid model with one of the two methods below. HybridDAE.fit wraps each of these stages into one function, where the method can be specified with the flag method=; the entry points below give stage-level control.

Method Entry point
Simultaneous HybridDAE.fit(method='simultaneous') Network weights, states, and algebraic variables are decision variables in a single NLP solved by POUNCE or IPOPT using either exact Hessian, or L-BFGS for the grey-box variant.
Decomposition HybridDAE.fit(method='decomposition') An outer Adam loop updates network weights while each inner step solves the DAE with network weights fixed and obtains gradients computing the sensitivity of the inner solve. Supports MPI across trajectories.

Both require the network to be twice continuously differentiable. Accordingly, the activation functions available in SiNDAE consist of smooth activations (tanh, softplus, swish) in the SimpleMLP class. See Defining a Network Architecture on how to define your own network structure.

Documentation

The complete documentation with detailed functionality explanations, examples, and optional dependencies can be found here.

Examples

Rendered notebooks in docs/examples_gallery/ show some of the package capabilities:

Notebook Demonstrates
four_tank_example.ipynb Simultaneous training on an index-2 DAE
leslie_gower_example.ipynb Decomposition training with a custom Lyapunov path constraint
fedbatch_example.ipynb Fedbatch bioreactor example using measured data
fedbatch_partial_obs_example.ipynb Fedbatch bioreactor example using only partially observed states
fedbatch_validation_example.ipynb Fedbatch bioreactor example determining optimal network size

The same systems are also available as runnable scripts in examples/ showcasing the fully configurable workflow HybridDAE encapsulates:

Script System
four_tank.py Four-tank hydraulic network (index-2 DAE)
leslie_gower.py Leslie-Gower predator-prey (ODE)
fedbatch.py Fed-batch bioreactor (ODE)
example_mpi.py Four-tank trained over MPI ranks

Set METHOD = 'simul' or METHOD = 'decomp' at the top of each script to switch backends.

Defining your own problem

Subclass ProblemDefinition and implement the three required methods. The network takes get_input_vars as input and produces get_output_vars; build_trajectory writes the mechanistic ODE/DAE and fixes the initial conditions.

import pyomo.environ as pyo
import pyomo.dae as dae
from sindae.problem import ProblemDefinition

class MyProblem(ProblemDefinition):
    def build_trajectory(self, block, traj_idx):
        block.t    = dae.ContinuousSet(bounds=self.t_span)
        block.x    = pyo.Var(block.t, range(2), initialize=1.0)
        block.z    = pyo.Var(block.t, range(1))            # the learned term
        block.dxdt = dae.DerivativeVar(block.x, wrt=block.t)
        # ... add ODE/DAE constraints that reference block.z[t, 0] ...
        block.x[self.t_span[0], 0].fix(self.ics[traj_idx, 0])

    def get_input_vars(self, block, t):
        return [block.x[t, j] for j in range(2)]           # fed into the network

    def get_output_vars(self, block, t):
        return [block.z[t, 0]]                             # produced by the network

Optional overrides let you customize the observation model (get_obs_vars), track extra variables (get_aux_vars), or define the true term for synthetic data generation (add_true_output_constraints, used only by generate_data). See sindae/example_problems.py for complete implementations of the four-tank DAE, Leslie-Gower ODE, and fed-batch bioreactor.

Hybrid model development with Claude

To reduce the learning curve of the package and streamline hybridizing a model, defining a ProblemDefinition, selecting a solution method, and solving the model to convergence, a CLAUDE.md file along with a set of skills is included in sindae-skills/.

Copy the bundle into your own modeling project and Claude will ask you about the process, draft the governing equations, and, critically, render them and refine the model with you before writing or running any code.

See sindae-skills/README.md for setup.

Citation

@misc{lueg2026sindae,
      title={A Simultaneous Approach for Training Neural Differential-Algebraic Systems of Equations}, 
      author={Laurens R. Lueg and Victor Alves and Daniel Schicksnus and John R. Kitchin and Carl D. Laird and Lorenz T. Biegler},
      year={2026},
      eprint={2504.04665},
      archivePrefix={arXiv},
      primaryClass={cs.LG},
      url={https://arxiv.org/abs/2504.04665}, 
}

License

This project is licensed under the EPL License. See the LICENSE file for details.

Metadata

Release files for sindae 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sindae 1.0.2
File Size Uploaded Provenance Actions
sindae-1.0.2.tar.gz 104.3 kB Without provenance Details for sindae-1.0.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for sindae 1.0.2
File Interpreter ABI Platform Actions
sindae-1.0.2-py3-none-any.whl Python 3 none any Details for sindae-1.0.2-py3-none-any.whl

Total release size: 201.2 kB

Release files / sindae-1.0.2.tar.gz

Download URL sindae-1.0.2.tar.gz
Size 104.3 kB
Tags Source
SHA-256 checksum
How to use checksums
5a61142bea76be0f35e0072ed4c7bc114c2b715fe0043fbd435649feb8c8c07d
BLAKE2b-256 checksum
How to use checksums
e14ca8cc3725d698022a2d462ee5774feeec46d97936ce06ed9121ab7a2e838c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / sindae-1.0.2-py3-none-any.whl

Download URL sindae-1.0.2-py3-none-any.whl
Size 96.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0e6936296e336d24f214223f15db65ddde90c7109baf2899e9dc8bf7cced403a
BLAKE2b-256 checksum
How to use checksums
ece993b051ddb437c30017544c665a758ddc33db4657e35a4ee7fd2d66603ecb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for sindae 1.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page