Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/linalg/_decomp_qz.py: 12%

120 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-23 06:43 +0000

1import warnings 

2 

3import numpy as np 

4from numpy import asarray_chkfinite 

5from ._misc import LinAlgError, _datacopied, LinAlgWarning 

6from .lapack import get_lapack_funcs 

7 

8 

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

10 

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

12 

13 

14def _select_function(sort): 

15 if callable(sort): 

16 # assume the user knows what they're doing 

17 sfunction = sort 

18 elif sort == 'lhp': 

19 sfunction = _lhp 

20 elif sort == 'rhp': 

21 sfunction = _rhp 

22 elif sort == 'iuc': 

23 sfunction = _iuc 

24 elif sort == 'ouc': 

25 sfunction = _ouc 

26 else: 

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

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

29 

30 return sfunction 

31 

32 

33def _lhp(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 

41 

42def _rhp(x, y): 

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

44 nonzero = (y != 0) 

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

46 out[~nonzero] = False 

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

48 return out 

49 

50 

51def _iuc(x, y): 

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

53 nonzero = (y != 0) 

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

55 out[~nonzero] = False 

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

57 return out 

58 

59 

60def _ouc(x, y): 

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

62 xzero = (x == 0) 

63 yzero = (y == 0) 

64 out[xzero & yzero] = False 

65 out[~xzero & yzero] = True 

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

67 return out 

68 

69 

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

71 overwrite_b=False, check_finite=True): 

72 if sort is not None: 

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

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

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

76 

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

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

79 

80 if check_finite: 

81 a1 = asarray_chkfinite(A) 

82 b1 = asarray_chkfinite(B) 

83 else: 

84 a1 = np.asarray(A) 

85 b1 = np.asarray(B) 

86 

87 a_m, a_n = a1.shape 

88 b_m, b_n = b1.shape 

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

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

91 

92 typa = a1.dtype.char 

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

94 if typa in _double_precision: 

95 a1 = a1.astype('D') 

96 typa = 'D' 

97 else: 

98 a1 = a1.astype('F') 

99 typa = 'F' 

100 typb = b1.dtype.char 

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

102 if typb in _double_precision: 

103 b1 = b1.astype('D') 

104 typb = 'D' 

105 else: 

106 b1 = b1.astype('F') 

107 typb = 'F' 

108 

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

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

111 

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

113 

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

115 # get optimal work array size 

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

117 lwork = result[-2][0].real.astype(np.int_) 

118 

119 def sfunction(x): 

120 return 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(f"Illegal value in argument {-info} of gges") 

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-by-n 

151 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 See Also 

221 -------- 

222 ordqz 

223 

224 Notes 

225 ----- 

226 Q is transposed versus the equivalent function in Matlab. 

227 

228 .. versionadded:: 0.11.0 

229 

230 Examples 

231 -------- 

232 >>> import numpy as np 

233 >>> from scipy.linalg import qz 

234 

235 >>> A = np.array([[1, 2, -1], [5, 5, 5], [2, 4, -8]]) 

236 >>> B = np.array([[1, 1, -3], [3, 1, -1], [5, 6, -2]]) 

237 

238 Compute the decomposition. The QZ decomposition is not unique, so 

239 depending on the underlying library that is used, there may be 

240 differences in the signs of coefficients in the following output. 

241 

242 >>> AA, BB, Q, Z = qz(A, B) 

243 >>> AA 

244 array([[-1.36949157, -4.05459025, 7.44389431], 

245 [ 0. , 7.65653432, 5.13476017], 

246 [ 0. , -0.65978437, 2.4186015 ]]) # may vary 

247 >>> BB 

248 array([[ 1.71890633, -1.64723705, -0.72696385], 

249 [ 0. , 8.6965692 , -0. ], 

250 [ 0. , 0. , 2.27446233]]) # may vary 

251 >>> Q 

252 array([[-0.37048362, 0.1903278 , 0.90912992], 

253 [-0.90073232, 0.16534124, -0.40167593], 

254 [ 0.22676676, 0.96769706, -0.11017818]]) # may vary 

255 >>> Z 

256 array([[-0.67660785, 0.63528924, -0.37230283], 

257 [ 0.70243299, 0.70853819, -0.06753907], 

258 [ 0.22088393, -0.30721526, -0.92565062]]) # may vary 

259 

260 Verify the QZ decomposition. With real output, we only need the 

261 transpose of ``Z`` in the following expressions. 

262 

263 >>> Q @ AA @ Z.T # Should be A 

264 array([[ 1., 2., -1.], 

265 [ 5., 5., 5.], 

266 [ 2., 4., -8.]]) 

267 >>> Q @ BB @ Z.T # Should be B 

268 array([[ 1., 1., -3.], 

269 [ 3., 1., -1.], 

270 [ 5., 6., -2.]]) 

271 

272 Repeat the decomposition, but with ``output='complex'``. 

273 

274 >>> AA, BB, Q, Z = qz(A, B, output='complex') 

275 

276 For conciseness in the output, we use ``np.set_printoptions()`` to set 

277 the output precision of NumPy arrays to 3 and display tiny values as 0. 

278 

279 >>> np.set_printoptions(precision=3, suppress=True) 

280 >>> AA 

281 array([[-1.369+0.j , 2.248+4.237j, 4.861-5.022j], 

282 [ 0. +0.j , 7.037+2.922j, 0.794+4.932j], 

283 [ 0. +0.j , 0. +0.j , 2.655-1.103j]]) # may vary 

284 >>> BB 

285 array([[ 1.719+0.j , -1.115+1.j , -0.763-0.646j], 

286 [ 0. +0.j , 7.24 +0.j , -3.144+3.322j], 

287 [ 0. +0.j , 0. +0.j , 2.732+0.j ]]) # may vary 

288 >>> Q 

289 array([[ 0.326+0.175j, -0.273-0.029j, -0.886-0.052j], 

290 [ 0.794+0.426j, -0.093+0.134j, 0.402-0.02j ], 

291 [-0.2 -0.107j, -0.816+0.482j, 0.151-0.167j]]) # may vary 

292 >>> Z 

293 array([[ 0.596+0.32j , -0.31 +0.414j, 0.393-0.347j], 

294 [-0.619-0.332j, -0.479+0.314j, 0.154-0.393j], 

295 [-0.195-0.104j, 0.576+0.27j , 0.715+0.187j]]) # may vary 

296 

297 With complex arrays, we must use ``Z.conj().T`` in the following 

298 expressions to verify the decomposition. 

299 

300 >>> Q @ AA @ Z.conj().T # Should be A 

301 array([[ 1.-0.j, 2.-0.j, -1.-0.j], 

302 [ 5.+0.j, 5.+0.j, 5.-0.j], 

303 [ 2.+0.j, 4.+0.j, -8.+0.j]]) 

304 >>> Q @ BB @ Z.conj().T # Should be B 

305 array([[ 1.+0.j, 1.+0.j, -3.+0.j], 

306 [ 3.-0.j, 1.-0.j, -1.+0.j], 

307 [ 5.+0.j, 6.+0.j, -2.+0.j]]) 

308 

309 """ 

310 # output for real 

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

312 # output for complex 

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

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

315 overwrite_a=overwrite_a, overwrite_b=overwrite_b, 

316 check_finite=check_finite) 

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

318 

319 

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

321 overwrite_b=False, check_finite=True): 

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

323 

324 Parameters 

325 ---------- 

326 A : (N, N) array_like 

327 2-D array to decompose 

328 B : (N, N) array_like 

329 2-D array to decompose 

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

331 Specifies whether the upper eigenvalues should be sorted. A 

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

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

334 returns a boolean denoting whether the eigenvalue should be 

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

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

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

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

339 array. Alternatively, string parameters may be used: 

340 

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

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

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

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

345 

346 With the predefined sorting functions, an infinite eigenvalue 

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

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

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

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

351 all return `False`. 

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

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

354 Default is 'real'. 

355 overwrite_a : bool, optional 

356 If True, the contents of A are overwritten. 

357 overwrite_b : bool, optional 

358 If True, the contents of B are overwritten. 

359 check_finite : bool, optional 

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

361 false does no checking and passes matrix through to 

362 underlying algorithm. 

363 

364 Returns 

365 ------- 

366 AA : (N, N) ndarray 

367 Generalized Schur form of A. 

368 BB : (N, N) ndarray 

369 Generalized Schur form of B. 

370 alpha : (N,) ndarray 

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

372 beta : (N,) ndarray 

373 See notes. 

374 Q : (N, N) ndarray 

375 The left Schur vectors. 

376 Z : (N, N) ndarray 

377 The right Schur vectors. 

378 

379 See Also 

380 -------- 

381 qz 

382 

383 Notes 

384 ----- 

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

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

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

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

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

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

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

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

393 

394 .. versionadded:: 0.17.0 

395 

396 Examples 

397 -------- 

398 >>> import numpy as np 

399 >>> from scipy.linalg import ordqz 

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

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

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

403 

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

405 

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

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

408 

409 """ 

410 (AA, BB, _, *ab, Q, Z, _, _), typ = _qz(A, B, output=output, sort=None, 

411 overwrite_a=overwrite_a, 

412 overwrite_b=overwrite_b, 

413 check_finite=check_finite) 

414 

415 if typ == 's': 

416 alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2] 

417 elif typ == 'd': 

418 alpha, beta = ab[0] + ab[1]*1.j, ab[2] 

419 else: 

420 alpha, beta = ab 

421 

422 sfunction = _select_function(sort) 

423 select = sfunction(alpha, beta) 

424 

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

426 # the real case needs 4n + 16 lwork 

427 lwork = 4*AA.shape[0] + 16 if typ in 'sd' else 1 

428 AAA, BBB, *ab, QQ, ZZ, _, _, _, _, info = tgsen(select, AA, BB, Q, Z, 

429 ijob=0, 

430 lwork=lwork, liwork=1) 

431 

432 # Once more for tgsen output 

433 if typ == 's': 

434 alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2] 

435 elif typ == 'd': 

436 alpha, beta = ab[0] + ab[1]*1.j, ab[2] 

437 else: 

438 alpha, beta = ab 

439 

440 if info < 0: 

441 raise ValueError(f"Illegal value in argument {-info} of tgsen") 

442 elif info == 1: 

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

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

445 "generalized Schur form; the problem is very " 

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

447 "reordered.") 

448 

449 return AAA, BBB, alpha, beta, QQ, ZZ