Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/linalg/_decomp_qz.py: 13%

119 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +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 sfunction = lambda x: None 

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

121 overwrite_b=overwrite_b, sort_t=0) 

122 

123 info = result[-1] 

124 if info < 0: 

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

126 elif info > 0 and info <= a_n: 

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

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

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

130 stacklevel=3) 

131 elif info == a_n+1: 

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

133 elif info == a_n+2: 

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

135 "complex eigenvalues so that leading eigenvalues " 

136 "in the Generalized Schur form no longer satisfy " 

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

138 elif info == a_n+3: 

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

140 

141 return result, gges.typecode 

142 

143 

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

145 overwrite_b=False, check_finite=True): 

146 """ 

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

148 

149 The QZ, or generalized Schur, decomposition for a pair of n-by-n 

150 matrices (A,B) is:: 

151 

152 (A,B) = (Q @ AA @ Z*, Q @ BB @ Z*) 

153 

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

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

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

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

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

159 the corresponding elements of BB have the form:: 

160 

161 [ a 0 ] 

162 [ 0 b ] 

163 

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

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

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

167 Q and Z are unitary matrices. 

168 

169 Parameters 

170 ---------- 

171 A : (N, N) array_like 

172 2-D array to decompose 

173 B : (N, N) array_like 

174 2-D array to decompose 

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

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

177 Default is 'real'. 

178 lwork : int, optional 

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

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

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

182 

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

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

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

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

187 (alphar, alphai, beta). The eigenvalue 

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

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

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

191 string parameters may be used: 

192 

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

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

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

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

197 

198 Defaults to None (no sorting). 

199 overwrite_a : bool, optional 

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

201 overwrite_b : bool, optional 

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

203 check_finite : bool, optional 

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

205 false does no checking and passes matrix through to 

206 underlying algorithm. 

207 

208 Returns 

209 ------- 

210 AA : (N, N) ndarray 

211 Generalized Schur form of A. 

212 BB : (N, N) ndarray 

213 Generalized Schur form of B. 

214 Q : (N, N) ndarray 

215 The left Schur vectors. 

216 Z : (N, N) ndarray 

217 The right Schur vectors. 

218 

219 See Also 

220 -------- 

221 ordqz 

222 

223 Notes 

224 ----- 

225 Q is transposed versus the equivalent function in Matlab. 

226 

227 .. versionadded:: 0.11.0 

228 

229 Examples 

230 -------- 

231 >>> import numpy as np 

232 >>> from scipy.linalg import qz 

233 

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

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

236 

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

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

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

240 

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

242 >>> AA 

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

244 [ 0. , 7.65653432, 5.13476017], 

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

246 >>> BB 

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

248 [ 0. , 8.6965692 , -0. ], 

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

250 >>> Q 

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

252 [-0.90073232, 0.16534124, -0.40167593], 

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

254 >>> Z 

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

256 [ 0.70243299, 0.70853819, -0.06753907], 

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

258 

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

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

261 

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

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

264 [ 5., 5., 5.], 

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

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

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

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

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

270 

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

272 

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

274 

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

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

277 

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

279 >>> AA 

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

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

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

283 >>> BB 

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

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

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

287 >>> Q 

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

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

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

291 >>> Z 

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

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

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

295 

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

297 expressions to verify the decomposition. 

298 

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

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

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

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

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

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

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

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

307 

308 """ 

309 # output for real 

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

311 # output for complex 

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

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

314 overwrite_a=overwrite_a, overwrite_b=overwrite_b, 

315 check_finite=check_finite) 

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

317 

318 

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

320 overwrite_b=False, check_finite=True): 

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

322 

323 Parameters 

324 ---------- 

325 A : (N, N) array_like 

326 2-D array to decompose 

327 B : (N, N) array_like 

328 2-D array to decompose 

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

330 Specifies whether the upper eigenvalues should be sorted. A 

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

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

333 returns a boolean denoting whether the eigenvalue should be 

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

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

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

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

338 array. Alternatively, string parameters may be used: 

339 

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

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

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

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

344 

345 With the predefined sorting functions, an infinite eigenvalue 

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

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

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

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

350 all return `False`. 

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

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

353 Default is 'real'. 

354 overwrite_a : bool, optional 

355 If True, the contents of A are overwritten. 

356 overwrite_b : bool, optional 

357 If True, the contents of B are overwritten. 

358 check_finite : bool, optional 

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

360 false does no checking and passes matrix through to 

361 underlying algorithm. 

362 

363 Returns 

364 ------- 

365 AA : (N, N) ndarray 

366 Generalized Schur form of A. 

367 BB : (N, N) ndarray 

368 Generalized Schur form of B. 

369 alpha : (N,) ndarray 

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

371 beta : (N,) ndarray 

372 See notes. 

373 Q : (N, N) ndarray 

374 The left Schur vectors. 

375 Z : (N, N) ndarray 

376 The right Schur vectors. 

377 

378 See Also 

379 -------- 

380 qz 

381 

382 Notes 

383 ----- 

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

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

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

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

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

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

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

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

392 

393 .. versionadded:: 0.17.0 

394 

395 Examples 

396 -------- 

397 >>> import numpy as np 

398 >>> from scipy.linalg import ordqz 

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

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

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

402 

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

404 

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

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

407 

408 """ 

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

410 overwrite_a=overwrite_a, 

411 overwrite_b=overwrite_b, 

412 check_finite=check_finite) 

413 

414 if typ == 's': 

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

416 elif typ == 'd': 

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

418 else: 

419 alpha, beta = ab 

420 

421 sfunction = _select_function(sort) 

422 select = sfunction(alpha, beta) 

423 

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

425 # the real case needs 4n + 16 lwork 

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

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

428 ijob=0, 

429 lwork=lwork, liwork=1) 

430 

431 # Once more for tgsen output 

432 if typ == 's': 

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

434 elif typ == 'd': 

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

436 else: 

437 alpha, beta = ab 

438 

439 if info < 0: 

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

441 elif info == 1: 

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

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

444 "generalized Schur form; the problem is very " 

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

446 "reordered.") 

447 

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