Skip to content

BradyPlanden/diffid

Repository files navigation

diffid

Python Versions from PEP 621 TOML License Releases

differential identification offers a different paradigm for a parameter inference library. Conventional Python-based inference libraries are constructed via bindings to a high-performance forward model with the inference algorithms implemented in Python. Diffid offers an alternative, where the Python-layer can act purely as a declarative configuration interface, while all computationally intensive work (Forward model evaluation, inference loops, etc.) happens entirely within the Rust runtime without crossing the FFI boundary repeatedly. This architecture is presented visually below,

Diffid architecture describing the two optimisation approaches.

Project goals

  • Speed and numerical accuracy through a Rust core.
  • Modular components with informative diagnostics.
  • Batteries-included experience spanning optimisation, sampling, and plotting.

Core capabilities

  • Gradient-free (Nelder-Mead, CMA-ES) and gradient-based (Adam) optimisers with configurable convergence criteria.
  • Multi-threaded differential equation fitting via DiffSL with dense or sparse Diffsol backends.
  • Customisable likelihood/cost metrics and Monte-Carlo sampling for posterior exploration.
  • Flexible integration with state-of-the-art differential solvers, such as Diffrax, DifferentialEquations.jl

Installation

Diffid targets Python >= 3.11. Windows builds are currently marked experimental.

pip install diffid

# Or with uv
uv pip install diffid

# Optional extras
pip install "diffid[plotting]"

Quickstart

ScalarProblem

import numpy as np
import diffid


def rosenbrock(x):
    value = (1 - x[0]) ** 2 + 100 * (x[1] - x[0] ** 2) ** 2
    return np.asarray([value])


builder = (
    diffid.ScalarBuilder()
    .with_objective(rosenbrock)
    .with_parameter("x", 1.5)
    .with_parameter("y", -1.5)
)
problem = builder.build()
result = problem.optimise()

print(f"Optimal parameters: {result.x}")
print(f"Objective value: {result.value:.3e}")
print(f"Success: {result.success}")

Differential solver workflow

import numpy as np
import diffid


# Example diffsol ODE (logistic growth)
dsl = """
in_i { r = 1, k = 1 }
u_i { y = 0.1 }
F_i { (r * y) * (1 - (y / k)) }
"""

t = np.linspace(0.0, 5.0, 51)
observations = np.exp(-1.3 * t)
data = np.column_stack((t, observations))

builder = (
    diffid.DiffsolBuilder()
    .with_diffsl(dsl)
    .with_data(data)
    .with_parameter("k", 1.0)
    .with_backend("dense")
)
problem = builder.build()

optimiser = diffid.CMAES().with_max_iter(1000)
result = optimiser.run(problem, [0.5,0.5])

print(result.x)

Development setup

Clone the repository and create the Python environment:

uv sync

Build the Rust extension with Python bindings:

uv run maturin develop

Regenerate .pyi stubs after changing the bindings:

uv run cargo run -p diffid-py --no-default-features --features stubgen --bin generate_stubs

Without uv, invoke the generator directly:

cargo run -p diffid-py --no-default-features --features stubgen --bin generate_stubs

Pre-commit hooks

uv tool install pre-commit
pre-commit install
pre-commit run --all-files

Tests

uv run maturin develop && uv run pytest -v # Python tests
cargo test # Rust tests

About

Fast differential equation parameter inference

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors