Skip to content

Advanced control (MPC)

Model predictive control end to end: a differentiable QP solver, linear and nonlinear MPC, state estimation (Kalman, extended/unscented Kalman, and moving horizon estimation), and closed-loop simulation and tuning.

See the advanced control guide for worked examples.

mpc

Differentiable model predictive control and state estimation for Fugacio.

Advanced process control is where a differentiable engine earns its keep: every controller here is built from the same autodiff primitives as the rest of Fugacio, so a closed-loop performance index has exact gradients with respect to the controller's own tuning, and the controllers compose with the dynamic flowsheet they regulate. The subpackage is layered bottom-up:

  • riccati: differentiable Riccati solvers (DARE/CARE), the infinite-horizon LQR gain (dlqr, lqr) and the steady-state Kalman gain (kalman_gain), via fixed-iteration structured-doubling / matrix-sign recursions that backprop cleanly.
  • qp: a small dense convex QP solver (solve_qp) in the OSQP/ADMM style with an active-set polish and an implicit-function-theorem custom_vjp, the differentiable core of linear MPC.
  • linear: condensed-QP linear and offset-free MPC (linear_mpc) with an LQR terminal cost, a disturbance-observer for zero steady-state offset, and hard input / soft output constraints.
  • estimation: the Kalman filter, the extended and unscented Kalman filters, and optimization-based moving-horizon estimation (moving_horizon_estimate), all differentiable.
  • nonlinear: nonlinear and economic MPC (nonlinear_mpc) by direct single-shooting over the true nonlinear model, optimized with fugacio.sim.argmin (differentiating through the optimum).
  • simulate: a one-scan closed-loop harness (simulate_closed_loop) and gradient-based weight tuning (tune_mpc).

Modules:

Name Description
estimation

Differentiable state estimation: Kalman, EKF, UKF, and moving-horizon estimation.

linear

Linear and offset-free model predictive control.

nonlinear

Nonlinear and economic model predictive control.

qp

A small, differentiable convex quadratic-program solver for MPC.

riccati

Riccati equations, LQR, and the steady-state Kalman gain: differentiably.

simulate

Closed-loop simulation and gradient-based controller tuning.

Classes:

Name Description
ExtendedKalmanFilter

Kalman filter on the exact autodiff linearization of a nonlinear model.

GaussianState

A Gaussian belief over the state.

KalmanFilter

Recursive linear-Gaussian Kalman filter for x+ = A x + B u + w, y = C x + D u + v.

MHEResult

Outcome of moving_horizon_estimate.

UnscentedKalmanFilter

Sigma-point (unscented) Kalman filter for a nonlinear model.

LinearMPC

A condensed-QP linear MPC with offset-free tracking and constraints.

MPCResult

Outcome of a single LinearMPC.solve.

MPCState

Estimator + memory carried between controller steps.

NMPCResult

Outcome of a single NonlinearMPC.solve.

NonlinearMPC

Receding-horizon nonlinear MPC over a discrete prediction model.

QPSettings

Algorithmic settings for the ADMM forward solve (all static, never differentiated).

QPSolution

Outcome of a QP solve.

ClosedLoop

Trajectories returned by simulate_closed_loop.

Functions:

Name Description
moving_horizon_estimate

Estimate the current state by fitting the model to a window of measurements.

c2d

Discretize a continuous StateSpace at sample time dt.

linear_mpc

Assemble a LinearMPC from a state-space model and weights.

discretize

Turn a continuous right-hand side into a discrete one-step transition.

nonlinear_mpc

Assemble a NonlinearMPC.

quadratic_tracking

Build quadratic tracking stage/terminal costs around a setpoint carried in theta.

solve_qp

Solve min 0.5 z^T P z + q^T z with optional equality/inequality/box constraints.

solve_qp_canonical

Differentiable minimizer of 0.5 z^T P z + q^T z s.t. low <= A z <= up.

care

Stabilizing solution X of the continuous algebraic Riccati equation.

dare

Stabilizing solution X of the discrete algebraic Riccati equation.

dlqr

Discrete infinite-horizon LQR gain and cost-to-go.

kalman_gain

Steady-state discrete Kalman filter gain and prior covariance.

lqr

Continuous infinite-horizon LQR gain and cost-to-go.

riccati_residual_continuous

Residual A^T X + X A - X B R^{-1} B^T X + Q of the continuous ARE (zero at solution).

riccati_residual_discrete

Residual A^T X A - X - A^T X B (R + B^T X B)^{-1} B^T X A + Q (zero at the solution).

closed_loop_cost

A differentiable quadratic closed-loop performance index.

constant_setpoint

A constant output setpoint trajectory (n_steps, p).

linear_feedback

Adapt a LinearMPC to the closed-loop step protocol.

nonlinear_feedback

Adapt a NonlinearMPC to the closed-loop step protocol.

simulate_closed_loop

Run a discrete closed loop and return its trajectories.

tune_mpc

Tune controller parameters by descending a closed-loop performance index.

ExtendedKalmanFilter dataclass

ExtendedKalmanFilter(
    f: Callable[[Array, Array], Array],
    h: Callable[[Array], Array],
    q: Array,
    r: Array,
)

Kalman filter on the exact autodiff linearization of a nonlinear model.

The discrete transition f(x, u) -> x+ and measurement h(x) -> y are arbitrary differentiable callables; the filter linearizes them on the fly with jax.jacobian (A = df/dx, C = dh/dx). For a continuous plant, pass a one-step integrator as f (e.g. a wrapped fugacio.sim.dynamics.odeint_final).

Methods:

Name Description
predict

Time update: push the belief through f and propagate covariance via df/dx.

update

Measurement update: fold in y through the linearized dh/dx (Joseph form).

step

One predict-then-update cycle for input u and measurement y.

filter

Run the EKF over input/measurement sequences; return the belief trajectory.

predict

predict(
    state: GaussianState, u: ArrayLike = 0.0
) -> GaussianState

Time update: push the belief through f and propagate covariance via df/dx.

update

update(state: GaussianState, y: ArrayLike) -> GaussianState

Measurement update: fold in y through the linearized dh/dx (Joseph form).

step

step(
    state: GaussianState, u: ArrayLike, y: ArrayLike
) -> GaussianState

One predict-then-update cycle for input u and measurement y.

filter

filter(
    state0: GaussianState, us: Array, ys: Array
) -> GaussianState

Run the EKF over input/measurement sequences; return the belief trajectory.

GaussianState

Bases: NamedTuple

A Gaussian belief over the state.

Attributes:

Name Type Description
mean Array

State estimate (n,).

cov Array

Estimate error covariance (n, n).

KalmanFilter dataclass

KalmanFilter(
    a: Array,
    b: Array,
    c: Array,
    q: Array,
    r: Array,
    d: Array | None = None,
)

Recursive linear-Gaussian Kalman filter for x+ = A x + B u + w, y = C x + D u + v.

Construct directly with the model and noise covariances (q process, r measurement; scalars/diagonals broadcast). A filter step corrects the prior with a prediction: step predicts from the previous posterior through the input and then updates with the new measurement.

Methods:

Name Description
predict

Time update: propagate the belief one step through the dynamics.

update

Measurement update: correct the belief with an observation y.

step

One predict-then-update cycle (previous posterior, input, new measurement).

filter

Run the filter over input/measurement sequences; return the belief trajectory.

steady_state_gain

The steady-state Kalman update gain (the dual-DARE solution).

predict

predict(
    state: GaussianState, u: ArrayLike = 0.0
) -> GaussianState

Time update: propagate the belief one step through the dynamics.

update

update(
    state: GaussianState, y: ArrayLike, u: ArrayLike = 0.0
) -> GaussianState

Measurement update: correct the belief with an observation y.

step

step(
    state: GaussianState, u: ArrayLike, y: ArrayLike
) -> GaussianState

One predict-then-update cycle (previous posterior, input, new measurement).

filter

filter(
    state0: GaussianState, us: Array, ys: Array
) -> GaussianState

Run the filter over input/measurement sequences; return the belief trajectory.

us and ys have a leading time axis of equal length T; the returned GaussianState has a leading time axis of length T on mean and cov.

steady_state_gain

steady_state_gain() -> Array

The steady-state Kalman update gain (the dual-DARE solution).

MHEResult

Bases: NamedTuple

Outcome of moving_horizon_estimate.

Attributes:

Name Type Description
x Array

Estimate of the state at the end of the window (the current state).

trajectory Array

The full estimated state trajectory over the window (N, n).

x0 Array

The estimated state at the start of the window.

noise Array

The estimated process-noise sequence (N - 1, n).

UnscentedKalmanFilter dataclass

UnscentedKalmanFilter(
    f: Callable[[Array, Array], Array],
    h: Callable[[Array], Array],
    q: Array,
    r: Array,
    alpha: float = 0.001,
    beta: float = 2.0,
    kappa: float = 0.0,
)

Sigma-point (unscented) Kalman filter for a nonlinear model.

Propagates 2n + 1 deterministic sigma points through the true nonlinear transition f(x, u) and measurement h(x) and reconstructs the Gaussian by weighted moments: no Jacobians, and accurate to higher order than the EKF for strongly nonlinear maps. alpha/beta/kappa are the standard spread parameters.

Methods:

Name Description
predict

Time update: propagate the sigma points through f and rebuild the Gaussian.

update

Measurement update: fold in y via the sigma-point output cross-covariance.

step

One predict-then-update cycle for input u and measurement y.

filter

Run the UKF over input/measurement sequences; return the belief trajectory.

predict

predict(
    state: GaussianState, u: ArrayLike = 0.0
) -> GaussianState

Time update: propagate the sigma points through f and rebuild the Gaussian.

update

update(state: GaussianState, y: ArrayLike) -> GaussianState

Measurement update: fold in y via the sigma-point output cross-covariance.

step

step(
    state: GaussianState, u: ArrayLike, y: ArrayLike
) -> GaussianState

One predict-then-update cycle for input u and measurement y.

filter

filter(
    state0: GaussianState, us: Array, ys: Array
) -> GaussianState

Run the UKF over input/measurement sequences; return the belief trajectory.

LinearMPC dataclass

LinearMPC(
    a: Array,
    b: Array,
    c: Array,
    q: Array,
    r: Array,
    s_du: Array,
    p_terminal: Array,
    u_min: Array,
    u_max: Array,
    du_max: Array,
    y_min: Array,
    y_max: Array,
    soft_weight: float,
    obs_gain: Array,
    n_pred: int,
    n_ctrl: int,
    dt: float,
    n_dist: int,
    has_rate_limit: bool,
    has_output_limit: bool,
)

A condensed-QP linear MPC with offset-free tracking and constraints.

Construct with linear_mpc, which discretizes (if needed), computes the LQR terminal cost and the disturbance-observer gain, and fills the bounds. The controller is discrete-time with sample period dt; drive a closed loop with step (estimate, optimize, advance) or query a one-shot optimal move with solve.

Methods:

Name Description
target

Steady-state target (x_ss, u_ss) giving C x_ss + d_hat = r at steady state.

solve

Solve the MPC QP for the current estimate; return the optimal first move.

init_state

Initial controller state (prior estimate and last input).

estimate

Correct the augmented [x; d] estimate from a new measurement (Kalman update).

step

One discrete controller iteration: estimate, optimize, advance the prior.

Attributes:

Name Type Description
n_state int

Number of plant states.

n_input int

Number of manipulated inputs.

n_output int

Number of controlled outputs.

n_state property

n_state: int

Number of plant states.

n_input property

n_input: int

Number of manipulated inputs.

n_output property

n_output: int

Number of controlled outputs.

target

target(r: Array, d_hat: Array) -> tuple[Array, Array]

Steady-state target (x_ss, u_ss) giving C x_ss + d_hat = r at steady state.

Solves [[I - A, -B], [C, 0]] [x_ss; u_ss] = [0; r - d_hat] (least squares when the actuator/output counts differ), the standard MPC target problem.

solve

solve(
    x_hat: Array,
    r: Array,
    u_prev: Array,
    d_hat: Array | None = None,
    *,
    settings: QPSettings | None = None,
) -> MPCResult

Solve the MPC QP for the current estimate; return the optimal first move.

Parameters:

Name Type Description Default
x_hat Array

Current state estimate (n,).

required
r Array

Output setpoint (p,).

required
u_prev Array

Previously applied input (m,) (rate reference).

required
d_hat Array | None

Output-disturbance estimate (p,) (defaults to zero).

None
settings QPSettings | None

Optional QP solver settings.

None

init_state

init_state(
    x0: ArrayLike,
    u0: ArrayLike | None = None,
    d0: ArrayLike | None = None,
) -> MPCState

Initial controller state (prior estimate and last input).

estimate

estimate(state: MPCState, y_meas: Array) -> MPCState

Correct the augmented [x; d] estimate from a new measurement (Kalman update).

step

step(
    state: MPCState,
    y_meas: Array,
    r: Array,
    *,
    settings: QPSettings | None = None,
) -> tuple[Array, MPCState]

One discrete controller iteration: estimate, optimize, advance the prior.

Mirrors fugacio.sim.control.PID.step: returns (u, new_state) where new_state carries the predicted prior estimate for the next sample. Drop into a closed-loop simulation (see fugacio.sim.mpc.simulate_closed_loop).

MPCResult

Bases: NamedTuple

Outcome of a single LinearMPC.solve.

Attributes:

Name Type Description
u Array

The first input move to apply (m,).

u_sequence Array

The full optimal move sequence (Nc, m).

x_target Array

The steady-state target state (n,).

u_target Array

The steady-state target input (m,).

objective Array

Optimal QP objective value.

feasible Array

Whether the QP converged within tolerance.

MPCState

Bases: NamedTuple

Estimator + memory carried between controller steps.

Attributes:

Name Type Description
x_hat Array

Current state estimate (n,) (the prior for this sample).

d_hat Array

Output-disturbance estimate (p,) (empty when disabled).

u_prev Array

Last applied input (m,) (for the rate penalty/limit).

NMPCResult

Bases: NamedTuple

Outcome of a single NonlinearMPC.solve.

Attributes:

Name Type Description
u Array

The first input move to apply (m,).

u_sequence Array

The optimal move sequence (Nc, m).

trajectory Array

Predicted state trajectory under the optimum (Np + 1, n).

cost Array

Optimal open-loop cost.

result OptimizeResult

The underlying fugacio.sim.OptimizeResult.

NonlinearMPC dataclass

NonlinearMPC(
    transition: Transition,
    stage_cost: StageCost,
    terminal_cost: TerminalCost | None,
    n_input: int,
    n_pred: int,
    n_ctrl: int,
    u_min: Array,
    u_max: Array,
    du_max: Array,
    has_rate_limit: bool,
    method: str,
    max_iter: int,
)

Receding-horizon nonlinear MPC over a discrete prediction model.

Construct with nonlinear_mpc. The controller optimizes the move sequence each call to solve; step runs one receding-horizon iteration and returns a shifted warm start for the next sample.

Methods:

Name Description
rollout

Predicted state trajectory (Np + 1, n) under a move sequence.

solve

Solve the open-loop optimal-control problem for the current state.

step

One receding-horizon iteration; returns (u, shifted_warm_start).

rollout

rollout(
    x0: Array, u_seq: Array, theta: Any = None
) -> Array

Predicted state trajectory (Np + 1, n) under a move sequence.

solve

solve(
    x0: Array,
    theta: Any = None,
    *,
    u_init: Array | None = None,
    u_prev: Array | None = None,
) -> NMPCResult

Solve the open-loop optimal-control problem for the current state.

Parameters:

Name Type Description Default
x0 Array

Current state (n,).

required
theta Any

Parameters forwarded to the model and cost (e.g. the setpoint).

None
u_init Array | None

Optional warm-start move sequence (Nc, m).

None
u_prev Array | None

Previous applied input, enabling the move-rate limit on the first move.

None

step

step(
    x0: Array,
    u_prev: Array,
    warm: Array | None = None,
    theta: Any = None,
) -> tuple[Array, Array]

One receding-horizon iteration; returns (u, shifted_warm_start).

QPSettings

Bases: NamedTuple

Algorithmic settings for the ADMM forward solve (all static, never differentiated).

Attributes:

Name Type Description
rho float

ADMM penalty on the consistency constraint (step size).

sigma float

Tiny proximal regularization that makes the KKT matrix nonsingular even when P is only positive semi-definite.

alpha float

Over-relaxation factor in (0, 2) (1.6 is the OSQP default).

iters int

Number of ADMM iterations (fixed, for a differentiable scan).

active_tol float

Distance below which a constraint counts as active in the polish.

QPSolution

Bases: NamedTuple

Outcome of a QP solve.

Attributes:

Name Type Description
x Array

The optimal decision vector (n,).

obj Array

Objective value 0.5 x^T P x + q^T x at x.

primal_residual Array

Max constraint violation max(l - Ax, Ax - u, 0) (inf-norm).

dual_residual Array

Max-norm stationarity residual P x + q + A^T y.

converged Array

Whether both residuals are below tolerance.

ClosedLoop

Bases: NamedTuple

Trajectories returned by simulate_closed_loop.

With T control steps, n states, m inputs and p outputs:

Attributes:

Name Type Description
t Array

Sample times (T + 1,).

states Array

Plant states (T + 1, n) (x_0 .. x_T).

outputs Array

Clean outputs measure(x) (T + 1, p).

measurements Array

Noisy outputs fed to the controller (T, p).

inputs Array

Applied inputs (T, m).

setpoints Array

Output setpoints used at each step (T, p).

moving_horizon_estimate

moving_horizon_estimate(
    f: Callable[[Array, Array], Array],
    h: Callable[[Array], Array],
    us: Array,
    ys: Array,
    x_prior: Array,
    *,
    q: ArrayLike,
    r: ArrayLike,
    p0: ArrayLike,
    state_bounds: tuple[Any, Any] | None = None,
    max_iter: int = 200,
) -> MHEResult

Estimate the current state by fitting the model to a window of measurements.

Minimizes the arrival cost plus the stage costs over a window of N measurements::

||x0 - x_prior||^2_{P0^{-1}}
    + sum_k ||w_k||^2_{Q^{-1}} + sum_k ||y_k - h(x_k)||^2_{R^{-1}}

subject to x_{k+1} = f(x_k, u_k) + w_k, over the decision variables x0 and the process-noise sequence w. The optimum is found with fugacio.sim.argmin, so the estimate is differentiable (and optional box state_bounds are enforced). For a linear-Gaussian model with no bounds this reproduces the Kalman filter's posterior mean.

Parameters:

Name Type Description Default
f Callable[[Array, Array], Array]

Discrete transition f(x, u) -> x+.

required
h Callable[[Array], Array]

Measurement map h(x) -> y.

required
us Array

Window inputs (N - 1, m).

required
ys Array

Window measurements (N, p).

required
x_prior Array

Prior mean of the window's first state (n,).

required
q ArrayLike

Process-noise covariance (n, n) (scalar/diagonal broadcast).

required
r ArrayLike

Measurement-noise covariance (p, p) (scalar/diagonal broadcast).

required
p0 ArrayLike

Arrival-cost covariance (n, n) on x0 - x_prior.

required
state_bounds tuple[Any, Any] | None

Optional (lower, upper) box on every windowed state.

None
max_iter int

Optimizer iteration cap.

200

Returns:

Type Description
MHEResult

An MHEResult.

c2d

c2d(
    ss: StateSpace, dt: ArrayLike, *, method: str = "zoh"
) -> StateSpace

Discretize a continuous StateSpace at sample time dt.

Zero-order-hold exactly via the matrix exponential of the augmented block [[A, B], [0, 0]]: expm(.. * dt) = [[Ad, Bd], [0, I]]. The output map (C, D) is unchanged. Differentiable in dt and the model matrices.

linear_mpc

linear_mpc(
    model: StateSpace,
    *,
    q: ArrayLike,
    r: ArrayLike,
    horizon: int,
    control_horizon: int | None = None,
    s_du: ArrayLike = 0.0,
    dt: ArrayLike | None = None,
    discretize: bool = False,
    u_min: ArrayLike = -inf,
    u_max: ArrayLike = inf,
    du_max: ArrayLike = inf,
    y_min: ArrayLike = -inf,
    y_max: ArrayLike = inf,
    soft_weight: float = 10000.0,
    terminal: str = "lqr",
    disturbance: str = "output",
    proc_noise: float = 0.01,
    dist_noise: float = 1.0,
    meas_noise: float = 0.01,
) -> LinearMPC

Assemble a LinearMPC from a state-space model and weights.

Parameters:

Name Type Description Default
model StateSpace

The plant StateSpace. Discrete by default; pass discretize=True with dt to ZOH-discretize a continuous model.

required
q ArrayLike

Output tracking weight (p, p) (scalar/diagonal broadcast).

required
r ArrayLike

Input weight (m, m).

required
horizon int

Prediction horizon Np (steps).

required
control_horizon int | None

Move (control) horizon Nc <= Np (defaults to Np).

None
s_du ArrayLike

Input-move (rate) weight (m, m) (default 0).

0.0
dt ArrayLike | None

Sample time; required if discretize (and recorded on the controller).

None
discretize bool

Whether model is continuous and must be ZOH-discretized.

False
u_min ArrayLike

Lower input-magnitude limit (vector).

-inf
u_max ArrayLike

Upper input-magnitude limit (vector).

inf
du_max ArrayLike

Per-step input-rate limit (vector).

inf
y_min ArrayLike

Lower output limit, imposed as a soft (slack) constraint.

-inf
y_max ArrayLike

Upper output limit, imposed as a soft (slack) constraint.

inf
soft_weight float

Linear penalty on output-constraint slacks.

10000.0
terminal str

Terminal cost, "lqr" (DARE cost-to-go, stabilizing) or "none" (use q mapped to states).

'lqr'
disturbance str

"output" for offset-free output-disturbance tracking or "none" to disable the disturbance model.

'output'
proc_noise float

Process-noise weight for the observer Kalman gain.

0.01
dist_noise float

Disturbance-state noise weight for the observer Kalman gain.

1.0
meas_noise float

Measurement-noise weight for the observer Kalman gain.

0.01

Returns:

Type Description
LinearMPC

A ready LinearMPC.

discretize

discretize(
    rhs: Callable[[Array, Array, Array, Any], Array],
    dt: ArrayLike,
    *,
    method: str = "rk4",
    substeps: int = 4,
) -> Transition

Turn a continuous right-hand side into a discrete one-step transition.

Wraps fugacio.sim.dynamics.odeint_final to integrate dx/dt = rhs(t, x, u, theta) over one sample dt holding u constant, yielding f(x, u, theta) -> x(dt) for use as an NMPC prediction model.

nonlinear_mpc

nonlinear_mpc(
    transition: Transition,
    *,
    stage_cost: StageCost,
    horizon: int,
    n_input: int,
    control_horizon: int | None = None,
    terminal_cost: TerminalCost | None = None,
    u_min: ArrayLike = -inf,
    u_max: ArrayLike = inf,
    du_max: ArrayLike = inf,
    method: str = "bfgs",
    max_iter: int = 100,
) -> NonlinearMPC

Assemble a NonlinearMPC.

Parameters:

Name Type Description Default
transition Transition

Discrete prediction model f(x, u, theta) -> x+ (see discretize for one built from a continuous RHS).

required
stage_cost StageCost

Stage cost g(x, u, theta) -> ().

required
horizon int

Prediction horizon Np.

required
n_input int

Number of manipulated inputs m.

required
control_horizon int | None

Move horizon Nc <= Np (defaults to Np).

None
terminal_cost TerminalCost | None

Optional terminal cost g(x, theta) -> ().

None
u_min ArrayLike

Lower input-magnitude limit.

-inf
u_max ArrayLike

Upper input-magnitude limit.

inf
du_max ArrayLike

Per-step input-rate limit.

inf
method str

Inner optimizer for the box-constrained solve ("bfgs" default; switched to the constrained solver automatically for rate limits).

'bfgs'
max_iter int

Optimizer iteration cap.

100

quadratic_tracking

quadratic_tracking(
    q: ArrayLike,
    r: ArrayLike,
    *,
    output: Callable[[Array], Array] | None = None,
) -> tuple[StageCost, TerminalCost]

Build quadratic tracking stage/terminal costs around a setpoint carried in theta.

The returned costs read the setpoint and reference input from theta (a dict with keys "r" and optional "u_ss"); the stage cost is ||y - r||^2_Q + ||u - u_ss||^2_R (with y = output(x), default y = x) and the terminal cost is ||y_N - r||^2_Q. Pair with NonlinearMPC.

solve_qp

solve_qp(
    p: ArrayLike,
    q: ArrayLike,
    *,
    a_eq: ArrayLike | None = None,
    b_eq: ArrayLike | None = None,
    g_ineq: ArrayLike | None = None,
    h_ineq: ArrayLike | None = None,
    lb: ArrayLike | None = None,
    ub: ArrayLike | None = None,
    settings: QPSettings = _DEFAULT_SETTINGS,
) -> QPSolution

Solve min 0.5 z^T P z + q^T z with optional equality/inequality/box constraints.

Parameters:

Name Type Description Default
p ArrayLike

Symmetric positive-semidefinite Hessian (n, n).

required
q ArrayLike

Linear term (n,).

required
a_eq ArrayLike | None

Optional equality-constraint matrix A_eq in A_eq z = b_eq.

None
b_eq ArrayLike | None

Optional equality-constraint vector b_eq.

None
g_ineq ArrayLike | None

Optional inequality-constraint matrix G in G z <= h.

None
h_ineq ArrayLike | None

Optional inequality-constraint vector h.

None
lb ArrayLike | None

Optional lower box bound lb <= z (scalars broadcast).

None
ub ArrayLike | None

Optional upper box bound z <= ub (scalars broadcast).

None
settings QPSettings

ADMM QPSettings.

_DEFAULT_SETTINGS

Returns:

Type Description
QPSolution

A QPSolution. The decision vector x is differentiable with

QPSolution

respect to every datum (p, q, the constraint matrices/vectors) by the

QPSolution

active-set implicit function theorem.

solve_qp_canonical

solve_qp_canonical(
    p: ArrayLike,
    q: ArrayLike,
    a: ArrayLike,
    low: ArrayLike,
    up: ArrayLike,
    *,
    settings: QPSettings = _DEFAULT_SETTINGS,
) -> Array

Differentiable minimizer of 0.5 z^T P z + q^T z s.t. low <= A z <= up.

The bare canonical form the MPC layer assembles directly. Returns just the optimal z (a la fugacio.sim.argmin), carrying exact implicit-diff gradients with respect to P, q, A, low, up.

care

care(
    a: Array,
    b: Array,
    q: Array,
    r: Array,
    *,
    iters: int = 40,
) -> Array

Stabilizing solution X of the continuous algebraic Riccati equation.

Solves A^T X + X A - X B R^{-1} B^T X + Q = 0 for the symmetric positive-semidefinite stabilizing solution via the matrix sign function of the Hamiltonian Z = [[A, -G], [-Q, -A^T]] (with G = B R^{-1} B^T), followed by Roberts' least-squares extraction. Differentiable in every input matrix.

dare

dare(
    a: Array,
    b: Array,
    q: Array,
    r: Array,
    *,
    iters: int = _DEFAULT_ITERS,
) -> Array

Stabilizing solution X of the discrete algebraic Riccati equation.

Solves A^T X A - X - A^T X B (R + B^T X B)^{-1} B^T X A + Q = 0 for the unique symmetric positive-semidefinite stabilizing solution by the structure-preserving doubling algorithm. The iteration is a fixed-length jax.lax.scan, so X is differentiable with respect to every input matrix.

Parameters:

Name Type Description Default
a Array

State matrix (n, n).

required
b Array

Input matrix (n, m).

required
q Array

State weight (n, n) (symmetric PSD); a 1-D input is read as a diagonal, a scalar as q * I.

required
r Array

Input weight (m, m) (symmetric PD); same broadcasting as q.

required
iters int

Number of doubling steps (each squares the horizon; the default is comfortably into the converged regime).

_DEFAULT_ITERS

Returns:

Type Description
Array

The symmetric stabilizing solution X of shape (n, n).

dlqr

dlqr(
    a: Array,
    b: Array,
    q: Array,
    r: Array,
    *,
    iters: int = _DEFAULT_ITERS,
) -> tuple[Array, Array]

Discrete infinite-horizon LQR gain and cost-to-go.

Returns (K, X) for the optimal state feedback u = -K x that minimizes sum_k x_k^T Q x_k + u_k^T R u_k subject to x_{k+1} = A x_k + B u_k, with X the dare solution (the cost-to-go x_0^T X x_0) and K = (R + B^T X B)^{-1} B^T X A.

kalman_gain

kalman_gain(
    a: Array,
    c: Array,
    qn: Array,
    rn: Array,
    *,
    iters: int = _DEFAULT_ITERS,
) -> tuple[Array, Array]

Steady-state discrete Kalman filter gain and prior covariance.

For x_{k+1} = A x_k + w_k with process covariance Qn and measurement y_k = C x_k + v_k with covariance Rn, returns (L, P) where P is the stabilizing solution of the filtering Riccati equation (the steady-state prior error covariance) and L = P C^T (C P C^T + Rn)^{-1} is the a-posteriori update gain (x_hat <- x_prior + L (y - C x_prior)).

Estimation is control run backwards: P is dare applied to the dual pair (A^T, C^T, Qn, Rn), so this shares the doubling solver and its differentiability.

lqr

lqr(
    a: Array,
    b: Array,
    q: Array,
    r: Array,
    *,
    iters: int = 40,
) -> tuple[Array, Array]

Continuous infinite-horizon LQR gain and cost-to-go.

Returns (K, X) for the optimal feedback u = -K x minimizing \int x^T Q x + u^T R u subject to x' = A x + B u, with X the care solution and K = R^{-1} B^T X.

riccati_residual_continuous

riccati_residual_continuous(
    a: Array, b: Array, q: Array, r: Array, x: Array
) -> Array

Residual A^T X + X A - X B R^{-1} B^T X + Q of the continuous ARE (zero at solution).

riccati_residual_discrete

riccati_residual_discrete(
    a: Array, b: Array, q: Array, r: Array, x: Array
) -> Array

Residual A^T X A - X - A^T X B (R + B^T X B)^{-1} B^T X A + Q (zero at the solution).

Useful as a self-contained correctness oracle: a valid DARE solution drives this matrix to zero with no external reference.

closed_loop_cost

closed_loop_cost(
    loop: ClosedLoop,
    *,
    error_weight: ArrayLike = 1.0,
    effort_weight: ArrayLike = 0.0,
    move_weight: ArrayLike = 0.0,
    u_ref: ArrayLike = 0.0,
) -> Array

A differentiable quadratic closed-loop performance index.

Sums the tracking error of the resulting outputs against the setpoints, plus optional input-effort and input-move penalties:

``sum_k ||y_{k+1} - r_k||^2_We + ||u_k - u_ref||^2_Wu + ||u_k - u_{k-1}||^2_Wd``

Each weight may be a scalar, a per-channel vector, or a full matrix. This is the natural objective for tune_mpc.

constant_setpoint

constant_setpoint(r: ArrayLike, n_steps: int) -> Array

A constant output setpoint trajectory (n_steps, p).

linear_feedback

linear_feedback(
    mpc: Any,
    x0: ArrayLike,
    *,
    u0: ArrayLike | None = None,
    d0: ArrayLike | None = None,
) -> tuple[ControllerStep, Any]

Adapt a LinearMPC to the closed-loop step protocol.

Returns (step, ctrl0) ready for simulate_closed_loop; step is the controller's own estimate-optimize-advance iteration and ctrl0 its initial estimator state.

nonlinear_feedback

nonlinear_feedback(
    mpc: Any,
    *,
    u0: ArrayLike | None = None,
    theta: Any = None,
) -> tuple[ControllerStep, Any]

Adapt a NonlinearMPC to the closed-loop step protocol.

Assumes full-state feedback (the measurement is the state). The setpoint passed by the harness is written into theta["r"] each step; any other entries of theta (e.g. "u_ss" or model parameters) are carried through unchanged. The controller state is (u_prev, warm_start).

simulate_closed_loop

simulate_closed_loop(
    plant: Plant,
    controller: ControllerStep,
    x0: ArrayLike,
    ctrl0: Any,
    setpoints: ArrayLike,
    *,
    dt: ArrayLike = 1.0,
    measure: Measure | None = None,
    process_noise: ArrayLike | None = None,
    meas_noise: ArrayLike | None = None,
) -> ClosedLoop

Run a discrete closed loop and return its trajectories.

At each step k the measurement y_k = measure(x_k) + v_k is formed, the controller produces u_k (advancing its own state), and the plant steps to x_{k+1} = plant(x_k, u_k) + w_k. The whole march is a single jax.lax.scan, hence differentiable and jit-able.

Parameters:

Name Type Description Default
plant Plant

Discrete plant map (x, u) -> x+.

required
controller ControllerStep

Step (state, measurement, setpoint) -> (input, state) (see linear_feedback / nonlinear_feedback).

required
x0 ArrayLike

Initial plant state (n,).

required
ctrl0 Any

Initial controller state.

required
setpoints ArrayLike

Output setpoints (T, p) (or (T,) for a scalar output); its length sets the number of control steps T.

required
dt ArrayLike

Sample time (for the reported time axis only).

1.0
measure Measure | None

Output map x -> y (defaults to the identity, full-state feedback).

None
process_noise ArrayLike | None

Optional additive state noise (T, n).

None
meas_noise ArrayLike | None

Optional additive measurement noise (T, p).

None

Returns:

Type Description
ClosedLoop

A ClosedLoop with the state, output, measurement, input and

ClosedLoop

setpoint trajectories.

tune_mpc

tune_mpc(
    simulate: Callable[[Any], ClosedLoop],
    params0: Any,
    *,
    performance: Callable[[ClosedLoop], Array]
    | None = None,
    bounds: tuple[Any, Any] | None = None,
    method: str = "bfgs",
    max_iter: int = 60,
) -> OptimizeResult

Tune controller parameters by descending a closed-loop performance index.

simulate(params) must build the controller from the params pytree, run the closed loop, and return the ClosedLoop; performance scores it (defaults to closed_loop_cost, i.e. tracking ISE). The gradient flows through the simulation and the MPC's own QP, so the tune is exact first-order.

Parameters:

Name Type Description Default
simulate Callable[[Any], ClosedLoop]

params -> ClosedLoop (the differentiable closed loop).

required
params0 Any

Initial parameter pytree (e.g. {"q": ..., "r": ...}; tune the weights, keeping integer horizons and constraint bounds static).

required
performance Callable[[ClosedLoop], Array] | None

Scalar score ClosedLoop -> () to minimize.

None
bounds tuple[Any, Any] | None

Optional (lower, upper) box on params (recommended: keeps weights positive).

None
method str

Unconstrained optimizer forwarded to fugacio.sim.minimize.

'bfgs'
max_iter int

Maximum optimizer iterations forwarded to fugacio.sim.minimize.

60

Returns:

Type Description
OptimizeResult

The fugacio.sim.OptimizeResult; result.x is the tuned params.