/Users/lyon/j4p/src/math/linearAlgebra/LUDecomposition.java
|
1 package math.linearAlgebra;
2
3 /** LU Decomposition.
4 <P>
5 For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
6 unit lower triangular matrix L, an n-by-n upper triangular matrix U,
7 and a permutation vector piv of length m so that A(piv,:) = L*U.
8 If m < n, then L is m-by-m and U is m-by-n.
9 <P>
10 The LU decompostion with pivoting always exists, even if the matrix is
11 singular, so the constructor will never fail. The primary use of the
12 LU decomposition is in the solution of square systems of simultaneous
13 linear equations. This will fail if isNonsingular() returns false.
14 */
15
16 public class LUDecomposition implements java.io.Serializable {
17
18 /* ------------------------
19 Class variables
20 * ------------------------ */
21
22 /** Array for internal storage of decomposition.
23 @serial internal array storage.
24 */
25 private double[][] LU;
26
27 /** Row and column dimensions, and pivot sign.
28 @serial column dimension.
29 @serial row dimension.
30 @serial pivot sign.
31 */
32 private int m, n, pivsign;
33
34 /** Internal storage of pivot vector.
35 @serial pivot vector.
36 */
37 private int[] piv;
38
39 /* ------------------------
40 Constructor
41 * ------------------------ */
42
43 /** LU Decomposition
44 @param A Rectangular matrix
45 Structure to access L, U and piv.
46 */
47
48 public LUDecomposition(Matrix A) {
49
50 // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
51
52 LU = A.getArrayCopy();
53 m = A.getRowDimension();
54 n = A.getColumnDimension();
55 piv = new int[m];
56 for (int i = 0; i < m; i++) {
57 piv[i] = i;
58 }
59 pivsign = 1;
60 double[] LUrowi;
61 double[] LUcolj = new double[m];
62
63 // Outer loop.
64
65 for (int j = 0; j < n; j++) {
66
67 // Make a copy of the j-th column to localize references.
68
69 for (int i = 0; i < m; i++) {
70 LUcolj[i] = LU[i][j];
71 }
72
73 // Apply previous transformations.
74
75 for (int i = 0; i < m; i++) {
76 LUrowi = LU[i];
77
78 // Most of the time is spent in the following dot product.
79
80 int kmax = Math.min(i, j);
81 double s = 0.0;
82 for (int k = 0; k < kmax; k++) {
83 s += LUrowi[k] * LUcolj[k];
84 }
85
86 LUrowi[j] = LUcolj[i] -= s;
87 }
88
89 // Find pivot and exchange if necessary.
90
91 int p = j;
92 for (int i = j + 1; i < m; i++) {
93 if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
94 p = i;
95 }
96 }
97 if (p != j) {
98 for (int k = 0; k < n; k++) {
99 double t = LU[p][k];
100 LU[p][k] = LU[j][k];
101 LU[j][k] = t;
102 }
103 int k = piv[p];
104 piv[p] = piv[j];
105 piv[j] = k;
106 pivsign = -pivsign;
107 }
108
109 // Compute multipliers.
110
111 if (j < m & LU[j][j] != 0.0) {
112 for (int i = j + 1; i < m; i++) {
113 LU[i][j] /= LU[j][j];
114 }
115 }
116 }
117 }
118
119 /* ------------------------
120 Temporary, experimental code.
121 ------------------------ *\
122
123 \** LU Decomposition, computed by Gaussian elimination.
124 <P>
125 This constructor computes L and U with the "daxpy"-based elimination
126 algorithm used in LINPACK and MATLAB. In Java, we suspect the dot-product,
127 Crout algorithm will be faster. We have temporarily included this
128 constructor until timing experiments confirm this suspicion.
129 <P>
130 @param A Rectangular matrix
131 @param linpackflag Use Gaussian elimination. Actual value ignored.
132 @return Structure to access L, U and piv.
133 *\
134
135 public LUDecomposition (Matrix A, int linpackflag) {
136 // Initialize.
137 LU = A.getArrayCopy();
138 m = A.getRowDimension();
139 n = A.getColumnDimension();
140 piv = new int[m];
141 for (int i = 0; i < m; i++) {
142 piv[i] = i;
143 }
144 pivsign = 1;
145 // Main loop.
146 for (int k = 0; k < n; k++) {
147 // Find pivot.
148 int p = k;
149 for (int i = k+1; i < m; i++) {
150 if (Math.abs(LU[i][k]) > Math.abs(LU[p][k])) {
151 p = i;
152 }
153 }
154 // Exchange if necessary.
155 if (p != k) {
156 for (int j = 0; j < n; j++) {
157 double t = LU[p][j]; LU[p][j] = LU[k][j]; LU[k][j] = t;
158 }
159 int t = piv[p]; piv[p] = piv[k]; piv[k] = t;
160 pivsign = -pivsign;
161 }
162 // Compute multipliers and eliminate k-th column.
163 if (LU[k][k] != 0.0) {
164 for (int i = k+1; i < m; i++) {
165 LU[i][k] /= LU[k][k];
166 for (int j = k+1; j < n; j++) {
167 LU[i][j] -= LU[i][k]*LU[k][j];
168 }
169 }
170 }
171 }
172 }
173
174 \* ------------------------
175 End of temporary code.
176 * ------------------------ */
177
178 /* ------------------------
179 Public Methods
180 * ------------------------ */
181
182 /** Is the matrix nonsingular?
183 @return true if U, and hence A, is nonsingular.
184 */
185
186 public boolean isNonsingular() {
187 for (int j = 0; j < n; j++) {
188 if (LU[j][j] == 0)
189 return false;
190 }
191 return true;
192 }
193
194 /** Return lower triangular factor
195 @return L
196 */
197
198 public Matrix getL() {
199 Matrix X = new Matrix(m, n);
200 double[][] L = X.getArray();
201 for (int i = 0; i < m; i++) {
202 for (int j = 0; j < n; j++) {
203 if (i > j) {
204 L[i][j] = LU[i][j];
205 } else if (i == j) {
206 L[i][j] = 1.0;
207 } else {
208 L[i][j] = 0.0;
209 }
210 }
211 }
212 return X;
213 }
214
215 /** Return upper triangular factor
216 @return U
217 */
218
219 public Matrix getU() {
220 Matrix X = new Matrix(n, n);
221 double[][] U = X.getArray();
222 for (int i = 0; i < n; i++) {
223 for (int j = 0; j < n; j++) {
224 if (i <= j) {
225 U[i][j] = LU[i][j];
226 } else {
227 U[i][j] = 0.0;
228 }
229 }
230 }
231 return X;
232 }
233
234 /** Return pivot permutation vector
235 @return piv
236 */
237
238 public int[] getPivot() {
239 int[] p = new int[m];
240 for (int i = 0; i < m; i++) {
241 p[i] = piv[i];
242 }
243 return p;
244 }
245
246 /** Return pivot permutation vector as a one-dimensional double array
247 @return (double) piv
248 */
249
250 public double[] getDoublePivot() {
251 double[] vals = new double[m];
252 for (int i = 0; i < m; i++) {
253 vals[i] = (double) piv[i];
254 }
255 return vals;
256 }
257
258 /** Determinant
259 @return det(A)
260 @exception IllegalArgumentException Matrix must be square
261 */
262
263 public double det() {
264 if (m != n) {
265 throw new IllegalArgumentException("Matrix must be square.");
266 }
267 double d = (double) pivsign;
268 for (int j = 0; j < n; j++) {
269 d *= LU[j][j];
270 }
271 return d;
272 }
273
274 /** Solve A*X = B
275 @param B A Matrix with as many rows as A and any number of columns.
276 @return X so that L*U*X = B(piv,:)
277 @exception IllegalArgumentException Matrix row dimensions must agree.
278 @exception RuntimeException Matrix is singular.
279 */
280
281 public Matrix solve(Matrix B) {
282 if (B.getRowDimension() != m) {
283 throw new IllegalArgumentException("Matrix row dimensions must agree.");
284 }
285 if (!this.isNonsingular()) {
286 throw new RuntimeException("Matrix is singular.");
287 }
288
289 // Copy right hand side with pivoting
290 int nx = B.getColumnDimension();
291 Matrix Xmat = B.getMatrix(piv, 0, nx - 1);
292 double[][] X = Xmat.getArray();
293
294 // Solve L*Y = B(piv,:)
295 for (int k = 0; k < n; k++) {
296 for (int i = k + 1; i < n; i++) {
297 for (int j = 0; j < nx; j++) {
298 X[i][j] -= X[k][j] * LU[i][k];
299 }
300 }
301 }
302 // Solve U*X = Y;
303 for (int k = n - 1; k >= 0; k--) {
304 for (int j = 0; j < nx; j++) {
305 X[k][j] /= LU[k][k];
306 }
307 for (int i = 0; i < k; i++) {
308 for (int j = 0; j < nx; j++) {
309 X[i][j] -= X[k][j] * LU[i][k];
310 }
311 }
312 }
313 return Xmat;
314 }
315 }
316