~ubuntu-branches/ubuntu/oneiric/python-scipy/oneiric-proposed

« back to all changes in this revision

Viewing changes to scipy/sparse/linalg/eigen/arpack/speigs.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-04-06 21:26:25 UTC
  • mfrom: (9.2.1 sid)
  • Revision ID: james.westby@ubuntu.com-20110406212625-3izdplobqe6fzeql
Tags: 0.9.0+dfsg1-1
* New upstream release (Closes: #614407, #579041, #569008)
* Convert to dh_python2 (Closes: #617028)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import numpy as np
2
 
import _arpack
3
 
 
4
 
__all___=['ArpackException','ARPACK_eigs', 'ARPACK_gen_eigs']
5
 
 
6
 
class ArpackException(RuntimeError):
7
 
    ARPACKErrors = { 0: """Normal exit.""",
8
 
                     3: """No shifts could be applied during a cycle of the
9
 
                     Implicitly restarted Arnoldi iteration. One possibility
10
 
                     is to increase the size of NCV relative to NEV.""",
11
 
                     -1: """N must be positive.""",
12
 
                     -2: """NEV must be positive.""",
13
 
                     -3: """NCV-NEV >= 2 and less than or equal to N.""",
14
 
                     -4: """The maximum number of Arnoldi update iteration
15
 
                     must be greater than zero.""",
16
 
                     -5: """WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'""",
17
 
                     -6: """BMAT must be one of 'I' or 'G'.""",
18
 
                     -7: """Length of private work array is not sufficient.""",
19
 
                     -8: """Error return from LAPACK eigenvalue calculation;""",
20
 
                     -9: """Starting vector is zero.""",
21
 
                     -10: """IPARAM(7) must be 1,2,3,4.""",
22
 
                     -11: """IPARAM(7) = 1 and BMAT = 'G' are incompatable.""",
23
 
                     -12: """IPARAM(1) must be equal to 0 or 1.""",
24
 
                     -9999: """Could not build an Arnoldi factorization.
25
 
                     IPARAM(5) returns the size of the current Arnoldi
26
 
                     factorization.""",
27
 
                     }
28
 
    def __init__(self, info):
29
 
        self.info = info
30
 
    def __str__(self):
31
 
        try: return self.ARPACKErrors[self.info]
32
 
        except KeyError: return "Unknown ARPACK error"
33
 
 
34
 
def check_init(n, nev, ncv):
35
 
    assert(nev <= n-4)  # ARPACK seems to cause a segfault otherwise
36
 
    if ncv is None:
37
 
        ncv = min(2*nev+1, n-1)
38
 
    maxitr = max(n, 1000)       # Maximum number of iterations
39
 
    return ncv, maxitr
40
 
 
41
 
def init_workspaces(n,nev,ncv):
42
 
    ipntr = np.zeros(14, np.int32) # Pointers into memory structure used by F77 calls
43
 
    d = np.zeros((ncv, 3), np.float64, order='FORTRAN') # Temp workspace
44
 
    # Temp workspace/error residuals upon iteration completion
45
 
    resid = np.zeros(n, np.float64)
46
 
    workd = np.zeros(3*n, np.float64) # workspace
47
 
    workl = np.zeros(3*ncv*ncv+6*ncv, np.float64) # more workspace
48
 
    # Storage for the Arnoldi basis vectors
49
 
    v = np.zeros((n, ncv), dtype=np.float64, order='FORTRAN')
50
 
    return (ipntr, d, resid, workd, workl, v)
51
 
 
52
 
def init_debug():
53
 
    # Causes various debug info to be printed by ARPACK
54
 
    _arpack.debug.ndigit = -3
55
 
    _arpack.debug.logfil = 6
56
 
    _arpack.debug.mnaitr = 0
57
 
    _arpack.debug.mnapps = 0
58
 
    _arpack.debug.mnaupd = 1
59
 
    _arpack.debug.mnaup2 = 0
60
 
    _arpack.debug.mneigh = 0
61
 
    _arpack.debug.mneupd = 1
62
 
 
63
 
def init_postproc_workspace(n, nev, ncv):
64
 
    # Used as workspace and to return eigenvectors if requested. Not touched if
65
 
    # eigenvectors are not requested
66
 
    workev = np.zeros(3*ncv, np.float64) # More workspace
67
 
    select = np.zeros(ncv, np.int32) # Used as internal workspace since dneupd
68
 
                                   # parameter HOWMNY == 'A'
69
 
    return (workev, select)
70
 
 
71
 
def postproc(n, nev, ncv, sigmar, sigmai, bmat, which,
72
 
             tol, resid, v, iparam, ipntr, workd, workl, info):
73
 
    workev, select = init_postproc_workspace(n, nev, ncv)
74
 
    ierr = 0
75
 
    # Postprocess the Arnouldi vectors to extract eigenvalues/vectors
76
 
    # If dneupd's first paramter is 'True' the eigenvectors are also calculated,
77
 
    # 'False' only the eigenvalues
78
 
    dr,di,z,info = _arpack.dneupd(
79
 
        True, 'A', select, sigmar, sigmai, workev, bmat, which, nev, tol, resid, v,
80
 
        iparam, ipntr, workd, workl, info)
81
 
 
82
 
    if np.abs(di[:-1]).max() == 0: dr = dr[:-1]
83
 
    else: dr =  dr[:-1] + 1j*di[:-1]
84
 
    return (dr, z[:,:-1])
85
 
 
86
 
 
87
 
def ARPACK_eigs(matvec, n, nev, which='SM', ncv=None, tol=1e-14):
88
 
    """
89
 
    Calculate eigenvalues for system with matrix-vector product matvec, dimension n
90
 
 
91
 
    Arguments
92
 
    =========
93
 
    matvec -- Function that provides matrix-vector product, i.e. matvec(x) -> A*x
94
 
    n -- Matrix dimension of the problem
95
 
    nev -- Number of eigenvalues to calculate
96
 
    which -- Spectrum selection. See details below. Defaults to 'SM'
97
 
    ncv -- Number of Arnoldi basisvectors to use. If None, default to 2*nev+1
98
 
    tol -- Numerical tollerance for Arnouldi iteration convergence. Defaults to 1e-14
99
 
 
100
 
    Spectrum Selection
101
 
    ==================
102
 
    which can take one of several values:
103
 
 
104
 
    'LM' -> Request eigenvalues with largest magnitude.
105
 
    'SM' -> Request eigenvalues with smallest magnitude.
106
 
    'LR' -> Request eigenvalues with largest real part.
107
 
    'SR' -> Request eigenvalues with smallest real part.
108
 
    'LI' -> Request eigenvalues with largest imaginary part.
109
 
    'SI' -> Request eigenvalues with smallest imaginary part.
110
 
 
111
 
    Return Values
112
 
    =============
113
 
    (eig_vals, eig_vecs) where eig_vals are the requested eigenvalues and
114
 
    eig_vecs the corresponding eigenvectors. If all the eigenvalues are real,
115
 
    eig_vals is a real array but if some eigenvalues are complex it is a
116
 
    complex array.
117
 
 
118
 
    """
119
 
    bmat = 'I'                          # Standard eigenproblem
120
 
    ncv, resid, iparam, ipntr, v, workd, workl, info = ARPACK_iteration(
121
 
        matvec, lambda x: x, n, bmat, which, nev, tol, ncv, mode=1)
122
 
    return postproc(n, nev, ncv, 0., 0., bmat, which, tol,
123
 
                    resid, v, iparam, ipntr, workd, workl, info)
124
 
 
125
 
def ARPACK_gen_eigs(matvec, sigma_solve, n, sigma, nev, which='LR', ncv=None, tol=1e-14):
126
 
    """
127
 
    Calculate eigenvalues close to sigma for generalised eigen system
128
 
 
129
 
    Given a system [A]x = k_i*[M]x where [A] and [M] are matrices and k_i are
130
 
    eigenvalues, nev eigenvalues close to sigma are calculated. The user needs
131
 
    to provide routines that calculate [M]*x and solve [A]-sigma*[M]*x = b for x.
132
 
 
133
 
    Arguments
134
 
    =========
135
 
    matvec -- Function that provides matrix-vector product, i.e. matvec(x) -> [M]*x
136
 
    sigma_solve -- sigma_solve(b) -> x, where [A]-sigma*[M]*x = b
137
 
    n -- Matrix dimension of the problem
138
 
    sigma -- Eigenvalue spectral shift real value
139
 
    nev -- Number of eigenvalues to calculate
140
 
    which -- Spectrum selection. See details below. Defaults to 'LR'
141
 
    ncv -- Number of Arnoldi basisvectors to use. If None, default to 2*nev+1
142
 
    tol -- Numerical tollerance for Arnouldi iteration convergence. Defaults to 1e-14
143
 
 
144
 
    Spectrum Shift
145
 
    ==============
146
 
 
147
 
    The spectrum of the orignal system is shifted by sigma. This transforms the
148
 
    original eigenvalues to be 1/(original_eig-sigma) in the shifted
149
 
    system. ARPACK then operates on the shifted system, transforming it back to
150
 
    the original system in a postprocessing step.
151
 
 
152
 
    The spectrum shift causes eigenvalues close to sigma to become very large
153
 
    in the transformed system. This allows quick convergence for these
154
 
    eigenvalues. This is particularly useful if a system has a number of
155
 
    trivial zero-eigenvalues that are to be ignored.
156
 
 
157
 
    Spectrum Selection
158
 
    ==================
159
 
    which can take one of several values:
160
 
 
161
 
    'LM' -> Request spectrum shifted eigenvalues with largest magnitude.
162
 
    'SM' -> Request spectrum shifted eigenvalues with smallest magnitude.
163
 
    'LR' -> Request spectrum shifted eigenvalues with largest real part.
164
 
    'SR' -> Request spectrum shifted eigenvalues with smallest real part.
165
 
    'LI' -> Request spectrum shifted eigenvalues with largest imaginary part.
166
 
    'SI' -> Request spectrum shifted eigenvalues with smallest imaginary part.
167
 
 
168
 
    The effect on the actual system is:
169
 
    'LM' -> Eigenvalues closest to sigma on the complex plane
170
 
    'LR' -> Eigenvalues with real part > sigma, provided they exist
171
 
 
172
 
 
173
 
    Return Values
174
 
    =============
175
 
    (eig_vals, eig_vecs) where eig_vals are the requested eigenvalues and
176
 
    eig_vecs the corresponding eigenvectors. If all the eigenvalues are real,
177
 
    eig_vals is a real array but if some eigenvalues are complex it is a
178
 
    complex array. The eigenvalues and vectors correspond to the original
179
 
    system, not the shifted system. The shifted system is only used interally.
180
 
 
181
 
    """
182
 
    bmat = 'G'                          # Generalised eigenproblem
183
 
    ncv, resid, iparam, ipntr, v, workd, workl, info = ARPACK_iteration(
184
 
        matvec, sigma_solve, n, bmat, which, nev, tol, ncv, mode=3)
185
 
    sigmar = sigma
186
 
    sigmai = 0.
187
 
    return postproc(n, nev, ncv, sigmar, sigmai, bmat, which, tol,
188
 
                    resid, v, iparam, ipntr, workd, workl, info)
189
 
 
190
 
def ARPACK_iteration(matvec, sigma_solve, n, bmat, which, nev, tol, ncv, mode):
191
 
    ncv, maxitr = check_init(n, nev, ncv)
192
 
    ipntr, d, resid, workd, workl, v = init_workspaces(n,nev,ncv)
193
 
    #init_debug()
194
 
    ishfts = 1         # Some random arpack parameter
195
 
    # Some random arpack parameter (I think it tells ARPACK to solve the
196
 
    # general eigenproblem using shift-invert
197
 
    iparam = np.zeros(11, np.int32) # Array with assorted extra paramters for F77 call
198
 
    iparam[[0,2,6]] = ishfts, maxitr, mode
199
 
    ido = 0                # Communication variable used by ARPACK to tell the user what to do
200
 
    info = 0               # Used for error reporting
201
 
    # Arnouldi iteration.
202
 
    while True:
203
 
        ido,resid,v,iparam,ipntr,info = _arpack.dnaupd(
204
 
            ido, bmat, which, nev, tol, resid, v, iparam, ipntr, workd, workl, info)
205
 
        if ido == -1 or ido == 1 and mode not in (3,4):
206
 
            # Perform y = inv[A - sigma*M]*M*x
207
 
            x = workd[ipntr[0]-1:ipntr[0]+n-1]
208
 
            Mx = matvec(x)    # Mx = [M]*x
209
 
            workd[ipntr[1]-1:ipntr[1]+n-1] = sigma_solve(Mx)
210
 
        elif ido == 1: # Perform y = inv[A - sigma*M]*M*x using saved M*x
211
 
            # Mx = [M]*x where it was saved by ARPACK
212
 
            Mx = workd[ipntr[2]-1:ipntr[2]+n-1]
213
 
            workd[ipntr[1]-1:ipntr[1]+n-1] = sigma_solve(Mx)
214
 
        elif ido == 2: # Perform y = M*x
215
 
            x = workd[ipntr[0]-1:ipntr[0]+n-1]
216
 
            workd[ipntr[1]-1:ipntr[1]+n-1] = matvec(x)
217
 
        else:          # Finished, or error
218
 
            break
219
 
        if info == 1:
220
 
            warn.warn("Maximum number of iterations taken: %s"%iparam[2])
221
 
        elif info != 0:
222
 
            raise ArpackException(info)
223
 
 
224
 
    return (ncv, resid, iparam, ipntr, v, workd, workl, info)