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