/Users/lyon/j4p/src/math/linearAlgebra/CholeskyDecomposition.java
|
1 package math.linearAlgebra;
2
3 /** Cholesky Decomposition.
4 <P>
5 For a symmetric, positive definite matrix A, the Cholesky decomposition
6 is an lower triangular matrix L so that A = L*L'.
7 <P>
8 If the matrix is not symmetric or positive definite, the constructor
9 returns a partial decomposition and sets an internal flag that may
10 be queried by the isSPD() method.
11 */
12
13 public class CholeskyDecomposition implements java.io.Serializable {
14
15 /* ------------------------
16 Class variables
17 * ------------------------ */
18
19 /** Array for internal storage of decomposition.
20 @serial internal array storage.
21 */
22 private double[][] L;
23
24 /** Row and column dimension (square matrix).
25 @serial matrix dimension.
26 */
27 private int n;
28
29 /** Symmetric and positive definite flag.
30 @serial is symmetric and positive definite flag.
31 */
32 private boolean isspd;
33
34 /* ------------------------
35 Constructor
36 * ------------------------ */
37
38 /** Cholesky algorithm for symmetric and positive definite matrix.
39 @param Arg Square, symmetric matrix.
40 Structure to access L and isspd flag.
41 */
42
43 public CholeskyDecomposition(Matrix Arg) {
44 // Initialize.
45 double[][] A = Arg.getArray();
46 n = Arg.getRowDimension();
47 L = new double[n][n];
48 isspd = (Arg.getColumnDimension() == n);
49 // Main loop.
50 for (int j = 0; j < n; j++) {
51 double[] Lrowj = L[j];
52 double d = 0.0;
53 for (int k = 0; k < j; k++) {
54 double[] Lrowk = L[k];
55 double s = 0.0;
56 for (int i = 0; i < k; i++) {
57 s += Lrowk[i] * Lrowj[i];
58 }
59 Lrowj[k] = s = (A[j][k] - s) / L[k][k];
60 d = d + s * s;
61 isspd = isspd & (A[k][j] == A[j][k]);
62 }
63 d = A[j][j] - d;
64 isspd = isspd & (d > 0.0);
65 L[j][j] = Math.sqrt(Math.max(d, 0.0));
66 for (int k = j + 1; k < n; k++) {
67 L[j][k] = 0.0;
68 }
69 }
70 }
71
72 /* ------------------------
73 Temporary, experimental code.
74 * ------------------------ *\
75
76 \** Right Triangular Cholesky Decomposition.
77 <P>
78 For a symmetric, positive definite matrix A, the Right Cholesky
79 decomposition is an upper triangular matrix R so that A = R'*R.
80 This constructor computes R with the Fortran inspired column oriented
81 algorithm used in LINPACK and MATLAB. In Java, we suspect a row oriented,
82 lower triangular decomposition is faster. We have temporarily included
83 this constructor here until timing experiments confirm this suspicion.
84 *\
85
86 \** Array for internal storage of right triangular decomposition. **\
87 private transient double[][] R;
88
89 \** Cholesky algorithm for symmetric and positive definite matrix.
90 @param A Square, symmetric matrix.
91 @param rightflag Actual value ignored.
92 @return Structure to access R and isspd flag.
93 *\
94
95 public CholeskyDecomposition (Matrix Arg, int rightflag) {
96 // Initialize.
97 double[][] A = Arg.getArray();
98 n = Arg.getColumnDimension();
99 R = new double[n][n];
100 isspd = (Arg.getColumnDimension() == n);
101 // Main loop.
102 for (int j = 0; j < n; j++) {
103 double d = 0.0;
104 for (int k = 0; k < j; k++) {
105 double s = A[k][j];
106 for (int i = 0; i < k; i++) {
107 s = s - R[i][k]*R[i][j];
108 }
109 R[k][j] = s = s/R[k][k];
110 d = d + s*s;
111 isspd = isspd & (A[k][j] == A[j][k]);
112 }
113 d = A[j][j] - d;
114 isspd = isspd & (d > 0.0);
115 R[j][j] = Math.sqrt(Math.max(d,0.0));
116 for (int k = j+1; k < n; k++) {
117 R[k][j] = 0.0;
118 }
119 }
120 }
121
122 \** Return upper triangular factor.
123 @return R
124 *\
125
126 public Matrix getR () {
127 return new Matrix(R,n,n);
128 }
129
130 \* ------------------------
131 End of temporary code.
132 * ------------------------ */
133
134 /* ------------------------
135 Public Methods
136 * ------------------------ */
137
138 /** Is the matrix symmetric and positive definite?
139 @return true if A is symmetric and positive definite.
140 */
141
142 public boolean isSPD() {
143 return isspd;
144 }
145
146 /** Return triangular factor.
147 @return L
148 */
149
150 public Matrix getL() {
151 return new Matrix(L, n, n);
152 }
153
154 /** Solve A*X = B
155 @param B A Matrix with as many rows as A and any number of columns.
156 @return X so that L*L'*X = B
157 @exception IllegalArgumentException Matrix row dimensions must agree.
158 @exception RuntimeException Matrix is not symmetric positive definite.
159 */
160
161 public Matrix solve(Matrix B) {
162 if (B.getRowDimension() != n) {
163 throw new IllegalArgumentException("Matrix row dimensions must agree.");
164 }
165 if (!isspd) {
166 throw new RuntimeException("Matrix is not symmetric positive definite.");
167 }
168
169 // Copy right hand side.
170 double[][] X = B.getArrayCopy();
171 int nx = B.getColumnDimension();
172
173 // Solve L*Y = B;
174 for (int k = 0; k < n; k++) {
175 for (int i = k + 1; i < n; i++) {
176 for (int j = 0; j < nx; j++) {
177 X[i][j] -= X[k][j] * L[i][k];
178 }
179 }
180 for (int j = 0; j < nx; j++) {
181 X[k][j] /= L[k][k];
182 }
183 }
184
185 // Solve L'*X = Y;
186 for (int k = n - 1; k >= 0; k--) {
187 for (int j = 0; j < nx; j++) {
188 X[k][j] /= L[k][k];
189 }
190 for (int i = 0; i < k; i++) {
191 for (int j = 0; j < nx; j++) {
192 X[i][j] -= X[k][j] * L[k][i];
193 }
194 }
195 }
196 return new Matrix(X, n, nx);
197 }
198 }
199