Changelog
v0.2.0 — 2026-07-22
First-class gradient support for the sparse polynomial RODAS4 solver and a new
model-specialized dense adaptive TSIT5 solver with gradients.
Sparse RODAS4 gradients
- Added
nvqsp.gradients.solve()andCoefficientDerivativesfor continuous
forward sensitivities ofA0, the sparseA1/A2values, and initial
conditions. Coefficient derivatives are combined analytically with the sparse
terms; the runtime uses no finite differences. - Added
GradientResult, returning primal trajectories and a parameter-last
Jacobian with shape(batch, n_times, neq, P). - Added shared and per-patient coefficient derivative inputs. The augmented
state hasneq * (P + 1)entries, so memory and work scale linearly with the
number of differentiated parameters. - Added
gradients.build_augmented_system()for callers that want to inspect or
stage the expanded polynomial coefficients before solving. - Added the optional
nvqsp.torchautograd bridge for direct coefficient
tensors.SensitivityTargetslimits the differentiated slots, ordinary
inference avoids the augmented solve, and the base package neither imports nor
requires PyTorch.
Dense TSIT5 solver and gradients
- Added
nvqsp.tsit5, a model-specialized dense adaptive 5th-order TSIT5
integrator for general (non-stiff and mildly stiff) systems. - Added
tsit5.build_model()to generate and compile a model-specialized CUDA
library (requires a CUDA Toolkit /nvcc), andtsit5.solve()to run batched
trajectories from a prebuilt library with no toolkit needed. - Added
tsit5.solve_with_gradients()for central-finite-difference gradients
with respect to selected parameters (theta) or initial conditions (y0),
returning a(batch, time, state, selected_input)Jacobian. - Added
tsit5.reference_solve_model_with_gradients()and
tsit5.validate_gradients()to cross-check against a tight SciPy CPU
reference, andtsit5.solve_torchfor a differentiable PyTorch operation. - Importing
nvqspdoes not import the TSIT5 build/codegen machinery or PyTorch.
Validation
- Added analytical linear, quadratic, batched, and fixed-dose gradient tests
against tight reference solutions. - Added forced multi-kernel gradient coverage alongside the default backend.
- Added PyTorch forward and VJP tests, plus optimizer-loop checks, for shared
and per-patient parameters. - Added an isolated installed-wheel import and bundled-library smoke check to
the release build.
Gradient limitations
- Forward-mode cost grows linearly with parameter count and is intended for a
modest number of differentiated parameters. - Sensitivities describe the continuous ODE solution; solver step-size and
controller decisions are not differentiated. - Fixed dose schedules are accepted, but gradients with respect to dose times
and dose amounts are not supported. - The PyTorch bridge supports first-order VJPs only and rejects higher-order
autograd requests. - Both
A1andA2must contain at least one structural entry; zero-onlyA1
models can use a single entry with value0.0. - Dense TSIT5 gradients use central finite differences; cost scales with the
number of requested coordinates. tsit5.build_model()requires a CUDA Toolkit (nvcc); the prebuilt sparse
RODAS4 solver does not.
v0.1.0 — 2026-03-09
Initial public release.
Solver
- RODAS4 (4th-order Rosenbrock) stiff ODE solver for polynomial systems of the
formdy/dt = A0 + A1*y + A2*(y x y). - Jacobi-preconditioned BiCGSTAB iterative linear solver with configurable
tolerances and iteration limits. - Adaptive step-size control with error estimation and automatic
accept/reject logic. - Two GPU backends (automatically selected at runtime):
- Single-kernel: persistent block-per-patient, shared-memory resident.
Best for models up to ~2,800 states. - Multi-kernel: host-driven, global-memory based. Handles arbitrarily
large models.
- Single-kernel: persistent block-per-patient, shared-memory resident.
- Per-patient parameter variation for A0, A1, and A2 coefficient values with
shared sparsity structure. - Discrete dosing events applied to state index 0. Dose schedule (times and
amounts) is shared across all patients in the batch. - Bit-exact reproducibility across runs on the same GPU.
Python API
nvqsp.sparse.solve()— high-level interface with automatic batch size
inference, flexible input shapes, and library auto-discovery.nvqsp.options.SparseOptions— dataclass with all solver parameters and
sensible defaults.
C API
sparse_rodas4_solve()— single function entry point with CSR matrix inputs,
batch support, and optional dosing.SparseRodas4Optionstypedef — C-compatible struct (works with both C and
C++ compilers).
Packaging
- pip wheel (
nvqsp-0.1.0-py3-none-linux_x86_64.whl) with bundled.so. - Debian package (
nvqsp_0.1.0_amd64.deb) with system-wide library and headers. - Fat binary with native SASS for sm_80 (Ampere), sm_89 (Ada Lovelace),
sm_90 (Hopper), and compute_90 PTX for forward compatibility.
Known Limitations
- Dosing is hardcoded to state index 0. Models with a different dosing
compartment must reorder the state vector before calling the solver. A2_nnzmust be >= 1. For purely linear models, add a single entry with
value1e-30.- Does not support Michaelis-Menten elimination, Hill-function PD, TMDD,
indirect response models, or DAE systems. - Dose schedule (both times and amounts) is shared across all patients in the
batch. Per-patient dosing is not supported; however, per-patient PK variation
can be achieved through per-patient A0, A1, and A2 coefficient values.