[SciPy-dev] band matrices in scipy

Arnd Baecker arnd.baecker at web.de
Sun Nov 16 03:57:46 CST 2003


Hi,

I am in the progress of wrapping the Lapack dsbev.f routine
(for band-matrices). Thanks to the amazing f2py I got pretty far
(actually in addition the good docs and Fernandos notes helped a lot!).
Once this is finished I would be happy to see this included in scipy ;-).

But before this could happen I have a couple of questions
(sorry, the text below is long,  but I wanted to be explicit ;-):
  - Is the following way of calling dsbev from python alright,
    (eg., I omitted ldab on the python side.)
    Would that parameter be useful to someone so that it is
    better to expose it (the full details of my few pyf lines are at
    the end of this mail)?

    In [7]: MODULENAME.dsbev?
    Type:           fortran
    String Form:    <fortran object at 0x4028ebf0>
    Namespace:      Interactive
    Docstring:
        dsbev - Function signature:
          w,z,info = dsbev(ab,[compute_v,lower])
        Required arguments:
          ab : input rank-2 array('d') with bounds (ldab,*)
        Optional arguments:
          compute_v := 1 input int
          lower := 0 input int
        Return objects:
          w : rank-1 array('d') with bounds (n)
          z : rank-2 array('d') with bounds (ldz,ldz)
          info : int

  - How can one supply better doc-strings to this routine?
    I.e., help(MODULENAME.dsbev) just gives
      "Help on fortran: <fortran object>"
    and scipy.info gives the same as ? from Ipython.

    I would like to add a more detailed description and an example.
    Is this somehow possible to do from within the .pyf wrapper?
        (I know that this is a pure f2py question, so maybe I should
         ask this question as well on the f2py mailing list...)

  - And one more technical aspect (before I forget about it):
    Is the wrapping done such that for _large_ matrices
    no unnecessary copies of arrays are made?

  - One thing which puzzled me a bit (presumably it is
    alright) concerns the return object z which contains the
    eigenvectors if compute_v=1. For compute_v=0
    I changed ldz in the wrapper such that it is of length 1
    (it is never referenced by dsbev.f). So if someone
    is just interested in eigenvalues and not eigenvectors
    a dummy variable for z has to be given.
    Is this alright (for a more "low" level routine like this)?
    In principle one could write a wrapper a la scipy.linalg.eig
    for the band matrices, but for my purposes I don't mind
    about the (sometimes needed) dummy variable.

  - Is the following correct?
    To get this into scipy.linalg.flapack the
    lines from MODULENAME.pyf have to be added to generic_flapack.pyf
    and <tchar=s,d> and <type_in_c>* and <type_in>
    have to be added at the corresponding place. And that's all?

  - Unittests: I think it would be good to have a few unit-tests
    for these routines. Presently I just created a sample
    10x10 band matrix (see below), converted this to upper band storage
    and compared the eigenvalues/eigenvectors  of scipy.linalg.eig
    with those of dsbev.
    In principle one should do this for both upper and lower
    triangle storage and for single and double.

    Would something like that be a reasonable unit-test for inclusion in
scipy
    or is there some better approach
    (better test-matrices etc.)?

  - Further routines I am thinking of wrapping in this context
    a) for symmetric band matrices: dsbevd, dsbevx
    b) for Hermitian band matrices: zhbev, zhbevd, zhbevx
    and their single precision partners.

    Any opinions?

Many thanks,


Arnd

#############################
And here the full example plus test:

MODULENAME.pyf:

# ------ snip here -------------------
!    -*- f90 -*-
python module MODULENAME ! in
    interface  ! in :MODULENAME
        !subroutine dsbev(ab,compute_v,lower) ! in :MODULENAME:dsbev.f
        subroutine dsbev(ab,compute_v,lower,n,ldab,kd,w,z,ldz,work,info) !
in :MODULENAME:dsbev.f


          callstatement
(*f2py_func)((compute_v?"V":"N"),(lower?"L":"U"),&n,&kd,ab,&ldab,w,z,&ldz,work,&info)

          callprotoargument
char*,char*,int*,int*,double*,int*,double*,double*,int*,double*,int*

          double precision dimension(ldab,*) :: ab

          integer optional,intent(in):: compute_v = 1
          check(compute_v==1||compute_v==0) compute_v
          integer optional,intent(in),check(lower==0||lower==1) :: lower =
0

          ! FIXME:  does one really need the ldab as optional argument ?
          !integer optional,check(shape(ab,0)==ldab),depend(ab) ::
ldab=shape(ab,0)
          integer intent(hide),depend(ab) :: ldab=shape(ab,0)
          integer intent(hide),depend(ab) :: n=shape(ab,1)
          integer intent(hide),depend(ab) :: kd=shape(ab,0)-1

          double precision dimension(n),intent(out),depend(n) :: w
          double  precision dimension(ldz,ldz),intent(out),depend(ldz) ::
z

          ! For compute_v=1 z is used and contains the eigenvectors
          integer intent(hide),depend(n) :: ldz=compute_v?n:1
          double precision dimension(ldz,ldz),depend(ldz) :: z

          double precision dimension(MAX(1,3*n-1)),intent(hide),depend(n)
:: work
          integer intent(out)::info
        end subroutine dsbev
    end interface
end python module MODULENAME

! This file was auto-generated with f2py (version:2.37.233-1545).
! See http://cens.ioc.ee/projects/f2py2e/
! and then modified ...
# ------ snip here -------------------


Then  (dsbev.f from lapack is needed for this)
  f2py --debug-capi -c MODULENAME.pyf dsbev.f -llapack
and a small test is
########### snip here ###########
import MODULENAME
from Numeric import *
from scipy import diag,linalg

N=10
fullmat=diag(-1.0*ones(N-1),-1)+diag(-1.0*ones(N-1),1)+diag(1.0*ones(N))

KD=2        # number of diagonals above the diagonal
LDAB=KD+1
mat=zeros( (LDAB,N),typecode=Float)

for i in arange(LDAB):       # Transform to band matrix
    mat[LDAB-i-1,i:N]=diag(fullmat,i)

w,evec,info=MODULENAME.dsbev(mat)
w_lin,evec_lin=linalg.eig(fullmat)

print sort(w)-sort(w_lin.real)

print evec[:,0]
print evec_lin[:,0]
########### snip here ###########




More information about the Scipy-dev mailing list