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