SciMLSensitivity.jl

repository·master·Indexed 18 days ago

https://github.com/sciml/scimlsensitivity.jl

A specialized package within the SciML ecosystem providing utilities for sensitivity analysis. It supports various adjoint methods including BacksolveAdjoint, InterpolatingAdjoint, QuadratureAdjoint, and TrackerAdjoint. The library provides tools for training Neural ODEs, optimizing parameters for Delay Differential Equations (DDEs), and handling hybrid differential equations with events and callbacks. It integrates with Lux.jl, Optimization.jl, and Turing.jl for Bayesian estimation.

Tokens
33.4K
Snippets
65
Records
96
Agent score
64%

What's inside SciMLSensitivity.jl

  1. Overview of SciMLSensitivity.jl

    master

    SciMLSensitivity.jl is a component package within the SciML Scientific Machine Learning ecosystem. It provides specialized utilities for sensitivity analysis.

    Note for users: If your primary goal is to perform sensitivity analysis in the context of solving differential equations, you should use DifferentialEquations.jl instead, as it integrates these capabilities for that specific purpose.

  2. Differentiate ODE solutions with SciMLSensitivity.jl

    master

    SciMLSensitivity.jl provides tools for obtaining derivatives of equation solvers (like ODE solvers). These derivatives are useful for local sensitivity analysis or computing gradients for model calibration and parameter estimation. While this guide focuses on differential equations, the interface applies to all SciML ecosystem solvers, including linear/nonlinear solvers and nonlinear optimization.

    import OrdinaryDiffEq as ODE
    import SciMLSensitivity as SMS
    
    # Example setup for a Lotka-Volterra system
    function lotka_volterra!(du, u, p, t)
        du[1] = dx = p[1] * u[1] - p[2] * u[1] * u[2]
        du[2] = dy = -p[3] * u[2] + p[4] * u[1] * u[2]
    end
    p = [1.5, 1.0, 3.0, 1.0];
    u0 = [1.0; 1.0];
    prob = ODE.ODEProblem(lotka_volterra!, u0, (0.0, 10.0), p)
    sol = ODE.solve(prob, ODE.Tsit5(), reltol = 1e-6, abstol = 1e-6)
  3. How Forward Sensitivity Analysis works

    master

    Forward Sensitivity Analysis computes local sensitivity by solving a sensitivity ODE simultaneously with the original ODE system. The sensitivity $S_j$ for a parameter $p_j$ is governed by:

    $$\frac{d}{dt}\frac{\partial u}{\partial p_{j}}=\frac{\partial f}{\partial u}\frac{\partial u}{\partial p_{j}}+\frac{\partial f}{\partial p_{j}}=J\cdot S_{j}+F_{j}$$

    Where:

    • $J$ is the Jacobian of the system ($\frac{\partial f}{\partial u}$).
    • $F_j$ are the parameter derivatives ($\frac{\partial f}{\partial p_j}$).
    • $S_j$ is the vector of sensitivities ($\frac{\partial u}{\partial p_j}$).

    To maintain efficiency, SciMLSensitivity.jl avoids explicitly forming the full Jacobian $J$ whenever possible. Instead, it computes the Jacobian-vector product $Jv$ using directional derivatives or dual numbers ($d = x + v \epsilon$), where $f(d) = f(x) + Jv \epsilon$.

  4. Compatibility of Sensitivity Methods with Events

    master

    When working with differential equations that include events (discontinuities/callbacks), sensitivity methods fall into two categories:

    Continuous Adjoint Sensitivities

    Methods like BacksolveAdjoint, InterpolatingAdjoint, and QuadratureAdjoint are compatible with events for ODEs.

    • BacksolveAdjoint and InterpolatingAdjoint also support events for SDEs.
    • Use BacksolveAdjoint if the event terminates the time evolution and several states are saved.
    • Limitation: Currently, continuous adjoint sensitivities do not support multiple events per time point.

    Discrete Sensitivity Analysis

    All methods based on discrete sensitivity analysis via automatic differentiation are compatible with events. This applies to ODEs, SDEs, DAEs, and DDEs. Examples include:

    • ReverseDiffAdjoint (commonly used with Enzyme)
    • TrackerAdjoint
    • ForwardDiffSensitivity
  5. Parallelize between ODE solves using EnsembleProblem

    master

    Instead of parallelizing the internal operations of a single ODE, you can parallelize the execution of multiple independent ODE solves (trajectories) using the DifferentialEquations.jl ensemble interface. This is highly effective when the ODE itself is small or the function f is not easily parallelizable.

    To implement this, you must define an EnsembleProblem with a prob_func. The prob_func uses the remake function to modify a prototype DEProblem for each trajectory, typically by sampling different initial conditions using the ctx.sim_id provided by the EnsembleContext.

    # 1. Define prototype problem
    prob = ODE.ODEProblem(f, u0, (t0, t1), p)
    
    # 2. Define how to vary the problem per trajectory
    function prob_func(prob, ctx)
        # ctx.sim_id is the trajectory index
        ODE.remake(prob, u0 = 0.5 .+ ctx.sim_id / 100 .* prob.u0)
    end
    
    # 3. Create EnsembleProblem
    ensemble_prob = ODE.EnsembleProblem(prob; prob_func)
    
    # 4. Solve using an ensembler (e.g., SciMLBase.EnsembleThreads())
    sim = ODE.solve(ensemble_prob, ODE.Tsit5(), SciMLBase.EnsembleThreads(), trajectories=100)
  6. Sensitivity algorithm compatibility for non-ODE problems

    master

    Different sensitivity algorithms have specific compatibility constraints depending on the problem type:

    Problem TypeCompatibility Notes
    DAEs (Index-1)All continuous adjoints are compatible. Avoid BacksolveAdjoint due to stiffness and reinitialization issues.
    SDEsAll adjoints are applicable except QuadratureAdjoint.
    DDEsOnly discretize-then-optimize methods apply. Note: Lag times cannot be estimated via these techniques.
    Hybrid/Jump EquationsForwardDiffSensitivity works if convert_tspan=true. ForwardSensitivity is incompatible. Discrete adjoints (ReverseDiffAdjoint, TrackerAdjoint, QuadratureAdjoint) are fully compatible. Continuous adjoints (BacksolveAdjoint, InterpolatingAdjoint, GaussAdjoint, QuadratureAdjoint) are compatible for ODEs.
    Multiple EventsContinuous adjoint sensitivities currently do not support multiple events per time point.
  7. When to use Forward vs Reverse mode

    master

    A general rule of thumb for choosing between differentiation modes is:

    • Forward-mode: Use when differentiating a system of fewer than 100 equations.
    • Reverse-mode: Use when differentiating systems with more than 100 equations.

    More complex scenarios may require a more nuanced choice based on the specific problem structure.

  8. How Adjoint Sensitivity Analysis works

    master

    Adjoint Sensitivity Analysis finds the gradient of a scalar functional $G(u, p)$ (often an integral of the solution over time) by solving an adjoint problem.

    Given a functional: $$G(u,p)=\int_{t_{0}}^{T}g(u(t,p),p)dt$$

    The adjoint problem is solved via: $$\frac{d\lambda^{\star}}{dt}=g_{u}(u(t,p),p)-\lambda^{\star}(t)f_{u}(t,u(t,p),p),\quad\lambda^{\star}(T)=0$$

    Key characteristics:

    • State Dependency: The adjoint requires the ability to evaluate the state $u$ at any point in time, meaning it requires the continuous forward solution.
    • Efficiency: The term $\lambda^{\star}(t)f_{u}(t)$ is a vector-transpose Jacobian product (VJP), which is computed efficiently using the pullback of backpropagation (reverse-mode AD).
    • Sensitivities: The resulting gradient is calculated through the integral: $$\frac{dG}{dp}=\int_{t_{0}}^{T}\lambda^{\star}(t)f_{p}(t)+g_{p}(t)dt+\lambda^{\star}(t_{0})u_{p}(t_{0})$$$
  9. Compatibility of sensitivity methods with ODE events

    master

    When working with hybrid systems (ODEs/SDEs/DAEs/DDEs with events), different sensitivity methods have different compatibility profiles:

    Continuous Adjoint Sensitivities

    Methods like BacksolveAdjoint, InterpolatingAdjoint, and QuadratureAdjoint are compatible with events for ODEs.

    • BacksolveAdjoint and InterpolatingAdjoint also support events for SDEs.
    • Recommendation: Use BacksolveAdjoint if the event terminates the time evolution and multiple states are saved.
    • Limitation: Continuous adjoint sensitivities currently do not support multiple events occurring at the same time point.

    Discrete Sensitivity Analysis (Automatic Differentiation)

    Methods based on discrete sensitivity analysis via AD are fully compatible with events for ODEs, SDEs, DAEs, and DDEs. Examples include:

    • ReverseDiffAdjoint (commonly used with AutoEnzyme)
    • TrackerAdjoint
    • ForwardDiffSensitivity
  10. Discretize a 2D PDE for simulation

    master

    To simulate the Brusselator PDE, discretize the unit square domain into an $N \times N$ grid. Store the state variables $U$ and $V$ in a single 3D array of shape (N, N, 2) to allow for efficient processing and easy extension to more variables.

    N_GRID = 16
    XYD = range(0.0f0, stop = 1.0f0, length = N_GRID)
    # State tensor shape: (N_GRID, N_GRID, 2)
    u0 = zeros(Float32, N_GRID, N_GRID, 2)
  11. What are continuous cost functionals in SciMLSensitivity.jl

    master

    In sensitivity analysis, a discrete cost functional relies on a finite number of time points (e.g., points returned by solve or specified via saveat).

    A continuous cost functional is defined as the integral of an instantaneous cost $g$ over the entire time interval $[t_0, T]$:

    $$G(u,p) = \int_{t_{0}}^{T}g(u(t,p),p)dt$$

    Because continuous functionals depend on the entire trajectory, they cannot be accurately computed using only discrete estimates of the state $u$. SciMLSensitivity.jl provides direct sensitivity analysis interfaces to evaluate these functionals and their gradients, which is a capability not available through standard automatic differentiation (AD) interfaces.

  12. Use second order sensitivity analysis for Newton-based optimization

    master

    SciMLSensitivity.jl provides second order sensitivity analysis (via forward-over-reverse) to enable fast Hessian and Hessian-vector product computations. This is particularly useful for training Neural/Universal Differential Equations using second-order optimization techniques.

    When using the Optimization.jl ecosystem, sciml_train (or OPT.solve) is configured to automatically utilize second order sensitivity analysis if a second order optimizer is requested via Optim.jl.

    Supported second-order optimization patterns include:

    • Newton and NewtonTrustRegion: These use second-order Hessian-based optimization.
    • KrylovTrustRegion: This utilizes a Krylov-based method with Hessian-vector products. This method is often the fastest for large parameter optimizations because it avoids explicitly forming the full Hessian matrix.
    import SciMLSensitivity as SMS
    import Optimization as OPT
    import OptimizationOptimJL as OOJ
    
    # ... setup optimization problem ...
    
    # Using KrylovTrustRegion for large parameter optimizations (Hessian-vector products)
    pmin = OPT.solve(optprob2, OOJ.KrylovTrustRegion(); callback, maxiters = 200)