Riccati Solvers¶
Riccati solvers turn infinite-horizon LQR problems into algebraic equations. Their implementation must also support the required JAX compilation and differentiation paths.
LQR Setup¶
Riccati solvers are the numerical core behind linear-quadratic regulator design. They provide both the forward solution and the differentiable map used inside controller-design objectives.
For the discrete-time model
the infinite-horizon cost is
and the optimal state-feedback law has the form
For the continuous-time model
the cost is
with optimal feedback
The Riccati equations are what turn those optimization problems into algebraic solver calls.
Riccati Equations¶
The stabilizing algebraic Riccati equations used by LQR are:
$$ A^\top S A - S - A^\top S B \left(R + B^\top S B\right)^{-1} B^\top S A + Q = 0 $$ for the discrete algebraic Riccati equation, and
$$ A^\top S + S A - S B R^{-1} B^\top S + Q = 0 $$ for the continuous algebraic Riccati equation.
Given the stabilizing solution S, the gains are
and the closed-loop matrices are
Solver Paths¶
The public paths are:
dare()for discrete systems, used bylqr()onDiscLTIcare()for continuous systems, used bylqr()onContLTI
Their maturity differs:
dare()is the most mature solver pathcare()is a validated continuous-time solver, but still less benchmarked thandare()
See the Control API for call signatures and the tutorials for complete design workflows.
Discrete Riccati: dare()¶
dare() solves the discrete algebraic Riccati equation with structured
doubling.
At the control level, that means dare() is the numerical heart of discrete
lqr(): solve for S, recover K_d, then inspect the poles of
\(A_{\mathrm{cl},d}\).
The solver provides:
- robust forward solves on the existing benchmark slice
- residual and closed-loop pole validation
- JAX-native execution without CPU-only Schur or QZ routines in the hot path
- implicit JVP and VJP rules that avoid unrolling solver iterations
The backward pass solves the adjoint discrete Lyapunov equation for the converged Riccati solution and then lets gain computation differentiate from that solution. Both Riccati adjoints share the public Lyapunov equation kernel: residual-validated squared Smith iteration, with a Cayley transform for the continuous equation. Automatic dense fallback is limited to eight states. For stable nonnormal systems, residual corrections refine a stagnated doubling solution within the same iteration budget and without relaxing its tolerance. The reported Lyapunov iteration count includes these correction solves. For larger systems, failed iterations return nonfinite gradients; they do not switch to a dense Kronecker solve. The public Lyapunov routines also expose implicit forward-mode differentiation and an explicit general dense solver.
The practical implication is that gradients with respect to A, B, Q, and
R are attached to the converged Riccati solution instead of the raw iteration
history.
Validation for dare() includes residual checks, closed-loop pole checks,
Octave-backed reference tests, JIT agreement, and finite-difference gradient
checks.
Continuous Riccati: care()¶
care() uses a Hamiltonian stable-subspace solve with residual-decreasing
Newton corrections and implicit forward/reverse differentiation.
At the control level, care() plays the same role for continuous lqr():
solve for S, recover K_c, then inspect the spectrum of
\(A_{\mathrm{cl},c}\).
Reference: Laub (1979), "A Schur Method for Solving Algebraic Riccati Equations". The implementation uses a JAX eigendecomposition to extract the stable subspace.
Newton corrections solve a continuous Lyapunov equation for the CARE residual.
A correction is accepted only if its residual decreases and the resulting
closed loop remains stable. The result validates cost definiteness, finite
values, residual and stability in JAX in eager and compiled execution.
num_steps counts accepted corrections. Poor separation of stable and unstable
Hamiltonian eigenvalues can still prevent an accurate candidate; inspect status
instead of assuming refinement guarantees success.
Solver Selection And Schur-Based Methods¶
Schur- and QZ-based methods are standard in classical control software. The JAX implementation uses methods that fit its execution model:
- CPU-only decomposition paths do not run within compiled JAX and GPU workflows
- a solver path that is numerically fine in forward mode may be a poor fit for differentiation
- unrolling an iterative solver in the backward pass can require excessive memory
Both the forward algorithm and the derivative rule determine whether a solver fits a compiled design workflow.
Failure Modes And Diagnostics¶
The first signs of trouble on unfamiliar systems are usually:
- large Riccati residuals
- non-stabilizing closed-loop poles
- failure to isolate the required stable Hamiltonian subspace
- poor agreement with reference solvers on representative systems
- unstable or non-finite gradients through small design objectives
How to Validate a Riccati Solve¶
On unfamiliar systems, the minimum useful checks are:
- Riccati residual size
- closed-loop stability
- agreement with Octave or SciPy on representative reference systems
- finite gradients through small objectives involving
Q,R,A, orB
Benchmark coverage is part of assessing solver maturity.
JAX Behavior¶
Both Riccati solvers are written to stay inside the JAX execution model:
dare()uses a JAX-native structured-doubling forward solvecare()uses a JAX-native Hamiltonian eigendecomposition and Newton refinement- both use a custom JVP for the implicit Riccati equation; JAX transposes its Lyapunov solve for reverse differentiation
Gain and Riccati-solution Hessians have analytic scalar reference coverage,
including a composed c2d -> lqr objective. Pole diagnostics are evaluated on
access, so they do not enter those objectives. Differentiating poles themselves
has the separate restrictions of JAX's nonsymmetric eigensolver.
Both paths can appear in compiled controller-design objectives; conditioning and benchmark coverage determine where they are suitable.
Related Pages¶
- Systems API for
ContLTIandDiscLTI - Differentiable LQR
- Continuous LQR
- JAX transform contract
- Control API