Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/linalg/_solvers.py : 10%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Matrix equation solver routines"""
2# Author: Jeffrey Armstrong <jeff@approximatrix.com>
3# February 24, 2012
5# Modified: Chad Fulton <ChadFulton@gmail.com>
6# June 19, 2014
8# Modified: Ilhan Polat <ilhanpolat@gmail.com>
9# September 13, 2016
11import warnings
12import numpy as np
13from numpy.linalg import inv, LinAlgError, norm, cond, svd
15from .basic import solve, solve_triangular, matrix_balance
16from .lapack import get_lapack_funcs
17from .decomp_schur import schur
18from .decomp_lu import lu
19from .decomp_qr import qr
20from ._decomp_qz import ordqz
21from .decomp import _asarray_validated
22from .special_matrices import kron, block_diag
24__all__ = ['solve_sylvester',
25 'solve_continuous_lyapunov', 'solve_discrete_lyapunov',
26 'solve_lyapunov',
27 'solve_continuous_are', 'solve_discrete_are']
30def solve_sylvester(a, b, q):
31 """
32 Computes a solution (X) to the Sylvester equation :math:`AX + XB = Q`.
34 Parameters
35 ----------
36 a : (M, M) array_like
37 Leading matrix of the Sylvester equation
38 b : (N, N) array_like
39 Trailing matrix of the Sylvester equation
40 q : (M, N) array_like
41 Right-hand side
43 Returns
44 -------
45 x : (M, N) ndarray
46 The solution to the Sylvester equation.
48 Raises
49 ------
50 LinAlgError
51 If solution was not found
53 Notes
54 -----
55 Computes a solution to the Sylvester matrix equation via the Bartels-
56 Stewart algorithm. The A and B matrices first undergo Schur
57 decompositions. The resulting matrices are used to construct an
58 alternative Sylvester equation (``RY + YS^T = F``) where the R and S
59 matrices are in quasi-triangular form (or, when R, S or F are complex,
60 triangular form). The simplified equation is then solved using
61 ``*TRSYL`` from LAPACK directly.
63 .. versionadded:: 0.11.0
65 Examples
66 --------
67 Given `a`, `b`, and `q` solve for `x`:
69 >>> from scipy import linalg
70 >>> a = np.array([[-3, -2, 0], [-1, -1, 3], [3, -5, -1]])
71 >>> b = np.array([[1]])
72 >>> q = np.array([[1],[2],[3]])
73 >>> x = linalg.solve_sylvester(a, b, q)
74 >>> x
75 array([[ 0.0625],
76 [-0.5625],
77 [ 0.6875]])
78 >>> np.allclose(a.dot(x) + x.dot(b), q)
79 True
81 """
83 # Compute the Schur decomposition form of a
84 r, u = schur(a, output='real')
86 # Compute the Schur decomposition of b
87 s, v = schur(b.conj().transpose(), output='real')
89 # Construct f = u'*q*v
90 f = np.dot(np.dot(u.conj().transpose(), q), v)
92 # Call the Sylvester equation solver
93 trsyl, = get_lapack_funcs(('trsyl',), (r, s, f))
94 if trsyl is None:
95 raise RuntimeError('LAPACK implementation does not contain a proper '
96 'Sylvester equation solver (TRSYL)')
97 y, scale, info = trsyl(r, s, f, tranb='C')
99 y = scale*y
101 if info < 0:
102 raise LinAlgError("Illegal value encountered in "
103 "the %d term" % (-info,))
105 return np.dot(np.dot(u, y), v.conj().transpose())
108def solve_continuous_lyapunov(a, q):
109 """
110 Solves the continuous Lyapunov equation :math:`AX + XA^H = Q`.
112 Uses the Bartels-Stewart algorithm to find :math:`X`.
114 Parameters
115 ----------
116 a : array_like
117 A square matrix
119 q : array_like
120 Right-hand side square matrix
122 Returns
123 -------
124 x : ndarray
125 Solution to the continuous Lyapunov equation
127 See Also
128 --------
129 solve_discrete_lyapunov : computes the solution to the discrete-time
130 Lyapunov equation
131 solve_sylvester : computes the solution to the Sylvester equation
133 Notes
134 -----
135 The continuous Lyapunov equation is a special form of the Sylvester
136 equation, hence this solver relies on LAPACK routine ?TRSYL.
138 .. versionadded:: 0.11.0
140 Examples
141 --------
142 Given `a` and `q` solve for `x`:
144 >>> from scipy import linalg
145 >>> a = np.array([[-3, -2, 0], [-1, -1, 0], [0, -5, -1]])
146 >>> b = np.array([2, 4, -1])
147 >>> q = np.eye(3)
148 >>> x = linalg.solve_continuous_lyapunov(a, q)
149 >>> x
150 array([[ -0.75 , 0.875 , -3.75 ],
151 [ 0.875 , -1.375 , 5.3125],
152 [ -3.75 , 5.3125, -27.0625]])
153 >>> np.allclose(a.dot(x) + x.dot(a.T), q)
154 True
155 """
157 a = np.atleast_2d(_asarray_validated(a, check_finite=True))
158 q = np.atleast_2d(_asarray_validated(q, check_finite=True))
160 r_or_c = float
162 for ind, _ in enumerate((a, q)):
163 if np.iscomplexobj(_):
164 r_or_c = complex
166 if not np.equal(*_.shape):
167 raise ValueError("Matrix {} should be square.".format("aq"[ind]))
169 # Shape consistency check
170 if a.shape != q.shape:
171 raise ValueError("Matrix a and q should have the same shape.")
173 # Compute the Schur decomposition form of a
174 r, u = schur(a, output='real')
176 # Construct f = u'*q*u
177 f = u.conj().T.dot(q.dot(u))
179 # Call the Sylvester equation solver
180 trsyl = get_lapack_funcs('trsyl', (r, f))
182 dtype_string = 'T' if r_or_c == float else 'C'
183 y, scale, info = trsyl(r, r, f, tranb=dtype_string)
185 if info < 0:
186 raise ValueError('?TRSYL exited with the internal error '
187 '"illegal value in argument number {}.". See '
188 'LAPACK documentation for the ?TRSYL error codes.'
189 ''.format(-info))
190 elif info == 1:
191 warnings.warn('Input "a" has an eigenvalue pair whose sum is '
192 'very close to or exactly zero. The solution is '
193 'obtained via perturbing the coefficients.',
194 RuntimeWarning)
195 y *= scale
197 return u.dot(y).dot(u.conj().T)
200# For backwards compatibility, keep the old name
201solve_lyapunov = solve_continuous_lyapunov
204def _solve_discrete_lyapunov_direct(a, q):
205 """
206 Solves the discrete Lyapunov equation directly.
208 This function is called by the `solve_discrete_lyapunov` function with
209 `method=direct`. It is not supposed to be called directly.
210 """
212 lhs = kron(a, a.conj())
213 lhs = np.eye(lhs.shape[0]) - lhs
214 x = solve(lhs, q.flatten())
216 return np.reshape(x, q.shape)
219def _solve_discrete_lyapunov_bilinear(a, q):
220 """
221 Solves the discrete Lyapunov equation using a bilinear transformation.
223 This function is called by the `solve_discrete_lyapunov` function with
224 `method=bilinear`. It is not supposed to be called directly.
225 """
226 eye = np.eye(a.shape[0])
227 aH = a.conj().transpose()
228 aHI_inv = inv(aH + eye)
229 b = np.dot(aH - eye, aHI_inv)
230 c = 2*np.dot(np.dot(inv(a + eye), q), aHI_inv)
231 return solve_lyapunov(b.conj().transpose(), -c)
234def solve_discrete_lyapunov(a, q, method=None):
235 """
236 Solves the discrete Lyapunov equation :math:`AXA^H - X + Q = 0`.
238 Parameters
239 ----------
240 a, q : (M, M) array_like
241 Square matrices corresponding to A and Q in the equation
242 above respectively. Must have the same shape.
244 method : {'direct', 'bilinear'}, optional
245 Type of solver.
247 If not given, chosen to be ``direct`` if ``M`` is less than 10 and
248 ``bilinear`` otherwise.
250 Returns
251 -------
252 x : ndarray
253 Solution to the discrete Lyapunov equation
255 See Also
256 --------
257 solve_continuous_lyapunov : computes the solution to the continuous-time
258 Lyapunov equation
260 Notes
261 -----
262 This section describes the available solvers that can be selected by the
263 'method' parameter. The default method is *direct* if ``M`` is less than 10
264 and ``bilinear`` otherwise.
266 Method *direct* uses a direct analytical solution to the discrete Lyapunov
267 equation. The algorithm is given in, for example, [1]_. However, it requires
268 the linear solution of a system with dimension :math:`M^2` so that
269 performance degrades rapidly for even moderately sized matrices.
271 Method *bilinear* uses a bilinear transformation to convert the discrete
272 Lyapunov equation to a continuous Lyapunov equation :math:`(BX+XB'=-C)`
273 where :math:`B=(A-I)(A+I)^{-1}` and
274 :math:`C=2(A' + I)^{-1} Q (A + I)^{-1}`. The continuous equation can be
275 efficiently solved since it is a special case of a Sylvester equation.
276 The transformation algorithm is from Popov (1964) as described in [2]_.
278 .. versionadded:: 0.11.0
280 References
281 ----------
282 .. [1] Hamilton, James D. Time Series Analysis, Princeton: Princeton
283 University Press, 1994. 265. Print.
284 http://doc1.lbfl.li/aca/FLMF037168.pdf
285 .. [2] Gajic, Z., and M.T.J. Qureshi. 2008.
286 Lyapunov Matrix Equation in System Stability and Control.
287 Dover Books on Engineering Series. Dover Publications.
289 Examples
290 --------
291 Given `a` and `q` solve for `x`:
293 >>> from scipy import linalg
294 >>> a = np.array([[0.2, 0.5],[0.7, -0.9]])
295 >>> q = np.eye(2)
296 >>> x = linalg.solve_discrete_lyapunov(a, q)
297 >>> x
298 array([[ 0.70872893, 1.43518822],
299 [ 1.43518822, -2.4266315 ]])
300 >>> np.allclose(a.dot(x).dot(a.T)-x, -q)
301 True
303 """
304 a = np.asarray(a)
305 q = np.asarray(q)
306 if method is None:
307 # Select automatically based on size of matrices
308 if a.shape[0] >= 10:
309 method = 'bilinear'
310 else:
311 method = 'direct'
313 meth = method.lower()
315 if meth == 'direct':
316 x = _solve_discrete_lyapunov_direct(a, q)
317 elif meth == 'bilinear':
318 x = _solve_discrete_lyapunov_bilinear(a, q)
319 else:
320 raise ValueError('Unknown solver %s' % method)
322 return x
325def solve_continuous_are(a, b, q, r, e=None, s=None, balanced=True):
326 r"""
327 Solves the continuous-time algebraic Riccati equation (CARE).
329 The CARE is defined as
331 .. math::
333 X A + A^H X - X B R^{-1} B^H X + Q = 0
335 The limitations for a solution to exist are :
337 * All eigenvalues of :math:`A` on the right half plane, should be
338 controllable.
340 * The associated hamiltonian pencil (See Notes), should have
341 eigenvalues sufficiently away from the imaginary axis.
343 Moreover, if ``e`` or ``s`` is not precisely ``None``, then the
344 generalized version of CARE
346 .. math::
348 E^HXA + A^HXE - (E^HXB + S) R^{-1} (B^HXE + S^H) + Q = 0
350 is solved. When omitted, ``e`` is assumed to be the identity and ``s``
351 is assumed to be the zero matrix with sizes compatible with ``a`` and
352 ``b``, respectively.
354 Parameters
355 ----------
356 a : (M, M) array_like
357 Square matrix
358 b : (M, N) array_like
359 Input
360 q : (M, M) array_like
361 Input
362 r : (N, N) array_like
363 Nonsingular square matrix
364 e : (M, M) array_like, optional
365 Nonsingular square matrix
366 s : (M, N) array_like, optional
367 Input
368 balanced : bool, optional
369 The boolean that indicates whether a balancing step is performed
370 on the data. The default is set to True.
372 Returns
373 -------
374 x : (M, M) ndarray
375 Solution to the continuous-time algebraic Riccati equation.
377 Raises
378 ------
379 LinAlgError
380 For cases where the stable subspace of the pencil could not be
381 isolated. See Notes section and the references for details.
383 See Also
384 --------
385 solve_discrete_are : Solves the discrete-time algebraic Riccati equation
387 Notes
388 -----
389 The equation is solved by forming the extended hamiltonian matrix pencil,
390 as described in [1]_, :math:`H - \lambda J` given by the block matrices ::
392 [ A 0 B ] [ E 0 0 ]
393 [-Q -A^H -S ] - \lambda * [ 0 E^H 0 ]
394 [ S^H B^H R ] [ 0 0 0 ]
396 and using a QZ decomposition method.
398 In this algorithm, the fail conditions are linked to the symmetry
399 of the product :math:`U_2 U_1^{-1}` and condition number of
400 :math:`U_1`. Here, :math:`U` is the 2m-by-m matrix that holds the
401 eigenvectors spanning the stable subspace with 2-m rows and partitioned
402 into two m-row matrices. See [1]_ and [2]_ for more details.
404 In order to improve the QZ decomposition accuracy, the pencil goes
405 through a balancing step where the sum of absolute values of
406 :math:`H` and :math:`J` entries (after removing the diagonal entries of
407 the sum) is balanced following the recipe given in [3]_.
409 .. versionadded:: 0.11.0
411 References
412 ----------
413 .. [1] P. van Dooren , "A Generalized Eigenvalue Approach For Solving
414 Riccati Equations.", SIAM Journal on Scientific and Statistical
415 Computing, Vol.2(2), DOI: 10.1137/0902010
417 .. [2] A.J. Laub, "A Schur Method for Solving Algebraic Riccati
418 Equations.", Massachusetts Institute of Technology. Laboratory for
419 Information and Decision Systems. LIDS-R ; 859. Available online :
420 http://hdl.handle.net/1721.1/1301
422 .. [3] P. Benner, "Symplectic Balancing of Hamiltonian Matrices", 2001,
423 SIAM J. Sci. Comput., 2001, Vol.22(5), DOI: 10.1137/S1064827500367993
425 Examples
426 --------
427 Given `a`, `b`, `q`, and `r` solve for `x`:
429 >>> from scipy import linalg
430 >>> a = np.array([[4, 3], [-4.5, -3.5]])
431 >>> b = np.array([[1], [-1]])
432 >>> q = np.array([[9, 6], [6, 4.]])
433 >>> r = 1
434 >>> x = linalg.solve_continuous_are(a, b, q, r)
435 >>> x
436 array([[ 21.72792206, 14.48528137],
437 [ 14.48528137, 9.65685425]])
438 >>> np.allclose(a.T.dot(x) + x.dot(a)-x.dot(b).dot(b.T).dot(x), -q)
439 True
441 """
443 # Validate input arguments
444 a, b, q, r, e, s, m, n, r_or_c, gen_are = _are_validate_args(
445 a, b, q, r, e, s, 'care')
447 H = np.empty((2*m+n, 2*m+n), dtype=r_or_c)
448 H[:m, :m] = a
449 H[:m, m:2*m] = 0.
450 H[:m, 2*m:] = b
451 H[m:2*m, :m] = -q
452 H[m:2*m, m:2*m] = -a.conj().T
453 H[m:2*m, 2*m:] = 0. if s is None else -s
454 H[2*m:, :m] = 0. if s is None else s.conj().T
455 H[2*m:, m:2*m] = b.conj().T
456 H[2*m:, 2*m:] = r
458 if gen_are and e is not None:
459 J = block_diag(e, e.conj().T, np.zeros_like(r, dtype=r_or_c))
460 else:
461 J = block_diag(np.eye(2*m), np.zeros_like(r, dtype=r_or_c))
463 if balanced:
464 # xGEBAL does not remove the diagonals before scaling. Also
465 # to avoid destroying the Symplectic structure, we follow Ref.3
466 M = np.abs(H) + np.abs(J)
467 M[np.diag_indices_from(M)] = 0.
468 _, (sca, _) = matrix_balance(M, separate=1, permute=0)
469 # do we need to bother?
470 if not np.allclose(sca, np.ones_like(sca)):
471 # Now impose diag(D,inv(D)) from Benner where D is
472 # square root of s_i/s_(n+i) for i=0,....
473 sca = np.log2(sca)
474 # NOTE: Py3 uses "Bankers Rounding: round to the nearest even" !!
475 s = np.round((sca[m:2*m] - sca[:m])/2)
476 sca = 2 ** np.r_[s, -s, sca[2*m:]]
477 # Elementwise multiplication via broadcasting.
478 elwisescale = sca[:, None] * np.reciprocal(sca)
479 H *= elwisescale
480 J *= elwisescale
482 # Deflate the pencil to 2m x 2m ala Ref.1, eq.(55)
483 q, r = qr(H[:, -n:])
484 H = q[:, n:].conj().T.dot(H[:, :2*m])
485 J = q[:2*m, n:].conj().T.dot(J[:2*m, :2*m])
487 # Decide on which output type is needed for QZ
488 out_str = 'real' if r_or_c == float else 'complex'
490 _, _, _, _, _, u = ordqz(H, J, sort='lhp', overwrite_a=True,
491 overwrite_b=True, check_finite=False,
492 output=out_str)
494 # Get the relevant parts of the stable subspace basis
495 if e is not None:
496 u, _ = qr(np.vstack((e.dot(u[:m, :m]), u[m:, :m])))
497 u00 = u[:m, :m]
498 u10 = u[m:, :m]
500 # Solve via back-substituion after checking the condition of u00
501 up, ul, uu = lu(u00)
502 if 1/cond(uu) < np.spacing(1.):
503 raise LinAlgError('Failed to find a finite solution.')
505 # Exploit the triangular structure
506 x = solve_triangular(ul.conj().T,
507 solve_triangular(uu.conj().T,
508 u10.conj().T,
509 lower=True),
510 unit_diagonal=True,
511 ).conj().T.dot(up.conj().T)
512 if balanced:
513 x *= sca[:m, None] * sca[:m]
515 # Check the deviation from symmetry for lack of success
516 # See proof of Thm.5 item 3 in [2]
517 u_sym = u00.conj().T.dot(u10)
518 n_u_sym = norm(u_sym, 1)
519 u_sym = u_sym - u_sym.conj().T
520 sym_threshold = np.max([np.spacing(1000.), 0.1*n_u_sym])
522 if norm(u_sym, 1) > sym_threshold:
523 raise LinAlgError('The associated Hamiltonian pencil has eigenvalues '
524 'too close to the imaginary axis')
526 return (x + x.conj().T)/2
529def solve_discrete_are(a, b, q, r, e=None, s=None, balanced=True):
530 r"""
531 Solves the discrete-time algebraic Riccati equation (DARE).
533 The DARE is defined as
535 .. math::
537 A^HXA - X - (A^HXB) (R + B^HXB)^{-1} (B^HXA) + Q = 0
539 The limitations for a solution to exist are :
541 * All eigenvalues of :math:`A` outside the unit disc, should be
542 controllable.
544 * The associated symplectic pencil (See Notes), should have
545 eigenvalues sufficiently away from the unit circle.
547 Moreover, if ``e`` and ``s`` are not both precisely ``None``, then the
548 generalized version of DARE
550 .. math::
552 A^HXA - E^HXE - (A^HXB+S) (R+B^HXB)^{-1} (B^HXA+S^H) + Q = 0
554 is solved. When omitted, ``e`` is assumed to be the identity and ``s``
555 is assumed to be the zero matrix.
557 Parameters
558 ----------
559 a : (M, M) array_like
560 Square matrix
561 b : (M, N) array_like
562 Input
563 q : (M, M) array_like
564 Input
565 r : (N, N) array_like
566 Square matrix
567 e : (M, M) array_like, optional
568 Nonsingular square matrix
569 s : (M, N) array_like, optional
570 Input
571 balanced : bool
572 The boolean that indicates whether a balancing step is performed
573 on the data. The default is set to True.
575 Returns
576 -------
577 x : (M, M) ndarray
578 Solution to the discrete algebraic Riccati equation.
580 Raises
581 ------
582 LinAlgError
583 For cases where the stable subspace of the pencil could not be
584 isolated. See Notes section and the references for details.
586 See Also
587 --------
588 solve_continuous_are : Solves the continuous algebraic Riccati equation
590 Notes
591 -----
592 The equation is solved by forming the extended symplectic matrix pencil,
593 as described in [1]_, :math:`H - \lambda J` given by the block matrices ::
595 [ A 0 B ] [ E 0 B ]
596 [ -Q E^H -S ] - \lambda * [ 0 A^H 0 ]
597 [ S^H 0 R ] [ 0 -B^H 0 ]
599 and using a QZ decomposition method.
601 In this algorithm, the fail conditions are linked to the symmetry
602 of the product :math:`U_2 U_1^{-1}` and condition number of
603 :math:`U_1`. Here, :math:`U` is the 2m-by-m matrix that holds the
604 eigenvectors spanning the stable subspace with 2-m rows and partitioned
605 into two m-row matrices. See [1]_ and [2]_ for more details.
607 In order to improve the QZ decomposition accuracy, the pencil goes
608 through a balancing step where the sum of absolute values of
609 :math:`H` and :math:`J` rows/cols (after removing the diagonal entries)
610 is balanced following the recipe given in [3]_. If the data has small
611 numerical noise, balancing may amplify their effects and some clean up
612 is required.
614 .. versionadded:: 0.11.0
616 References
617 ----------
618 .. [1] P. van Dooren , "A Generalized Eigenvalue Approach For Solving
619 Riccati Equations.", SIAM Journal on Scientific and Statistical
620 Computing, Vol.2(2), DOI: 10.1137/0902010
622 .. [2] A.J. Laub, "A Schur Method for Solving Algebraic Riccati
623 Equations.", Massachusetts Institute of Technology. Laboratory for
624 Information and Decision Systems. LIDS-R ; 859. Available online :
625 http://hdl.handle.net/1721.1/1301
627 .. [3] P. Benner, "Symplectic Balancing of Hamiltonian Matrices", 2001,
628 SIAM J. Sci. Comput., 2001, Vol.22(5), DOI: 10.1137/S1064827500367993
630 Examples
631 --------
632 Given `a`, `b`, `q`, and `r` solve for `x`:
634 >>> from scipy import linalg as la
635 >>> a = np.array([[0, 1], [0, -1]])
636 >>> b = np.array([[1, 0], [2, 1]])
637 >>> q = np.array([[-4, -4], [-4, 7]])
638 >>> r = np.array([[9, 3], [3, 1]])
639 >>> x = la.solve_discrete_are(a, b, q, r)
640 >>> x
641 array([[-4., -4.],
642 [-4., 7.]])
643 >>> R = la.solve(r + b.T.dot(x).dot(b), b.T.dot(x).dot(a))
644 >>> np.allclose(a.T.dot(x).dot(a) - x - a.T.dot(x).dot(b).dot(R), -q)
645 True
647 """
649 # Validate input arguments
650 a, b, q, r, e, s, m, n, r_or_c, gen_are = _are_validate_args(
651 a, b, q, r, e, s, 'dare')
653 # Form the matrix pencil
654 H = np.zeros((2*m+n, 2*m+n), dtype=r_or_c)
655 H[:m, :m] = a
656 H[:m, 2*m:] = b
657 H[m:2*m, :m] = -q
658 H[m:2*m, m:2*m] = np.eye(m) if e is None else e.conj().T
659 H[m:2*m, 2*m:] = 0. if s is None else -s
660 H[2*m:, :m] = 0. if s is None else s.conj().T
661 H[2*m:, 2*m:] = r
663 J = np.zeros_like(H, dtype=r_or_c)
664 J[:m, :m] = np.eye(m) if e is None else e
665 J[m:2*m, m:2*m] = a.conj().T
666 J[2*m:, m:2*m] = -b.conj().T
668 if balanced:
669 # xGEBAL does not remove the diagonals before scaling. Also
670 # to avoid destroying the Symplectic structure, we follow Ref.3
671 M = np.abs(H) + np.abs(J)
672 M[np.diag_indices_from(M)] = 0.
673 _, (sca, _) = matrix_balance(M, separate=1, permute=0)
674 # do we need to bother?
675 if not np.allclose(sca, np.ones_like(sca)):
676 # Now impose diag(D,inv(D)) from Benner where D is
677 # square root of s_i/s_(n+i) for i=0,....
678 sca = np.log2(sca)
679 # NOTE: Py3 uses "Bankers Rounding: round to the nearest even" !!
680 s = np.round((sca[m:2*m] - sca[:m])/2)
681 sca = 2 ** np.r_[s, -s, sca[2*m:]]
682 # Elementwise multiplication via broadcasting.
683 elwisescale = sca[:, None] * np.reciprocal(sca)
684 H *= elwisescale
685 J *= elwisescale
687 # Deflate the pencil by the R column ala Ref.1
688 q_of_qr, _ = qr(H[:, -n:])
689 H = q_of_qr[:, n:].conj().T.dot(H[:, :2*m])
690 J = q_of_qr[:, n:].conj().T.dot(J[:, :2*m])
692 # Decide on which output type is needed for QZ
693 out_str = 'real' if r_or_c == float else 'complex'
695 _, _, _, _, _, u = ordqz(H, J, sort='iuc',
696 overwrite_a=True,
697 overwrite_b=True,
698 check_finite=False,
699 output=out_str)
701 # Get the relevant parts of the stable subspace basis
702 if e is not None:
703 u, _ = qr(np.vstack((e.dot(u[:m, :m]), u[m:, :m])))
704 u00 = u[:m, :m]
705 u10 = u[m:, :m]
707 # Solve via back-substituion after checking the condition of u00
708 up, ul, uu = lu(u00)
710 if 1/cond(uu) < np.spacing(1.):
711 raise LinAlgError('Failed to find a finite solution.')
713 # Exploit the triangular structure
714 x = solve_triangular(ul.conj().T,
715 solve_triangular(uu.conj().T,
716 u10.conj().T,
717 lower=True),
718 unit_diagonal=True,
719 ).conj().T.dot(up.conj().T)
720 if balanced:
721 x *= sca[:m, None] * sca[:m]
723 # Check the deviation from symmetry for lack of success
724 # See proof of Thm.5 item 3 in [2]
725 u_sym = u00.conj().T.dot(u10)
726 n_u_sym = norm(u_sym, 1)
727 u_sym = u_sym - u_sym.conj().T
728 sym_threshold = np.max([np.spacing(1000.), 0.1*n_u_sym])
730 if norm(u_sym, 1) > sym_threshold:
731 raise LinAlgError('The associated symplectic pencil has eigenvalues'
732 'too close to the unit circle')
734 return (x + x.conj().T)/2
737def _are_validate_args(a, b, q, r, e, s, eq_type='care'):
738 """
739 A helper function to validate the arguments supplied to the
740 Riccati equation solvers. Any discrepancy found in the input
741 matrices leads to a ``ValueError`` exception.
743 Essentially, it performs:
745 - a check whether the input is free of NaN and Infs
746 - a pass for the data through ``numpy.atleast_2d()``
747 - squareness check of the relevant arrays
748 - shape consistency check of the arrays
749 - singularity check of the relevant arrays
750 - symmetricity check of the relevant matrices
751 - a check whether the regular or the generalized version is asked.
753 This function is used by ``solve_continuous_are`` and
754 ``solve_discrete_are``.
756 Parameters
757 ----------
758 a, b, q, r, e, s : array_like
759 Input data
760 eq_type : str
761 Accepted arguments are 'care' and 'dare'.
763 Returns
764 -------
765 a, b, q, r, e, s : ndarray
766 Regularized input data
767 m, n : int
768 shape of the problem
769 r_or_c : type
770 Data type of the problem, returns float or complex
771 gen_or_not : bool
772 Type of the equation, True for generalized and False for regular ARE.
774 """
776 if not eq_type.lower() in ('dare', 'care'):
777 raise ValueError("Equation type unknown. "
778 "Only 'care' and 'dare' is understood")
780 a = np.atleast_2d(_asarray_validated(a, check_finite=True))
781 b = np.atleast_2d(_asarray_validated(b, check_finite=True))
782 q = np.atleast_2d(_asarray_validated(q, check_finite=True))
783 r = np.atleast_2d(_asarray_validated(r, check_finite=True))
785 # Get the correct data types otherwise NumPy complains
786 # about pushing complex numbers into real arrays.
787 r_or_c = complex if np.iscomplexobj(b) else float
789 for ind, mat in enumerate((a, q, r)):
790 if np.iscomplexobj(mat):
791 r_or_c = complex
793 if not np.equal(*mat.shape):
794 raise ValueError("Matrix {} should be square.".format("aqr"[ind]))
796 # Shape consistency checks
797 m, n = b.shape
798 if m != a.shape[0]:
799 raise ValueError("Matrix a and b should have the same number of rows.")
800 if m != q.shape[0]:
801 raise ValueError("Matrix a and q should have the same shape.")
802 if n != r.shape[0]:
803 raise ValueError("Matrix b and r should have the same number of cols.")
805 # Check if the data matrices q, r are (sufficiently) hermitian
806 for ind, mat in enumerate((q, r)):
807 if norm(mat - mat.conj().T, 1) > np.spacing(norm(mat, 1))*100:
808 raise ValueError("Matrix {} should be symmetric/hermitian."
809 "".format("qr"[ind]))
811 # Continuous time ARE should have a nonsingular r matrix.
812 if eq_type == 'care':
813 min_sv = svd(r, compute_uv=False)[-1]
814 if min_sv == 0. or min_sv < np.spacing(1.)*norm(r, 1):
815 raise ValueError('Matrix r is numerically singular.')
817 # Check if the generalized case is required with omitted arguments
818 # perform late shape checking etc.
819 generalized_case = e is not None or s is not None
821 if generalized_case:
822 if e is not None:
823 e = np.atleast_2d(_asarray_validated(e, check_finite=True))
824 if not np.equal(*e.shape):
825 raise ValueError("Matrix e should be square.")
826 if m != e.shape[0]:
827 raise ValueError("Matrix a and e should have the same shape.")
828 # numpy.linalg.cond doesn't check for exact zeros and
829 # emits a runtime warning. Hence the following manual check.
830 min_sv = svd(e, compute_uv=False)[-1]
831 if min_sv == 0. or min_sv < np.spacing(1.) * norm(e, 1):
832 raise ValueError('Matrix e is numerically singular.')
833 if np.iscomplexobj(e):
834 r_or_c = complex
835 if s is not None:
836 s = np.atleast_2d(_asarray_validated(s, check_finite=True))
837 if s.shape != b.shape:
838 raise ValueError("Matrix b and s should have the same shape.")
839 if np.iscomplexobj(s):
840 r_or_c = complex
842 return a, b, q, r, e, s, m, n, r_or_c, generalized_case