Tutorials › Physiologically-Based Pharmacokinetics (PBPK) › Using Jacobians and Sparsity in Stiff Integration

Numerical Implementation and Stiff Solvers · Tutorial 683 of 799

Using Jacobians and Sparsity in Stiff Integration

Learn how PBPK Jacobian structure guides stiff integration, how to calculate or approximate its entries, and when sparse Jacobian information can reduce computational cost.

Intermediate 10 min read

What You'll Learn

  • Define the PBPK system Jacobian and interpret its entries as local state-to-derivative sensitivities.
  • Construct a Jacobian for a flow-coupled compartment model and identify its structural nonzero pattern.
  • Approximate a nonlinear clearance derivative with a finite difference and compare it with the analytic derivative.
  • Understand how Jacobians enter Newton iterations for implicit stiff integration.
  • Compare dense, sparse, analytic, and finite-difference Jacobian strategies and their computational trade-offs.
  • Diagnose incorrect sparsity patterns and inaccurate Jacobian approximations.

Why Jacobian Information Matters

A PBPK model written as an ODE system has the form \(d\mathbf{x}/dt=\mathbf{f}(t,\mathbf{x})\), where \(\mathbf{x}\) contains the model states and \(\mathbf{f}\) gives their rates of change. In a stiff system, an implicit solver typically needs to solve linear systems involving derivatives of \(\mathbf{f}\) with respect to the states. The matrix of those derivatives is the system Jacobian. Providing it accurately can reduce repeated derivative calculations and help the solver exploit the fact that many PBPK states interact with only a subset of the other states.

The goal is not to make every Jacobian entry nonzero or to provide a complicated analytic expression at any cost. It is to represent the model’s actual local dependencies reliably, then choose a computational representation suited to their structure and the solver. A missing nonzero can damage the numerical solution; a false nonzero can make a sparse calculation unnecessarily expensive.

Definition: For a state vector \(\mathbf{x}=(x_1,\ldots,x_m)^\mathsf{T}\) and derivative function \(\mathbf{f}=(f_1,\ldots,f_m)^\mathsf{T}\), the system Jacobian is the \(m\times m\) matrix \(J(t,\mathbf{x})\) whose entry in row \(i\), column \(j\) is \(J_{ij}=\partial f_i/\partial x_j\). It describes how a small change in state \(x_j\) changes the instantaneous rate of state \(x_i\), with the other states held fixed.

A Jacobian is local: it is evaluated at a particular time and state, and its entries may change as concentrations or amounts change. A structural zero is an entry that is zero for all permitted states and parameter values because the derivative function does not depend on that state. An entry that happens to equal zero at one particular state is not necessarily a structural zero. The pattern of structural nonzeros is the Jacobian’s sparsity pattern.

A PBPK Example: Flow-Coupled Compartments

Consider a hypothetical linear amount model with four states: central blood amount \(A_b\), liver amount \(A_h\), kidney amount \(A_k\), and peripheral-tissue amount \(A_p\), all measured in mg. The exchange and elimination rate constants below have units of \(\mathrm{h}^{-1}\). Assume they are constant over the integration interval, and that each listed exchange term transfers drug between the named compartments.

Worked Example: Constructing a Compartment Jacobian

Suppose the model equations are

$$ \begin{aligned} \dot A_b&=-(k_{bh}+k_{bk}+k_{bp}+k_e)A_b+k_{hb}A_h+k_{kb}A_k+k_{pb}A_p,\\ \dot A_h&=k_{bh}A_b-(k_{hb}+k_{mh})A_h,\\ \dot A_k&=k_{bk}A_b-(k_{kb}+k_{mk})A_k,\\ \dot A_p&=k_{bp}A_b-k_{pb}A_p. \end{aligned} $$

For this example, let \(k_{bh}=0.8\), \(k_{bk}=0.5\), \(k_{bp}=0.3\), \(k_e=0.4\), \(k_{hb}=0.2\), \(k_{kb}=0.25\), \(k_{pb}=0.1\), \(k_{mh}=0.15\), and \(k_{mk}=0.05\), each in \(\mathrm{h}^{-1}\). Differentiate each right-hand side with respect to the four amounts. For example, \(\partial\dot A_b/\partial A_b=-(0.8+0.5+0.3+0.4)=-2.0\ \mathrm{h}^{-1}\), while \(\partial\dot A_h/\partial A_b=k_{bh}=0.8\ \mathrm{h}^{-1}\). The resulting Jacobian, with rows and columns ordered \(A_b,A_h,A_k,A_p\), is

$$ J= \begin{pmatrix} -2.0&0.2&0.25&0.1\\ 0.8&-0.35&0&0\\ 0.5&0&-0.30&0\\ 0.3&0&0&-0.1 \end{pmatrix}\ \mathrm{h}^{-1}. $$

There are 10 nonzero entries out of 16. The central-blood equation depends on every state, so its row is full. Each tissue equation depends on central blood and its own tissue amount, so the other two entries in that row are structural zeros. This pattern follows from the model’s coupling, not from the values of the amounts at a particular time.

The same reasoning applies to larger PBPK systems. A compartment’s derivative often depends on its own state and on states in compartments connected by blood flow, diffusion, or another exchange process. Local metabolism may add a dependence on the metabolizing compartment’s state. However, physiological connectivity alone does not guarantee a particular pattern: the pattern must be read from the actual derivative function, including any shared constraints or cross-compartment terms.

How the Jacobian Enters an Implicit Step

Implicit methods use equations in which the unknown state at the end of a step appears inside the derivative function. A Newton iteration solves the resulting nonlinear equation by repeatedly linearizing it. For a simple implicit Euler step from \(t_n\) to \(t_{n+1}=t_n+h\), define the residual for a candidate endpoint \(\mathbf{z}\) as

$$ \mathbf{G}(\mathbf{z})=\mathbf{z}-\mathbf{x}_n-h\mathbf{f}(t_{n+1},\mathbf{z}). $$

A solution has \(\mathbf{G}(\mathbf{z})=\mathbf{0}\). Newton’s method approximates the residual near the current candidate \(\mathbf{z}\) by its first-order change. Its derivative matrix is \(I-hJ(t_{n+1},\mathbf{z})\). Thus the Newton correction \(\boldsymbol{\delta}\) solves the following linear system:

$$ [I-hJ(t_{n+1},\mathbf{z})]\boldsymbol{\delta}=-\mathbf{G}(\mathbf{z}). $$

To see why, the first-order approximation is \(\mathbf{G}(\mathbf{z}+\boldsymbol{\delta})\approx\mathbf{G}(\mathbf{z})+[I-hJ]\boldsymbol{\delta}\). Setting this approximation equal to zero gives the displayed correction equation. Other implicit formulas change the step coefficient, but use the same basic idea: a matrix built from the identity and a scaled Jacobian is factored or otherwise solved against one or more right-hand sides. A Jacobian that captures the true dependencies can therefore save work in both derivative evaluation and linear algebra.

Worked Example: Checking a Nonlinear Clearance Derivative

Suppose central-compartment metabolism removes drug at the rate \(R(A)=V_{\max}C/(K_m+C)\), where \(C=A/V\). Let \(V_{\max}=12\ \mathrm{mg/h}\), \(K_m=2\ \mathrm{mg/L}\), \(V=10\ \mathrm{L}\), and evaluate at \(A=10\ \mathrm{mg}\). The contribution to the amount derivative is \(f(A)=-R(A)\). Since \(dC/dA=1/V\), differentiation gives

$$ \frac{df}{dA} =-\frac{V_{\max}K_m}{(K_m+C)^2}\frac{1}{V}. $$

At \(A=10\ \mathrm{mg}\), \(C=10/10=1\ \mathrm{mg/L}\), so

$$ \frac{df}{dA} =-\frac{(12\ \mathrm{mg/h})(2\ \mathrm{mg/L})}{(3\ \mathrm{mg/L})^2(10\ \mathrm{L})} =-0.2667\ \mathrm{h}^{-1}. $$

A forward finite difference with \(\Delta A=0.01\ \mathrm{mg}\) gives a numerical check. At \(A=10\ \mathrm{mg}\), \(R=12(1)/(2+1)=4.000\ \mathrm{mg/h}\). At \(A=10.01\ \mathrm{mg}\), \(C=1.001\ \mathrm{mg/L}\) and \(R=12(1.001)/(3.001)\approx4.002666\ \mathrm{mg/h}\). Therefore,

$$ \frac{f(10.01)-f(10)}{0.01} =\frac{-4.002666-(-4.000)}{0.01} \approx-0.2666\ \mathrm{h}^{-1}. $$

The approximation is close to the analytic derivative, as expected for this small perturbation. It is not exact: finite-difference error depends on the perturbation size and floating-point rounding. The units are \(\mathrm{h}^{-1}\), because this derivative is a rate of change in \(\mathrm{mg/h}\) divided by a change in amount in mg.

Analytic, Finite-Difference, Dense, and Sparse Options

An analytic Jacobian is computed from derivative formulas. It can be accurate and inexpensive when the formulas are available, but implementation errors are possible, especially in a large model with nonlinear or conditional code. A finite-difference Jacobian estimates derivatives by perturbing states and reevaluating \(\mathbf{f}\). It is easier to implement, but perturbations introduce truncation and rounding error and can require many derivative-function evaluations.

A dense representation stores every matrix entry, including structural zeros. A sparse representation stores only nonzeros and their locations, allowing suitable algorithms to reduce storage and linear-algebra work. Sparse finite differences can also use column coloring: columns whose nonzeros occur in disjoint rows can be perturbed together, because their effects can be separated in those rows. Coloring must be based on a correct sparsity pattern. If two columns share a row, combining their perturbations makes their contributions indistinguishable in that row.

ApproachPotential advantageMain caution
Analytic, denseDirect derivative formulas; appropriate for small or dense systemsStores and processes structural zeros; formulas can be coded incorrectly
Finite difference, denseSimple way to approximate derivatives without deriving each entryMay require many function evaluations and depends on perturbation size
Analytic, sparseCan combine accurate derivatives with sparse storage and linear algebraRequires a correct sparse implementation and solver support
Finite difference, sparse with coloringCan reduce perturbation evaluations when the pattern permits grouped columnsA missing structural nonzero can corrupt several derivative estimates

Worked Example: Comparing Jacobian Evaluation Costs

Consider a hypothetical PBPK model with 120 states and a known structural sparsity pattern. Suppose the pattern can be grouped into 8 valid finite-difference color groups, meaning the states in each group have nonoverlapping sets of affected derivative rows. For a transparent operation-count comparison, assume both runs require 2,400 ordinary right-hand-side evaluations for integration and refresh a finite-difference Jacobian 100 times. Assume one perturbed right-hand-side evaluation per state for a dense finite-difference Jacobian, and one per color group for the sparse colored approximation.

With dense finite differences, the 100 refreshes require \(100\times120=12{,}000\) perturbation evaluations. Including the 2,400 integration evaluations gives \(12{,}000+2{,}400=14{,}400\) right-hand-side evaluations. With coloring, Jacobian refreshes require \(100\times8=800\) perturbation evaluations, for \(800+2{,}400=3{,}200\) evaluations in total. The reduction in this count is

$$ \frac{14{,}400-3{,}200}{14{,}400}\times100\%=77.78\%. $$

This calculation illustrates a possible saving in derivative-function evaluations; it is not a claim that elapsed time will fall by the same percentage. Sparse factorization, memory access, Jacobian refresh policy, and solver overhead also affect runtime. The actual benefit must be measured for the model, solver, and implementation in use.

Diagnostics, Failure Modes, and Limits

A Jacobian should be checked as part of the derivative-function implementation. For a selected state \(\mathbf{x}\) and direction vector \(\mathbf{v}\), compare the product \(J\mathbf{v}\) with a directional finite difference:

$$ J\mathbf{v}\ \approx\ \frac{\mathbf{f}(t,\mathbf{x}+\epsilon\mathbf{v})-\mathbf{f}(t,\mathbf{x})}{\epsilon}. $$

Use perturbations that are meaningful relative to the state scales, and check more than one state and direction. Large discrepancies can reveal a derivative sign error, a missing dependency, inconsistent units, or a finite-difference step that is too large or too small. The earlier material on solver tolerances for unequal state scales is relevant here: an unscaled perturbation may be negligible for a large state but overwhelming for a small one.

Other warning signs include solver convergence slowing after an analytic Jacobian is supplied, repeated changes in the sparse pattern, or a sparse calculation behaving differently from a dense calculation under otherwise comparable settings. These symptoms do not by themselves prove the Jacobian is wrong, but they justify checking derivatives and pattern assumptions. In particular, do not declare an entry structurally zero just because its derivative is small at one state. Saturable processes, state-dependent flows, and nonlinear distribution terms may have derivatives that vary substantially across the simulated range.

Sparse Jacobians are not automatically faster. A small matrix may be cheaper to handle densely, and a model with broad shared dependencies may have little sparsity. Sparse factorization can also create fill-in: entries that are zero in the original matrix may become nonzero during factorization, increasing memory and computational work. Finally, finite-difference accuracy can degrade near sharp nonlinear transitions or when function evaluations contain numerical noise. Analytic derivatives, automatic differentiation, or carefully configured finite differences are alternatives; none removes the need to verify that the computed derivatives match the implemented model.

Common pitfall: “This derivative is nearly zero at the current concentration, so I can remove that Jacobian entry.” That reasoning confuses a value at one state with a structural zero. Keep the entry if the derivative function can depend on that state elsewhere in the relevant domain; otherwise a sparse solver may receive an incorrect dependency pattern.
1
Write the state dependencies.
For each derivative equation, identify which states appear in its implemented expression.
2
Construct and verify derivatives.
Differentiate analytically or approximate numerically, then compare selected Jacobian-vector products with directional finite differences.
3
Choose a representation and measure.
Use dense or sparse storage according to the actual pattern and system size, then compare evaluation counts and runtime under equivalent accuracy settings.

Check Your Understanding

Answer each question, then compare with the solutions below.

  1. In a two-state system, \(\dot x_1=-a x_1+b x_2\) and \(\dot x_2=c x_1-d x_2\). Write its Jacobian and identify the conditions under which it is structurally dense.
  2. Why can two columns be perturbed together in a colored finite-difference calculation only when their affected rows do not overlap?
  3. A Jacobian entry is zero at one simulated state but nonzero at another. Is it a structural zero? Explain.
  4. For a 50-state model, suppose a dense finite-difference refresh requires 50 perturbed function evaluations, while a valid coloring uses 5 groups. Across 40 refreshes, how many perturbation evaluations are saved?

Solutions. (1) \(J=\begin{pmatrix}-a&b\\c&-d\end{pmatrix}\). It is structurally dense when all four dependencies are present, for example when \(a,b,c,d\) are nonzero constants in this model. (2) If two columns affect the same row, the combined perturbation changes that row by the sum of both effects, so the separate derivatives cannot be recovered from that row alone. Disjoint affected rows allow the contributions to be distinguished. (3) No. A structural zero must remain zero throughout the permitted model domain because there is no functional dependence; a state-specific zero may be a temporary value of a state-dependent derivative. (4) Dense refreshes use \(40\times50=2{,}000\) perturbation evaluations; colored refreshes use \(40\times5=200\). The saving is \(2{,}000-200=1{,}800\) evaluations.