Solvers and diagnostics¶
Shared numerical reports, checked roots, and implicit differentiation support thermodynamic calculations and process solves. See the reliability guide for convergence checks, scaling, failure handling, and derivative limits.
Solve reports¶
diagnostics
¶
Numerical solve reports shared by thermodynamics and process simulation.
Reports contain only arrays and are JAX pytrees. Host applications can raise a
descriptive exception with :func:require_converged; compiled applications
inspect report.converged and retain the report alongside their results.
Classes:
| Name | Description |
|---|---|
SolveStatus |
Stable, machine-readable termination codes. |
SolveReport |
Termination information for a nonlinear or linear calculation. |
SolveResult |
A best available solution and its independently checked solve report. |
ConvergenceError |
A failed calculation, with its machine-readable report attached. |
Functions:
| Name | Description |
|---|---|
require_converged |
Raise for a concrete failed report; leave traced reports to compiled callers. |
residual_report |
Grade a scaled residual independently of an iteration's stopping rule. |
SolveReport
¶
Bases: NamedTuple
Termination information for a nonlinear or linear calculation.
Attributes:
| Name | Type | Description |
|---|---|---|
status |
Array
|
A :class: |
iterations |
Array
|
Number of attempted iterations. |
residual_norm |
Array
|
Maximum absolute scaled residual. |
step_norm |
Array
|
Maximum absolute scaled last step. |
worst_equation |
Array
|
Index of the largest scaled residual, or -1 if absent. |
Methods:
| Name | Description |
|---|---|
to_dict |
Return a strict-JSON-compatible report for a concrete calculation. |
SolveResult
¶
ConvergenceError
¶
ConvergenceError(
report: SolveReport,
context: str = "calculation",
labels: tuple[str, ...] = (),
)
require_converged
¶
require_converged(
report: SolveReport,
context: str = "calculation",
labels: tuple[str, ...] = (),
) -> None
Raise for a concrete failed report; leave traced reports to compiled callers.
This function performs no callbacks or side effects inside JAX transforms. Differentiable solver values use nonfinite derivatives on failed solves; compiled callers should also inspect the returned report explicitly.
Raises:
| Type | Description |
|---|---|
ConvergenceError
|
If a concrete report indicates failure. |
residual_report
¶
residual_report(
residual: Array,
tol: float = 1e-08,
*,
iterations: Array | int = 0,
step_norm: Array | float = 0.0,
failure: SolveStatus = MAX_ITERATIONS,
) -> SolveReport
Grade a scaled residual independently of an iteration's stopping rule.
Implicit solvers¶
implicit
¶
Implicit differentiation of fixed-point solvers.
Phase-equilibrium calculations are iterative: a flash, a bubble point, or a saturation pressure is the solution of a fixed-point or root-finding loop. Back-propagating through the individual iterations would be wasteful and numerically noisy. Instead Fugacio differentiates the converged solution directly, via the implicit function theorem.
For a fixed point x* = g(x*, theta) the sensitivity to the parameters
theta satisfies::
(I - dg/dx) dx*/dtheta = dg/dtheta
so a linear solve of the residual Jacobian yields implicit sensitivities
regardless of how many iterations the forward solve took. This is the same trick
used by the cubic-root fugacio.thermo.eos.compress_factor, generalized to vector unknowns.
Functions:
| Name | Description |
|---|---|
bracketed_root |
Solve a scalar |
newton_root |
Solve a scalar |
implicit_solution |
Attach an implicit derivative to a detached, independently solved root. |
newton_system_with_info |
Solve a square residual system and report convergence independently. |
newton_system |
Solve a vector root with implicit forward and reverse derivatives. |
fixed_point_with_info |
Converge a contraction and solve its derivative as a linear system. |
fixed_point |
Return a contraction's fixed point with implicit forward/reverse derivatives. |
bracketed_root_with_info |
Bisect a validated scalar bracket and check the resulting residual. |
newton_root_with_info |
Solve a scalar root with residual-decreasing steps and a checked report. |
bracketed_root
¶
bracketed_root(
residual: ResidualFn,
params: Any,
lo: Array,
hi: Array,
tol: float = 1e-12,
max_iter: int = 200,
) -> Array
Solve a scalar residual(x, params) = 0 for x in [lo, hi] by bisection.
The forward pass uses only residual values, so it is robust through the
poles and kinks that scalar equilibrium residuals (bubble/dew temperature,
Underwood roots, saturation lines) routinely exhibit at the bracket ends. The
root is differentiated with respect to the parameter pytree params by the
implicit function theorem in the custom_jvp rule below; the locators
lo/hi carry no gradient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
residual
|
ResidualFn
|
Scalar function |
required |
params
|
Any
|
Differentiable parameter pytree forwarded to |
required |
lo
|
Array
|
Lower bracket ( |
required |
hi
|
Array
|
Upper bracket. |
required |
tol
|
float
|
Absolute width of the final bracket. |
1e-12
|
max_iter
|
int
|
Bisection iteration cap. |
200
|
Returns:
| Type | Description |
|---|---|
Array
|
The bracketed root |
newton_root
¶
newton_root(
residual: ResidualFn,
params: Any,
x0: Array,
tol: float = 1e-12,
max_iter: int = 100,
damping: float = 1.0,
) -> Array
Solve a scalar residual(x, params) = 0 by a damped Newton iteration.
The forward Newton step uses the autodiff slope dr/dx and an optional
damping (step multiplier in (0, 1]) for stability; the converged root
is differentiated with respect to params by the implicit function theorem
(the iteration itself is not traced). Prefer bracketed_root when a
reliable bracket is available; newton_root is for smooth residuals where a
good initial guess is cheap (saturation updates, Poynting corrections).
Returns:
| Type | Description |
|---|---|
Array
|
The root |
implicit_solution
¶
implicit_solution(
residual: ResidualFn,
value: Array,
theta: Any,
valid: Array,
jacobian: JacobianFn
| Literal["sequential"]
| None = None,
) -> Array
Attach an implicit derivative to a detached, independently solved root.
The initial guess and iteration history carry no derivative. Both forward
and reverse differentiation solve the linearized residual system. A failed
primal has a nonfinite sensitivity, preventing optimization from silently
consuming derivatives at an unconverged iterate.
jacobian optionally supplies an exact structured linearization. The
same operator is used for forward derivatives and its transposed adjoint.
jacobian="sequential" builds a dense matrix one direction at a time,
sharing one residual linearization between state and parameter directions.
This avoids repeated nested unit linearizations in a recycle residual.
newton_system_with_info
¶
newton_system_with_info(
residual: ResidualFn,
x0: Array,
theta: Any,
tol: float = 1e-10,
max_iter: int = 50,
*,
scale: Array | None = None,
residual_scale: Array | None = None,
lower: Array | None = None,
upper: Array | None = None,
jacobian: JacobianFn | None = None,
) -> SolveResult
Solve a square residual system and report convergence independently.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
residual
|
ResidualFn
|
Vector residual |
required |
x0
|
Array
|
Starting vector; may be a previously converged solution. |
required |
theta
|
Any
|
Differentiable parameters. Pass all varying quantities here. |
required |
tol
|
float
|
Maximum scaled residual accepted as converged. |
1e-10
|
max_iter
|
int
|
Maximum number of Newton steps. |
50
|
scale
|
Array | None
|
Positive characteristic variable magnitudes; defaults to one. |
None
|
residual_scale
|
Array | None
|
Positive equation scales; defaults to one. |
None
|
lower
|
Array | None
|
Optional lower bounds used only during initialization and search. |
None
|
upper
|
Array | None
|
Optional upper bounds used only during initialization and search. |
None
|
jacobian
|
JacobianFn | None
|
Optional exact structured Jacobian factory |
None
|
Returns:
| Type | Description |
|---|---|
SolveResult
|
Best iterate and a :class: |
SolveResult
|
aids, not additional equations. Sensitivities are defined only when the |
SolveResult
|
original residual converges to a locally nonsingular root. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the tolerance or iteration cap is invalid. |
newton_system
¶
newton_system(
residual: ResidualFn,
x0: Array,
theta: Any,
tol: float = 1e-10,
max_iter: int = 50,
) -> Array
Solve a vector root with implicit forward and reverse derivatives.
Returns the best iterate for compatibility. Use :func:newton_system_with_info
when accepting a result; a finite iterate alone does not prove convergence.
Derivatives of an unconverged solution are nonfinite.
fixed_point_with_info
¶
fixed_point_with_info(
g: Callable[[Array, Any], Array],
x0: Array,
theta: Any,
tol: float = 1e-12,
max_iter: int = 200,
) -> SolveResult
Converge a contraction and solve its derivative as a linear system.
The derivative does not repeat the forward fixed-point iteration, so a slowly converging adjoint cannot silently exhaust a separate iteration cap.
fixed_point
¶
fixed_point(
g: Callable[[Array, Any], Array],
x0: Array,
theta: Any,
tol: float = 1e-12,
max_iter: int = 200,
) -> Array
Return a contraction's fixed point with implicit forward/reverse derivatives.
Use :func:fixed_point_with_info to inspect termination. An unconverged
iterate has nonfinite derivatives.
bracketed_root_with_info
¶
bracketed_root_with_info(
residual: ResidualFn,
params: Any,
lo: Array,
hi: Array,
tol: float = 1e-12,
max_iter: int = 200,
*,
residual_tol: float = 1e-08,
) -> SolveResult
Bisect a validated scalar bracket and check the resulting residual.
tol limits bracket width; residual_tol independently limits the
function residual in its own units. A sign change across a discontinuity
can reduce the width without satisfying the equation and is reported as a
failure. Endpoints that already solve the equation take zero iterations.
Raises:
| Type | Description |
|---|---|
ValueError
|
If tolerances or the iteration limit are invalid. |
newton_root_with_info
¶
newton_root_with_info(
residual: ResidualFn,
params: Any,
x0: Array,
tol: float = 1e-10,
max_iter: int = 100,
*,
lower: Array | None = None,
upper: Array | None = None,
) -> SolveResult
Solve a scalar root with residual-decreasing steps and a checked report.
Structured linear algebra¶
linear
¶
Structured Jacobians and checked, implicitly differentiated linear solves.
The block representation stores a nearest-neighbor chain and a small dense border. Coloring assembles its Jacobian without a stage-sized autodiff batch. Block elimination pivots within each diagonal block; a residual check selects a pivoted dense fallback when that elimination isn't adequate. The fallback changes the linear algorithm, never the nonlinear equations.
Classes:
| Name | Description |
|---|---|
LinearReport |
Independent linear residual and information about the actual algorithm. |
LinearResult |
Solution and residual evidence; a rejected solution has nonfinite values. |
Jacobian |
Matrix operations required by Newton and implicit differentiation. |
DenseJacobian |
A dense Jacobian using JAX's pivoted linear solve. |
BorderedBlockJacobian |
Block tridiagonal matrix with a small, unrestricted border. |
BlockLayout |
Static declaration of a chain's exact Jacobian sparsity pattern. |
Functions:
| Name | Description |
|---|---|
dense_jacobian |
Linearize a square residual, optionally evaluating directions sequentially. |
LinearReport
¶
LinearResult
¶
Jacobian
¶
DenseJacobian
¶
Bases: NamedTuple
A dense Jacobian using JAX's pivoted linear solve.
Methods:
| Name | Description |
|---|---|
solve |
Solve using the differentiable dense reference implementation. |
scaled |
Apply independent equation and unknown scales. |
to_dense |
Return the stored matrix. |
scaled
¶
scaled(rows: Array, columns: Array) -> DenseJacobian
Apply independent equation and unknown scales.
BorderedBlockJacobian
¶
Bases: NamedTuple
Block tridiagonal matrix with a small, unrestricted border.
lower, diagonal, and upper have shape (n, b, b). The first
lower and last upper blocks are zero. Border columns have shape
(n, b, k), border rows (k, n, b), and the corner (k, k).
Unknowns and residuals are ordered by block, followed by border entries.
Methods:
| Name | Description |
|---|---|
matvec |
Multiply a vector or a matrix of right-hand sides without densifying. |
transpose |
Transpose both the chain and its border. |
scaled |
Apply row and column scales while preserving the sparsity pattern. |
to_dense |
Materialize a reference matrix without differentiating the residual again. |
solve_with_info |
Solve with implicit gradients and independently checked linear residuals. |
solve |
Return the checked, differentiable linear solution. |
Attributes:
| Name | Type | Description |
|---|---|---|
shape |
tuple[int, int]
|
Square matrix shape. |
matvec
¶
Multiply a vector or a matrix of right-hand sides without densifying.
scaled
¶
scaled(
rows: Array, columns: Array
) -> BorderedBlockJacobian
Apply row and column scales while preserving the sparsity pattern.
to_dense
¶
Materialize a reference matrix without differentiating the residual again.
solve_with_info
¶
solve_with_info(rhs: Array) -> LinearResult
Solve with implicit gradients and independently checked linear residuals.
The transposed solve has its own factorization and residual check. Neither the block pivot choices nor dense fallback choices are differentiated. Singular or unresolved equations return NaNs.
BlockLayout
dataclass
¶
Static declaration of a chain's exact Jacobian sparsity pattern.
Each core residual block may depend on its own and neighboring unknown
blocks and every border unknown. Border equations may depend on every
unknown. This is a structural contract, not a pattern inferred from zeros
at one operating point. Use check against a dense Jacobian when adding
a model adapter.
Methods:
| Name | Description |
|---|---|
linearize |
Assemble the exact block Jacobian using colored JVPs and border VJPs. |
check |
Compare declared structure with dense autodiff at a concrete point. |
Attributes:
| Name | Type | Description |
|---|---|---|
size |
int
|
Total number of unknowns and equations. |
directions |
int
|
Forward coloring directions, independent of chain length beyond three blocks. |
directions
property
¶
directions: int
Forward coloring directions, independent of chain length beyond three blocks.
linearize
¶
linearize(
residual: Callable[..., Array], x: Array, theta: Any
) -> BorderedBlockJacobian
Assemble the exact block Jacobian using colored JVPs and border VJPs.
check
¶
check(
residual: Callable[..., Array],
x: Array,
theta: Any,
*,
tolerance: float = 1e-09,
) -> dict[str, Any]
Compare declared structure with dense autodiff at a concrete point.
This diagnostic detects missing couplings at the supplied point. It isn't a proof of a pattern's validity throughout the model domain.
dense_jacobian
¶
dense_jacobian(
residual: Callable[..., Array],
x: Array,
theta: Any,
*,
vectorize: bool = True,
) -> DenseJacobian
Linearize a square residual, optionally evaluating directions sequentially.
vectorize=False retains one primal linearization and maps over tangent
directions with a compiled loop. Nested unit solves then needn't acquire
another tangent batch dimension. The resulting matrix and solve remain
dense; this option bounds derivative working storage, not matrix storage.
sparsity
¶
Declared residual incidence, deterministic coloring, and structural diagnostics.
Classes:
| Name | Description |
|---|---|
SparsityPattern |
Conservative row dependencies in a fixed scalar unknown order. |
SparsityPattern
dataclass
¶
Conservative row dependencies in a fixed scalar unknown order.
Every true derivative entry must appear in rows. Extra entries cost
work but don't change the equations. Never infer this contract from zeros
at a single operating point. Unknown custom blocks should declare every
variable until their dependencies are known.
Methods:
| Name | Description |
|---|---|
coloring |
Color the column-intersection graph in a deterministic greedy order. |
jacobian |
Create an exact colored AD assembler with a dense reference solve. |
matching |
Find a maximum equation-to-variable matching; unmatched rows contain -1. |
diagnose |
Describe incidence and unmatched equations/variables without a numerical solve. |
coloring
¶
Color the column-intersection graph in a deterministic greedy order.
jacobian
¶
jacobian(
residual: Callable[..., Array],
) -> Callable[[Array, Any], DenseJacobian]
Create an exact colored AD assembler with a dense reference solve.
The result still stores a dense global matrix. This adapter reduces differentiation work for sparse flowsheet connections; it doesn't claim a sparse factorization or expand a procedural unit's equations.
matching
¶
Find a maximum equation-to-variable matching; unmatched rows contain -1.
Breadth-first augmenting paths avoid Python recursion limits on long process trains. A complete matching is only a structural upper bound on numerical rank, especially with conservative block dependencies.
Reusable sensitivities¶
sensitivity
¶
Reusable local derivatives of a converged numerical calculation.
One linearization evaluates the primal once and retains the residual data needed by its JVP. Its transpose supplies VJPs without retracing the primal. The object belongs to one operating point; construct a new one when that point, its model parameters, or its initialization changes.
Classes:
| Name | Description |
|---|---|
Linearization |
A value and reusable JVP/VJP at one array-valued operating point. |
Functions:
| Name | Description |
|---|---|
derivative_strategy |
Validate derivative options and describe the selected direction count. |
linearize |
Evaluate once and retain a reusable local derivative, optionally with reports. |
Linearization
dataclass
¶
A value and reusable JVP/VJP at one array-valued operating point.
Create this object with linearize. The stored callables are local
linear maps, not nonlinear approximations valid at subsequent points.
Auxiliary data, such as solve reports, isn't differentiated. Releasing
the object releases its retained residuals; no global point cache exists.
Methods:
| Name | Description |
|---|---|
block_until_ready |
Wait for both outputs and the residual buffers retained by the JVP. |
jvp |
Apply the local derivative to a tangent with the input's shape. |
vjp |
Apply the transposed derivative to an output cotangent. |
jacobian |
Build a Jacobian using bounded batches of the cheaper orientation. |
block_until_ready
¶
block_until_ready() -> Linearization
Wait for both outputs and the residual buffers retained by the JVP.
JAX's returned linear map is a registered partial pytree whose leaves
include those buffers. Waiting for the primal output alone can miss
asynchronous work that only a later derivative application consumes.
This method also lets jax.block_until_ready synchronize this object.
jacobian
¶
Build a Jacobian using bounded batches of the cheaper orientation.
The result has shape value.shape + point.shape, matching JAX's
Jacobian convention. Forward and reverse modes use the same retained
primal and the implicit derivative rules of the underlying solvers.
derivative_strategy
¶
derivative_strategy(
inputs: int,
outputs: int,
*,
mode: str = "auto",
batch_size: int = 1,
) -> dict[str, Any]
Validate derivative options and describe the selected direction count.
auto uses the smaller of the input and output dimensions, preferring
forward mode on a tie. This is a direction-count heuristic, not a promise
that one orientation is faster for every property package. A batch size
of one keeps tangent storage bounded for large nested process models.
linearize
¶
linearize(
function: Callable[..., Any],
point: Array,
*,
has_aux: bool = False,
) -> Linearization
Evaluate once and retain a reusable local derivative, optionally with reports.
Inputs and differentiated outputs must be nonempty real floating arrays. Use an explicit vector adapter for parameter dictionaries or metric trees. The calculation needn't be wrapped in a single JIT; compiled unit kernels can retain their own compilation boundaries in a larger flowsheet.