# K_inner is guaranteed to be symmetric, because X and R are symmetric
# Gradient computations come from Kao and Hennequin (2020), https://arxiv.org/pdf/2011.11430.pdf
K_inner_inv_BT=solve(K_inner,B.T,assume_a="sym")
A,B,Q,R=inputs
K=matrix_dot(K_inner_inv_BT,X,A)
(dX,)=output_grads
A_tilde=A-B.dot(K)
X=self(A,B,Q,R)
K_inner=R+matrix_dot(B.T,X,B)
dX_symm=0.5*(dX+dX.T)
S=solve_discrete_lyapunov(A_tilde,dX_symm)
# K_inner is guaranteed to be symmetric, because X and R are symmetric
A_bar=2*matrix_dot(X,A_tilde,S)
K_inner_inv_BT=solve(K_inner,B.T,assume_a="sym")
B_bar=-2*matrix_dot(X,A_tilde,S,K.T)
K=matrix_dot(K_inner_inv_BT,X,A)
Q_bar=S
R_bar=matrix_dot(K,S,K.T)
A_tilde=A-B.dot(K)
return[A_bar,B_bar,Q_bar,R_bar]
dX_symm=0.5*(dX+dX.T)
S=solve_discrete_lyapunov(A_tilde,dX_symm)
A_bar=2*matrix_dot(X,A_tilde,S)
classSolveDiscreteARE(OpFromGraph):
B_bar=-2*matrix_dot(X,A_tilde,S,K.T)
"""
Q_bar=S
Wrapper Op for solving the discrete Algebraic Riccati equation
R_bar=matrix_dot(K,S,K.T)
:math:`A^TXA - X - (A^TXB)(R + B^TXB)^{-1}(B^TXA) + Q = 0` for :math:`X`.
"""
return[A_bar,B_bar,Q_bar,R_bar]
gufunc_signature="(m,m),(m,n),(m,m),(n,n)->(m,m)"
defsolve_discrete_are(
defsolve_discrete_are(
...
@@ -322,7 +304,6 @@ def solve_discrete_are(
...
@@ -322,7 +304,6 @@ def solve_discrete_are(
B:TensorLike,
B:TensorLike,
Q:TensorLike,
Q:TensorLike,
R:TensorLike,
R:TensorLike,
enforce_Q_symmetric:bool=False,
)->TensorVariable:
)->TensorVariable:
"""
"""
Solve the discrete Algebraic Riccati equation :math:`A^TXA - X - (A^TXB)(R + B^TXB)^{-1}(B^TXA) + Q = 0`.
Solve the discrete Algebraic Riccati equation :math:`A^TXA - X - (A^TXB)(R + B^TXB)^{-1}(B^TXA) + Q = 0`.
...
@@ -344,19 +325,129 @@ def solve_discrete_are(
...
@@ -344,19 +325,129 @@ def solve_discrete_are(
Symmetric square matrix of shape M x M
Symmetric square matrix of shape M x M
R: TensorLike
R: TensorLike
Square matrix of shape N x N
Square matrix of shape N x N
enforce_Q_symmetric: bool
If True, the provided Q matrix is transformed to 0.5 * (Q + Q.T) to ensure symmetry
Returns
Returns
-------
-------
X: TensorVariable
X: TensorVariable
Square matrix of shape M x M, representing the solution to the DARE
Square matrix of shape M x M, representing the solution to the DARE
Notes
-----
This function is copied from the scipy implementation, found here: https://github.com/scipy/scipy/blob/892baa06054c31bed734423c0f53eaed52b1914b/scipy/linalg/_solvers.py#L687
Notes are also adapted from the scipy documentation.
The equation is solved by forming the extended symplectic matrix pencil as described in [1]_,
:math: `H - \\lambda J`, given by the block matrices:
.. math::
H = \begin{bmatrix} A & 0 & B \\\\
-Q & I & 0 \\\\
0 & 0 & R \\end{bmatrix}
, \\quad
J = \begin{bmatrix} I & 0 & 0 \\\\
0 & A^H & 0 \\\\
0 & -B^H & 0 \\end{bmatrix}
The stable invariant subspace of the pencil is then computed via the QZ decomposition. Failure conditions are
linked to the symmetry of the solution matrix :math:`U_2 U_1^{-1}`, as described in [1]_ and [2]_. When the
solution is not symmetric, NaNs are returned.
[3]_ describes a balancing procedure for Hamiltonian matrices that can improve numerical stability. This procedure
is not yet implemented in this function.
References
----------
.. [1] P. van Dooren , "A Generalized Eigenvalue Approach For Solving
Riccati Equations.", SIAM Journal on Scientific and Statistical
Computing, Vol.2(2), :doi:`10.1137/0902010`
.. [2] A.J. Laub, "A Schur Method for Solving Algebraic Riccati
Equations.", Massachusetts Institute of Technology. Laboratory for
Information and Decision Systems. LIDS-R ; 859. Available online :
http://hdl.handle.net/1721.1/1301
.. [3] P. Benner, "Symplectic Balancing of Hamiltonian Matrices", 2001,
SIAM J. Sci. Comput., 2001, Vol.22(5), :doi:`10.1137/S1064827500367993`