1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.analysis.solvers;
18
19
20 import org.apache.commons.math4.legacy.core.Field;
21 import org.apache.commons.math4.legacy.core.RealFieldElement;
22 import org.apache.commons.math4.legacy.analysis.RealFieldUnivariateFunction;
23 import org.apache.commons.math4.legacy.exception.MathInternalError;
24 import org.apache.commons.math4.legacy.exception.NoBracketingException;
25 import org.apache.commons.math4.legacy.exception.NullArgumentException;
26 import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
27 import org.apache.commons.math4.legacy.core.IntegerSequence;
28 import org.apache.commons.math4.legacy.core.MathArrays;
29 import org.apache.commons.numbers.core.Precision;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class FieldBracketingNthOrderBrentSolver<T extends RealFieldElement<T>>
48 implements BracketedRealFieldUnivariateSolver<T> {
49
50
51 private static final int MAXIMAL_AGING = 2;
52
53
54 private final Field<T> field;
55
56
57 private final int maximalOrder;
58
59
60 private final T functionValueAccuracy;
61
62
63 private final T absoluteAccuracy;
64
65
66 private final T relativeAccuracy;
67
68
69 private IntegerSequence.Incrementor evaluations;
70
71
72
73
74
75
76
77
78
79
80 public FieldBracketingNthOrderBrentSolver(final T relativeAccuracy,
81 final T absoluteAccuracy,
82 final T functionValueAccuracy,
83 final int maximalOrder)
84 throws NumberIsTooSmallException {
85 if (maximalOrder < 2) {
86 throw new NumberIsTooSmallException(maximalOrder, 2, true);
87 }
88 this.field = relativeAccuracy.getField();
89 this.maximalOrder = maximalOrder;
90 this.absoluteAccuracy = absoluteAccuracy;
91 this.relativeAccuracy = relativeAccuracy;
92 this.functionValueAccuracy = functionValueAccuracy;
93 this.evaluations = IntegerSequence.Incrementor.create();
94 }
95
96
97
98
99 public int getMaximalOrder() {
100 return maximalOrder;
101 }
102
103
104
105
106
107
108 @Override
109 public int getMaxEvaluations() {
110 return evaluations.getMaximalCount();
111 }
112
113
114
115
116
117
118
119
120
121 @Override
122 public int getEvaluations() {
123 return evaluations.getCount();
124 }
125
126
127
128
129
130 @Override
131 public T getAbsoluteAccuracy() {
132 return absoluteAccuracy;
133 }
134
135
136
137
138
139 @Override
140 public T getRelativeAccuracy() {
141 return relativeAccuracy;
142 }
143
144
145
146
147
148 @Override
149 public T getFunctionValueAccuracy() {
150 return functionValueAccuracy;
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 @Override
170 public T solve(final int maxEval, final RealFieldUnivariateFunction<T> f,
171 final T min, final T max, final AllowedSolution allowedSolution)
172 throws NullArgumentException, NoBracketingException {
173 return solve(maxEval, f, min, max, min.add(max).divide(2), allowedSolution);
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @Override
194 public T solve(final int maxEval, final RealFieldUnivariateFunction<T> f,
195 final T min, final T max, final T startValue,
196 final AllowedSolution allowedSolution)
197 throws NullArgumentException, NoBracketingException {
198
199
200 NullArgumentException.check(f);
201
202
203 evaluations = evaluations.withMaximalCount(maxEval).withStart(0);
204 T zero = field.getZero();
205 T nan = zero.add(Double.NaN);
206
207
208 final T[] x = MathArrays.buildArray(field, maximalOrder + 1);
209 final T[] y = MathArrays.buildArray(field, maximalOrder + 1);
210 x[0] = min;
211 x[1] = startValue;
212 x[2] = max;
213
214
215 evaluations.increment();
216 y[1] = f.value(x[1]);
217 if (Precision.equals(y[1].getReal(), 0.0, 1)) {
218
219 return x[1];
220 }
221
222
223 evaluations.increment();
224 y[0] = f.value(x[0]);
225 if (Precision.equals(y[0].getReal(), 0.0, 1)) {
226
227 return x[0];
228 }
229
230 int nbPoints;
231 int signChangeIndex;
232 if (y[0].multiply(y[1]).getReal() < 0) {
233
234
235 nbPoints = 2;
236 signChangeIndex = 1;
237 } else {
238
239
240 evaluations.increment();
241 y[2] = f.value(x[2]);
242 if (Precision.equals(y[2].getReal(), 0.0, 1)) {
243
244 return x[2];
245 }
246
247 if (y[1].multiply(y[2]).getReal() < 0) {
248
249 nbPoints = 3;
250 signChangeIndex = 2;
251 } else {
252 throw new NoBracketingException(x[0].getReal(), x[2].getReal(),
253 y[0].getReal(), y[2].getReal());
254 }
255 }
256
257
258 final T[] tmpX = MathArrays.buildArray(field, x.length);
259
260
261 T xA = x[signChangeIndex - 1];
262 T yA = y[signChangeIndex - 1];
263 T absXA = xA.abs();
264 T absYA = yA.abs();
265 int agingA = 0;
266 T xB = x[signChangeIndex];
267 T yB = y[signChangeIndex];
268 T absXB = xB.abs();
269 T absYB = yB.abs();
270 int agingB = 0;
271
272
273 while (true) {
274
275
276 T maxX = absXA.subtract(absXB).getReal() < 0 ? absXB : absXA;
277 T maxY = absYA.subtract(absYB).getReal() < 0 ? absYB : absYA;
278 final T xTol = absoluteAccuracy.add(relativeAccuracy.multiply(maxX));
279 if (xB.subtract(xA).subtract(xTol).getReal() <= 0 ||
280 maxY.subtract(functionValueAccuracy).getReal() < 0) {
281 switch (allowedSolution) {
282 case ANY_SIDE :
283 return absYA.subtract(absYB).getReal() < 0 ? xA : xB;
284 case LEFT_SIDE :
285 return xA;
286 case RIGHT_SIDE :
287 return xB;
288 case BELOW_SIDE :
289 return yA.getReal() <= 0 ? xA : xB;
290 case ABOVE_SIDE :
291 return yA.getReal() < 0 ? xB : xA;
292 default :
293
294 throw new MathInternalError(null);
295 }
296 }
297
298
299 T targetY;
300 if (agingA >= MAXIMAL_AGING) {
301
302 targetY = yB.divide(16).negate();
303 } else if (agingB >= MAXIMAL_AGING) {
304
305 targetY = yA.divide(16).negate();
306 } else {
307
308 targetY = zero;
309 }
310
311
312 T nextX;
313 int start = 0;
314 int end = nbPoints;
315 do {
316
317
318 System.arraycopy(x, start, tmpX, start, end - start);
319 nextX = guessX(targetY, tmpX, y, start, end);
320
321 if (!(nextX.subtract(xA).getReal() > 0 && nextX.subtract(xB).getReal() < 0)) {
322
323
324
325
326
327 if (signChangeIndex - start >= end - signChangeIndex) {
328
329 ++start;
330 } else {
331
332 --end;
333 }
334
335
336 nextX = nan;
337 }
338 } while (Double.isNaN(nextX.getReal()) && end - start > 1);
339
340 if (Double.isNaN(nextX.getReal())) {
341
342 nextX = xA.add(xB.subtract(xA).divide(2));
343 start = signChangeIndex - 1;
344 end = signChangeIndex;
345 }
346
347
348 evaluations.increment();
349 final T nextY = f.value(nextX);
350 if (Precision.equals(nextY.getReal(), 0.0, 1)) {
351
352
353 return nextX;
354 }
355
356 if (nbPoints > 2 && end - start != nbPoints) {
357
358
359
360 nbPoints = end - start;
361 System.arraycopy(x, start, x, 0, nbPoints);
362 System.arraycopy(y, start, y, 0, nbPoints);
363 signChangeIndex -= start;
364 } else if (nbPoints == x.length) {
365
366
367 nbPoints--;
368
369
370 if (signChangeIndex >= (x.length + 1) / 2) {
371
372 System.arraycopy(x, 1, x, 0, nbPoints);
373 System.arraycopy(y, 1, y, 0, nbPoints);
374 --signChangeIndex;
375 }
376 }
377
378
379
380 System.arraycopy(x, signChangeIndex, x, signChangeIndex + 1, nbPoints - signChangeIndex);
381 x[signChangeIndex] = nextX;
382 System.arraycopy(y, signChangeIndex, y, signChangeIndex + 1, nbPoints - signChangeIndex);
383 y[signChangeIndex] = nextY;
384 ++nbPoints;
385
386
387 if (nextY.multiply(yA).getReal() <= 0) {
388
389 xB = nextX;
390 yB = nextY;
391 absYB = yB.abs();
392 ++agingA;
393 agingB = 0;
394 } else {
395
396 xA = nextX;
397 yA = nextY;
398 absYA = yA.abs();
399 agingA = 0;
400 ++agingB;
401
402
403 signChangeIndex++;
404 }
405 }
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422 private T guessX(final T targetY, final T[] x, final T[] y,
423 final int start, final int end) {
424
425
426 for (int i = start; i < end - 1; ++i) {
427 final int delta = i + 1 - start;
428 for (int j = end - 1; j > i; --j) {
429 x[j] = x[j].subtract(x[j-1]).divide(y[j].subtract(y[j - delta]));
430 }
431 }
432
433
434 T x0 = field.getZero();
435 for (int j = end - 1; j >= start; --j) {
436 x0 = x[j].add(x0.multiply(targetY.subtract(y[j])));
437 }
438
439 return x0;
440 }
441 }