1 '''
2 QZ alias generalized Schur decomposition (complex or real) for Python/Numpy.
3
4 You need to import the qz() function of this module, check out its docstring,
5 especially what it says about the required lapack shared library. Run this
6 module for some quick tests of the setup.
7
8 This is free but copyrighted software, distributed under the same license
9 as Python 2.5, copyright Sven Schreiber.
10
11 If you think a different license would make (more) sense, please say so
12 on the Numpy mailing list (see scipy.org).
13 '''
14
15 from ctypes import cdll, c_int, c_char, POINTER
16 import numpy as np
17 from numpy.ctypeslib import load_library, ndpointer
18 import sys
19
21 '''Loads the lapack shared lib and does some input checks.
22
23 The defaults for lapackname and location are platform-specific:
24 Win32: 'lapack' (due to scilab's lapack.dll)
25 'c:\\winnt\\system32\\'
26 Otherwise: 'liblapack'
27 '/usr/lib/'
28 '''
29
30 assert A.ndim == 2
31 assert A.shape == B.shape
32 assert A.shape[0] == A.shape[1]
33
34 if lpname == '':
35 if sys.platform == 'win32': lpname = 'lapack'
36 else: lpname = 'liblapack'
37 if lppath == '':
38 if sys.platform == 'win32': lppath = 'c:\\winnt\\system32\\'
39 else: lppath = '/usr/lib/'
40 lapack = load_library(lpname, lppath)
41 return lapack
42
43 -def dgges4numpy(A,B, jobvsl='V', jobvsr='V', lapackname='', lapackpath=''):
44 '''wraps lapack function dgges, no sorting done'''
45 lapack = setuplapack4xgges(A,B,lapackname,lapackpath)
46 rows = A.shape[0]
47
48 Aintype = type(A)
49
50
51 A = np.asfortranarray(A, dtype=np.float64)
52 B = np.asfortranarray(B, dtype=np.float64)
53
54 jobvsl = jobvsl[0]
55 jobvsr = jobvsr[0]
56
57
58 sort = 'N'
59 dummy = 0
60 info = c_int(1)
61 lda = c_int(rows)
62 ldb = c_int(rows)
63 ldvsl = c_int(rows)
64 ldvsr = c_int(rows)
65 plwork = 16*rows
66 lwork = c_int(lwork)
67 n = c_int(rows)
68 csdim = c_int(rows)
69
70
71 Alphar = np.asfortranarray(np.empty(rows), dtype=np.float64)
72 Alphai = np.asfortranarray(np.empty(rows), dtype=np.float64)
73 Beta = np.asfortranarray(np.empty(rows), dtype=np.float64)
74 Vsl = np.asfortranarray(np.empty([rows,rows]), dtype=np.float64)
75 Vsr = np.asfortranarray(np.empty([rows,rows]), dtype=np.float64)
76 Work = np.asfortranarray(np.empty(plwork), dtype=np.float64)
77 Rwork = np.asfortranarray(np.empty(8*rows), dtype=np.float64)
78
79 lapack.dgges_.argtypes = [
80 POINTER(c_char),
81 POINTER(c_char),
82 POINTER(c_char),
83
84
85 c_int,
86 POINTER(c_int),
87 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'),
88 POINTER(c_int),
89 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'),
90 POINTER(c_int),
91 POINTER(c_int),
92 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'),
93 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'),
94 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'),
95 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'),
96 POINTER(c_int),
97 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'),
98 POINTER(c_int),
99 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'),
100 POINTER(c_int),
101
102 c_int,
103 POINTER(c_int) ]
104
105 lapack.dgges_(jobvsl,jobvsr,sort,dummy,n,A,lda,B,ldb,sdim,Alphar,Alphai,
106 Beta,Vsl,ldvsl,Vsr,ldvsr,Work,lwork,dummy,info)
107
108
109 if Aintype == type(np.mat(1)):
110 A=np.mat(A); B=np.mat(B); Vsl=np.mat(Vsl); Vsr=np.mat(Vsr)
111 if info.value == 0:
112 if jobvsl=='V' and jobvsr=='V': return A,B,Alphar,Alphai,Beta,Vsl,Vsr
113 elif jobvsl=='V' and jobvsr=='N': return A,B,Alphar,Alphai,Beta,Vsl
114 elif jobvsl=='N' and jobvsr=='V': return A,B,Alphar,Alphai,Beta,Vsr
115 else: return A,B,Alphar,Alphai,Beta
116 elif info.value < 0:
117 raise ValueError, 'Illegal argument (' + str(abs(info.value)) + ')'
118 elif info.value <= rows:
119 raise RuntimeError, 'QZ iteration failed'
120 elif info.value <= rows+3:
121 raise RuntimeError, 'something other than QZ iteration failed'
122 else: raise RuntimeError, 'INFO not updated by dgges, complete failure!?'
123
124 -def zgges4numpy(A,B, jobvsl='V', jobvsr='V', lapackname='', lapackpath=''):
125 '''Wraps lapack function zgges, no sorting done.
126
127 Returns complex arrays, use real_if_close() if needed/possible.
128 '''
129 lapack = setuplapack4xgges(A,B,lapackname,lapackpath)
130 rows = A.shape[0]
131
132 Aintype = type(A)
133
134
135
136 A = np.asfortranarray(A, dtype=np.complex128)
137 B = np.asfortranarray(B, dtype=np.complex128)
138
139 jobvsl = jobvsl[0]
140 jobvsr = jobvsr[0]
141
142
143 sort = 'N'
144 dummy = 0
145 info = c_int(rows+4)
146 lda = c_int(rows)
147 ldb = c_int(rows)
148 ldvsl = c_int(rows)
149 ldvsr = c_int(rows)
150 plwork = 16*rows
151 lwork = c_int(plwork)
152 n = c_int(rows)
153 sdim = c_int(0)
154
155
156 Alpha = np.asfortranarray(np.empty(rows), dtype=np.complex128)
157 Beta = np.asfortranarray(np.empty(rows), dtype=np.complex128)
158 Vsl = np.asfortranarray(np.empty([rows,rows]), dtype=np.complex128)
159 Vsr = np.asfortranarray(np.empty([rows,rows]), dtype=np.complex128)
160 Work = np.asfortranarray(np.empty(plwork), dtype=np.complex128)
161 Rwork = np.asfortranarray(np.empty(8*rows), dtype=np.float64)
162
163 lapack.zgges_.argtypes = [
164 POINTER(c_char),
165 POINTER(c_char),
166 POINTER(c_char),
167 c_int,
168 POINTER(c_int),
169 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'),
170 POINTER(c_int),
171 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'),
172 POINTER(c_int),
173 POINTER(c_int),
174 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'),
175 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'),
176 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'),
177 POINTER(c_int),
178 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'),
179 POINTER(c_int),
180 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'),
181 POINTER(c_int),
182 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'),
183 c_int,
184 POINTER(c_int) ]
185
186 lapack.zgges_(jobvsl,jobvsr,sort,dummy,n,A,lda,B,ldb,sdim,Alpha,
187 Beta,Vsl,ldvsl,Vsr,ldvsr,Work,lwork,Rwork,dummy,info)
188
189
190 if Aintype == type(np.mat(1)):
191 A=np.mat(A); B=np.mat(B); Vsl=np.mat(Vsl); Vsr=np.mat(Vsr)
192
193 if info.value == 0:
194 if jobvsl=='V' and jobvsr=='V': return A,B,Alpha,Beta,Vsl,Vsr
195 elif jobvsl=='V' and jobvsr=='N': return A,B,Alpha,Beta,Vsl
196 elif jobvsl=='N' and jobvsr=='V': return A,B,Alpha,Beta,Vsr
197 else: return A,B,Alpha,Beta
198 elif info.value < 0:
199 raise ValueError, 'Illegal argument (' + str(abs(info.value)) + ')'
200 elif info.value <= rows:
201 raise RuntimeError, 'QZ iteration failed'
202 elif info.value <= rows+3:
203 raise RuntimeError, 'something other than QZ iteration failed'
204 else: raise RuntimeError, 'INFO not updated by zgges, complete failure!?'
205
206 -def qz(A,B, mode='complex', lapackname='', lapackpath=''):
207 '''Equivalent to Matlab's qz function [AA,BB,Q,Z] = qz(A,B).
208
209 Requires Lapack as a shared compiled library on the system (one that
210 contains the functions dgges for real and zgges for complex use -- on
211 Windows the one shipped with Scilab works). The underlying defaults for
212 lapackname and lapackpath are platform-specific:
213 Win32: 'lapack' (due to scilab's lapack.dll)
214 'c:\\winnt\\system32\\'
215 Otherwise: 'liblapack'
216 '/usr/lib/'
217
218 This function should exactly match Matlab's usage, unlike octave's qz
219 function which returns the conjugate-transpose of one of the matrices. Thus
220 it holds that
221 AA = Q*A*Z
222 BB = Q*B*Z,
223 where Q and Z are unitary (orthogonal if real).
224
225 If mode is 'complex', then:
226 returns complex-type arrays,
227 AA and BB are upper triangular,
228 and diag(AA)/diag(BB) are the generalized eigenvalues of (A,B).
229
230 If the real qz decomposition is explicitly requested --as in Matlab:
231 qz(A,B,'real')-- then:
232 returns real-type arrays,
233 AA is only block upper triangular,
234 and calculating the eigenvalues is more complicated.
235
236 Other variants like [AA,BB,Q,Z,V,W] = qz(A,B) are not implemented, i.e.
237 no generalized eigenvectors are calculated.
238 '''
239 if mode == 'real':
240 AA,BB,dum1,dum2,dum3,VSL,VSR = dgges4numpy(A,B,
241 lapackname=lapackname,lapackpath=lapackpath)
242 return AA, BB, VSL.T, VSR
243 elif mode == 'complex':
244 AA,BB,dum1,dum2,VSL,VSR = zgges4numpy(A,B,
245 lapackname=lapackname,lapackpath=lapackpath)
246 return AA, BB, VSL.conj().T, VSR
247 else: raise ValueError, 'bogus choice for mode'
248
249 -def eig2(A,B, lapackname='', lapackpath=''):
250 '''Calculates generalized eigenvalues of pair (A,B).
251
252 This should correspond to Matlab's lambda = eig(A,B),
253 and also to some (the same?) scipy function.
254
255 Eigenvalues will be of complex type, are unsorted, and are returned as 1d.
256 '''
257 AA,BB,dum1,dum2,VSL,VSR = zgges4numpy(A,B,
258 lapackname=lapackname,lapackpath=lapackpath)
259 return np.diag(AA)/np.diag(BB)
260
261 -def eigwithqz(A,B, lapackname='', lapackpath=''):
262 '''Does complex QZ decomp. and also returns the eigenvalues'''
263 AA, BB, Q, Z = qz(A,B,lapackname=lapackname,lapackpath=lapackpath)
264 evals = np.diag(AA)/np.diag(BB)
265 return evals,AA,BB,Q,Z
266
267 if __name__ == '__main__':
268
269
270
271 myA = np.random.rand(4,4)
272 myB = np.random.rand(4,4)
273 myqz = qz(myA, myB, lapackname='lapack')
274 AAd = np.diag(myqz[0])
275 BBd = np.diag(myqz[1])
276 myeig = eig2(myA,myB,lapackname='lapack')
277 assert np.allclose(AAd/BBd, myeig)
278 print myeig
279 print eigwithqz(myA,myB)
280
281
282 print type(qz(np.mat(myA),np.mat(myB))[0])
283 assert type(qz(np.mat(myA),myB)[0]) == type(np.mat(np.eye(2)))
284