1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.math4.legacy.linear;
19
20 import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
21 import org.apache.commons.math4.core.jdkmath.JdkMath;
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class CholeskyDecomposition {
48
49
50
51
52 public static final double DEFAULT_RELATIVE_SYMMETRY_THRESHOLD = 1.0e-15;
53
54
55
56
57 public static final double DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD = 1.0e-10;
58
59 private final double[][] lTData;
60
61 private RealMatrix cachedL;
62
63 private RealMatrix cachedLT;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public CholeskyDecomposition(final RealMatrix matrix) {
84 this(matrix, DEFAULT_RELATIVE_SYMMETRY_THRESHOLD,
85 DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public CholeskyDecomposition(final RealMatrix matrix,
104 final double relativeSymmetryThreshold,
105 final double absolutePositivityThreshold) {
106 if (!matrix.isSquare()) {
107 throw new NonSquareMatrixException(matrix.getRowDimension(),
108 matrix.getColumnDimension());
109 }
110
111 final int order = matrix.getRowDimension();
112 lTData = matrix.getData();
113 cachedL = null;
114 cachedLT = null;
115
116
117 for (int i = 0; i < order; ++i) {
118 final double[] lI = lTData[i];
119
120
121 for (int j = i + 1; j < order; ++j) {
122 final double[] lJ = lTData[j];
123 final double lIJ = lI[j];
124 final double lJI = lJ[i];
125 final double maxDelta =
126 relativeSymmetryThreshold * JdkMath.max(JdkMath.abs(lIJ), JdkMath.abs(lJI));
127 if (JdkMath.abs(lIJ - lJI) > maxDelta) {
128 throw new NonSymmetricMatrixException(i, j, relativeSymmetryThreshold);
129 }
130 lJ[i] = 0;
131 }
132 }
133
134
135 for (int i = 0; i < order; ++i) {
136
137 final double[] ltI = lTData[i];
138
139
140 if (ltI[i] <= absolutePositivityThreshold) {
141 throw new NonPositiveDefiniteMatrixException(ltI[i], i, absolutePositivityThreshold);
142 }
143
144 ltI[i] = JdkMath.sqrt(ltI[i]);
145 final double inverse = 1.0 / ltI[i];
146
147 for (int q = order - 1; q > i; --q) {
148 ltI[q] *= inverse;
149 final double[] ltQ = lTData[q];
150 for (int p = q; p < order; ++p) {
151 ltQ[p] -= ltI[q] * ltI[p];
152 }
153 }
154 }
155 }
156
157
158
159
160
161
162 public RealMatrix getL() {
163 if (cachedL == null) {
164 cachedL = getLT().transpose();
165 }
166 return cachedL;
167 }
168
169
170
171
172
173
174 public RealMatrix getLT() {
175
176 if (cachedLT == null) {
177 cachedLT = MatrixUtils.createRealMatrix(lTData);
178 }
179
180
181 return cachedLT;
182 }
183
184
185
186
187
188 public double getDeterminant() {
189 double determinant = 1.0;
190 for (int i = 0; i < lTData.length; ++i) {
191 double lTii = lTData[i][i];
192 determinant *= lTii * lTii;
193 }
194 return determinant;
195 }
196
197
198
199
200
201 public DecompositionSolver getSolver() {
202 return new Solver(lTData);
203 }
204
205
206 private static final class Solver implements DecompositionSolver {
207
208 private final double[][] lTData;
209
210
211
212
213
214 private Solver(final double[][] lTData) {
215 this.lTData = lTData;
216 }
217
218
219 @Override
220 public boolean isNonSingular() {
221
222 return true;
223 }
224
225
226 @Override
227 public RealVector solve(final RealVector b) {
228 final int m = lTData.length;
229 if (b.getDimension() != m) {
230 throw new DimensionMismatchException(b.getDimension(), m);
231 }
232
233 final double[] x = b.toArray();
234
235
236 for (int j = 0; j < m; j++) {
237 final double[] lJ = lTData[j];
238 x[j] /= lJ[j];
239 final double xJ = x[j];
240 for (int i = j + 1; i < m; i++) {
241 x[i] -= xJ * lJ[i];
242 }
243 }
244
245
246 for (int j = m - 1; j >= 0; j--) {
247 x[j] /= lTData[j][j];
248 final double xJ = x[j];
249 for (int i = 0; i < j; i++) {
250 x[i] -= xJ * lTData[i][j];
251 }
252 }
253
254 return new ArrayRealVector(x, false);
255 }
256
257
258 @Override
259 public RealMatrix solve(RealMatrix b) {
260 final int m = lTData.length;
261 if (b.getRowDimension() != m) {
262 throw new DimensionMismatchException(b.getRowDimension(), m);
263 }
264
265 final int nColB = b.getColumnDimension();
266 final double[][] x = b.getData();
267
268
269 for (int j = 0; j < m; j++) {
270 final double[] lJ = lTData[j];
271 final double lJJ = lJ[j];
272 final double[] xJ = x[j];
273 for (int k = 0; k < nColB; ++k) {
274 xJ[k] /= lJJ;
275 }
276 for (int i = j + 1; i < m; i++) {
277 final double[] xI = x[i];
278 final double lJI = lJ[i];
279 for (int k = 0; k < nColB; ++k) {
280 xI[k] -= xJ[k] * lJI;
281 }
282 }
283 }
284
285
286 for (int j = m - 1; j >= 0; j--) {
287 final double lJJ = lTData[j][j];
288 final double[] xJ = x[j];
289 for (int k = 0; k < nColB; ++k) {
290 xJ[k] /= lJJ;
291 }
292 for (int i = 0; i < j; i++) {
293 final double[] xI = x[i];
294 final double lIJ = lTData[i][j];
295 for (int k = 0; k < nColB; ++k) {
296 xI[k] -= xJ[k] * lIJ;
297 }
298 }
299 }
300
301 return new Array2DRowRealMatrix(x);
302 }
303
304
305
306
307
308
309 @Override
310 public RealMatrix getInverse() {
311 return solve(MatrixUtils.createRealIdentityMatrix(lTData.length));
312 }
313 }
314 }