Source code for diffilqrax.parallel_dilqr

"""
Module solves the differential iterative linear quadratic regulator (DiLQR) problem with the parallel
(associative-scan) iLQR solver.
"""

from typing import Tuple

from jax import Array, lax
import jax.numpy as jnp

from diffilqrax.typs import iLQRParams, LQRParams, ParallelSystem, System
from diffilqrax.ilqr import approx_lqr_offset
from diffilqrax.diff_ilqr import add_dynamics_curvature
from diffilqrax.parallel_ilqr import pilqr_solver
from diffilqrax.parallel_dlqr import pdllqr


[docs] def pdilqr( parallel_model: ParallelSystem | System, params: iLQRParams, Us_init: Array, exact_hessian: bool = True, adjoint: str = "parallel", **kwargs, ) -> Array: """ Solves the differential iLQR problem with :func:`diffilqrax.parallel_ilqr.pilqr_solver`. Parallel counterpart of :func:`diffilqrax.diff_ilqr.dilqr`. Parameters ---------- parallel_model : ParallelSystem | System The system model. params : iLQRParams The iLQR parameters. Us_init : Array The initial control sequence. exact_hessian : bool, optional If True (default), the implicit gradients use the Hessian of the Lagrangian and match direct differentiation; if False, only the cost Hessians (Gauss-Newton). See :func:`dilqr`. adjoint : str, optional Solver of the adjoint LQR problem in the reverse pass: "parallel" (associative scan, default) or "sequential" (Riccati recursion). See :func:`diffilqrax.parallel_dlqr.pdllqr`. **kwargs Additional keyword arguments for :func:`diffilqrax.parallel_ilqr.pilqr_solver`, e.g. ``rollout``. Returns ------- Array The optimized state-control trajectory [(T+1)x(n+m)]. Notes ----- The gradients are implicit: they are exact only if the solver has converged (see ``kkt_tol`` and ``max_iter`` of :func:`pilqr_solver`). ``Us_init`` receives no gradient and the solver is never differentiated, so it can run as a ``lax.while_loop`` (``loop="while"``, the default). """ sol = pilqr_solver(parallel_model, lax.stop_gradient(params), lax.stop_gradient(Us_init), **kwargs) return implicit_pdilqr(parallel_model, params, sol[0], exact_hessian, adjoint)
[docs] def implicit_pdilqr( parallel_model: ParallelSystem | System, params: iLQRParams, solution: Tuple[Array, Array, Array], exact_hessian: bool = True, adjoint: str = "parallel", ) -> Array: """ The differentiable part of :func:`pdilqr`: the optimal trajectory, with implicit gradients wrt ``params``. Parameters ---------- parallel_model : ParallelSystem | System The system model. params : iLQRParams The iLQR parameters. solution : Tuple[Array, Array, Array] Optimal (Xs, Us, Lambs) of the solver for ``params``; they receive no gradient. exact_hessian, adjoint See :func:`pdilqr`. Returns ------- Array The optimized state-control trajectory [(T+1)x(n+m)]. """ model = parallel_model.model if isinstance(parallel_model, ParallelSystem) else parallel_model Xs_star, Us_star, Lambs_star = lax.stop_gradient(solution) tau_star = jnp.c_[Xs_star, jnp.r_[Us_star, jnp.zeros(shape=(1, model.dims.m))]] local_lqr = approx_lqr_offset(model, Xs_star, Us_star, params) if exact_hessian: local_lqr = add_dynamics_curvature(model, local_lqr, Xs_star, Us_star, Lambs_star, params) return pdllqr(model.dims, LQRParams(lqr=local_lqr, x0=params.x0), tau_star, adjoint=adjoint)
parallel_dilqr = pdilqr