1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.analysis.differentiation;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22 import java.util.concurrent.atomic.AtomicReference;
23
24 import org.apache.commons.numbers.core.Sum;
25 import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
26 import org.apache.commons.math4.legacy.exception.MathArithmeticException;
27 import org.apache.commons.math4.legacy.exception.MathInternalError;
28 import org.apache.commons.math4.legacy.exception.NotPositiveException;
29 import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
30 import org.apache.commons.numbers.combinatorics.Factorial;
31 import org.apache.commons.math4.core.jdkmath.JdkMath;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 public final class DSCompiler {
126
127 private static AtomicReference<DSCompiler[][]> compilers =
128 new AtomicReference<>(null);
129
130
131 private final int parameters;
132
133
134 private final int order;
135
136
137 private final int[][] sizes;
138
139
140 private final int[][] derivativesIndirection;
141
142
143 private final int[] lowerIndirection;
144
145
146 private final int[][][] multIndirection;
147
148
149 private final int[][][] compIndirection;
150
151
152
153
154
155
156
157
158 private DSCompiler(final int parameters, final int order,
159 final DSCompiler valueCompiler, final DSCompiler derivativeCompiler)
160 throws NumberIsTooLargeException {
161
162 this.parameters = parameters;
163 this.order = order;
164 this.sizes = compileSizes(parameters, order, valueCompiler);
165 this.derivativesIndirection =
166 compileDerivativesIndirection(parameters, order,
167 valueCompiler, derivativeCompiler);
168 this.lowerIndirection =
169 compileLowerIndirection(parameters, order,
170 valueCompiler, derivativeCompiler);
171 this.multIndirection =
172 compileMultiplicationIndirection(parameters, order,
173 valueCompiler, derivativeCompiler, lowerIndirection);
174 this.compIndirection =
175 compileCompositionIndirection(parameters, order,
176 valueCompiler, derivativeCompiler,
177 sizes, derivativesIndirection);
178 }
179
180
181
182
183
184
185
186 public static DSCompiler getCompiler(int parameters, int order)
187 throws NumberIsTooLargeException {
188
189
190 final DSCompiler[][] cache = compilers.get();
191 if (cache != null && cache.length > parameters &&
192 cache[parameters].length > order && cache[parameters][order] != null) {
193
194 return cache[parameters][order];
195 }
196
197
198 final int maxParameters = JdkMath.max(parameters, cache == null ? 0 : cache.length);
199 final int maxOrder = JdkMath.max(order, cache == null ? 0 : cache[0].length);
200 final DSCompiler[][] newCache = new DSCompiler[maxParameters + 1][maxOrder + 1];
201
202 if (cache != null) {
203
204 for (int i = 0; i < cache.length; ++i) {
205 System.arraycopy(cache[i], 0, newCache[i], 0, cache[i].length);
206 }
207 }
208
209
210 for (int diag = 0; diag <= parameters + order; ++diag) {
211 for (int o = JdkMath.max(0, diag - parameters); o <= JdkMath.min(order, diag); ++o) {
212 final int p = diag - o;
213 if (newCache[p][o] == null) {
214 final DSCompiler valueCompiler = (p == 0) ? null : newCache[p - 1][o];
215 final DSCompiler derivativeCompiler = (o == 0) ? null : newCache[p][o - 1];
216 newCache[p][o] = new DSCompiler(p, o, valueCompiler, derivativeCompiler);
217 }
218 }
219 }
220
221
222 compilers.compareAndSet(cache, newCache);
223
224 return newCache[parameters][order];
225 }
226
227
228
229
230
231
232
233 private static int[][] compileSizes(final int parameters, final int order,
234 final DSCompiler valueCompiler) {
235
236 final int[][] sizes = new int[parameters + 1][order + 1];
237 if (parameters == 0) {
238 Arrays.fill(sizes[0], 1);
239 } else {
240 System.arraycopy(valueCompiler.sizes, 0, sizes, 0, parameters);
241 sizes[parameters][0] = 1;
242 for (int i = 0; i < order; ++i) {
243 sizes[parameters][i + 1] = sizes[parameters][i] + sizes[parameters - 1][i + 1];
244 }
245 }
246
247 return sizes;
248 }
249
250
251
252
253
254
255
256
257 private static int[][] compileDerivativesIndirection(final int parameters, final int order,
258 final DSCompiler valueCompiler,
259 final DSCompiler derivativeCompiler) {
260
261 if (parameters == 0 || order == 0) {
262 return new int[1][parameters];
263 }
264
265 final int vSize = valueCompiler.derivativesIndirection.length;
266 final int dSize = derivativeCompiler.derivativesIndirection.length;
267 final int[][] derivativesIndirection = new int[vSize + dSize][parameters];
268
269
270 for (int i = 0; i < vSize; ++i) {
271
272 System.arraycopy(valueCompiler.derivativesIndirection[i], 0,
273 derivativesIndirection[i], 0,
274 parameters - 1);
275 }
276
277
278 for (int i = 0; i < dSize; ++i) {
279
280
281 System.arraycopy(derivativeCompiler.derivativesIndirection[i], 0,
282 derivativesIndirection[vSize + i], 0,
283 parameters);
284
285
286 derivativesIndirection[vSize + i][parameters - 1]++;
287 }
288
289 return derivativesIndirection;
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303 private static int[] compileLowerIndirection(final int parameters, final int order,
304 final DSCompiler valueCompiler,
305 final DSCompiler derivativeCompiler) {
306
307 if (parameters == 0 || order <= 1) {
308 return new int[] { 0 };
309 }
310
311
312 final int vSize = valueCompiler.lowerIndirection.length;
313 final int dSize = derivativeCompiler.lowerIndirection.length;
314 final int[] lowerIndirection = new int[vSize + dSize];
315 System.arraycopy(valueCompiler.lowerIndirection, 0, lowerIndirection, 0, vSize);
316 for (int i = 0; i < dSize; ++i) {
317 lowerIndirection[vSize + i] = valueCompiler.getSize() + derivativeCompiler.lowerIndirection[i];
318 }
319
320 return lowerIndirection;
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 private static int[][][] compileMultiplicationIndirection(final int parameters, final int order,
337 final DSCompiler valueCompiler,
338 final DSCompiler derivativeCompiler,
339 final int[] lowerIndirection) {
340
341 if (parameters == 0 || order == 0) {
342 return new int[][][] { { { 1, 0, 0 } } };
343 }
344
345
346 final int vSize = valueCompiler.multIndirection.length;
347 final int dSize = derivativeCompiler.multIndirection.length;
348 final int[][][] multIndirection = new int[vSize + dSize][][];
349
350 System.arraycopy(valueCompiler.multIndirection, 0, multIndirection, 0, vSize);
351
352 for (int i = 0; i < dSize; ++i) {
353 final int[][] dRow = derivativeCompiler.multIndirection[i];
354 List<int[]> row = new ArrayList<>(dRow.length * 2);
355 for (int j = 0; j < dRow.length; ++j) {
356 row.add(new int[] { dRow[j][0], lowerIndirection[dRow[j][1]], vSize + dRow[j][2] });
357 row.add(new int[] { dRow[j][0], vSize + dRow[j][1], lowerIndirection[dRow[j][2]] });
358 }
359
360
361 final List<int[]> combined = new ArrayList<>(row.size());
362 for (int j = 0; j < row.size(); ++j) {
363 final int[] termJ = row.get(j);
364 if (termJ[0] > 0) {
365 for (int k = j + 1; k < row.size(); ++k) {
366 final int[] termK = row.get(k);
367 if (termJ[1] == termK[1] && termJ[2] == termK[2]) {
368
369 termJ[0] += termK[0];
370
371 termK[0] = 0;
372 }
373 }
374 combined.add(termJ);
375 }
376 }
377
378 multIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
379 }
380
381 return multIndirection;
382 }
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 private static int[][][] compileCompositionIndirection(final int parameters, final int order,
400 final DSCompiler valueCompiler,
401 final DSCompiler derivativeCompiler,
402 final int[][] sizes,
403 final int[][] derivativesIndirection)
404 throws NumberIsTooLargeException {
405
406 if (parameters == 0 || order == 0) {
407 return new int[][][] { { { 1, 0 } } };
408 }
409
410 final int vSize = valueCompiler.compIndirection.length;
411 final int dSize = derivativeCompiler.compIndirection.length;
412 final int[][][] compIndirection = new int[vSize + dSize][][];
413
414
415 System.arraycopy(valueCompiler.compIndirection, 0, compIndirection, 0, vSize);
416
417
418
419
420
421 for (int i = 0; i < dSize; ++i) {
422 List<int[]> row = new ArrayList<>();
423 for (int[] term : derivativeCompiler.compIndirection[i]) {
424
425
426
427
428 int[] derivedTermF = new int[term.length + 1];
429 derivedTermF[0] = term[0];
430 derivedTermF[1] = term[1] + 1;
431 int[] orders = new int[parameters];
432 orders[parameters - 1] = 1;
433 derivedTermF[term.length] = getPartialDerivativeIndex(parameters, order, sizes, orders);
434 for (int j = 2; j < term.length; ++j) {
435
436
437 derivedTermF[j] = convertIndex(term[j], parameters,
438 derivativeCompiler.derivativesIndirection,
439 parameters, order, sizes);
440 }
441 Arrays.sort(derivedTermF, 2, derivedTermF.length);
442 row.add(derivedTermF);
443
444
445 for (int l = 2; l < term.length; ++l) {
446 int[] derivedTermG = new int[term.length];
447 derivedTermG[0] = term[0];
448 derivedTermG[1] = term[1];
449 for (int j = 2; j < term.length; ++j) {
450
451
452 derivedTermG[j] = convertIndex(term[j], parameters,
453 derivativeCompiler.derivativesIndirection,
454 parameters, order, sizes);
455 if (j == l) {
456
457 System.arraycopy(derivativesIndirection[derivedTermG[j]], 0, orders, 0, parameters);
458 orders[parameters - 1]++;
459 derivedTermG[j] = getPartialDerivativeIndex(parameters, order, sizes, orders);
460 }
461 }
462 Arrays.sort(derivedTermG, 2, derivedTermG.length);
463 row.add(derivedTermG);
464 }
465 }
466
467
468 final List<int[]> combined = new ArrayList<>(row.size());
469 for (int j = 0; j < row.size(); ++j) {
470 final int[] termJ = row.get(j);
471 if (termJ[0] > 0) {
472 for (int k = j + 1; k < row.size(); ++k) {
473 final int[] termK = row.get(k);
474 boolean equals = termJ.length == termK.length;
475 for (int l = 1; equals && l < termJ.length; ++l) {
476 equals &= termJ[l] == termK[l];
477 }
478 if (equals) {
479
480 termJ[0] += termK[0];
481
482 termK[0] = 0;
483 }
484 }
485 combined.add(termJ);
486 }
487 }
488
489 compIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
490 }
491
492 return compIndirection;
493 }
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 public int getPartialDerivativeIndex(final int ... orders)
528 throws DimensionMismatchException, NumberIsTooLargeException {
529
530
531 if (orders.length != getFreeParameters()) {
532 throw new DimensionMismatchException(orders.length, getFreeParameters());
533 }
534
535 return getPartialDerivativeIndex(parameters, order, sizes, orders);
536 }
537
538
539
540
541
542
543
544
545
546
547
548 private static int getPartialDerivativeIndex(final int parameters, final int order,
549 final int[][] sizes, final int ... orders)
550 throws NumberIsTooLargeException {
551
552
553
554 int index = 0;
555 int m = order;
556 int ordersSum = 0;
557 for (int i = parameters - 1; i >= 0; --i) {
558
559
560 int derivativeOrder = orders[i];
561
562
563 ordersSum += derivativeOrder;
564 if (ordersSum > order) {
565 throw new NumberIsTooLargeException(ordersSum, order, true);
566 }
567
568 while (derivativeOrder-- > 0) {
569
570
571
572 index += sizes[i][m--];
573 }
574 }
575
576 return index;
577 }
578
579
580
581
582
583
584
585
586
587
588
589
590
591 private static int convertIndex(final int index,
592 final int srcP, final int[][] srcDerivativesIndirection,
593 final int destP, final int destO, final int[][] destSizes)
594 throws NumberIsTooLargeException {
595 int[] orders = new int[destP];
596 System.arraycopy(srcDerivativesIndirection[index], 0, orders, 0, JdkMath.min(srcP, destP));
597 return getPartialDerivativeIndex(destP, destO, destSizes, orders);
598 }
599
600
601
602
603
604
605
606
607
608 public int[] getPartialDerivativeOrders(final int index) {
609 return derivativesIndirection[index];
610 }
611
612
613
614
615 public int getFreeParameters() {
616 return parameters;
617 }
618
619
620
621
622 public int getOrder() {
623 return order;
624 }
625
626
627
628
629
630
631
632
633 public int getSize() {
634 return sizes[parameters][order];
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648
649 public void linearCombination(final double a1, final double[] c1, final int offset1,
650 final double a2, final double[] c2, final int offset2,
651 final double[] result, final int resultOffset) {
652 for (int i = 0; i < getSize(); ++i) {
653 result[resultOffset + i] = Sum.create()
654 .addProduct(a1, c1[offset1 + i])
655 .addProduct(a2, c2[offset2 + i]).getAsDouble();
656 }
657 }
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674 public void linearCombination(final double a1, final double[] c1, final int offset1,
675 final double a2, final double[] c2, final int offset2,
676 final double a3, final double[] c3, final int offset3,
677 final double[] result, final int resultOffset) {
678 for (int i = 0; i < getSize(); ++i) {
679 result[resultOffset + i] = Sum.create()
680 .addProduct(a1, c1[offset1 + i])
681 .addProduct(a2, c2[offset2 + i])
682 .addProduct(a3, c3[offset3 + i]).getAsDouble();
683 }
684 }
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704 public void linearCombination(final double a1, final double[] c1, final int offset1,
705 final double a2, final double[] c2, final int offset2,
706 final double a3, final double[] c3, final int offset3,
707 final double a4, final double[] c4, final int offset4,
708 final double[] result, final int resultOffset) {
709 for (int i = 0; i < getSize(); ++i) {
710 result[resultOffset + i] = Sum.create()
711 .addProduct(a1, c1[offset1 + i])
712 .addProduct(a2, c2[offset2 + i])
713 .addProduct(a3, c3[offset3 + i])
714 .addProduct(a4, c4[offset4 + i]).getAsDouble();
715 }
716 }
717
718
719
720
721
722
723
724
725
726
727 public void add(final double[] lhs, final int lhsOffset,
728 final double[] rhs, final int rhsOffset,
729 final double[] result, final int resultOffset) {
730 for (int i = 0; i < getSize(); ++i) {
731 result[resultOffset + i] = lhs[lhsOffset + i] + rhs[rhsOffset + i];
732 }
733 }
734
735
736
737
738
739
740
741
742
743 public void subtract(final double[] lhs, final int lhsOffset,
744 final double[] rhs, final int rhsOffset,
745 final double[] result, final int resultOffset) {
746 for (int i = 0; i < getSize(); ++i) {
747 result[resultOffset + i] = lhs[lhsOffset + i] - rhs[rhsOffset + i];
748 }
749 }
750
751
752
753
754
755
756
757
758
759
760
761 public void multiply(final double[] lhs, final int lhsOffset,
762 final double[] rhs, final int rhsOffset,
763 final double[] result, final int resultOffset) {
764 for (int i = 0; i < multIndirection.length; ++i) {
765 final int[][] mappingI = multIndirection[i];
766 double r = 0;
767 for (int j = 0; j < mappingI.length; ++j) {
768 r += mappingI[j][0] *
769 lhs[lhsOffset + mappingI[j][1]] *
770 rhs[rhsOffset + mappingI[j][2]];
771 }
772 result[resultOffset + i] = r;
773 }
774 }
775
776
777
778
779
780
781
782
783
784
785
786 public void divide(final double[] lhs, final int lhsOffset,
787 final double[] rhs, final int rhsOffset,
788 final double[] result, final int resultOffset) {
789 final double[] reciprocal = new double[getSize()];
790 pow(rhs, lhsOffset, -1, reciprocal, 0);
791 multiply(lhs, lhsOffset, reciprocal, 0, result, resultOffset);
792 }
793
794
795
796
797
798
799
800
801
802
803 public void remainder(final double[] lhs, final int lhsOffset,
804 final double[] rhs, final int rhsOffset,
805 final double[] result, final int resultOffset) {
806
807
808 final double rem = JdkMath.IEEEremainder(lhs[lhsOffset], rhs[rhsOffset]);
809 final double k = JdkMath.rint((lhs[lhsOffset] - rem) / rhs[rhsOffset]);
810
811
812 result[resultOffset] = rem;
813
814
815 for (int i = 1; i < getSize(); ++i) {
816 result[resultOffset + i] = lhs[lhsOffset + i] - k * rhs[rhsOffset + i];
817 }
818 }
819
820
821
822
823
824
825
826
827
828
829
830 public void pow(final double a,
831 final double[] operand, final int operandOffset,
832 final double[] result, final int resultOffset) {
833
834
835
836 final double[] function = new double[1 + order];
837 if (a == 0) {
838 if (operand[operandOffset] == 0) {
839 function[0] = 1;
840 double infinity = Double.POSITIVE_INFINITY;
841 for (int i = 1; i < function.length; ++i) {
842 infinity = -infinity;
843 function[i] = infinity;
844 }
845 } else if (operand[operandOffset] < 0) {
846 Arrays.fill(function, Double.NaN);
847 }
848 } else {
849 function[0] = JdkMath.pow(a, operand[operandOffset]);
850 final double lnA = JdkMath.log(a);
851 for (int i = 1; i < function.length; ++i) {
852 function[i] = lnA * function[i - 1];
853 }
854 }
855
856
857
858 compose(operand, operandOffset, function, result, resultOffset);
859 }
860
861
862
863
864
865
866
867
868
869
870 public void pow(final double[] operand, final int operandOffset, final double p,
871 final double[] result, final int resultOffset) {
872
873 if (p == 0) {
874
875 result[resultOffset] = 1.0;
876 Arrays.fill(result, resultOffset + 1, resultOffset + getSize(), 0);
877 return;
878 }
879
880 if (operand[operandOffset] == 0) {
881
882 Arrays.fill(result, resultOffset, resultOffset + getSize(), 0);
883 return;
884 }
885
886
887
888 double[] function = new double[1 + order];
889 double xk = JdkMath.pow(operand[operandOffset], p - order);
890 for (int i = order; i > 0; --i) {
891 function[i] = xk;
892 xk *= operand[operandOffset];
893 }
894 function[0] = xk;
895 double coefficient = p;
896 for (int i = 1; i <= order; ++i) {
897 function[i] *= coefficient;
898 coefficient *= p - i;
899 }
900
901
902 compose(operand, operandOffset, function, result, resultOffset);
903 }
904
905
906
907
908
909
910
911
912
913
914 public void pow(final double[] operand, final int operandOffset, final int n,
915 final double[] result, final int resultOffset) {
916
917 if (n == 0) {
918
919 result[resultOffset] = 1.0;
920 Arrays.fill(result, resultOffset + 1, resultOffset + getSize(), 0);
921 return;
922 }
923
924
925
926 double[] function = new double[1 + order];
927
928 if (n > 0) {
929
930 final int maxOrder = JdkMath.min(order, n);
931 double xk = JdkMath.pow(operand[operandOffset], n - maxOrder);
932 for (int i = maxOrder; i > 0; --i) {
933 function[i] = xk;
934 xk *= operand[operandOffset];
935 }
936 function[0] = xk;
937 } else {
938
939 final double inv = 1.0 / operand[operandOffset];
940 double xk = JdkMath.pow(inv, -n);
941 for (int i = 0; i <= order; ++i) {
942 function[i] = xk;
943 xk *= inv;
944 }
945 }
946
947 double coefficient = n;
948 for (int i = 1; i <= order; ++i) {
949 function[i] *= coefficient;
950 coefficient *= n - i;
951 }
952
953
954 compose(operand, operandOffset, function, result, resultOffset);
955 }
956
957
958
959
960
961
962
963
964
965
966
967 public void pow(final double[] x, final int xOffset,
968 final double[] y, final int yOffset,
969 final double[] result, final int resultOffset) {
970 final double[] logX = new double[getSize()];
971 log(x, xOffset, logX, 0);
972 final double[] yLogX = new double[getSize()];
973 multiply(logX, 0, y, yOffset, yLogX, 0);
974 exp(yLogX, 0, result, resultOffset);
975 }
976
977
978
979
980
981
982
983
984
985
986 public void rootN(final double[] operand, final int operandOffset, final int n,
987 final double[] result, final int resultOffset) {
988
989
990
991 double[] function = new double[1 + order];
992 double xk;
993 if (n == 2) {
994 function[0] = JdkMath.sqrt(operand[operandOffset]);
995 xk = 0.5 / function[0];
996 } else if (n == 3) {
997 function[0] = JdkMath.cbrt(operand[operandOffset]);
998 xk = 1.0 / (3.0 * function[0] * function[0]);
999 } else {
1000 function[0] = JdkMath.pow(operand[operandOffset], 1.0 / n);
1001 xk = 1.0 / (n * JdkMath.pow(function[0], n - 1));
1002 }
1003 final double nReciprocal = 1.0 / n;
1004 final double xReciprocal = 1.0 / operand[operandOffset];
1005 for (int i = 1; i <= order; ++i) {
1006 function[i] = xk;
1007 xk *= xReciprocal * (nReciprocal - i);
1008 }
1009
1010
1011 compose(operand, operandOffset, function, result, resultOffset);
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022 public void exp(final double[] operand, final int operandOffset,
1023 final double[] result, final int resultOffset) {
1024
1025
1026 double[] function = new double[1 + order];
1027 Arrays.fill(function, JdkMath.exp(operand[operandOffset]));
1028
1029
1030 compose(operand, operandOffset, function, result, resultOffset);
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 public void expm1(final double[] operand, final int operandOffset,
1042 final double[] result, final int resultOffset) {
1043
1044
1045 double[] function = new double[1 + order];
1046 function[0] = JdkMath.expm1(operand[operandOffset]);
1047 Arrays.fill(function, 1, 1 + order, JdkMath.exp(operand[operandOffset]));
1048
1049
1050 compose(operand, operandOffset, function, result, resultOffset);
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 public void log(final double[] operand, final int operandOffset,
1062 final double[] result, final int resultOffset) {
1063
1064
1065 double[] function = new double[1 + order];
1066 function[0] = JdkMath.log(operand[operandOffset]);
1067 if (order > 0) {
1068 double inv = 1.0 / operand[operandOffset];
1069 double xk = inv;
1070 for (int i = 1; i <= order; ++i) {
1071 function[i] = xk;
1072 xk *= -i * inv;
1073 }
1074 }
1075
1076
1077 compose(operand, operandOffset, function, result, resultOffset);
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087 public void log1p(final double[] operand, final int operandOffset,
1088 final double[] result, final int resultOffset) {
1089
1090
1091 double[] function = new double[1 + order];
1092 function[0] = JdkMath.log1p(operand[operandOffset]);
1093 if (order > 0) {
1094 double inv = 1.0 / (1.0 + operand[operandOffset]);
1095 double xk = inv;
1096 for (int i = 1; i <= order; ++i) {
1097 function[i] = xk;
1098 xk *= -i * inv;
1099 }
1100 }
1101
1102
1103 compose(operand, operandOffset, function, result, resultOffset);
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113 public void log10(final double[] operand, final int operandOffset,
1114 final double[] result, final int resultOffset) {
1115
1116
1117 double[] function = new double[1 + order];
1118 function[0] = JdkMath.log10(operand[operandOffset]);
1119 if (order > 0) {
1120 double inv = 1.0 / operand[operandOffset];
1121 double xk = inv / JdkMath.log(10.0);
1122 for (int i = 1; i <= order; ++i) {
1123 function[i] = xk;
1124 xk *= -i * inv;
1125 }
1126 }
1127
1128
1129 compose(operand, operandOffset, function, result, resultOffset);
1130 }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140 public void cos(final double[] operand, final int operandOffset,
1141 final double[] result, final int resultOffset) {
1142
1143
1144 double[] function = new double[1 + order];
1145 function[0] = JdkMath.cos(operand[operandOffset]);
1146 if (order > 0) {
1147 function[1] = -JdkMath.sin(operand[operandOffset]);
1148 for (int i = 2; i <= order; ++i) {
1149 function[i] = -function[i - 2];
1150 }
1151 }
1152
1153
1154 compose(operand, operandOffset, function, result, resultOffset);
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165 public void sin(final double[] operand, final int operandOffset,
1166 final double[] result, final int resultOffset) {
1167
1168
1169 double[] function = new double[1 + order];
1170 function[0] = JdkMath.sin(operand[operandOffset]);
1171 if (order > 0) {
1172 function[1] = JdkMath.cos(operand[operandOffset]);
1173 for (int i = 2; i <= order; ++i) {
1174 function[i] = -function[i - 2];
1175 }
1176 }
1177
1178
1179 compose(operand, operandOffset, function, result, resultOffset);
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190 public void tan(final double[] operand, final int operandOffset,
1191 final double[] result, final int resultOffset) {
1192
1193
1194 final double[] function = new double[1 + order];
1195 final double t = JdkMath.tan(operand[operandOffset]);
1196 function[0] = t;
1197
1198 if (order > 0) {
1199
1200
1201
1202
1203
1204
1205
1206
1207 final double[] p = new double[order + 2];
1208 p[1] = 1;
1209 final double t2 = t * t;
1210 for (int n = 1; n <= order; ++n) {
1211
1212
1213 double v = 0;
1214 p[n + 1] = n * p[n];
1215 for (int k = n + 1; k >= 0; k -= 2) {
1216 v = v * t2 + p[k];
1217 if (k > 2) {
1218 p[k - 2] = (k - 1) * p[k - 1] + (k - 3) * p[k - 3];
1219 } else if (k == 2) {
1220 p[0] = p[1];
1221 }
1222 }
1223 if ((n & 0x1) == 0) {
1224 v *= t;
1225 }
1226
1227 function[n] = v;
1228 }
1229 }
1230
1231
1232 compose(operand, operandOffset, function, result, resultOffset);
1233 }
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243 public void acos(final double[] operand, final int operandOffset,
1244 final double[] result, final int resultOffset) {
1245
1246
1247 double[] function = new double[1 + order];
1248 final double x = operand[operandOffset];
1249 function[0] = JdkMath.acos(x);
1250 if (order > 0) {
1251
1252
1253
1254
1255
1256
1257
1258 final double[] p = new double[order];
1259 p[0] = -1;
1260 final double x2 = x * x;
1261 final double f = 1.0 / (1 - x2);
1262 double coeff = JdkMath.sqrt(f);
1263 function[1] = coeff * p[0];
1264 for (int n = 2; n <= order; ++n) {
1265
1266
1267 double v = 0;
1268 p[n - 1] = (n - 1) * p[n - 2];
1269 for (int k = n - 1; k >= 0; k -= 2) {
1270 v = v * x2 + p[k];
1271 if (k > 2) {
1272 p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
1273 } else if (k == 2) {
1274 p[0] = p[1];
1275 }
1276 }
1277 if ((n & 0x1) == 0) {
1278 v *= x;
1279 }
1280
1281 coeff *= f;
1282 function[n] = coeff * v;
1283 }
1284 }
1285
1286
1287 compose(operand, operandOffset, function, result, resultOffset);
1288 }
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298 public void asin(final double[] operand, final int operandOffset,
1299 final double[] result, final int resultOffset) {
1300
1301
1302 double[] function = new double[1 + order];
1303 final double x = operand[operandOffset];
1304 function[0] = JdkMath.asin(x);
1305 if (order > 0) {
1306
1307
1308
1309
1310
1311
1312
1313 final double[] p = new double[order];
1314 p[0] = 1;
1315 final double x2 = x * x;
1316 final double f = 1.0 / (1 - x2);
1317 double coeff = JdkMath.sqrt(f);
1318 function[1] = coeff * p[0];
1319 for (int n = 2; n <= order; ++n) {
1320
1321
1322 double v = 0;
1323 p[n - 1] = (n - 1) * p[n - 2];
1324 for (int k = n - 1; k >= 0; k -= 2) {
1325 v = v * x2 + p[k];
1326 if (k > 2) {
1327 p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
1328 } else if (k == 2) {
1329 p[0] = p[1];
1330 }
1331 }
1332 if ((n & 0x1) == 0) {
1333 v *= x;
1334 }
1335
1336 coeff *= f;
1337 function[n] = coeff * v;
1338 }
1339 }
1340
1341
1342 compose(operand, operandOffset, function, result, resultOffset);
1343 }
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353 public void atan(final double[] operand, final int operandOffset,
1354 final double[] result, final int resultOffset) {
1355
1356
1357 double[] function = new double[1 + order];
1358 final double x = operand[operandOffset];
1359 function[0] = JdkMath.atan(x);
1360 if (order > 0) {
1361
1362
1363
1364
1365
1366
1367
1368 final double[] q = new double[order];
1369 q[0] = 1;
1370 final double x2 = x * x;
1371 final double f = 1.0 / (1 + x2);
1372 double coeff = f;
1373 function[1] = coeff * q[0];
1374 for (int n = 2; n <= order; ++n) {
1375
1376
1377 double v = 0;
1378 q[n - 1] = -n * q[n - 2];
1379 for (int k = n - 1; k >= 0; k -= 2) {
1380 v = v * x2 + q[k];
1381 if (k > 2) {
1382 q[k - 2] = (k - 1) * q[k - 1] + (k - 1 - 2 * n) * q[k - 3];
1383 } else if (k == 2) {
1384 q[0] = q[1];
1385 }
1386 }
1387 if ((n & 0x1) == 0) {
1388 v *= x;
1389 }
1390
1391 coeff *= f;
1392 function[n] = coeff * v;
1393 }
1394 }
1395
1396
1397 compose(operand, operandOffset, function, result, resultOffset);
1398 }
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410 public void atan2(final double[] y, final int yOffset,
1411 final double[] x, final int xOffset,
1412 final double[] result, final int resultOffset) {
1413
1414
1415 double[] tmp1 = new double[getSize()];
1416 multiply(x, xOffset, x, xOffset, tmp1, 0);
1417 double[] tmp2 = new double[getSize()];
1418 multiply(y, yOffset, y, yOffset, tmp2, 0);
1419 add(tmp1, 0, tmp2, 0, tmp2, 0);
1420 rootN(tmp2, 0, 2, tmp1, 0);
1421
1422 if (x[xOffset] >= 0) {
1423
1424
1425 add(tmp1, 0, x, xOffset, tmp2, 0);
1426 divide(y, yOffset, tmp2, 0, tmp1, 0);
1427 atan(tmp1, 0, tmp2, 0);
1428 for (int i = 0; i < tmp2.length; ++i) {
1429 result[resultOffset + i] = 2 * tmp2[i];
1430 }
1431 } else {
1432
1433
1434 subtract(tmp1, 0, x, xOffset, tmp2, 0);
1435 divide(y, yOffset, tmp2, 0, tmp1, 0);
1436 atan(tmp1, 0, tmp2, 0);
1437 result[resultOffset] =
1438 ((tmp2[0] <= 0) ? -JdkMath.PI : JdkMath.PI) - 2 * tmp2[0];
1439 for (int i = 1; i < tmp2.length; ++i) {
1440 result[resultOffset + i] = -2 * tmp2[i];
1441 }
1442 }
1443
1444
1445 result[resultOffset] = JdkMath.atan2(y[yOffset], x[xOffset]);
1446 }
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456 public void cosh(final double[] operand, final int operandOffset,
1457 final double[] result, final int resultOffset) {
1458
1459
1460 double[] function = new double[1 + order];
1461 function[0] = JdkMath.cosh(operand[operandOffset]);
1462 if (order > 0) {
1463 function[1] = JdkMath.sinh(operand[operandOffset]);
1464 for (int i = 2; i <= order; ++i) {
1465 function[i] = function[i - 2];
1466 }
1467 }
1468
1469
1470 compose(operand, operandOffset, function, result, resultOffset);
1471 }
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481 public void sinh(final double[] operand, final int operandOffset,
1482 final double[] result, final int resultOffset) {
1483
1484
1485 double[] function = new double[1 + order];
1486 function[0] = JdkMath.sinh(operand[operandOffset]);
1487 if (order > 0) {
1488 function[1] = JdkMath.cosh(operand[operandOffset]);
1489 for (int i = 2; i <= order; ++i) {
1490 function[i] = function[i - 2];
1491 }
1492 }
1493
1494
1495 compose(operand, operandOffset, function, result, resultOffset);
1496 }
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506 public void tanh(final double[] operand, final int operandOffset,
1507 final double[] result, final int resultOffset) {
1508
1509
1510 final double[] function = new double[1 + order];
1511 final double t = JdkMath.tanh(operand[operandOffset]);
1512 function[0] = t;
1513
1514 if (order > 0) {
1515
1516
1517
1518
1519
1520
1521
1522
1523 final double[] p = new double[order + 2];
1524 p[1] = 1;
1525 final double t2 = t * t;
1526 for (int n = 1; n <= order; ++n) {
1527
1528
1529 double v = 0;
1530 p[n + 1] = -n * p[n];
1531 for (int k = n + 1; k >= 0; k -= 2) {
1532 v = v * t2 + p[k];
1533 if (k > 2) {
1534 p[k - 2] = (k - 1) * p[k - 1] - (k - 3) * p[k - 3];
1535 } else if (k == 2) {
1536 p[0] = p[1];
1537 }
1538 }
1539 if ((n & 0x1) == 0) {
1540 v *= t;
1541 }
1542
1543 function[n] = v;
1544 }
1545 }
1546
1547
1548 compose(operand, operandOffset, function, result, resultOffset);
1549 }
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559 public void acosh(final double[] operand, final int operandOffset,
1560 final double[] result, final int resultOffset) {
1561
1562
1563 double[] function = new double[1 + order];
1564 final double x = operand[operandOffset];
1565 function[0] = JdkMath.acosh(x);
1566 if (order > 0) {
1567
1568
1569
1570
1571
1572
1573
1574 final double[] p = new double[order];
1575 p[0] = 1;
1576 final double x2 = x * x;
1577 final double f = 1.0 / (x2 - 1);
1578 double coeff = JdkMath.sqrt(f);
1579 function[1] = coeff * p[0];
1580 for (int n = 2; n <= order; ++n) {
1581
1582
1583 double v = 0;
1584 p[n - 1] = (1 - n) * p[n - 2];
1585 for (int k = n - 1; k >= 0; k -= 2) {
1586 v = v * x2 + p[k];
1587 if (k > 2) {
1588 p[k - 2] = (1 - k) * p[k - 1] + (k - 2 * n) * p[k - 3];
1589 } else if (k == 2) {
1590 p[0] = -p[1];
1591 }
1592 }
1593 if ((n & 0x1) == 0) {
1594 v *= x;
1595 }
1596
1597 coeff *= f;
1598 function[n] = coeff * v;
1599 }
1600 }
1601
1602
1603 compose(operand, operandOffset, function, result, resultOffset);
1604 }
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614 public void asinh(final double[] operand, final int operandOffset,
1615 final double[] result, final int resultOffset) {
1616
1617
1618 double[] function = new double[1 + order];
1619 final double x = operand[operandOffset];
1620 function[0] = JdkMath.asinh(x);
1621 if (order > 0) {
1622
1623
1624
1625
1626
1627
1628
1629 final double[] p = new double[order];
1630 p[0] = 1;
1631 final double x2 = x * x;
1632 final double f = 1.0 / (1 + x2);
1633 double coeff = JdkMath.sqrt(f);
1634 function[1] = coeff * p[0];
1635 for (int n = 2; n <= order; ++n) {
1636
1637
1638 double v = 0;
1639 p[n - 1] = (1 - n) * p[n - 2];
1640 for (int k = n - 1; k >= 0; k -= 2) {
1641 v = v * x2 + p[k];
1642 if (k > 2) {
1643 p[k - 2] = (k - 1) * p[k - 1] + (k - 2 * n) * p[k - 3];
1644 } else if (k == 2) {
1645 p[0] = p[1];
1646 }
1647 }
1648 if ((n & 0x1) == 0) {
1649 v *= x;
1650 }
1651
1652 coeff *= f;
1653 function[n] = coeff * v;
1654 }
1655 }
1656
1657
1658 compose(operand, operandOffset, function, result, resultOffset);
1659 }
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669 public void atanh(final double[] operand, final int operandOffset,
1670 final double[] result, final int resultOffset) {
1671
1672
1673 double[] function = new double[1 + order];
1674 final double x = operand[operandOffset];
1675 function[0] = JdkMath.atanh(x);
1676 if (order > 0) {
1677
1678
1679
1680
1681
1682
1683
1684 final double[] q = new double[order];
1685 q[0] = 1;
1686 final double x2 = x * x;
1687 final double f = 1.0 / (1 - x2);
1688 double coeff = f;
1689 function[1] = coeff * q[0];
1690 for (int n = 2; n <= order; ++n) {
1691
1692
1693 double v = 0;
1694 q[n - 1] = n * q[n - 2];
1695 for (int k = n - 1; k >= 0; k -= 2) {
1696 v = v * x2 + q[k];
1697 if (k > 2) {
1698 q[k - 2] = (k - 1) * q[k - 1] + (2 * n - k + 1) * q[k - 3];
1699 } else if (k == 2) {
1700 q[0] = q[1];
1701 }
1702 }
1703 if ((n & 0x1) == 0) {
1704 v *= x;
1705 }
1706
1707 coeff *= f;
1708 function[n] = coeff * v;
1709 }
1710 }
1711
1712
1713 compose(operand, operandOffset, function, result, resultOffset);
1714 }
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726 public void compose(final double[] operand, final int operandOffset, final double[] f,
1727 final double[] result, final int resultOffset) {
1728 for (int i = 0; i < compIndirection.length; ++i) {
1729 final int[][] mappingI = compIndirection[i];
1730 double r = 0;
1731 for (int j = 0; j < mappingI.length; ++j) {
1732 final int[] mappingIJ = mappingI[j];
1733 double product = mappingIJ[0] * f[mappingIJ[1]];
1734 for (int k = 2; k < mappingIJ.length; ++k) {
1735 product *= operand[operandOffset + mappingIJ[k]];
1736 }
1737 r += product;
1738 }
1739 result[resultOffset + i] = r;
1740 }
1741 }
1742
1743
1744
1745
1746
1747
1748
1749
1750 public double taylor(final double[] ds, final int dsOffset, final double ... delta)
1751 throws MathArithmeticException {
1752 double value = 0;
1753 for (int i = getSize() - 1; i >= 0; --i) {
1754 final int[] orders = getPartialDerivativeOrders(i);
1755 double term = ds[dsOffset + i];
1756 for (int k = 0; k < orders.length; ++k) {
1757 if (orders[k] > 0) {
1758 try {
1759 term *= JdkMath.pow(delta[k], orders[k]) / Factorial.doubleValue(orders[k]);
1760 } catch (NotPositiveException e) {
1761
1762 throw new MathInternalError(e);
1763 }
1764 }
1765 }
1766 value += term;
1767 }
1768 return value;
1769 }
1770
1771
1772
1773
1774
1775 public void checkCompatibility(final DSCompiler compiler)
1776 throws DimensionMismatchException {
1777 if (parameters != compiler.parameters) {
1778 throw new DimensionMismatchException(parameters, compiler.parameters);
1779 }
1780 if (order != compiler.order) {
1781 throw new DimensionMismatchException(order, compiler.order);
1782 }
1783 }
1784 }