Discretization And Linearization¶
Discretization and linearization are the bridge between nonlinear plant models and the most mature control-design slice in Contrax.
They are also where “JAX-native” starts to matter for model construction, not just for solvers.
Where They Fit¶
That flow matters because Contrax’s strongest controller-design path is
still the discrete Riccati slice around dare().
Linearization¶
linearize() computes local Jacobians A and B around an operating point.
linearize_ss() computes A, B, C, and D and returns them as a
ContLTI.
The key design choice is that these are direct JAX differentiation helpers, not symbolic or offline preprocessing steps. That makes vmapped operating-point sweeps and compiled local-model construction part of the intended workflow.
Use:
linearize(f, x0, u0)when you only need the dynamics Jacobianslinearize_ss(f, x0, u0, output=h)when you have plain callableslinearize_ss(sys, x0, u0)when you want a state-space model from a reusableNonlinearSystemorPHSSystem
The local approximation is the usual first-order model around an operating
point (x_0, u_0):
with
Contrax computes those Jacobians directly with JAX automatic differentiation, so operating-point sweeps and vmapped local-model construction are part of the intended workflow rather than separate preprocessing steps.
Discretization¶
c2d() converts a ContLTI to a DiscLTI.
The public methods are:
zoh: zero-order hold, the stronger pathtustin: bilinear transform, mainly a convenience discretization
The zoh path matters more because it is the one intended for differentiable
and reference-checked design workflows.
Zero-Order Hold Path¶
The zero-order-hold discretization goes through the matrix exponential. In plain forward mode that is straightforward, but gradients through the matrix exponential can become numerically fragile on stiff systems.
That is why Contrax uses a custom VJP around the matrix exponential in this path: the forward formula alone is not the whole contract. The gradient story is part of the feature.
For zero-order hold, the continuous-to-discrete map is
$$ \begin{bmatrix} \Phi & \Gamma \ 0 & I \end{bmatrix}
= \exp\left( \begin{bmatrix} A & B \ 0 & 0 \end{bmatrix} \Delta t \right) $$
so that the discrete model becomes
Caveats¶
linearize()andlinearize_ss()are local approximations, not global model guaranteesc2d()is precision-sensitive and requiresjax_enable_x64=Truezohis the path to prefer for serious workflows todaytustinis useful, but not the headline differentiable discretization path
Practical Checks¶
On unfamiliar workflows, the useful checks are:
- linearized shapes match what the downstream control design expects
- vmapped operating-point pipelines stay finite
- discrete closed-loop poles look reasonable after
c2d() -> lqr() - gradients through representative
c2d()objectives stay finite