1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.optim.nonlinear.vector.leastsquares;
23
24 import org.hipparchus.exception.MathIllegalArgumentException;
25 import org.hipparchus.exception.MathIllegalStateException;
26 import org.hipparchus.exception.NullArgumentException;
27 import org.hipparchus.linear.ArrayRealVector;
28 import org.hipparchus.linear.MatrixDecomposer;
29 import org.hipparchus.linear.MatrixUtils;
30 import org.hipparchus.linear.QRDecomposer;
31 import org.hipparchus.linear.RealMatrix;
32 import org.hipparchus.linear.RealVector;
33 import org.hipparchus.optim.ConvergenceChecker;
34 import org.hipparchus.optim.LocalizedOptimFormats;
35 import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Evaluation;
36 import org.hipparchus.util.Incrementor;
37 import org.hipparchus.util.Pair;
38
39
40
41
42
43
44
45
46
47
48
49 public class GaussNewtonOptimizer implements LeastSquaresOptimizer {
50
51
52
53
54
55
56 private static final double SINGULARITY_THRESHOLD = 1e-11;
57
58
59 private final MatrixDecomposer decomposer;
60
61
62 private final boolean formNormalEquations;
63
64
65
66
67
68
69
70
71 public GaussNewtonOptimizer() {
72 this(new QRDecomposer(SINGULARITY_THRESHOLD), false);
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public GaussNewtonOptimizer(final MatrixDecomposer decomposer,
88 final boolean formNormalEquations) {
89 this.decomposer = decomposer;
90 this.formNormalEquations = formNormalEquations;
91 }
92
93
94
95
96
97
98 public MatrixDecomposer getDecomposer() {
99 return decomposer;
100 }
101
102
103
104
105
106
107
108 public GaussNewtonOptimizer withDecomposer(final MatrixDecomposer newDecomposer) {
109 return new GaussNewtonOptimizer(newDecomposer, this.isFormNormalEquations());
110 }
111
112
113
114
115
116
117
118
119 public boolean isFormNormalEquations() {
120 return formNormalEquations;
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134 public GaussNewtonOptimizer withFormNormalEquations(final boolean newFormNormalEquations) {
135 return new GaussNewtonOptimizer(this.getDecomposer(), newFormNormalEquations);
136 }
137
138
139 @Override
140 public Optimum optimize(final LeastSquaresProblem lsp) {
141
142 final Incrementor evaluationCounter = lsp.getEvaluationCounter();
143 final Incrementor iterationCounter = lsp.getIterationCounter();
144 final ConvergenceChecker<Evaluation> checker
145 = lsp.getConvergenceChecker();
146
147
148 if (checker == null) {
149 throw new NullArgumentException();
150 }
151
152 RealVector currentPoint = lsp.getStart();
153
154
155 Evaluation current = null;
156 while (true) {
157 iterationCounter.increment();
158
159
160 Evaluation previous = current;
161
162 evaluationCounter.increment();
163 current = lsp.evaluate(currentPoint);
164 final RealVector currentResiduals = current.getResiduals();
165 final RealMatrix weightedJacobian = current.getJacobian();
166 currentPoint = current.getPoint();
167
168
169 if (previous != null &&
170 checker.converged(iterationCounter.getCount(), previous, current)) {
171 return Optimum.of(current,
172 evaluationCounter.getCount(),
173 iterationCounter.getCount());
174 }
175
176
177 final RealMatrix lhs;
178 final RealVector rhs;
179 if (this.formNormalEquations) {
180 final Pair<RealMatrix, RealVector> normalEquation =
181 computeNormalMatrix(weightedJacobian, currentResiduals);
182 lhs = normalEquation.getFirst();
183 rhs = normalEquation.getSecond();
184 } else {
185 lhs = weightedJacobian;
186 rhs = currentResiduals;
187 }
188 final RealVector dX;
189 try {
190 dX = this.decomposer.decompose(lhs).solve(rhs);
191 } catch (MathIllegalArgumentException e) {
192
193 throw new MathIllegalStateException(
194 LocalizedOptimFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM, e);
195 }
196
197 currentPoint = currentPoint.add(dX);
198 }
199 }
200
201
202 @Override
203 public String toString() {
204 return "GaussNewtonOptimizer{" +
205 "decomposer=" + decomposer +
206 ", formNormalEquations=" + formNormalEquations +
207 '}';
208 }
209
210
211
212
213
214
215
216
217 private static Pair<RealMatrix, RealVector> computeNormalMatrix(final RealMatrix jacobian,
218 final RealVector residuals) {
219
220 final int nR = jacobian.getRowDimension();
221 final int nC = jacobian.getColumnDimension();
222
223 final RealMatrix normal = MatrixUtils.createRealMatrix(nC, nC);
224 final RealVector jTr = new ArrayRealVector(nC);
225
226 for (int i = 0; i < nR; ++i) {
227
228 for (int j = 0; j < nC; j++) {
229 jTr.setEntry(j, jTr.getEntry(j) +
230 residuals.getEntry(i) * jacobian.getEntry(i, j));
231 }
232
233
234 for (int k = 0; k < nC; ++k) {
235
236 for (int l = k; l < nC; ++l) {
237 normal.setEntry(k, l, normal.getEntry(k, l) +
238 jacobian.getEntry(i, k) * jacobian.getEntry(i, l));
239 }
240 }
241 }
242
243 for (int i = 0; i < nC; i++) {
244 for (int j = 0; j < i; j++) {
245 normal.setEntry(i, j, normal.getEntry(j, i));
246 }
247 }
248 return new Pair<RealMatrix, RealVector>(normal, jTr);
249 }
250
251 }