1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.analysis.interpolation;
18
19 import java.util.Arrays;
20 import java.util.function.DoubleBinaryOperator;
21 import java.util.function.Function;
22
23 import org.apache.commons.numbers.core.Sum;
24 import org.apache.commons.math4.legacy.analysis.BivariateFunction;
25 import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
26 import org.apache.commons.math4.legacy.exception.NoDataException;
27 import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
28 import org.apache.commons.math4.legacy.exception.OutOfRangeException;
29 import org.apache.commons.math4.legacy.core.MathArrays;
30
31
32
33
34
35
36
37
38 public class BicubicInterpolatingFunction
39 implements BivariateFunction {
40
41 private static final int NUM_COEFF = 16;
42
43
44
45
46 private static final double[][] AINV = {
47 { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
48 { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
49 { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
50 { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
51 { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
52 { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
53 { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
54 { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
55 { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
56 { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
57 { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
58 { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
59 { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
60 { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
61 { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
62 { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
63 };
64
65
66 private final double[] xval;
67
68 private final double[] yval;
69
70 private final BicubicFunction[][] splines;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public BicubicInterpolatingFunction(double[] x,
89 double[] y,
90 double[][] f,
91 double[][] dFdX,
92 double[][] dFdY,
93 double[][] d2FdXdY)
94 throws DimensionMismatchException,
95 NoDataException,
96 NonMonotonicSequenceException {
97 this(x, y, f, dFdX, dFdY, d2FdXdY, false);
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public BicubicInterpolatingFunction(double[] x,
120 double[] y,
121 double[][] f,
122 double[][] dFdX,
123 double[][] dFdY,
124 double[][] d2FdXdY,
125 boolean initializeDerivatives)
126 throws DimensionMismatchException,
127 NoDataException,
128 NonMonotonicSequenceException {
129 final int xLen = x.length;
130 final int yLen = y.length;
131
132 if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
133 throw new NoDataException();
134 }
135 if (xLen != f.length) {
136 throw new DimensionMismatchException(xLen, f.length);
137 }
138 if (xLen != dFdX.length) {
139 throw new DimensionMismatchException(xLen, dFdX.length);
140 }
141 if (xLen != dFdY.length) {
142 throw new DimensionMismatchException(xLen, dFdY.length);
143 }
144 if (xLen != d2FdXdY.length) {
145 throw new DimensionMismatchException(xLen, d2FdXdY.length);
146 }
147
148 MathArrays.checkOrder(x);
149 MathArrays.checkOrder(y);
150
151 xval = x.clone();
152 yval = y.clone();
153
154 final int lastI = xLen - 1;
155 final int lastJ = yLen - 1;
156 splines = new BicubicFunction[lastI][lastJ];
157
158 for (int i = 0; i < lastI; i++) {
159 if (f[i].length != yLen) {
160 throw new DimensionMismatchException(f[i].length, yLen);
161 }
162 if (dFdX[i].length != yLen) {
163 throw new DimensionMismatchException(dFdX[i].length, yLen);
164 }
165 if (dFdY[i].length != yLen) {
166 throw new DimensionMismatchException(dFdY[i].length, yLen);
167 }
168 if (d2FdXdY[i].length != yLen) {
169 throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
170 }
171 final int ip1 = i + 1;
172 final double xR = xval[ip1] - xval[i];
173 for (int j = 0; j < lastJ; j++) {
174 final int jp1 = j + 1;
175 final double yR = yval[jp1] - yval[j];
176 final double xRyR = xR * yR;
177 final double[] beta = new double[] {
178 f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
179 dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, dFdX[ip1][jp1] * xR,
180 dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, dFdY[ip1][jp1] * yR,
181 d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR
182 };
183
184 splines[i][j] = new BicubicFunction(computeSplineCoefficients(beta),
185 xR,
186 yR,
187 initializeDerivatives);
188 }
189 }
190 }
191
192
193
194
195 @Override
196 public double value(double x, double y)
197 throws OutOfRangeException {
198 final int i = searchIndex(x, xval);
199 final int j = searchIndex(y, yval);
200
201 final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
202 final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
203
204 return splines[i][j].value(xN, yN);
205 }
206
207
208
209
210
211
212
213
214 public boolean isValidPoint(double x, double y) {
215 return !(x < xval[0] ||
216 x > xval[xval.length - 1] ||
217 y < yval[0] ||
218 y > yval[yval.length - 1]);
219 }
220
221
222
223
224
225
226
227 public DoubleBinaryOperator partialDerivativeX() {
228 return partialDerivative(BicubicFunction::partialDerivativeX);
229 }
230
231
232
233
234
235
236
237 public DoubleBinaryOperator partialDerivativeY() {
238 return partialDerivative(BicubicFunction::partialDerivativeY);
239 }
240
241
242
243
244
245
246
247 public DoubleBinaryOperator partialDerivativeXX() {
248 return partialDerivative(BicubicFunction::partialDerivativeXX);
249 }
250
251
252
253
254
255
256
257 public DoubleBinaryOperator partialDerivativeYY() {
258 return partialDerivative(BicubicFunction::partialDerivativeYY);
259 }
260
261
262
263
264
265
266
267 public DoubleBinaryOperator partialDerivativeXY() {
268 return partialDerivative(BicubicFunction::partialDerivativeXY);
269 }
270
271
272
273
274
275
276
277
278 private DoubleBinaryOperator partialDerivative(Function<BicubicFunction, BivariateFunction> which) {
279 return (x, y) -> {
280 final int i = searchIndex(x, xval);
281 final int j = searchIndex(y, yval);
282
283 final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
284 final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
285
286 return which.apply(splines[i][j]).value(xN, yN);
287 };
288 }
289
290
291
292
293
294
295
296
297
298 private static int searchIndex(double c, double[] val) {
299 final int r = Arrays.binarySearch(val, c);
300
301 if (r == -1 ||
302 r == -val.length - 1) {
303 throw new OutOfRangeException(c, val[0], val[val.length - 1]);
304 }
305
306 if (r < 0) {
307
308
309 return -r - 2;
310 }
311 final int last = val.length - 1;
312 if (r == last) {
313
314
315 return last - 1;
316 }
317
318
319 return r;
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 private static double[] computeSplineCoefficients(double[] beta) {
352 final double[] a = new double[NUM_COEFF];
353
354 for (int i = 0; i < NUM_COEFF; i++) {
355 double result = 0;
356 final double[] row = AINV[i];
357 for (int j = 0; j < NUM_COEFF; j++) {
358 result += row[j] * beta[j];
359 }
360 a[i] = result;
361 }
362
363 return a;
364 }
365
366
367
368
369 static final class BicubicFunction implements BivariateFunction {
370
371 private static final short N = 4;
372
373 private final double[][] a;
374
375 private final BivariateFunction partialDerivativeX;
376
377 private final BivariateFunction partialDerivativeY;
378
379 private final BivariateFunction partialDerivativeXX;
380
381 private final BivariateFunction partialDerivativeYY;
382
383 private final BivariateFunction partialDerivativeXY;
384
385
386
387
388
389
390
391
392
393
394
395 BicubicFunction(double[] coeff,
396 double xR,
397 double yR,
398 boolean initializeDerivatives) {
399 a = new double[N][N];
400 for (int j = 0; j < N; j++) {
401 final double[] aJ = a[j];
402 for (int i = 0; i < N; i++) {
403 aJ[i] = coeff[i * N + j];
404 }
405 }
406
407 if (initializeDerivatives) {
408
409 final double[][] aX = new double[N][N];
410 final double[][] aY = new double[N][N];
411 final double[][] aXX = new double[N][N];
412 final double[][] aYY = new double[N][N];
413 final double[][] aXY = new double[N][N];
414
415 for (int i = 0; i < N; i++) {
416 for (int j = 0; j < N; j++) {
417 final double c = a[i][j];
418 aX[i][j] = i * c;
419 aY[i][j] = j * c;
420 aXX[i][j] = (i - 1) * aX[i][j];
421 aYY[i][j] = (j - 1) * aY[i][j];
422 aXY[i][j] = j * aX[i][j];
423 }
424 }
425
426 partialDerivativeX = (double x, double y) -> {
427 final double x2 = x * x;
428 final double[] pX = {0, 1, x, x2};
429
430 final double y2 = y * y;
431 final double y3 = y2 * y;
432 final double[] pY = {1, y, y2, y3};
433
434 return apply(pX, 1, pY, 0, aX) / xR;
435 };
436 partialDerivativeY = (double x, double y) -> {
437 final double x2 = x * x;
438 final double x3 = x2 * x;
439 final double[] pX = {1, x, x2, x3};
440
441 final double y2 = y * y;
442 final double[] pY = {0, 1, y, y2};
443
444 return apply(pX, 0, pY, 1, aY) / yR;
445 };
446 partialDerivativeXX = (double x, double y) -> {
447 final double[] pX = {0, 0, 1, x};
448
449 final double y2 = y * y;
450 final double y3 = y2 * y;
451 final double[] pY = {1, y, y2, y3};
452
453 return apply(pX, 2, pY, 0, aXX) / (xR * xR);
454 };
455 partialDerivativeYY = (double x, double y) -> {
456 final double x2 = x * x;
457 final double x3 = x2 * x;
458 final double[] pX = {1, x, x2, x3};
459
460 final double[] pY = {0, 0, 1, y};
461
462 return apply(pX, 0, pY, 2, aYY) / (yR * yR);
463 };
464 partialDerivativeXY = (double x, double y) -> {
465 final double x2 = x * x;
466 final double[] pX = {0, 1, x, x2};
467
468 final double y2 = y * y;
469 final double[] pY = {0, 1, y, y2};
470
471 return apply(pX, 1, pY, 1, aXY) / (xR * yR);
472 };
473 } else {
474 partialDerivativeX = null;
475 partialDerivativeY = null;
476 partialDerivativeXX = null;
477 partialDerivativeYY = null;
478 partialDerivativeXY = null;
479 }
480 }
481
482
483
484
485 @Override
486 public double value(double x, double y) {
487 if (x < 0 || x > 1) {
488 throw new OutOfRangeException(x, 0, 1);
489 }
490 if (y < 0 || y > 1) {
491 throw new OutOfRangeException(y, 0, 1);
492 }
493
494 final double x2 = x * x;
495 final double x3 = x2 * x;
496 final double[] pX = {1, x, x2, x3};
497
498 final double y2 = y * y;
499 final double y3 = y2 * y;
500 final double[] pY = {1, y, y2, y3};
501
502 return apply(pX, 0, pY, 0, a);
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 private static double apply(double[] pX, int i, double[] pY, int j, double[][] coeff) {
520
521 double result = sumOfProducts(coeff[i], pY, j);
522 while (++i < N) {
523 final double r = sumOfProducts(coeff[i], pY, j);
524 result += r * pX[i];
525 }
526 return result;
527 }
528
529
530
531
532
533
534
535
536
537
538 private static double sumOfProducts(double[] a, double[] b, int j) {
539
540 final Sum sum = Sum.of(a[j]);
541 while (++j < N) {
542 sum.addProduct(a[j], b[j]);
543 }
544 return sum.getAsDouble();
545 }
546
547
548
549
550 BivariateFunction partialDerivativeX() {
551 return partialDerivativeX;
552 }
553
554
555
556
557 BivariateFunction partialDerivativeY() {
558 return partialDerivativeY;
559 }
560
561
562
563
564 BivariateFunction partialDerivativeXX() {
565 return partialDerivativeXX;
566 }
567
568
569
570
571 BivariateFunction partialDerivativeYY() {
572 return partialDerivativeYY;
573 }
574
575
576
577
578 BivariateFunction partialDerivativeXY() {
579 return partialDerivativeXY;
580 }
581 }
582 }
583