Skip to content
Newer
Older
100644 226 lines (192 sloc) 7.39 KB
4c7752c linalg:
warren.weckesser authored
1 """Schur decomposition functions."""
2
3 import numpy
4 from numpy import asarray_chkfinite, single
5
6 # Local imports.
7 import misc
43a8f33 @pv BUG: linalg: more robust data ovewrite behavior
pv authored
8 from misc import LinAlgError, _datacopied
4c7752c linalg:
warren.weckesser authored
9 from lapack import get_lapack_funcs
10 from decomp import eigvals
11
12
13 __all__ = ['schur', 'rsf2csf']
14
15 _double_precision = ['i','l','d']
16
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
17 def schur(a, output='real', lwork=None, overwrite_a=False, sort=None):
4c7752c linalg:
warren.weckesser authored
18 """Compute Schur decomposition of a matrix.
19
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
20 The Schur decomposition is::
4c7752c linalg:
warren.weckesser authored
21
22 A = Z T Z^H
23
24 where Z is unitary and T is either upper-triangular, or for real
25 Schur decomposition (output='real'), quasi-upper triangular. In
26 the quasi-triangular form, 2x2 blocks describing complex-valued
27 eigenvalue pairs may extrude from the diagonal.
28
29 Parameters
30 ----------
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
31 a : ndarray, shape (M, M)
4c7752c linalg:
warren.weckesser authored
32 Matrix to decompose
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
33 output : {'real', 'complex'}, optional
4c7752c linalg:
warren.weckesser authored
34 Construct the real or complex Schur decomposition (for real matrices).
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
35 lwork : int, optional
4c7752c linalg:
warren.weckesser authored
36 Work array size. If None or -1, it is automatically computed.
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
37 overwrite_a : bool, optional
38 Whether to overwrite data in a (may improve performance).
39 sort : {None, callable, 'lhp', 'rhp', 'iuc', 'ouc'}, optional
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
40 Specifies whether the upper eigenvalues should be sorted. A callable
41 may be passed that, given a eigenvalue, returns a boolean denoting
42 whether the eigenvalue should be sorted to the top-left (True).
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
43 Alternatively, string parameters may be used::
44
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
45 'lhp' Left-hand plane (x.real < 0.0)
46 'rhp' Right-hand plane (x.real > 0.0)
47 'iuc' Inside the unit circle (x*x.conjugate() <= 1.0)
48 'ouc' Outside the unit circle (x*x.conjugate() > 1.0)
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
49
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
50 Defaults to None (no sorting).
4c7752c linalg:
warren.weckesser authored
51
52 Returns
53 -------
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
54 T : ndarray, shape (M, M)
4c7752c linalg:
warren.weckesser authored
55 Schur form of A. It is real-valued for the real Schur decomposition.
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
56 Z : ndarray, shape (M, M)
4c7752c linalg:
warren.weckesser authored
57 An unitary Schur transformation matrix for A.
58 It is real-valued for the real Schur decomposition.
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
59 sdim : int
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
60 If and only if sorting was requested, a third return value will
61 contain the number of eigenvalues satisfying the sort condition.
62
63 Raises
64 ------
65 LinAlgError
66 Error raised under three conditions:
878dd58 @rgommers DOC: fix more Sphinx warnings.
rgommers authored
67
f57eec9 @rgommers MAINT: run reindent on all .py files.
rgommers authored
68 1. The algorithm failed due to a failure of the QR algorithm to
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
69 compute all eigenvalues
70 2. If eigenvalue sorting was requested, the eigenvalues could not be
71 reordered due to a failure to separate eigenvalues, usually because
72 of poor conditioning
73 3. If eigenvalue sorting was requested, roundoff errors caused the
74 leading eigenvalues to no longer satisfy the sorting condition
4c7752c linalg:
warren.weckesser authored
75
76 See also
77 --------
78 rsf2csf : Convert real Schur form to complex Schur form
79
80 """
81 if not output in ['real','complex','r','c']:
df53c6b ENH: linalg: update 'raise' statements
warren.weckesser authored
82 raise ValueError("argument must be 'real', or 'complex'")
4c7752c linalg:
warren.weckesser authored
83 a1 = asarray_chkfinite(a)
84 if len(a1.shape) != 2 or (a1.shape[0] != a1.shape[1]):
df53c6b ENH: linalg: update 'raise' statements
warren.weckesser authored
85 raise ValueError('expected square matrix')
4c7752c linalg:
warren.weckesser authored
86 typ = a1.dtype.char
87 if output in ['complex','c'] and typ not in ['F','D']:
88 if typ in _double_precision:
89 a1 = a1.astype('D')
90 typ = 'D'
91 else:
92 a1 = a1.astype('F')
93 typ = 'F'
43a8f33 @pv BUG: linalg: more robust data ovewrite behavior
pv authored
94 overwrite_a = overwrite_a or (_datacopied(a1, a))
4c7752c linalg:
warren.weckesser authored
95 gees, = get_lapack_funcs(('gees',), (a1,))
96 if lwork is None or lwork == -1:
97 # get optimal work array
43a8f33 @pv BUG: linalg: more robust data ovewrite behavior
pv authored
98 result = gees(lambda x: None, a1, lwork=-1)
b1061c5 @rgommers TST: Avoid ComplexWarning in QR and Schur decomposition.
rgommers authored
99 lwork = result[-2][0].real.astype(numpy.int)
f57eec9 @rgommers MAINT: run reindent on all .py files.
rgommers authored
100
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
101 if sort is None:
102 sort_t = 0
103 sfunction = lambda x: None
104 else:
105 sort_t = 1
106 if callable(sort):
107 sfunction = sort
108 elif sort == 'lhp':
6bac494 @pv BUG: linalg: fix schur sort python 2.4 compatibility issue
pv authored
109 sfunction = lambda x: (numpy.real(x) < 0.0)
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
110 elif sort == 'rhp':
6bac494 @pv BUG: linalg: fix schur sort python 2.4 compatibility issue
pv authored
111 sfunction = lambda x: (numpy.real(x) >= 0.0)
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
112 elif sort == 'iuc':
113 sfunction = lambda x: (abs(x) <= 1.0)
114 elif sort == 'ouc':
115 sfunction = lambda x: (abs(x) > 1.0)
116 else:
117 raise ValueError("sort parameter must be None, a callable, or " +
118 "one of ('lhp','rhp','iuc','ouc')")
f57eec9 @rgommers MAINT: run reindent on all .py files.
rgommers authored
119
120 result = gees(sfunction, a1, lwork=lwork, overwrite_a=overwrite_a,
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
121 sort_t=sort_t)
f57eec9 @rgommers MAINT: run reindent on all .py files.
rgommers authored
122
4c7752c linalg:
warren.weckesser authored
123 info = result[-1]
124 if info < 0:
125 raise ValueError('illegal value in %d-th argument of internal gees'
126 % -info)
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
127 elif info == a1.shape[0] + 1:
128 raise LinAlgError('Eigenvalues could not be separated for reordering.')
129 elif info == a1.shape[0] + 2:
130 raise LinAlgError('Leading eigenvalues do not satisfy sort condition.')
4c7752c linalg:
warren.weckesser authored
131 elif info > 0:
132 raise LinAlgError("Schur form not found. Possibly ill-conditioned.")
f57eec9 @rgommers MAINT: run reindent on all .py files.
rgommers authored
133
517ba49 Added switches to order the eigenvalues in the Schur decomposition re…
ArmstrongJ authored
134 if sort_t == 0:
135 return result[0], result[-3]
136 else:
137 return result[0], result[-3], result[1]
4c7752c linalg:
warren.weckesser authored
138
139
140 eps = numpy.finfo(float).eps
141 feps = numpy.finfo(single).eps
142
143 _array_kind = {'b':0, 'h':0, 'B': 0, 'i':0, 'l': 0, 'f': 0, 'd': 0, 'F': 1, 'D': 1}
144 _array_precision = {'i': 1, 'l': 1, 'f': 0, 'd': 1, 'F': 0, 'D': 1}
145 _array_type = [['f', 'd'], ['F', 'D']]
146
147 def _commonType(*arrays):
148 kind = 0
149 precision = 0
150 for a in arrays:
151 t = a.dtype.char
152 kind = max(kind, _array_kind[t])
153 precision = max(precision, _array_precision[t])
154 return _array_type[kind][precision]
155
156 def _castCopy(type, *arrays):
157 cast_arrays = ()
158 for a in arrays:
159 if a.dtype.char == type:
160 cast_arrays = cast_arrays + (a.copy(),)
161 else:
162 cast_arrays = cast_arrays + (a.astype(type),)
163 if len(cast_arrays) == 1:
164 return cast_arrays[0]
165 else:
166 return cast_arrays
167
168
169 def rsf2csf(T, Z):
170 """Convert real Schur form to complex Schur form.
171
172 Convert a quasi-diagonal real-valued Schur form to the upper triangular
173 complex-valued Schur form.
174
175 Parameters
176 ----------
177 T : array, shape (M, M)
178 Real Schur form of the original matrix
179 Z : array, shape (M, M)
180 Schur transformation matrix
181
182 Returns
183 -------
184 T : array, shape (M, M)
185 Complex Schur form of the original matrix
186 Z : array, shape (M, M)
187 Schur transformation matrix corresponding to the complex form
188
189 See also
190 --------
191 schur : Schur decompose a matrix
192
193 """
194 Z, T = map(asarray_chkfinite, (Z, T))
195 if len(Z.shape) != 2 or Z.shape[0] != Z.shape[1]:
196 raise ValueError("matrix must be square.")
197 if len(T.shape) != 2 or T.shape[0] != T.shape[1]:
198 raise ValueError("matrix must be square.")
199 if T.shape[0] != Z.shape[0]:
200 raise ValueError("matrices must be same dimension.")
201 N = T.shape[0]
202 arr = numpy.array
203 t = _commonType(Z, T, arr([3.0],'F'))
204 Z, T = _castCopy(t, Z, T)
205 conj = numpy.conj
206 dot = numpy.dot
207 r_ = numpy.r_
208 transp = numpy.transpose
209 for m in range(N-1, 0, -1):
210 if abs(T[m,m-1]) > eps*(abs(T[m-1,m-1]) + abs(T[m,m])):
211 k = slice(m-1, m+1)
212 mu = eigvals(T[k,k]) - T[m,m]
213 r = misc.norm([mu[0], T[m,m-1]])
214 c = mu[0] / r
215 s = T[m,m-1] / r
216 G = r_[arr([[conj(c), s]], dtype=t), arr([[-s, c]], dtype=t)]
217 Gc = conj(transp(G))
218 j = slice(m-1, N)
219 T[k,j] = dot(G, T[k,j])
220 i = slice(0, m+1)
221 T[i,k] = dot(T[i,k], Gc)
222 i = slice(0, N)
223 Z[i,k] = dot(Z[i,k], Gc)
224 T[m,m-1] = 0.0;
225 return T, Z
Something went wrong with that request. Please try again.