_decomp_qz.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. from __future__ import division, print_function, absolute_import
  2. import warnings
  3. import numpy as np
  4. from numpy import asarray_chkfinite
  5. from .misc import LinAlgError, _datacopied, LinAlgWarning
  6. from .lapack import get_lapack_funcs
  7. from scipy._lib.six import callable
  8. __all__ = ['qz', 'ordqz']
  9. _double_precision = ['i', 'l', 'd']
  10. def _select_function(sort):
  11. if callable(sort):
  12. # assume the user knows what they're doing
  13. sfunction = sort
  14. elif sort == 'lhp':
  15. sfunction = _lhp
  16. elif sort == 'rhp':
  17. sfunction = _rhp
  18. elif sort == 'iuc':
  19. sfunction = _iuc
  20. elif sort == 'ouc':
  21. sfunction = _ouc
  22. else:
  23. raise ValueError("sort parameter must be None, a callable, or "
  24. "one of ('lhp','rhp','iuc','ouc')")
  25. return sfunction
  26. def _lhp(x, y):
  27. out = np.empty_like(x, dtype=bool)
  28. nonzero = (y != 0)
  29. # handles (x, y) = (0, 0) too
  30. out[~nonzero] = False
  31. out[nonzero] = (np.real(x[nonzero]/y[nonzero]) < 0.0)
  32. return out
  33. def _rhp(x, y):
  34. out = np.empty_like(x, dtype=bool)
  35. nonzero = (y != 0)
  36. # handles (x, y) = (0, 0) too
  37. out[~nonzero] = False
  38. out[nonzero] = (np.real(x[nonzero]/y[nonzero]) > 0.0)
  39. return out
  40. def _iuc(x, y):
  41. out = np.empty_like(x, dtype=bool)
  42. nonzero = (y != 0)
  43. # handles (x, y) = (0, 0) too
  44. out[~nonzero] = False
  45. out[nonzero] = (abs(x[nonzero]/y[nonzero]) < 1.0)
  46. return out
  47. def _ouc(x, y):
  48. out = np.empty_like(x, dtype=bool)
  49. xzero = (x == 0)
  50. yzero = (y == 0)
  51. out[xzero & yzero] = False
  52. out[~xzero & yzero] = True
  53. out[~yzero] = (abs(x[~yzero]/y[~yzero]) > 1.0)
  54. return out
  55. def _qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False,
  56. overwrite_b=False, check_finite=True):
  57. if sort is not None:
  58. # Disabled due to segfaults on win32, see ticket 1717.
  59. raise ValueError("The 'sort' input of qz() has to be None and will be "
  60. "removed in a future release. Use ordqz instead.")
  61. if output not in ['real', 'complex', 'r', 'c']:
  62. raise ValueError("argument must be 'real', or 'complex'")
  63. if check_finite:
  64. a1 = asarray_chkfinite(A)
  65. b1 = asarray_chkfinite(B)
  66. else:
  67. a1 = np.asarray(A)
  68. b1 = np.asarray(B)
  69. a_m, a_n = a1.shape
  70. b_m, b_n = b1.shape
  71. if not (a_m == a_n == b_m == b_n):
  72. raise ValueError("Array dimensions must be square and agree")
  73. typa = a1.dtype.char
  74. if output in ['complex', 'c'] and typa not in ['F', 'D']:
  75. if typa in _double_precision:
  76. a1 = a1.astype('D')
  77. typa = 'D'
  78. else:
  79. a1 = a1.astype('F')
  80. typa = 'F'
  81. typb = b1.dtype.char
  82. if output in ['complex', 'c'] and typb not in ['F', 'D']:
  83. if typb in _double_precision:
  84. b1 = b1.astype('D')
  85. typb = 'D'
  86. else:
  87. b1 = b1.astype('F')
  88. typb = 'F'
  89. overwrite_a = overwrite_a or (_datacopied(a1, A))
  90. overwrite_b = overwrite_b or (_datacopied(b1, B))
  91. gges, = get_lapack_funcs(('gges',), (a1, b1))
  92. if lwork is None or lwork == -1:
  93. # get optimal work array size
  94. result = gges(lambda x: None, a1, b1, lwork=-1)
  95. lwork = result[-2][0].real.astype(np.int)
  96. sfunction = lambda x: None
  97. result = gges(sfunction, a1, b1, lwork=lwork, overwrite_a=overwrite_a,
  98. overwrite_b=overwrite_b, sort_t=0)
  99. info = result[-1]
  100. if info < 0:
  101. raise ValueError("Illegal value in argument {} of gges".format(-info))
  102. elif info > 0 and info <= a_n:
  103. warnings.warn("The QZ iteration failed. (a,b) are not in Schur "
  104. "form, but ALPHAR(j), ALPHAI(j), and BETA(j) should be "
  105. "correct for J={},...,N".format(info-1), LinAlgWarning,
  106. stacklevel=3)
  107. elif info == a_n+1:
  108. raise LinAlgError("Something other than QZ iteration failed")
  109. elif info == a_n+2:
  110. raise LinAlgError("After reordering, roundoff changed values of some "
  111. "complex eigenvalues so that leading eigenvalues "
  112. "in the Generalized Schur form no longer satisfy "
  113. "sort=True. This could also be due to scaling.")
  114. elif info == a_n+3:
  115. raise LinAlgError("Reordering failed in <s,d,c,z>tgsen")
  116. return result, gges.typecode
  117. def qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False,
  118. overwrite_b=False, check_finite=True):
  119. """
  120. QZ decomposition for generalized eigenvalues of a pair of matrices.
  121. The QZ, or generalized Schur, decomposition for a pair of N x N
  122. nonsymmetric matrices (A,B) is::
  123. (A,B) = (Q*AA*Z', Q*BB*Z')
  124. where AA, BB is in generalized Schur form if BB is upper-triangular
  125. with non-negative diagonal and AA is upper-triangular, or for real QZ
  126. decomposition (``output='real'``) block upper triangular with 1x1
  127. and 2x2 blocks. In this case, the 1x1 blocks correspond to real
  128. generalized eigenvalues and 2x2 blocks are 'standardized' by making
  129. the corresponding elements of BB have the form::
  130. [ a 0 ]
  131. [ 0 b ]
  132. and the pair of corresponding 2x2 blocks in AA and BB will have a complex
  133. conjugate pair of generalized eigenvalues. If (``output='complex'``) or
  134. A and B are complex matrices, Z' denotes the conjugate-transpose of Z.
  135. Q and Z are unitary matrices.
  136. Parameters
  137. ----------
  138. A : (N, N) array_like
  139. 2d array to decompose
  140. B : (N, N) array_like
  141. 2d array to decompose
  142. output : {'real', 'complex'}, optional
  143. Construct the real or complex QZ decomposition for real matrices.
  144. Default is 'real'.
  145. lwork : int, optional
  146. Work array size. If None or -1, it is automatically computed.
  147. sort : {None, callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional
  148. NOTE: THIS INPUT IS DISABLED FOR NOW. Use ordqz instead.
  149. Specifies whether the upper eigenvalues should be sorted. A callable
  150. may be passed that, given a eigenvalue, returns a boolean denoting
  151. whether the eigenvalue should be sorted to the top-left (True). For
  152. real matrix pairs, the sort function takes three real arguments
  153. (alphar, alphai, beta). The eigenvalue
  154. ``x = (alphar + alphai*1j)/beta``. For complex matrix pairs or
  155. output='complex', the sort function takes two complex arguments
  156. (alpha, beta). The eigenvalue ``x = (alpha/beta)``. Alternatively,
  157. string parameters may be used:
  158. - 'lhp' Left-hand plane (x.real < 0.0)
  159. - 'rhp' Right-hand plane (x.real > 0.0)
  160. - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0)
  161. - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0)
  162. Defaults to None (no sorting).
  163. overwrite_a : bool, optional
  164. Whether to overwrite data in a (may improve performance)
  165. overwrite_b : bool, optional
  166. Whether to overwrite data in b (may improve performance)
  167. check_finite : bool, optional
  168. If true checks the elements of `A` and `B` are finite numbers. If
  169. false does no checking and passes matrix through to
  170. underlying algorithm.
  171. Returns
  172. -------
  173. AA : (N, N) ndarray
  174. Generalized Schur form of A.
  175. BB : (N, N) ndarray
  176. Generalized Schur form of B.
  177. Q : (N, N) ndarray
  178. The left Schur vectors.
  179. Z : (N, N) ndarray
  180. The right Schur vectors.
  181. Notes
  182. -----
  183. Q is transposed versus the equivalent function in Matlab.
  184. .. versionadded:: 0.11.0
  185. Examples
  186. --------
  187. >>> from scipy import linalg
  188. >>> np.random.seed(1234)
  189. >>> A = np.arange(9).reshape((3, 3))
  190. >>> B = np.random.randn(3, 3)
  191. >>> AA, BB, Q, Z = linalg.qz(A, B)
  192. >>> AA
  193. array([[-13.40928183, -4.62471562, 1.09215523],
  194. [ 0. , 0. , 1.22805978],
  195. [ 0. , 0. , 0.31973817]])
  196. >>> BB
  197. array([[ 0.33362547, -1.37393632, 0.02179805],
  198. [ 0. , 1.68144922, 0.74683866],
  199. [ 0. , 0. , 0.9258294 ]])
  200. >>> Q
  201. array([[ 0.14134727, -0.97562773, 0.16784365],
  202. [ 0.49835904, -0.07636948, -0.86360059],
  203. [ 0.85537081, 0.20571399, 0.47541828]])
  204. >>> Z
  205. array([[-0.24900855, -0.51772687, 0.81850696],
  206. [-0.79813178, 0.58842606, 0.12938478],
  207. [-0.54861681, -0.6210585 , -0.55973739]])
  208. See also
  209. --------
  210. ordqz
  211. """
  212. # output for real
  213. # AA, BB, sdim, alphar, alphai, beta, vsl, vsr, work, info
  214. # output for complex
  215. # AA, BB, sdim, alpha, beta, vsl, vsr, work, info
  216. result, _ = _qz(A, B, output=output, lwork=lwork, sort=sort,
  217. overwrite_a=overwrite_a, overwrite_b=overwrite_b,
  218. check_finite=check_finite)
  219. return result[0], result[1], result[-4], result[-3]
  220. def ordqz(A, B, sort='lhp', output='real', overwrite_a=False,
  221. overwrite_b=False, check_finite=True):
  222. """QZ decomposition for a pair of matrices with reordering.
  223. .. versionadded:: 0.17.0
  224. Parameters
  225. ----------
  226. A : (N, N) array_like
  227. 2d array to decompose
  228. B : (N, N) array_like
  229. 2d array to decompose
  230. sort : {callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional
  231. Specifies whether the upper eigenvalues should be sorted. A
  232. callable may be passed that, given an ordered pair ``(alpha,
  233. beta)`` representing the eigenvalue ``x = (alpha/beta)``,
  234. returns a boolean denoting whether the eigenvalue should be
  235. sorted to the top-left (True). For the real matrix pairs
  236. ``beta`` is real while ``alpha`` can be complex, and for
  237. complex matrix pairs both ``alpha`` and ``beta`` can be
  238. complex. The callable must be able to accept a numpy
  239. array. Alternatively, string parameters may be used:
  240. - 'lhp' Left-hand plane (x.real < 0.0)
  241. - 'rhp' Right-hand plane (x.real > 0.0)
  242. - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0)
  243. - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0)
  244. With the predefined sorting functions, an infinite eigenvalue
  245. (i.e. ``alpha != 0`` and ``beta = 0``) is considered to lie in
  246. neither the left-hand nor the right-hand plane, but it is
  247. considered to lie outside the unit circle. For the eigenvalue
  248. ``(alpha, beta) = (0, 0)`` the predefined sorting functions
  249. all return `False`.
  250. output : str {'real','complex'}, optional
  251. Construct the real or complex QZ decomposition for real matrices.
  252. Default is 'real'.
  253. overwrite_a : bool, optional
  254. If True, the contents of A are overwritten.
  255. overwrite_b : bool, optional
  256. If True, the contents of B are overwritten.
  257. check_finite : bool, optional
  258. If true checks the elements of `A` and `B` are finite numbers. If
  259. false does no checking and passes matrix through to
  260. underlying algorithm.
  261. Returns
  262. -------
  263. AA : (N, N) ndarray
  264. Generalized Schur form of A.
  265. BB : (N, N) ndarray
  266. Generalized Schur form of B.
  267. alpha : (N,) ndarray
  268. alpha = alphar + alphai * 1j. See notes.
  269. beta : (N,) ndarray
  270. See notes.
  271. Q : (N, N) ndarray
  272. The left Schur vectors.
  273. Z : (N, N) ndarray
  274. The right Schur vectors.
  275. Notes
  276. -----
  277. On exit, ``(ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N``, will be the
  278. generalized eigenvalues. ``ALPHAR(j) + ALPHAI(j)*i`` and
  279. ``BETA(j),j=1,...,N`` are the diagonals of the complex Schur form (S,T)
  280. that would result if the 2-by-2 diagonal blocks of the real generalized
  281. Schur form of (A,B) were further reduced to triangular form using complex
  282. unitary transformations. If ALPHAI(j) is zero, then the j-th eigenvalue is
  283. real; if positive, then the ``j``-th and ``(j+1)``-st eigenvalues are a
  284. complex conjugate pair, with ``ALPHAI(j+1)`` negative.
  285. See also
  286. --------
  287. qz
  288. Examples
  289. --------
  290. >>> from scipy.linalg import ordqz
  291. >>> A = np.array([[2, 5, 8, 7], [5, 2, 2, 8], [7, 5, 6, 6], [5, 4, 4, 8]])
  292. >>> B = np.array([[0, 6, 0, 0], [5, 0, 2, 1], [5, 2, 6, 6], [4, 7, 7, 7]])
  293. >>> AA, BB, alpha, beta, Q, Z = ordqz(A, B, sort='lhp')
  294. Since we have sorted for left half plane eigenvalues, negatives come first
  295. >>> (alpha/beta).real < 0
  296. array([ True, True, False, False], dtype=bool)
  297. """
  298. # NOTE: should users be able to set these?
  299. lwork = None
  300. result, typ = _qz(A, B, output=output, lwork=lwork, sort=None,
  301. overwrite_a=overwrite_a, overwrite_b=overwrite_b,
  302. check_finite=check_finite)
  303. AA, BB, Q, Z = result[0], result[1], result[-4], result[-3]
  304. if typ not in 'cz':
  305. alpha, beta = result[3] + result[4]*1.j, result[5]
  306. else:
  307. alpha, beta = result[3], result[4]
  308. sfunction = _select_function(sort)
  309. select = sfunction(alpha, beta)
  310. tgsen, = get_lapack_funcs(('tgsen',), (AA, BB))
  311. if lwork is None or lwork == -1:
  312. result = tgsen(select, AA, BB, Q, Z, lwork=-1)
  313. lwork = result[-3][0].real.astype(np.int)
  314. # looks like wrong value passed to ZTGSYL if not
  315. lwork += 1
  316. liwork = None
  317. if liwork is None or liwork == -1:
  318. result = tgsen(select, AA, BB, Q, Z, liwork=-1)
  319. liwork = result[-2][0]
  320. result = tgsen(select, AA, BB, Q, Z, lwork=lwork, liwork=liwork)
  321. info = result[-1]
  322. if info < 0:
  323. raise ValueError("Illegal value in argument %d of tgsen" % -info)
  324. elif info == 1:
  325. raise ValueError("Reordering of (A, B) failed because the transformed"
  326. " matrix pair (A, B) would be too far from "
  327. "generalized Schur form; the problem is very "
  328. "ill-conditioned. (A, B) may have been partially "
  329. "reorded. If requested, 0 is returned in DIF(*), "
  330. "PL, and PR.")
  331. # for real results has a, b, alphar, alphai, beta, q, z, m, pl, pr, dif,
  332. # work, iwork, info
  333. if typ in ['f', 'd']:
  334. alpha = result[2] + result[3] * 1.j
  335. return (result[0], result[1], alpha, result[4], result[5], result[6])
  336. # for complex results has a, b, alpha, beta, q, z, m, pl, pr, dif, work,
  337. # iwork, info
  338. else:
  339. return result[0], result[1], result[2], result[3], result[4], result[5]