Hide keyboard shortcuts

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

1import warnings 

2 

3import numpy as np 

4from numpy import asarray_chkfinite 

5 

6from .misc import LinAlgError, _datacopied, LinAlgWarning 

7from .lapack import get_lapack_funcs 

8 

9 

10__all__ = ['qz', 'ordqz'] 

11 

12_double_precision = ['i', 'l', 'd'] 

13 

14 

15def _select_function(sort): 

16 if callable(sort): 

17 # assume the user knows what they're doing 

18 sfunction = sort 

19 elif sort == 'lhp': 

20 sfunction = _lhp 

21 elif sort == 'rhp': 

22 sfunction = _rhp 

23 elif sort == 'iuc': 

24 sfunction = _iuc 

25 elif sort == 'ouc': 

26 sfunction = _ouc 

27 else: 

28 raise ValueError("sort parameter must be None, a callable, or " 

29 "one of ('lhp','rhp','iuc','ouc')") 

30 

31 return sfunction 

32 

33 

34def _lhp(x, y): 

35 out = np.empty_like(x, dtype=bool) 

36 nonzero = (y != 0) 

37 # handles (x, y) = (0, 0) too 

38 out[~nonzero] = False 

39 out[nonzero] = (np.real(x[nonzero]/y[nonzero]) < 0.0) 

40 return out 

41 

42 

43def _rhp(x, y): 

44 out = np.empty_like(x, dtype=bool) 

45 nonzero = (y != 0) 

46 # handles (x, y) = (0, 0) too 

47 out[~nonzero] = False 

48 out[nonzero] = (np.real(x[nonzero]/y[nonzero]) > 0.0) 

49 return out 

50 

51 

52def _iuc(x, y): 

53 out = np.empty_like(x, dtype=bool) 

54 nonzero = (y != 0) 

55 # handles (x, y) = (0, 0) too 

56 out[~nonzero] = False 

57 out[nonzero] = (abs(x[nonzero]/y[nonzero]) < 1.0) 

58 return out 

59 

60 

61def _ouc(x, y): 

62 out = np.empty_like(x, dtype=bool) 

63 xzero = (x == 0) 

64 yzero = (y == 0) 

65 out[xzero & yzero] = False 

66 out[~xzero & yzero] = True 

67 out[~yzero] = (abs(x[~yzero]/y[~yzero]) > 1.0) 

68 return out 

69 

70 

71def _qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False, 

72 overwrite_b=False, check_finite=True): 

73 if sort is not None: 

74 # Disabled due to segfaults on win32, see ticket 1717. 

75 raise ValueError("The 'sort' input of qz() has to be None and will be " 

76 "removed in a future release. Use ordqz instead.") 

77 

78 if output not in ['real', 'complex', 'r', 'c']: 

79 raise ValueError("argument must be 'real', or 'complex'") 

80 

81 if check_finite: 

82 a1 = asarray_chkfinite(A) 

83 b1 = asarray_chkfinite(B) 

84 else: 

85 a1 = np.asarray(A) 

86 b1 = np.asarray(B) 

87 

88 a_m, a_n = a1.shape 

89 b_m, b_n = b1.shape 

90 if not (a_m == a_n == b_m == b_n): 

91 raise ValueError("Array dimensions must be square and agree") 

92 

93 typa = a1.dtype.char 

94 if output in ['complex', 'c'] and typa not in ['F', 'D']: 

95 if typa in _double_precision: 

96 a1 = a1.astype('D') 

97 typa = 'D' 

98 else: 

99 a1 = a1.astype('F') 

100 typa = 'F' 

101 typb = b1.dtype.char 

102 if output in ['complex', 'c'] and typb not in ['F', 'D']: 

103 if typb in _double_precision: 

104 b1 = b1.astype('D') 

105 typb = 'D' 

106 else: 

107 b1 = b1.astype('F') 

108 typb = 'F' 

109 

110 overwrite_a = overwrite_a or (_datacopied(a1, A)) 

111 overwrite_b = overwrite_b or (_datacopied(b1, B)) 

112 

113 gges, = get_lapack_funcs(('gges',), (a1, b1)) 

114 

115 if lwork is None or lwork == -1: 

116 # get optimal work array size 

117 result = gges(lambda x: None, a1, b1, lwork=-1) 

118 lwork = result[-2][0].real.astype(np.int) 

119 

120 sfunction = lambda x: None 

121 result = gges(sfunction, a1, b1, lwork=lwork, overwrite_a=overwrite_a, 

122 overwrite_b=overwrite_b, sort_t=0) 

123 

124 info = result[-1] 

125 if info < 0: 

126 raise ValueError("Illegal value in argument {} of gges".format(-info)) 

127 elif info > 0 and info <= a_n: 

128 warnings.warn("The QZ iteration failed. (a,b) are not in Schur " 

129 "form, but ALPHAR(j), ALPHAI(j), and BETA(j) should be " 

130 "correct for J={},...,N".format(info-1), LinAlgWarning, 

131 stacklevel=3) 

132 elif info == a_n+1: 

133 raise LinAlgError("Something other than QZ iteration failed") 

134 elif info == a_n+2: 

135 raise LinAlgError("After reordering, roundoff changed values of some " 

136 "complex eigenvalues so that leading eigenvalues " 

137 "in the Generalized Schur form no longer satisfy " 

138 "sort=True. This could also be due to scaling.") 

139 elif info == a_n+3: 

140 raise LinAlgError("Reordering failed in <s,d,c,z>tgsen") 

141 

142 return result, gges.typecode 

143 

144 

145def qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False, 

146 overwrite_b=False, check_finite=True): 

147 """ 

148 QZ decomposition for generalized eigenvalues of a pair of matrices. 

149 

150 The QZ, or generalized Schur, decomposition for a pair of N x N 

151 nonsymmetric matrices (A,B) is:: 

152 

153 (A,B) = (Q*AA*Z', Q*BB*Z') 

154 

155 where AA, BB is in generalized Schur form if BB is upper-triangular 

156 with non-negative diagonal and AA is upper-triangular, or for real QZ 

157 decomposition (``output='real'``) block upper triangular with 1x1 

158 and 2x2 blocks. In this case, the 1x1 blocks correspond to real 

159 generalized eigenvalues and 2x2 blocks are 'standardized' by making 

160 the corresponding elements of BB have the form:: 

161 

162 [ a 0 ] 

163 [ 0 b ] 

164 

165 and the pair of corresponding 2x2 blocks in AA and BB will have a complex 

166 conjugate pair of generalized eigenvalues. If (``output='complex'``) or 

167 A and B are complex matrices, Z' denotes the conjugate-transpose of Z. 

168 Q and Z are unitary matrices. 

169 

170 Parameters 

171 ---------- 

172 A : (N, N) array_like 

173 2-D array to decompose 

174 B : (N, N) array_like 

175 2-D array to decompose 

176 output : {'real', 'complex'}, optional 

177 Construct the real or complex QZ decomposition for real matrices. 

178 Default is 'real'. 

179 lwork : int, optional 

180 Work array size. If None or -1, it is automatically computed. 

181 sort : {None, callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional 

182 NOTE: THIS INPUT IS DISABLED FOR NOW. Use ordqz instead. 

183 

184 Specifies whether the upper eigenvalues should be sorted. A callable 

185 may be passed that, given a eigenvalue, returns a boolean denoting 

186 whether the eigenvalue should be sorted to the top-left (True). For 

187 real matrix pairs, the sort function takes three real arguments 

188 (alphar, alphai, beta). The eigenvalue 

189 ``x = (alphar + alphai*1j)/beta``. For complex matrix pairs or 

190 output='complex', the sort function takes two complex arguments 

191 (alpha, beta). The eigenvalue ``x = (alpha/beta)``. Alternatively, 

192 string parameters may be used: 

193 

194 - 'lhp' Left-hand plane (x.real < 0.0) 

195 - 'rhp' Right-hand plane (x.real > 0.0) 

196 - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0) 

197 - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0) 

198 

199 Defaults to None (no sorting). 

200 overwrite_a : bool, optional 

201 Whether to overwrite data in a (may improve performance) 

202 overwrite_b : bool, optional 

203 Whether to overwrite data in b (may improve performance) 

204 check_finite : bool, optional 

205 If true checks the elements of `A` and `B` are finite numbers. If 

206 false does no checking and passes matrix through to 

207 underlying algorithm. 

208 

209 Returns 

210 ------- 

211 AA : (N, N) ndarray 

212 Generalized Schur form of A. 

213 BB : (N, N) ndarray 

214 Generalized Schur form of B. 

215 Q : (N, N) ndarray 

216 The left Schur vectors. 

217 Z : (N, N) ndarray 

218 The right Schur vectors. 

219 

220 Notes 

221 ----- 

222 Q is transposed versus the equivalent function in Matlab. 

223 

224 .. versionadded:: 0.11.0 

225 

226 Examples 

227 -------- 

228 >>> from scipy import linalg 

229 >>> np.random.seed(1234) 

230 >>> A = np.arange(9).reshape((3, 3)) 

231 >>> B = np.random.randn(3, 3) 

232 

233 >>> AA, BB, Q, Z = linalg.qz(A, B) 

234 >>> AA 

235 array([[-13.40928183, -4.62471562, 1.09215523], 

236 [ 0. , 0. , 1.22805978], 

237 [ 0. , 0. , 0.31973817]]) 

238 >>> BB 

239 array([[ 0.33362547, -1.37393632, 0.02179805], 

240 [ 0. , 1.68144922, 0.74683866], 

241 [ 0. , 0. , 0.9258294 ]]) 

242 >>> Q 

243 array([[ 0.14134727, -0.97562773, 0.16784365], 

244 [ 0.49835904, -0.07636948, -0.86360059], 

245 [ 0.85537081, 0.20571399, 0.47541828]]) 

246 >>> Z 

247 array([[-0.24900855, -0.51772687, 0.81850696], 

248 [-0.79813178, 0.58842606, 0.12938478], 

249 [-0.54861681, -0.6210585 , -0.55973739]]) 

250 

251 See also 

252 -------- 

253 ordqz 

254 """ 

255 # output for real 

256 # AA, BB, sdim, alphar, alphai, beta, vsl, vsr, work, info 

257 # output for complex 

258 # AA, BB, sdim, alpha, beta, vsl, vsr, work, info 

259 result, _ = _qz(A, B, output=output, lwork=lwork, sort=sort, 

260 overwrite_a=overwrite_a, overwrite_b=overwrite_b, 

261 check_finite=check_finite) 

262 return result[0], result[1], result[-4], result[-3] 

263 

264 

265def ordqz(A, B, sort='lhp', output='real', overwrite_a=False, 

266 overwrite_b=False, check_finite=True): 

267 """QZ decomposition for a pair of matrices with reordering. 

268 

269 .. versionadded:: 0.17.0 

270 

271 Parameters 

272 ---------- 

273 A : (N, N) array_like 

274 2-D array to decompose 

275 B : (N, N) array_like 

276 2-D array to decompose 

277 sort : {callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional 

278 Specifies whether the upper eigenvalues should be sorted. A 

279 callable may be passed that, given an ordered pair ``(alpha, 

280 beta)`` representing the eigenvalue ``x = (alpha/beta)``, 

281 returns a boolean denoting whether the eigenvalue should be 

282 sorted to the top-left (True). For the real matrix pairs 

283 ``beta`` is real while ``alpha`` can be complex, and for 

284 complex matrix pairs both ``alpha`` and ``beta`` can be 

285 complex. The callable must be able to accept a NumPy 

286 array. Alternatively, string parameters may be used: 

287 

288 - 'lhp' Left-hand plane (x.real < 0.0) 

289 - 'rhp' Right-hand plane (x.real > 0.0) 

290 - 'iuc' Inside the unit circle (x*x.conjugate() < 1.0) 

291 - 'ouc' Outside the unit circle (x*x.conjugate() > 1.0) 

292 

293 With the predefined sorting functions, an infinite eigenvalue 

294 (i.e., ``alpha != 0`` and ``beta = 0``) is considered to lie in 

295 neither the left-hand nor the right-hand plane, but it is 

296 considered to lie outside the unit circle. For the eigenvalue 

297 ``(alpha, beta) = (0, 0)``, the predefined sorting functions 

298 all return `False`. 

299 output : str {'real','complex'}, optional 

300 Construct the real or complex QZ decomposition for real matrices. 

301 Default is 'real'. 

302 overwrite_a : bool, optional 

303 If True, the contents of A are overwritten. 

304 overwrite_b : bool, optional 

305 If True, the contents of B are overwritten. 

306 check_finite : bool, optional 

307 If true checks the elements of `A` and `B` are finite numbers. If 

308 false does no checking and passes matrix through to 

309 underlying algorithm. 

310 

311 Returns 

312 ------- 

313 AA : (N, N) ndarray 

314 Generalized Schur form of A. 

315 BB : (N, N) ndarray 

316 Generalized Schur form of B. 

317 alpha : (N,) ndarray 

318 alpha = alphar + alphai * 1j. See notes. 

319 beta : (N,) ndarray 

320 See notes. 

321 Q : (N, N) ndarray 

322 The left Schur vectors. 

323 Z : (N, N) ndarray 

324 The right Schur vectors. 

325 

326 Notes 

327 ----- 

328 On exit, ``(ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N``, will be the 

329 generalized eigenvalues. ``ALPHAR(j) + ALPHAI(j)*i`` and 

330 ``BETA(j),j=1,...,N`` are the diagonals of the complex Schur form (S,T) 

331 that would result if the 2-by-2 diagonal blocks of the real generalized 

332 Schur form of (A,B) were further reduced to triangular form using complex 

333 unitary transformations. If ALPHAI(j) is zero, then the jth eigenvalue is 

334 real; if positive, then the ``j``th and ``(j+1)``st eigenvalues are a 

335 complex conjugate pair, with ``ALPHAI(j+1)`` negative. 

336 

337 See also 

338 -------- 

339 qz 

340 

341 Examples 

342 -------- 

343 >>> from scipy.linalg import ordqz 

344 >>> A = np.array([[2, 5, 8, 7], [5, 2, 2, 8], [7, 5, 6, 6], [5, 4, 4, 8]]) 

345 >>> B = np.array([[0, 6, 0, 0], [5, 0, 2, 1], [5, 2, 6, 6], [4, 7, 7, 7]]) 

346 >>> AA, BB, alpha, beta, Q, Z = ordqz(A, B, sort='lhp') 

347 

348 Since we have sorted for left half plane eigenvalues, negatives come first 

349 

350 >>> (alpha/beta).real < 0 

351 array([ True, True, False, False], dtype=bool) 

352 

353 """ 

354 # NOTE: should users be able to set these? 

355 lwork = None 

356 result, typ = _qz(A, B, output=output, lwork=lwork, sort=None, 

357 overwrite_a=overwrite_a, overwrite_b=overwrite_b, 

358 check_finite=check_finite) 

359 AA, BB, Q, Z = result[0], result[1], result[-4], result[-3] 

360 if typ not in 'cz': 

361 alpha, beta = result[3] + result[4]*1.j, result[5] 

362 else: 

363 alpha, beta = result[3], result[4] 

364 

365 sfunction = _select_function(sort) 

366 select = sfunction(alpha, beta) 

367 

368 tgsen, = get_lapack_funcs(('tgsen',), (AA, BB)) 

369 

370 if lwork is None or lwork == -1: 

371 result = tgsen(select, AA, BB, Q, Z, lwork=-1) 

372 lwork = result[-3][0].real.astype(np.int) 

373 # looks like wrong value passed to ZTGSYL if not 

374 lwork += 1 

375 

376 liwork = None 

377 if liwork is None or liwork == -1: 

378 result = tgsen(select, AA, BB, Q, Z, liwork=-1) 

379 liwork = result[-2][0] 

380 

381 result = tgsen(select, AA, BB, Q, Z, lwork=lwork, liwork=liwork) 

382 

383 info = result[-1] 

384 if info < 0: 

385 raise ValueError("Illegal value in argument %d of tgsen" % -info) 

386 elif info == 1: 

387 raise ValueError("Reordering of (A, B) failed because the transformed" 

388 " matrix pair (A, B) would be too far from " 

389 "generalized Schur form; the problem is very " 

390 "ill-conditioned. (A, B) may have been partially " 

391 "reorded. If requested, 0 is returned in DIF(*), " 

392 "PL, and PR.") 

393 

394 # for real results has a, b, alphar, alphai, beta, q, z, m, pl, pr, dif, 

395 # work, iwork, info 

396 if typ in ['f', 'd']: 

397 alpha = result[2] + result[3] * 1.j 

398 return (result[0], result[1], alpha, result[4], result[5], result[6]) 

399 # for complex results has a, b, alpha, beta, q, z, m, pl, pr, dif, work, 

400 # iwork, info 

401 else: 

402 return result[0], result[1], result[2], result[3], result[4], result[5]