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,
- Speed and numerical accuracy through a Rust core.
- Modular components with informative diagnostics.
- Batteries-included experience spanning optimisation, sampling, and plotting.
- 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
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]"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}")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)Clone the repository and create the Python environment:
uv syncBuild the Rust extension with Python bindings:
uv run maturin developRegenerate .pyi stubs after changing the bindings:
uv run cargo run -p diffid-py --no-default-features --features stubgen --bin generate_stubsWithout uv, invoke the generator directly:
cargo run -p diffid-py --no-default-features --features stubgen --bin generate_stubsuv tool install pre-commit
pre-commit install
pre-commit run --all-filesuv run maturin develop && uv run pytest -v # Python tests
cargo test # Rust tests