Architecture & invariants
The canonical contributor rules live in AGENTS.md. This page is the
orientation map.
Package layout
ipax/
typing.py options.py result.py solve.py
problem/ Problem ABC, FunctionProblem, derivative resolution, autodiff adapters
backend/ namespace resolution, LinearOperator hierarchy, sparse adapters
linalg/ LinearSolver protocol: dense / krylov / sparse-direct + regularization
ipm/ barrier, kkt, step, filter_ls, restoration, breedveld_ls, hessian, init, driver
testing/ analytic oracle problems shared with benchmarks
The two protocols that carry the design
LinearOperator(backend/operators.py) — every Jacobian and the Lagrangian Hessian is one of these (Dense,Diagonal,LowRank,LBFGSOperator,MatrixFreeJacobian,SparseOperator,Composite). The IPM loop never inspects a concrete matrix.LinearSolver(linalg/solver.py) — every KKT solve goes through this:DenseSolver,KrylovSolver(matrix-free, default at scale),SparseDirectSolver. Auto-selected by size/density/capabilities.
Non-negotiable invariants
- No concrete array library in the core — only in
backend/sparse/andproblem/autodiff/. Enforced byscripts/check_purity.pyandtests/test_purity_gate.py. - Stay inside the Array API standard (main namespace + optional
linalg).array-api-strictis the purity gate in the test matrix. - Linear algebra is injected, never hard-wired into
ipm/driver.py. - Sparsity is an adapter concern — the core emits index/value vectors only.
- No global mutable state — solver state lives in explicit objects.
Two KKT solve routes
- Condensed normal-equations (Array-API-native default): PD via Powell-damped
L-BFGS + Friedlander–Orban primal–dual regularization — no inertia oracle.
With an exact (assemblable) Hessian the dense reference solver additionally
Cholesky-probes the condensed block
N:xp.linalg.solve(LU) would accept an indefiniteNand return a non-descent step, so a failed Cholesky drives the same δ_w escalation, steering nonconvex solves away from saddle points. - Indefinite augmented (sparse-direct only): inequalities and the L-BFGS
low-rank term stay explicit as symmetric border blocks (
∇gwith−Σ_s⁻¹), so the bordered matrix the sparse LDLᵀ factors is indefinite but its Schur complement is exactly the condensedN. When the backend reports inertia (Feral / cuDSS), the IPM runs the IPOPT inertia-guided δ_w correction — bumping δ_w until the factor's inertia matches the operator's target, which steers nonconvex solves away from saddle points.
See concepts/algorithm.md for the math.