Package daredare :: Package extern :: Module qz
[hide private]
[frames] | no frames]

Source Code for Module daredare.extern.qz

  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  
 
20 -def setuplapack4xgges(A,B,lpname,lppath):
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 # some input checks 30 assert A.ndim == 2 31 assert A.shape == B.shape 32 assert A.shape[0] == A.shape[1] 33 # load the lapack shared library 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 # to determine matrix subclass 48 Aintype = type(A) 49 50 # actual inputs 51 A = np.asfortranarray(A, dtype=np.float64) 52 B = np.asfortranarray(B, dtype=np.float64) 53 # seems ok to pass strings directly, but the function expects only 1 char! 54 jobvsl = jobvsl[0] 55 jobvsr = jobvsr[0] 56 57 # dummy inputs 58 sort = 'N' # we don't want sorting 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 # needed again later 66 lwork = c_int(lwork) 67 n = c_int(rows) 68 csdim = c_int(rows) # because we don't sort 69 70 # auxiliary arrays 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), # JOBVSL 81 POINTER(c_char), # JOBVSR 82 POINTER(c_char), # SORT 83 # for the dummy the POINTER thing didn't work, 84 # but plain c_int apparently does... 85 c_int, # dummy SELCTG 86 POINTER(c_int), # N 87 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'), # A 88 POINTER(c_int), # LDA 89 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'), # B 90 POINTER(c_int), # LDB 91 POINTER(c_int), # SDIM 92 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'), # ALPHAr 93 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'), # ALPHAi 94 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'), # BETA 95 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'), # VSL 96 POINTER(c_int), # LDVSL 97 ndpointer(dtype=np.float64, ndim=2, flags='FORTRAN'), # VSR 98 POINTER(c_int), # LDVSR 99 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'), # WORK 100 POINTER(c_int), # LWORK 101 # same as with SELCTG... 102 c_int, # dummy BWORK 103 POINTER(c_int) ] # INFO 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 # preserve matrix subclass 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 # determine matrix subclass 132 Aintype = type(A) 133 134 # actual inputs 135 # The COMPLEX*16 type in Fortran translates to numpy's complex128 136 A = np.asfortranarray(A, dtype=np.complex128) 137 B = np.asfortranarray(B, dtype=np.complex128) 138 # seems ok to pass strings directly, but the function expects only 1 char! 139 jobvsl = jobvsl[0] 140 jobvsr = jobvsr[0] 141 142 # dummy inputs 143 sort = 'N' # we don't want sorting 144 dummy = 0 # a placeholder for what would be needed for sorting 145 info = c_int(rows+4) # >n+3 aren't used as error codes of zgges 146 lda = c_int(rows) 147 ldb = c_int(rows) 148 ldvsl = c_int(rows) 149 ldvsr = c_int(rows) 150 plwork = 16*rows # needed again later 151 lwork = c_int(plwork) 152 n = c_int(rows) 153 sdim = c_int(0) # because we don't sort 154 155 # auxiliary arrays 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), # JOBVSL 165 POINTER(c_char), # JOBVSR 166 POINTER(c_char), # SORT 167 c_int, # dummy SELCTG 168 POINTER(c_int), # N 169 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'), # A 170 POINTER(c_int), # LDA 171 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'), # B 172 POINTER(c_int), # LDB 173 POINTER(c_int), # SDIM 174 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'), # ALPHA 175 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'), # BETA 176 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'), # VSL 177 POINTER(c_int), # LDVSL 178 ndpointer(dtype=np.complex128, ndim=2, flags='FORTRAN'), # VSR 179 POINTER(c_int), # LDVSR 180 ndpointer(dtype=np.complex128, ndim=1, flags='FORTRAN'), # WORK 181 POINTER(c_int), # LWORK 182 ndpointer(dtype=np.float64, ndim=1, flags='FORTRAN'), # RWORK 183 c_int, # dummy BWORK 184 POINTER(c_int) ] # INFO 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 # preserve matrix subclass 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 # use .value for ctypes safety, although probably redundant 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 #print help(np.asfortranarray) 269 270 # test case: 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 # does it preserve matrices? 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