_decomp_qz.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. import warnings
  2. import numpy as np
  3. from numpy import asarray_chkfinite
  4. from ._misc import LinAlgError, _datacopied, LinAlgWarning
  5. from .lapack import get_lapack_funcs
  6. __all__ = ['qz', 'ordqz']
  7. _double_precision = ['i', 'l', 'd']
  8. def _select_function(sort):
  9. if callable(sort):
  10. # assume the user knows what they're doing
  11. sfunction = sort
  12. elif sort == 'lhp':
  13. sfunction = _lhp
  14. elif sort == 'rhp':
  15. sfunction = _rhp
  16. elif sort == 'iuc':
  17. sfunction = _iuc
  18. elif sort == 'ouc':
  19. sfunction = _ouc
  20. else:
  21. raise ValueError("sort parameter must be None, a callable, or "
  22. "one of ('lhp','rhp','iuc','ouc')")
  23. return sfunction
  24. def _lhp(x, y):
  25. out = np.empty_like(x, dtype=bool)
  26. nonzero = (y != 0)
  27. # handles (x, y) = (0, 0) too
  28. out[~nonzero] = False
  29. out[nonzero] = (np.real(x[nonzero]/y[nonzero]) < 0.0)
  30. return out
  31. def _rhp(x, y):
  32. out = np.empty_like(x, dtype=bool)
  33. nonzero = (y != 0)
  34. # handles (x, y) = (0, 0) too
  35. out[~nonzero] = False
  36. out[nonzero] = (np.real(x[nonzero]/y[nonzero]) > 0.0)
  37. return out
  38. def _iuc(x, y):
  39. out = np.empty_like(x, dtype=bool)
  40. nonzero = (y != 0)
  41. # handles (x, y) = (0, 0) too
  42. out[~nonzero] = False
  43. out[nonzero] = (abs(x[nonzero]/y[nonzero]) < 1.0)
  44. return out
  45. def _ouc(x, y):
  46. out = np.empty_like(x, dtype=bool)
  47. xzero = (x == 0)
  48. yzero = (y == 0)
  49. out[xzero & yzero] = False
  50. out[~xzero & yzero] = True
  51. out[~yzero] = (abs(x[~yzero]/y[~yzero]) > 1.0)
  52. return out
  53. def _qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False,
  54. overwrite_b=False, check_finite=True):
  55. if sort is not None:
  56. # Disabled due to segfaults on win32, see ticket 1717.
  57. raise ValueError("The 'sort' input of qz() has to be None and will be "
  58. "removed in a future release. Use ordqz instead.")
  59. if output not in ['real', 'complex', 'r', 'c']:
  60. raise ValueError("argument must be 'real', or 'complex'")
  61. if check_finite:
  62. a1 = asarray_chkfinite(A)
  63. b1 = asarray_chkfinite(B)
  64. else:
  65. a1 = np.asarray(A)
  66. b1 = np.asarray(B)
  67. a_m, a_n = a1.shape
  68. b_m, b_n = b1.shape
  69. if not (a_m == a_n == b_m == b_n):
  70. raise ValueError("Array dimensions must be square and agree")
  71. typa = a1.dtype.char
  72. if output in ['complex', 'c'] and typa not in ['F', 'D']:
  73. if typa in _double_precision:
  74. a1 = a1.astype('D')
  75. typa = 'D'
  76. else:
  77. a1 = a1.astype('F')
  78. typa = 'F'
  79. typb = b1.dtype.char
  80. if output in ['complex', 'c'] and typb not in ['F', 'D']:
  81. if typb in _double_precision:
  82. b1 = b1.astype('D')
  83. typb = 'D'
  84. else:
  85. b1 = b1.astype('F')
  86. typb = 'F'
  87. overwrite_a = overwrite_a or (_datacopied(a1, A))
  88. overwrite_b = overwrite_b or (_datacopied(b1, B))
  89. gges, = get_lapack_funcs(('gges',), (a1, b1))
  90. if lwork is None or lwork == -1:
  91. # get optimal work array size
  92. result = gges(lambda x: None, a1, b1, lwork=-1)
  93. lwork = result[-2][0].real.astype(np.int_)
  94. sfunction = lambda x: None
  95. result = gges(sfunction, a1, b1, lwork=lwork, overwrite_a=overwrite_a,
  96. overwrite_b=overwrite_b, sort_t=0)
  97. info = result[-1]
  98. if info < 0:
  99. raise ValueError("Illegal value in argument {} of gges".format(-info))
  100. elif info > 0 and info <= a_n:
  101. warnings.warn("The QZ iteration failed. (a,b) are not in Schur "
  102. "form, but ALPHAR(j), ALPHAI(j), and BETA(j) should be "
  103. "correct for J={},...,N".format(info-1), LinAlgWarning,
  104. stacklevel=3)
  105. elif info == a_n+1:
  106. raise LinAlgError("Something other than QZ iteration failed")
  107. elif info == a_n+2:
  108. raise LinAlgError("After reordering, roundoff changed values of some "
  109. "complex eigenvalues so that leading eigenvalues "
  110. "in the Generalized Schur form no longer satisfy "
  111. "sort=True. This could also be due to scaling.")
  112. elif info == a_n+3:
  113. raise LinAlgError("Reordering failed in <s,d,c,z>tgsen")
  114. return result, gges.typecode
  115. def qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False,
  116. overwrite_b=False, check_finite=True):
  117. """
  118. QZ decomposition for generalized eigenvalues of a pair of matrices.
  119. The QZ, or generalized Schur, decomposition for a pair of n-by-n
  120. matrices (A,B) is::
  121. (A,B) = (Q @ AA @ Z*, Q @ BB @ Z*)
  122. where AA, BB is in generalized Schur form if BB is upper-triangular
  123. with non-negative diagonal and AA is upper-triangular, or for real QZ
  124. decomposition (``output='real'``) block upper triangular with 1x1
  125. and 2x2 blocks. In this case, the 1x1 blocks correspond to real
  126. generalized eigenvalues and 2x2 blocks are 'standardized' by making
  127. the corresponding elements of BB have the form::
  128. [ a 0 ]
  129. [ 0 b ]
  130. and the pair of corresponding 2x2 blocks in AA and BB will have a complex
  131. conjugate pair of generalized eigenvalues. If (``output='complex'``) or
  132. A and B are complex matrices, Z' denotes the conjugate-transpose of Z.
  133. Q and Z are unitary matrices.
  134. Parameters
  135. ----------
  136. A : (N, N) array_like
  137. 2-D array to decompose
  138. B : (N, N) array_like
  139. 2-D array to decompose
  140. output : {'real', 'complex'}, optional
  141. Construct the real or complex QZ decomposition for real matrices.
  142. Default is 'real'.
  143. lwork : int, optional
  144. Work array size. If None or -1, it is automatically computed.
  145. sort : {None, callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional
  146. NOTE: THIS INPUT IS DISABLED FOR NOW. Use ordqz instead.
  147. Specifies whether the upper eigenvalues should be sorted. A callable
  148. may be passed that, given a eigenvalue, returns a boolean denoting
  149. whether the eigenvalue should be sorted to the top-left (True). For
  150. real matrix pairs, the sort function takes three real arguments
  151. (alphar, alphai, beta). The eigenvalue
  152. ``x = (alphar + alphai*1j)/beta``. For complex matrix pairs or
  153. output='complex', the sort function takes two complex arguments
  154. (alpha, beta). The eigenvalue ``x = (alpha/beta)``. Alternatively,
  155. string parameters may be used:
  156. - 'lhp' Left-hand plane (x.real < 0.0)
  157. - 'rhp' Right-hand plane (x.real > 0.0)
  158. - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0)
  159. - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0)
  160. Defaults to None (no sorting).
  161. overwrite_a : bool, optional
  162. Whether to overwrite data in a (may improve performance)
  163. overwrite_b : bool, optional
  164. Whether to overwrite data in b (may improve performance)
  165. check_finite : bool, optional
  166. If true checks the elements of `A` and `B` are finite numbers. If
  167. false does no checking and passes matrix through to
  168. underlying algorithm.
  169. Returns
  170. -------
  171. AA : (N, N) ndarray
  172. Generalized Schur form of A.
  173. BB : (N, N) ndarray
  174. Generalized Schur form of B.
  175. Q : (N, N) ndarray
  176. The left Schur vectors.
  177. Z : (N, N) ndarray
  178. The right Schur vectors.
  179. See Also
  180. --------
  181. ordqz
  182. Notes
  183. -----
  184. Q is transposed versus the equivalent function in Matlab.
  185. .. versionadded:: 0.11.0
  186. Examples
  187. --------
  188. >>> import numpy as np
  189. >>> from scipy.linalg import qz
  190. >>> A = np.array([[1, 2, -1], [5, 5, 5], [2, 4, -8]])
  191. >>> B = np.array([[1, 1, -3], [3, 1, -1], [5, 6, -2]])
  192. Compute the decomposition. The QZ decomposition is not unique, so
  193. depending on the underlying library that is used, there may be
  194. differences in the signs of coefficients in the following output.
  195. >>> AA, BB, Q, Z = qz(A, B)
  196. >>> AA
  197. array([[-1.36949157, -4.05459025, 7.44389431],
  198. [ 0. , 7.65653432, 5.13476017],
  199. [ 0. , -0.65978437, 2.4186015 ]]) # may vary
  200. >>> BB
  201. array([[ 1.71890633, -1.64723705, -0.72696385],
  202. [ 0. , 8.6965692 , -0. ],
  203. [ 0. , 0. , 2.27446233]]) # may vary
  204. >>> Q
  205. array([[-0.37048362, 0.1903278 , 0.90912992],
  206. [-0.90073232, 0.16534124, -0.40167593],
  207. [ 0.22676676, 0.96769706, -0.11017818]]) # may vary
  208. >>> Z
  209. array([[-0.67660785, 0.63528924, -0.37230283],
  210. [ 0.70243299, 0.70853819, -0.06753907],
  211. [ 0.22088393, -0.30721526, -0.92565062]]) # may vary
  212. Verify the QZ decomposition. With real output, we only need the
  213. transpose of ``Z`` in the following expressions.
  214. >>> Q @ AA @ Z.T # Should be A
  215. array([[ 1., 2., -1.],
  216. [ 5., 5., 5.],
  217. [ 2., 4., -8.]])
  218. >>> Q @ BB @ Z.T # Should be B
  219. array([[ 1., 1., -3.],
  220. [ 3., 1., -1.],
  221. [ 5., 6., -2.]])
  222. Repeat the decomposition, but with ``output='complex'``.
  223. >>> AA, BB, Q, Z = qz(A, B, output='complex')
  224. For conciseness in the output, we use ``np.set_printoptions()`` to set
  225. the output precision of NumPy arrays to 3 and display tiny values as 0.
  226. >>> np.set_printoptions(precision=3, suppress=True)
  227. >>> AA
  228. array([[-1.369+0.j , 2.248+4.237j, 4.861-5.022j],
  229. [ 0. +0.j , 7.037+2.922j, 0.794+4.932j],
  230. [ 0. +0.j , 0. +0.j , 2.655-1.103j]]) # may vary
  231. >>> BB
  232. array([[ 1.719+0.j , -1.115+1.j , -0.763-0.646j],
  233. [ 0. +0.j , 7.24 +0.j , -3.144+3.322j],
  234. [ 0. +0.j , 0. +0.j , 2.732+0.j ]]) # may vary
  235. >>> Q
  236. array([[ 0.326+0.175j, -0.273-0.029j, -0.886-0.052j],
  237. [ 0.794+0.426j, -0.093+0.134j, 0.402-0.02j ],
  238. [-0.2 -0.107j, -0.816+0.482j, 0.151-0.167j]]) # may vary
  239. >>> Z
  240. array([[ 0.596+0.32j , -0.31 +0.414j, 0.393-0.347j],
  241. [-0.619-0.332j, -0.479+0.314j, 0.154-0.393j],
  242. [-0.195-0.104j, 0.576+0.27j , 0.715+0.187j]]) # may vary
  243. With complex arrays, we must use ``Z.conj().T`` in the following
  244. expressions to verify the decomposition.
  245. >>> Q @ AA @ Z.conj().T # Should be A
  246. array([[ 1.-0.j, 2.-0.j, -1.-0.j],
  247. [ 5.+0.j, 5.+0.j, 5.-0.j],
  248. [ 2.+0.j, 4.+0.j, -8.+0.j]])
  249. >>> Q @ BB @ Z.conj().T # Should be B
  250. array([[ 1.+0.j, 1.+0.j, -3.+0.j],
  251. [ 3.-0.j, 1.-0.j, -1.+0.j],
  252. [ 5.+0.j, 6.+0.j, -2.+0.j]])
  253. """
  254. # output for real
  255. # AA, BB, sdim, alphar, alphai, beta, vsl, vsr, work, info
  256. # output for complex
  257. # AA, BB, sdim, alpha, beta, vsl, vsr, work, info
  258. result, _ = _qz(A, B, output=output, lwork=lwork, sort=sort,
  259. overwrite_a=overwrite_a, overwrite_b=overwrite_b,
  260. check_finite=check_finite)
  261. return result[0], result[1], result[-4], result[-3]
  262. def ordqz(A, B, sort='lhp', output='real', overwrite_a=False,
  263. overwrite_b=False, check_finite=True):
  264. """QZ decomposition for a pair of matrices with reordering.
  265. Parameters
  266. ----------
  267. A : (N, N) array_like
  268. 2-D array to decompose
  269. B : (N, N) array_like
  270. 2-D array to decompose
  271. sort : {callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional
  272. Specifies whether the upper eigenvalues should be sorted. A
  273. callable may be passed that, given an ordered pair ``(alpha,
  274. beta)`` representing the eigenvalue ``x = (alpha/beta)``,
  275. returns a boolean denoting whether the eigenvalue should be
  276. sorted to the top-left (True). For the real matrix pairs
  277. ``beta`` is real while ``alpha`` can be complex, and for
  278. complex matrix pairs both ``alpha`` and ``beta`` can be
  279. complex. The callable must be able to accept a NumPy
  280. array. Alternatively, string parameters may be used:
  281. - 'lhp' Left-hand plane (x.real < 0.0)
  282. - 'rhp' Right-hand plane (x.real > 0.0)
  283. - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0)
  284. - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0)
  285. With the predefined sorting functions, an infinite eigenvalue
  286. (i.e., ``alpha != 0`` and ``beta = 0``) is considered to lie in
  287. neither the left-hand nor the right-hand plane, but it is
  288. considered to lie outside the unit circle. For the eigenvalue
  289. ``(alpha, beta) = (0, 0)``, the predefined sorting functions
  290. all return `False`.
  291. output : str {'real','complex'}, optional
  292. Construct the real or complex QZ decomposition for real matrices.
  293. Default is 'real'.
  294. overwrite_a : bool, optional
  295. If True, the contents of A are overwritten.
  296. overwrite_b : bool, optional
  297. If True, the contents of B are overwritten.
  298. check_finite : bool, optional
  299. If true checks the elements of `A` and `B` are finite numbers. If
  300. false does no checking and passes matrix through to
  301. underlying algorithm.
  302. Returns
  303. -------
  304. AA : (N, N) ndarray
  305. Generalized Schur form of A.
  306. BB : (N, N) ndarray
  307. Generalized Schur form of B.
  308. alpha : (N,) ndarray
  309. alpha = alphar + alphai * 1j. See notes.
  310. beta : (N,) ndarray
  311. See notes.
  312. Q : (N, N) ndarray
  313. The left Schur vectors.
  314. Z : (N, N) ndarray
  315. The right Schur vectors.
  316. See Also
  317. --------
  318. qz
  319. Notes
  320. -----
  321. On exit, ``(ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N``, will be the
  322. generalized eigenvalues. ``ALPHAR(j) + ALPHAI(j)*i`` and
  323. ``BETA(j),j=1,...,N`` are the diagonals of the complex Schur form (S,T)
  324. that would result if the 2-by-2 diagonal blocks of the real generalized
  325. Schur form of (A,B) were further reduced to triangular form using complex
  326. unitary transformations. If ALPHAI(j) is zero, then the jth eigenvalue is
  327. real; if positive, then the ``j``th and ``(j+1)``st eigenvalues are a
  328. complex conjugate pair, with ``ALPHAI(j+1)`` negative.
  329. .. versionadded:: 0.17.0
  330. Examples
  331. --------
  332. >>> import numpy as np
  333. >>> from scipy.linalg import ordqz
  334. >>> A = np.array([[2, 5, 8, 7], [5, 2, 2, 8], [7, 5, 6, 6], [5, 4, 4, 8]])
  335. >>> B = np.array([[0, 6, 0, 0], [5, 0, 2, 1], [5, 2, 6, 6], [4, 7, 7, 7]])
  336. >>> AA, BB, alpha, beta, Q, Z = ordqz(A, B, sort='lhp')
  337. Since we have sorted for left half plane eigenvalues, negatives come first
  338. >>> (alpha/beta).real < 0
  339. array([ True, True, False, False], dtype=bool)
  340. """
  341. (AA, BB, _, *ab, Q, Z, _, _), typ = _qz(A, B, output=output, sort=None,
  342. overwrite_a=overwrite_a,
  343. overwrite_b=overwrite_b,
  344. check_finite=check_finite)
  345. if typ == 's':
  346. alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2]
  347. elif typ == 'd':
  348. alpha, beta = ab[0] + ab[1]*1.j, ab[2]
  349. else:
  350. alpha, beta = ab
  351. sfunction = _select_function(sort)
  352. select = sfunction(alpha, beta)
  353. tgsen = get_lapack_funcs('tgsen', (AA, BB))
  354. # the real case needs 4n + 16 lwork
  355. lwork = 4*AA.shape[0] + 16 if typ in 'sd' else 1
  356. AAA, BBB, *ab, QQ, ZZ, _, _, _, _, info = tgsen(select, AA, BB, Q, Z,
  357. ijob=0,
  358. lwork=lwork, liwork=1)
  359. # Once more for tgsen output
  360. if typ == 's':
  361. alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2]
  362. elif typ == 'd':
  363. alpha, beta = ab[0] + ab[1]*1.j, ab[2]
  364. else:
  365. alpha, beta = ab
  366. if info < 0:
  367. raise ValueError(f"Illegal value in argument {-info} of tgsen")
  368. elif info == 1:
  369. raise ValueError("Reordering of (A, B) failed because the transformed"
  370. " matrix pair (A, B) would be too far from "
  371. "generalized Schur form; the problem is very "
  372. "ill-conditioned. (A, B) may have been partially "
  373. "reordered.")
  374. return AAA, BBB, alpha, beta, QQ, ZZ