daredare.extern.qz

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