1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.math3.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.math3.exception.DimensionMismatchException;
25 import org.apache.commons.math3.exception.MathArithmeticException;
26 import org.apache.commons.math3.exception.MathInternalError;
27 import org.apache.commons.math3.exception.NotPositiveException;
28 import org.apache.commons.math3.exception.NumberIsTooLargeException;
29 import org.apache.commons.math3.util.ArithmeticUtils;
30 import org.apache.commons.math3.util.FastMath;
31 import org.apache.commons.math3.util.MathArrays;
32
33 /** Class holding "compiled" computation rules for derivative structures.
34 * <p>This class implements the computation rules described in Dan Kalman's paper <a
35 * href="http://www.math.american.edu/People/kalman/pdffiles/mmgautodiff.pdf">Doubly
36 * Recursive Multivariate Automatic Differentiation</a>, Mathematics Magazine, vol. 75,
37 * no. 3, June 2002. However, in order to avoid performances bottlenecks, the recursive
38 * rules are "compiled" once in an unfold form. This class does this recursion unrolling
39 * and stores the computation rules as simple loops with pre-computed indirection arrays.</p>
40 * <p>
41 * This class maps all derivative computation into single dimension arrays that hold the
42 * value and partial derivatives. The class does not hold these arrays, which remains under
43 * the responsibility of the caller. For each combination of number of free parameters and
44 * derivation order, only one compiler is necessary, and this compiler will be used to
45 * perform computations on all arrays provided to it, which can represent hundreds or
46 * thousands of different parameters kept together with all theur partial derivatives.
47 * </p>
48 * <p>
49 * The arrays on which compilers operate contain only the partial derivatives together
50 * with the 0<sup>th</sup> derivative, i.e. the value. The partial derivatives are stored in
51 * a compiler-specific order, which can be retrieved using methods {@link
52 * #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} and {@link
53 * #getPartialDerivativeOrders(int)}. The value is guaranteed to be stored as the first element
54 * (i.e. the {@link #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} method returns
55 * 0 when called with 0 for all derivation orders and {@link #getPartialDerivativeOrders(int)
56 * getPartialDerivativeOrders} returns an array filled with 0 when called with 0 as the index).
57 * </p>
58 * <p>
59 * Note that the ordering changes with number of parameters and derivation order. For example
60 * given 2 parameters x and y, df/dy is stored at index 2 when derivation order is set to 1 (in
61 * this case the array has three elements: f, df/dx and df/dy). If derivation order is set to
62 * 2, then df/dy will be stored at index 3 (in this case the array has six elements: f, df/dx,
63 * df/dxdx, df/dy, df/dxdy and df/dydy).
64 * </p>
65 * <p>
66 * Given this structure, users can perform some simple operations like adding, subtracting
67 * or multiplying constants and negating the elements by themselves, knowing if they want to
68 * mutate their array or create a new array. These simple operations are not provided by
69 * the compiler. The compiler provides only the more complex operations between several arrays.
70 * </p>
71 * <p>This class is mainly used as the engine for scalar variable {@link DerivativeStructure}.
72 * It can also be used directly to hold several variables in arrays for more complex data
73 * structures. User can for example store a vector of n variables depending on three x, y
74 * and z free parameters in one array as follows:
75 * <pre>
76 * // parameter 0 is x, parameter 1 is y, parameter 2 is z
77 * int parameters = 3;
78 * DSCompiler compiler = DSCompiler.getCompiler(parameters, order);
79 * int size = compiler.getSize();
80 *
81 * // pack all elements in a single array
82 * double[] array = new double[n * size];
83 * for (int i = 0; i < n; ++i) {
84 *
85 * // we know value is guaranteed to be the first element
86 * array[i * size] = v[i];
87 *
88 * // we don't know where first derivatives are stored, so we ask the compiler
89 * array[i * size + compiler.getPartialDerivativeIndex(1, 0, 0) = dvOnDx[i][0];
90 * array[i * size + compiler.getPartialDerivativeIndex(0, 1, 0) = dvOnDy[i][0];
91 * array[i * size + compiler.getPartialDerivativeIndex(0, 0, 1) = dvOnDz[i][0];
92 *
93 * // we let all higher order derivatives set to 0
94 *
95 * }
96 * </pre>
97 * Then in another function, user can perform some operations on all elements stored
98 * in the single array, such as a simple product of all variables:
99 * <pre>
100 * // compute the product of all elements
101 * double[] product = new double[size];
102 * prod[0] = 1.0;
103 * for (int i = 0; i < n; ++i) {
104 * double[] tmp = product.clone();
105 * compiler.multiply(tmp, 0, array, i * size, product, 0);
106 * }
107 *
108 * // value
109 * double p = product[0];
110 *
111 * // first derivatives
112 * double dPdX = product[compiler.getPartialDerivativeIndex(1, 0, 0)];
113 * double dPdY = product[compiler.getPartialDerivativeIndex(0, 1, 0)];
114 * double dPdZ = product[compiler.getPartialDerivativeIndex(0, 0, 1)];
115 *
116 * // cross derivatives (assuming order was at least 2)
117 * double dPdXdX = product[compiler.getPartialDerivativeIndex(2, 0, 0)];
118 * double dPdXdY = product[compiler.getPartialDerivativeIndex(1, 1, 0)];
119 * double dPdXdZ = product[compiler.getPartialDerivativeIndex(1, 0, 1)];
120 * double dPdYdY = product[compiler.getPartialDerivativeIndex(0, 2, 0)];
121 * double dPdYdZ = product[compiler.getPartialDerivativeIndex(0, 1, 1)];
122 * double dPdZdZ = product[compiler.getPartialDerivativeIndex(0, 0, 2)];
123 * </p>
124 * @see DerivativeStructure
125 * @version $Id: DSCompiler.java 1462503 2013-03-29 15:48:27Z luc $
126 * @since 3.1
127 */
128 public class DSCompiler {
129
130 /** Array of all compilers created so far. */
131 private static AtomicReference<DSCompiler[][]> compilers =
132 new AtomicReference<DSCompiler[][]>(null);
133
134 /** Number of free parameters. */
135 private final int parameters;
136
137 /** Derivation order. */
138 private final int order;
139
140 /** Number of partial derivatives (including the single 0 order derivative element). */
141 private final int[][] sizes;
142
143 /** Indirection array for partial derivatives. */
144 private final int[][] derivativesIndirection;
145
146 /** Indirection array of the lower derivative elements. */
147 private final int[] lowerIndirection;
148
149 /** Indirection arrays for multiplication. */
150 private final int[][][] multIndirection;
151
152 /** Indirection arrays for function composition. */
153 private final int[][][] compIndirection;
154
155 /** Private constructor, reserved for the factory method {@link #getCompiler(int, int)}.
156 * @param parameters number of free parameters
157 * @param order derivation order
158 * @param valueCompiler compiler for the value part
159 * @param derivativeCompiler compiler for the derivative part
160 * @throws NumberIsTooLargeException if order is too large
161 */
162 private DSCompiler(final int parameters, final int order,
163 final DSCompiler valueCompiler, final DSCompiler derivativeCompiler)
164 throws NumberIsTooLargeException {
165
166 this.parameters = parameters;
167 this.order = order;
168 this.sizes = compileSizes(parameters, order, valueCompiler);
169 this.derivativesIndirection =
170 compileDerivativesIndirection(parameters, order,
171 valueCompiler, derivativeCompiler);
172 this.lowerIndirection =
173 compileLowerIndirection(parameters, order,
174 valueCompiler, derivativeCompiler);
175 this.multIndirection =
176 compileMultiplicationIndirection(parameters, order,
177 valueCompiler, derivativeCompiler, lowerIndirection);
178 this.compIndirection =
179 compileCompositionIndirection(parameters, order,
180 valueCompiler, derivativeCompiler,
181 sizes, derivativesIndirection);
182
183 }
184
185 /** Get the compiler for number of free parameters and order.
186 * @param parameters number of free parameters
187 * @param order derivation order
188 * @return cached rules set
189 * @throws NumberIsTooLargeException if order is too large
190 */
191 public static DSCompiler getCompiler(int parameters, int order)
192 throws NumberIsTooLargeException {
193
194 // get the cached compilers
195 final DSCompiler[][] cache = compilers.get();
196 if (cache != null && cache.length > parameters &&
197 cache[parameters].length > order && cache[parameters][order] != null) {
198 // the compiler has already been created
199 return cache[parameters][order];
200 }
201
202 // we need to create more compilers
203 final int maxParameters = FastMath.max(parameters, cache == null ? 0 : cache.length);
204 final int maxOrder = FastMath.max(order, cache == null ? 0 : cache[0].length);
205 final DSCompiler[][] newCache = new DSCompiler[maxParameters + 1][maxOrder + 1];
206
207 if (cache != null) {
208 // preserve the already created compilers
209 for (int i = 0; i < cache.length; ++i) {
210 System.arraycopy(cache[i], 0, newCache[i], 0, cache[i].length);
211 }
212 }
213
214 // create the array in increasing diagonal order
215 for (int diag = 0; diag <= parameters + order; ++diag) {
216 for (int o = FastMath.max(0, diag - parameters); o <= FastMath.min(order, diag); ++o) {
217 final int p = diag - o;
218 if (newCache[p][o] == null) {
219 final DSCompiler valueCompiler = (p == 0) ? null : newCache[p - 1][o];
220 final DSCompiler derivativeCompiler = (o == 0) ? null : newCache[p][o - 1];
221 newCache[p][o] = new DSCompiler(p, o, valueCompiler, derivativeCompiler);
222 }
223 }
224 }
225
226 // atomically reset the cached compilers array
227 compilers.compareAndSet(cache, newCache);
228
229 return newCache[parameters][order];
230
231 }
232
233 /** Compile the sizes array.
234 * @param parameters number of free parameters
235 * @param order derivation order
236 * @param valueCompiler compiler for the value part
237 * @return sizes array
238 */
239 private static int[][] compileSizes(final int parameters, final int order,
240 final DSCompiler valueCompiler) {
241
242 final int[][] sizes = new int[parameters + 1][order + 1];
243 if (parameters == 0) {
244 Arrays.fill(sizes[0], 1);
245 } else {
246 System.arraycopy(valueCompiler.sizes, 0, sizes, 0, parameters);
247 sizes[parameters][0] = 1;
248 for (int i = 0; i < order; ++i) {
249 sizes[parameters][i + 1] = sizes[parameters][i] + sizes[parameters - 1][i + 1];
250 }
251 }
252
253 return sizes;
254
255 }
256
257 /** Compile the derivatives indirection array.
258 * @param parameters number of free parameters
259 * @param order derivation order
260 * @param valueCompiler compiler for the value part
261 * @param derivativeCompiler compiler for the derivative part
262 * @return derivatives indirection array
263 */
264 private static int[][] compileDerivativesIndirection(final int parameters, final int order,
265 final DSCompiler valueCompiler,
266 final DSCompiler derivativeCompiler) {
267
268 if (parameters == 0 || order == 0) {
269 return new int[1][parameters];
270 }
271
272 final int vSize = valueCompiler.derivativesIndirection.length;
273 final int dSize = derivativeCompiler.derivativesIndirection.length;
274 final int[][] derivativesIndirection = new int[vSize + dSize][parameters];
275
276 // set up the indices for the value part
277 for (int i = 0; i < vSize; ++i) {
278 // copy the first indices, the last one remaining set to 0
279 System.arraycopy(valueCompiler.derivativesIndirection[i], 0,
280 derivativesIndirection[i], 0,
281 parameters - 1);
282 }
283
284 // set up the indices for the derivative part
285 for (int i = 0; i < dSize; ++i) {
286
287 // copy the indices
288 System.arraycopy(derivativeCompiler.derivativesIndirection[i], 0,
289 derivativesIndirection[vSize + i], 0,
290 parameters);
291
292 // increment the derivation order for the last parameter
293 derivativesIndirection[vSize + i][parameters - 1]++;
294
295 }
296
297 return derivativesIndirection;
298
299 }
300
301 /** Compile the lower derivatives indirection array.
302 * <p>
303 * This indirection array contains the indices of all elements
304 * except derivatives for last derivation order.
305 * </p>
306 * @param parameters number of free parameters
307 * @param order derivation order
308 * @param valueCompiler compiler for the value part
309 * @param derivativeCompiler compiler for the derivative part
310 * @return lower derivatives indirection array
311 */
312 private static int[] compileLowerIndirection(final int parameters, final int order,
313 final DSCompiler valueCompiler,
314 final DSCompiler derivativeCompiler) {
315
316 if (parameters == 0 || order <= 1) {
317 return new int[] { 0 };
318 }
319
320 // this is an implementation of definition 6 in Dan Kalman's paper.
321 final int vSize = valueCompiler.lowerIndirection.length;
322 final int dSize = derivativeCompiler.lowerIndirection.length;
323 final int[] lowerIndirection = new int[vSize + dSize];
324 System.arraycopy(valueCompiler.lowerIndirection, 0, lowerIndirection, 0, vSize);
325 for (int i = 0; i < dSize; ++i) {
326 lowerIndirection[vSize + i] = valueCompiler.getSize() + derivativeCompiler.lowerIndirection[i];
327 }
328
329 return lowerIndirection;
330
331 }
332
333 /** Compile the multiplication indirection array.
334 * <p>
335 * This indirection array contains the indices of all pairs of elements
336 * involved when computing a multiplication. This allows a straightforward
337 * loop-based multiplication (see {@link #multiply(double[], int, double[], int, double[], int)}).
338 * </p>
339 * @param parameters number of free parameters
340 * @param order derivation order
341 * @param valueCompiler compiler for the value part
342 * @param derivativeCompiler compiler for the derivative part
343 * @param lowerIndirection lower derivatives indirection array
344 * @return multiplication indirection array
345 */
346 private static int[][][] compileMultiplicationIndirection(final int parameters, final int order,
347 final DSCompiler valueCompiler,
348 final DSCompiler derivativeCompiler,
349 final int[] lowerIndirection) {
350
351 if ((parameters == 0) || (order == 0)) {
352 return new int[][][] { { { 1, 0, 0 } } };
353 }
354
355 // this is an implementation of definition 3 in Dan Kalman's paper.
356 final int vSize = valueCompiler.multIndirection.length;
357 final int dSize = derivativeCompiler.multIndirection.length;
358 final int[][][] multIndirection = new int[vSize + dSize][][];
359
360 System.arraycopy(valueCompiler.multIndirection, 0, multIndirection, 0, vSize);
361
362 for (int i = 0; i < dSize; ++i) {
363 final int[][] dRow = derivativeCompiler.multIndirection[i];
364 List<int[]> row = new ArrayList<int[]>();
365 for (int j = 0; j < dRow.length; ++j) {
366 row.add(new int[] { dRow[j][0], lowerIndirection[dRow[j][1]], vSize + dRow[j][2] });
367 row.add(new int[] { dRow[j][0], vSize + dRow[j][1], lowerIndirection[dRow[j][2]] });
368 }
369
370 // combine terms with similar derivation orders
371 final List<int[]> combined = new ArrayList<int[]>(row.size());
372 for (int j = 0; j < row.size(); ++j) {
373 final int[] termJ = row.get(j);
374 if (termJ[0] > 0) {
375 for (int k = j + 1; k < row.size(); ++k) {
376 final int[] termK = row.get(k);
377 if (termJ[1] == termK[1] && termJ[2] == termK[2]) {
378 // combine termJ and termK
379 termJ[0] += termK[0];
380 // make sure we will skip termK later on in the outer loop
381 termK[0] = 0;
382 }
383 }
384 combined.add(termJ);
385 }
386 }
387
388 multIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
389
390 }
391
392 return multIndirection;
393
394 }
395
396 /** Compile the function composition indirection array.
397 * <p>
398 * This indirection array contains the indices of all sets of elements
399 * involved when computing a composition. This allows a straightforward
400 * loop-based composition (see {@link #compose(double[], int, double[], double[], int)}).
401 * </p>
402 * @param parameters number of free parameters
403 * @param order derivation order
404 * @param valueCompiler compiler for the value part
405 * @param derivativeCompiler compiler for the derivative part
406 * @param sizes sizes array
407 * @param derivativesIndirection derivatives indirection array
408 * @return multiplication indirection array
409 * @throws NumberIsTooLargeException if order is too large
410 */
411 private static int[][][] compileCompositionIndirection(final int parameters, final int order,
412 final DSCompiler valueCompiler,
413 final DSCompiler derivativeCompiler,
414 final int[][] sizes,
415 final int[][] derivativesIndirection)
416 throws NumberIsTooLargeException {
417
418 if ((parameters == 0) || (order == 0)) {
419 return new int[][][] { { { 1, 0 } } };
420 }
421
422 final int vSize = valueCompiler.compIndirection.length;
423 final int dSize = derivativeCompiler.compIndirection.length;
424 final int[][][] compIndirection = new int[vSize + dSize][][];
425
426 // the composition rules from the value part can be reused as is
427 System.arraycopy(valueCompiler.compIndirection, 0, compIndirection, 0, vSize);
428
429 // the composition rules for the derivative part are deduced by
430 // differentiation the rules from the underlying compiler once
431 // with respect to the parameter this compiler handles and the
432 // underlying one did not handle
433 for (int i = 0; i < dSize; ++i) {
434 List<int[]> row = new ArrayList<int[]>();
435 for (int[] term : derivativeCompiler.compIndirection[i]) {
436
437 // handle term p * f_k(g(x)) * g_l1(x) * g_l2(x) * ... * g_lp(x)
438
439 // derive the first factor in the term: f_k with respect to new parameter
440 int[] derivedTermF = new int[term.length + 1];
441 derivedTermF[0] = term[0]; // p
442 derivedTermF[1] = term[1] + 1; // f_(k+1)
443 int[] orders = new int[parameters];
444 orders[parameters - 1] = 1;
445 derivedTermF[term.length] = getPartialDerivativeIndex(parameters, order, sizes, orders); // g_1
446 for (int j = 2; j < term.length; ++j) {
447 // convert the indices as the mapping for the current order
448 // is different from the mapping with one less order
449 derivedTermF[j] = convertIndex(term[j], parameters,
450 derivativeCompiler.derivativesIndirection,
451 parameters, order, sizes);
452 }
453 Arrays.sort(derivedTermF, 2, derivedTermF.length);
454 row.add(derivedTermF);
455
456 // derive the various g_l
457 for (int l = 2; l < term.length; ++l) {
458 int[] derivedTermG = new int[term.length];
459 derivedTermG[0] = term[0];
460 derivedTermG[1] = term[1];
461 for (int j = 2; j < term.length; ++j) {
462 // convert the indices as the mapping for the current order
463 // is different from the mapping with one less order
464 derivedTermG[j] = convertIndex(term[j], parameters,
465 derivativeCompiler.derivativesIndirection,
466 parameters, order, sizes);
467 if (j == l) {
468 // derive this term
469 System.arraycopy(derivativesIndirection[derivedTermG[j]], 0, orders, 0, parameters);
470 orders[parameters - 1]++;
471 derivedTermG[j] = getPartialDerivativeIndex(parameters, order, sizes, orders);
472 }
473 }
474 Arrays.sort(derivedTermG, 2, derivedTermG.length);
475 row.add(derivedTermG);
476 }
477
478 }
479
480 // combine terms with similar derivation orders
481 final List<int[]> combined = new ArrayList<int[]>(row.size());
482 for (int j = 0; j < row.size(); ++j) {
483 final int[] termJ = row.get(j);
484 if (termJ[0] > 0) {
485 for (int k = j + 1; k < row.size(); ++k) {
486 final int[] termK = row.get(k);
487 boolean equals = termJ.length == termK.length;
488 for (int l = 1; equals && l < termJ.length; ++l) {
489 equals &= termJ[l] == termK[l];
490 }
491 if (equals) {
492 // combine termJ and termK
493 termJ[0] += termK[0];
494 // make sure we will skip termK later on in the outer loop
495 termK[0] = 0;
496 }
497 }
498 combined.add(termJ);
499 }
500 }
501
502 compIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
503
504 }
505
506 return compIndirection;
507
508 }
509
510 /** Get the index of a partial derivative in the array.
511 * <p>
512 * If all orders are set to 0, then the 0<sup>th</sup> order derivative
513 * is returned, which is the value of the function.
514 * </p>
515 * <p>The indices of derivatives are between 0 and {@link #getSize() getSize()} - 1.
516 * Their specific order is fixed for a given compiler, but otherwise not
517 * publicly specified. There are however some simple cases which have guaranteed
518 * indices:
519 * </p>
520 * <ul>
521 * <li>the index of 0<sup>th</sup> order derivative is always 0</li>
522 * <li>if there is only 1 {@link #getFreeParameters() free parameter}, then the
523 * derivatives are sorted in increasing derivation order (i.e. f at index 0, df/dp
524 * at index 1, d<sup>2</sup>f/dp<sup>2</sup> at index 2 ...
525 * d<sup>k</sup>f/dp<sup>k</sup> at index k),</li>
526 * <li>if the {@link #getOrder() derivation order} is 1, then the derivatives
527 * are sorted in increasing free parameter order (i.e. f at index 0, df/dx<sub>1</sub>
528 * at index 1, df/dx<sub>2</sub> at index 2 ... df/dx<sub>k</sub> at index k),</li>
529 * <li>all other cases are not publicly specified</li>
530 * </ul>
531 * <p>
532 * This method is the inverse of method {@link #getPartialDerivativeOrders(int)}
533 * </p>
534 * @param orders derivation orders with respect to each parameter
535 * @return index of the partial derivative
536 * @exception DimensionMismatchException if the numbers of parameters does not
537 * match the instance
538 * @exception NumberIsTooLargeException if sum of derivation orders is larger
539 * than the instance limits
540 * @see #getPartialDerivativeOrders(int)
541 */
542 public int getPartialDerivativeIndex(final int ... orders)
543 throws DimensionMismatchException, NumberIsTooLargeException {
544
545 // safety check
546 if (orders.length != getFreeParameters()) {
547 throw new DimensionMismatchException(orders.length, getFreeParameters());
548 }
549
550 return getPartialDerivativeIndex(parameters, order, sizes, orders);
551
552 }
553
554 /** Get the index of a partial derivative in an array.
555 * @param parameters number of free parameters
556 * @param order derivation order
557 * @param sizes sizes array
558 * @param orders derivation orders with respect to each parameter
559 * (the lenght of this array must match the number of parameters)
560 * @return index of the partial derivative
561 * @exception NumberIsTooLargeException if sum of derivation orders is larger
562 * than the instance limits
563 */
564 private static int getPartialDerivativeIndex(final int parameters, final int order,
565 final int[][] sizes, final int ... orders)
566 throws NumberIsTooLargeException {
567
568 // the value is obtained by diving into the recursive Dan Kalman's structure
569 // this is theorem 2 of his paper, with recursion replaced by iteration
570 int index = 0;
571 int m = order;
572 int ordersSum = 0;
573 for (int i = parameters - 1; i >= 0; --i) {
574
575 // derivative order for current free parameter
576 int derivativeOrder = orders[i];
577
578 // safety check
579 ordersSum += derivativeOrder;
580 if (ordersSum > order) {
581 throw new NumberIsTooLargeException(ordersSum, order, true);
582 }
583
584 while (derivativeOrder-- > 0) {
585 // as long as we differentiate according to current free parameter,
586 // we have to skip the value part and dive into the derivative part
587 // so we add the size of the value part to the base index
588 index += sizes[i][m--];
589 }
590
591 }
592
593 return index;
594
595 }
596
597 /** Convert an index from one (parameters, order) structure to another.
598 * @param index index of a partial derivative in source derivative structure
599 * @param srcP number of free parameters in source derivative structure
600 * @param srcDerivativesIndirection derivatives indirection array for the source
601 * derivative structure
602 * @param destP number of free parameters in destination derivative structure
603 * @param destO derivation order in destination derivative structure
604 * @param destSizes sizes array for the destination derivative structure
605 * @return index of the partial derivative with the <em>same</em> characteristics
606 * in destination derivative structure
607 * @throws NumberIsTooLargeException if order is too large
608 */
609 private static int convertIndex(final int index,
610 final int srcP, final int[][] srcDerivativesIndirection,
611 final int destP, final int destO, final int[][] destSizes)
612 throws NumberIsTooLargeException {
613 int[] orders = new int[destP];
614 System.arraycopy(srcDerivativesIndirection[index], 0, orders, 0, FastMath.min(srcP, destP));
615 return getPartialDerivativeIndex(destP, destO, destSizes, orders);
616 }
617
618 /** Get the derivation orders for a specific index in the array.
619 * <p>
620 * This method is the inverse of {@link #getPartialDerivativeIndex(int...)}.
621 * </p>
622 * @param index of the partial derivative
623 * @return orders derivation orders with respect to each parameter
624 * @see #getPartialDerivativeIndex(int...)
625 */
626 public int[] getPartialDerivativeOrders(final int index) {
627 return derivativesIndirection[index];
628 }
629
630 /** Get the number of free parameters.
631 * @return number of free parameters
632 */
633 public int getFreeParameters() {
634 return parameters;
635 }
636
637 /** Get the derivation order.
638 * @return derivation order
639 */
640 public int getOrder() {
641 return order;
642 }
643
644 /** Get the array size required for holding partial derivatives data.
645 * <p>
646 * This number includes the single 0 order derivative element, which is
647 * guaranteed to be stored in the first element of the array.
648 * </p>
649 * @return array size required for holding partial derivatives data
650 */
651 public int getSize() {
652 return sizes[parameters][order];
653 }
654
655 /** Compute linear combination.
656 * The derivative structure built will be a1 * ds1 + a2 * ds2
657 * @param a1 first scale factor
658 * @param c1 first base (unscaled) component
659 * @param offset1 offset of first operand in its array
660 * @param a2 second scale factor
661 * @param c2 second base (unscaled) component
662 * @param offset2 offset of second operand in its array
663 * @param result array where result must be stored (it may be
664 * one of the input arrays)
665 * @param resultOffset offset of the result in its array
666 */
667 public void linearCombination(final double a1, final double[] c1, final int offset1,
668 final double a2, final double[] c2, final int offset2,
669 final double[] result, final int resultOffset) {
670 for (int i = 0; i < getSize(); ++i) {
671 result[resultOffset + i] =
672 MathArrays.linearCombination(a1, c1[offset1 + i], a2, c2[offset2 + i]);
673 }
674 }
675
676 /** Compute linear combination.
677 * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
678 * @param a1 first scale factor
679 * @param c1 first base (unscaled) component
680 * @param offset1 offset of first operand in its array
681 * @param a2 second scale factor
682 * @param c2 second base (unscaled) component
683 * @param offset2 offset of second operand in its array
684 * @param a3 third scale factor
685 * @param c3 third base (unscaled) component
686 * @param offset3 offset of third operand in its array
687 * @param result array where result must be stored (it may be
688 * one of the input arrays)
689 * @param resultOffset offset of the result in its array
690 */
691 public void linearCombination(final double a1, final double[] c1, final int offset1,
692 final double a2, final double[] c2, final int offset2,
693 final double a3, final double[] c3, final int offset3,
694 final double[] result, final int resultOffset) {
695 for (int i = 0; i < getSize(); ++i) {
696 result[resultOffset + i] =
697 MathArrays.linearCombination(a1, c1[offset1 + i],
698 a2, c2[offset2 + i],
699 a3, c3[offset3 + i]);
700 }
701 }
702
703 /** Compute linear combination.
704 * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
705 * @param a1 first scale factor
706 * @param c1 first base (unscaled) component
707 * @param offset1 offset of first operand in its array
708 * @param a2 second scale factor
709 * @param c2 second base (unscaled) component
710 * @param offset2 offset of second operand in its array
711 * @param a3 third scale factor
712 * @param c3 third base (unscaled) component
713 * @param offset3 offset of third operand in its array
714 * @param a4 fourth scale factor
715 * @param c4 fourth base (unscaled) component
716 * @param offset4 offset of fourth operand in its array
717 * @param result array where result must be stored (it may be
718 * one of the input arrays)
719 * @param resultOffset offset of the result in its array
720 */
721 public void linearCombination(final double a1, final double[] c1, final int offset1,
722 final double a2, final double[] c2, final int offset2,
723 final double a3, final double[] c3, final int offset3,
724 final double a4, final double[] c4, final int offset4,
725 final double[] result, final int resultOffset) {
726 for (int i = 0; i < getSize(); ++i) {
727 result[resultOffset + i] =
728 MathArrays.linearCombination(a1, c1[offset1 + i],
729 a2, c2[offset2 + i],
730 a3, c3[offset3 + i],
731 a4, c4[offset4 + i]);
732 }
733 }
734
735 /** Perform addition of two derivative structures.
736 * @param lhs array holding left hand side of addition
737 * @param lhsOffset offset of the left hand side in its array
738 * @param rhs array right hand side of addition
739 * @param rhsOffset offset of the right hand side in its array
740 * @param result array where result must be stored (it may be
741 * one of the input arrays)
742 * @param resultOffset offset of the result in its array
743 */
744 public void add(final double[] lhs, final int lhsOffset,
745 final double[] rhs, final int rhsOffset,
746 final double[] result, final int resultOffset) {
747 for (int i = 0; i < getSize(); ++i) {
748 result[resultOffset + i] = lhs[lhsOffset + i] + rhs[rhsOffset + i];
749 }
750 }
751 /** Perform subtraction of two derivative structures.
752 * @param lhs array holding left hand side of subtraction
753 * @param lhsOffset offset of the left hand side in its array
754 * @param rhs array right hand side of subtraction
755 * @param rhsOffset offset of the right hand side in its array
756 * @param result array where result must be stored (it may be
757 * one of the input arrays)
758 * @param resultOffset offset of the result in its array
759 */
760 public void subtract(final double[] lhs, final int lhsOffset,
761 final double[] rhs, final int rhsOffset,
762 final double[] result, final int resultOffset) {
763 for (int i = 0; i < getSize(); ++i) {
764 result[resultOffset + i] = lhs[lhsOffset + i] - rhs[rhsOffset + i];
765 }
766 }
767
768 /** Perform multiplication of two derivative structures.
769 * @param lhs array holding left hand side of multiplication
770 * @param lhsOffset offset of the left hand side in its array
771 * @param rhs array right hand side of multiplication
772 * @param rhsOffset offset of the right hand side in its array
773 * @param result array where result must be stored (for
774 * multiplication the result array <em>cannot</em> be one of
775 * the input arrays)
776 * @param resultOffset offset of the result in its array
777 */
778 public void multiply(final double[] lhs, final int lhsOffset,
779 final double[] rhs, final int rhsOffset,
780 final double[] result, final int resultOffset) {
781 for (int i = 0; i < multIndirection.length; ++i) {
782 final int[][] mappingI = multIndirection[i];
783 double r = 0;
784 for (int j = 0; j < mappingI.length; ++j) {
785 r += mappingI[j][0] *
786 lhs[lhsOffset + mappingI[j][1]] *
787 rhs[rhsOffset + mappingI[j][2]];
788 }
789 result[resultOffset + i] = r;
790 }
791 }
792
793 /** Perform division of two derivative structures.
794 * @param lhs array holding left hand side of division
795 * @param lhsOffset offset of the left hand side in its array
796 * @param rhs array right hand side of division
797 * @param rhsOffset offset of the right hand side in its array
798 * @param result array where result must be stored (for
799 * division the result array <em>cannot</em> be one of
800 * the input arrays)
801 * @param resultOffset offset of the result in its array
802 */
803 public void divide(final double[] lhs, final int lhsOffset,
804 final double[] rhs, final int rhsOffset,
805 final double[] result, final int resultOffset) {
806 final double[] reciprocal = new double[getSize()];
807 pow(rhs, lhsOffset, -1, reciprocal, 0);
808 multiply(lhs, lhsOffset, reciprocal, 0, result, resultOffset);
809 }
810
811 /** Perform remainder of two derivative structures.
812 * @param lhs array holding left hand side of remainder
813 * @param lhsOffset offset of the left hand side in its array
814 * @param rhs array right hand side of remainder
815 * @param rhsOffset offset of the right hand side in its array
816 * @param result array where result must be stored (it may be
817 * one of the input arrays)
818 * @param resultOffset offset of the result in its array
819 */
820 public void remainder(final double[] lhs, final int lhsOffset,
821 final double[] rhs, final int rhsOffset,
822 final double[] result, final int resultOffset) {
823
824 // compute k such that lhs % rhs = lhs - k rhs
825 final double rem = FastMath.IEEEremainder(lhs[lhsOffset], rhs[rhsOffset]);
826 final double k = FastMath.rint((lhs[lhsOffset] - rem) / rhs[rhsOffset]);
827
828 // set up value
829 result[resultOffset] = rem;
830
831 // set up partial derivatives
832 for (int i = 1; i < getSize(); ++i) {
833 result[resultOffset + i] = lhs[lhsOffset + i] - k * rhs[rhsOffset + i];
834 }
835
836 }
837
838 /** Compute power of a derivative structure.
839 * @param operand array holding the operand
840 * @param operandOffset offset of the operand in its array
841 * @param p power to apply
842 * @param result array where result must be stored (for
843 * power the result array <em>cannot</em> be the input
844 * array)
845 * @param resultOffset offset of the result in its array
846 */
847 public void pow(final double[] operand, final int operandOffset, final double p,
848 final double[] result, final int resultOffset) {
849
850 // create the function value and derivatives
851 // [x^p, px^(p-1), p(p-1)x^(p-2), ... ]
852 double[] function = new double[1 + order];
853 double xk = FastMath.pow(operand[operandOffset], p - order);
854 for (int i = order; i > 0; --i) {
855 function[i] = xk;
856 xk *= operand[operandOffset];
857 }
858 function[0] = xk;
859 double coefficient = p;
860 for (int i = 1; i <= order; ++i) {
861 function[i] *= coefficient;
862 coefficient *= p - i;
863 }
864
865 // apply function composition
866 compose(operand, operandOffset, function, result, resultOffset);
867
868 }
869
870 /** Compute integer power of a derivative structure.
871 * @param operand array holding the operand
872 * @param operandOffset offset of the operand in its array
873 * @param n power to apply
874 * @param result array where result must be stored (for
875 * power the result array <em>cannot</em> be the input
876 * array)
877 * @param resultOffset offset of the result in its array
878 */
879 public void pow(final double[] operand, final int operandOffset, final int n,
880 final double[] result, final int resultOffset) {
881
882 if (n == 0) {
883 // special case, x^0 = 1 for all x
884 result[resultOffset] = 1.0;
885 Arrays.fill(result, resultOffset + 1, resultOffset + getSize(), 0);
886 return;
887 }
888
889 // create the power function value and derivatives
890 // [x^n, nx^(n-1), n(n-1)x^(n-2), ... ]
891 double[] function = new double[1 + order];
892
893 if (n > 0) {
894 // strictly positive power
895 final int maxOrder = FastMath.min(order, n);
896 double xk = FastMath.pow(operand[operandOffset], n - maxOrder);
897 for (int i = maxOrder; i > 0; --i) {
898 function[i] = xk;
899 xk *= operand[operandOffset];
900 }
901 function[0] = xk;
902 } else {
903 // strictly negative power
904 final double inv = 1.0 / operand[operandOffset];
905 double xk = FastMath.pow(inv, -n);
906 for (int i = 0; i <= order; ++i) {
907 function[i] = xk;
908 xk *= inv;
909 }
910 }
911
912 double coefficient = n;
913 for (int i = 1; i <= order; ++i) {
914 function[i] *= coefficient;
915 coefficient *= n - i;
916 }
917
918 // apply function composition
919 compose(operand, operandOffset, function, result, resultOffset);
920
921 }
922
923 /** Compute power of a derivative structure.
924 * @param x array holding the base
925 * @param xOffset offset of the base in its array
926 * @param y array holding the exponent
927 * @param yOffset offset of the exponent in its array
928 * @param result array where result must be stored (for
929 * power the result array <em>cannot</em> be the input
930 * array)
931 * @param resultOffset offset of the result in its array
932 */
933 public void pow(final double[] x, final int xOffset,
934 final double[] y, final int yOffset,
935 final double[] result, final int resultOffset) {
936 final double[] logX = new double[getSize()];
937 log(x, xOffset, logX, 0);
938 final double[] yLogX = new double[getSize()];
939 multiply(logX, 0, y, yOffset, yLogX, 0);
940 exp(yLogX, 0, result, resultOffset);
941 }
942
943 /** Compute n<sup>th</sup> root of a derivative structure.
944 * @param operand array holding the operand
945 * @param operandOffset offset of the operand in its array
946 * @param n order of the root
947 * @param result array where result must be stored (for
948 * n<sup>th</sup> root the result array <em>cannot</em> be the input
949 * array)
950 * @param resultOffset offset of the result in its array
951 */
952 public void rootN(final double[] operand, final int operandOffset, final int n,
953 final double[] result, final int resultOffset) {
954
955 // create the function value and derivatives
956 // [x^(1/n), (1/n)x^((1/n)-1), (1-n)/n^2x^((1/n)-2), ... ]
957 double[] function = new double[1 + order];
958 double xk;
959 if (n == 2) {
960 function[0] = FastMath.sqrt(operand[operandOffset]);
961 xk = 0.5 / function[0];
962 } else if (n == 3) {
963 function[0] = FastMath.cbrt(operand[operandOffset]);
964 xk = 1.0 / (3.0 * function[0] * function[0]);
965 } else {
966 function[0] = FastMath.pow(operand[operandOffset], 1.0 / n);
967 xk = 1.0 / (n * FastMath.pow(function[0], n - 1));
968 }
969 final double nReciprocal = 1.0 / n;
970 final double xReciprocal = 1.0 / operand[operandOffset];
971 for (int i = 1; i <= order; ++i) {
972 function[i] = xk;
973 xk *= xReciprocal * (nReciprocal - i);
974 }
975
976 // apply function composition
977 compose(operand, operandOffset, function, result, resultOffset);
978
979 }
980
981 /** Compute exponential of a derivative structure.
982 * @param operand array holding the operand
983 * @param operandOffset offset of the operand in its array
984 * @param result array where result must be stored (for
985 * exponential the result array <em>cannot</em> be the input
986 * array)
987 * @param resultOffset offset of the result in its array
988 */
989 public void exp(final double[] operand, final int operandOffset,
990 final double[] result, final int resultOffset) {
991
992 // create the function value and derivatives
993 double[] function = new double[1 + order];
994 Arrays.fill(function, FastMath.exp(operand[operandOffset]));
995
996 // apply function composition
997 compose(operand, operandOffset, function, result, resultOffset);
998
999 }
1000
1001 /** Compute exp(x) - 1 of a derivative structure.
1002 * @param operand array holding the operand
1003 * @param operandOffset offset of the operand in its array
1004 * @param result array where result must be stored (for
1005 * exponential the result array <em>cannot</em> be the input
1006 * array)
1007 * @param resultOffset offset of the result in its array
1008 */
1009 public void expm1(final double[] operand, final int operandOffset,
1010 final double[] result, final int resultOffset) {
1011
1012 // create the function value and derivatives
1013 double[] function = new double[1 + order];
1014 function[0] = FastMath.expm1(operand[operandOffset]);
1015 Arrays.fill(function, 1, 1 + order, FastMath.exp(operand[operandOffset]));
1016
1017 // apply function composition
1018 compose(operand, operandOffset, function, result, resultOffset);
1019
1020 }
1021
1022 /** Compute natural logarithm of a derivative structure.
1023 * @param operand array holding the operand
1024 * @param operandOffset offset of the operand in its array
1025 * @param result array where result must be stored (for
1026 * logarithm the result array <em>cannot</em> be the input
1027 * array)
1028 * @param resultOffset offset of the result in its array
1029 */
1030 public void log(final double[] operand, final int operandOffset,
1031 final double[] result, final int resultOffset) {
1032
1033 // create the function value and derivatives
1034 double[] function = new double[1 + order];
1035 function[0] = FastMath.log(operand[operandOffset]);
1036 if (order > 0) {
1037 double inv = 1.0 / operand[operandOffset];
1038 double xk = inv;
1039 for (int i = 1; i <= order; ++i) {
1040 function[i] = xk;
1041 xk *= -i * inv;
1042 }
1043 }
1044
1045 // apply function composition
1046 compose(operand, operandOffset, function, result, resultOffset);
1047
1048 }
1049
1050 /** Computes shifted logarithm of a derivative structure.
1051 * @param operand array holding the operand
1052 * @param operandOffset offset of the operand in its array
1053 * @param result array where result must be stored (for
1054 * shifted logarithm the result array <em>cannot</em> be the input array)
1055 * @param resultOffset offset of the result in its array
1056 */
1057 public void log1p(final double[] operand, final int operandOffset,
1058 final double[] result, final int resultOffset) {
1059
1060 // create the function value and derivatives
1061 double[] function = new double[1 + order];
1062 function[0] = FastMath.log1p(operand[operandOffset]);
1063 if (order > 0) {
1064 double inv = 1.0 / (1.0 + operand[operandOffset]);
1065 double xk = inv;
1066 for (int i = 1; i <= order; ++i) {
1067 function[i] = xk;
1068 xk *= -i * inv;
1069 }
1070 }
1071
1072 // apply function composition
1073 compose(operand, operandOffset, function, result, resultOffset);
1074
1075 }
1076
1077 /** Computes base 10 logarithm of a derivative structure.
1078 * @param operand array holding the operand
1079 * @param operandOffset offset of the operand in its array
1080 * @param result array where result must be stored (for
1081 * base 10 logarithm the result array <em>cannot</em> be the input array)
1082 * @param resultOffset offset of the result in its array
1083 */
1084 public void log10(final double[] operand, final int operandOffset,
1085 final double[] result, final int resultOffset) {
1086
1087 // create the function value and derivatives
1088 double[] function = new double[1 + order];
1089 function[0] = FastMath.log10(operand[operandOffset]);
1090 if (order > 0) {
1091 double inv = 1.0 / operand[operandOffset];
1092 double xk = inv / FastMath.log(10.0);
1093 for (int i = 1; i <= order; ++i) {
1094 function[i] = xk;
1095 xk *= -i * inv;
1096 }
1097 }
1098
1099 // apply function composition
1100 compose(operand, operandOffset, function, result, resultOffset);
1101
1102 }
1103
1104 /** Compute cosine of a derivative structure.
1105 * @param operand array holding the operand
1106 * @param operandOffset offset of the operand in its array
1107 * @param result array where result must be stored (for
1108 * cosine the result array <em>cannot</em> be the input
1109 * array)
1110 * @param resultOffset offset of the result in its array
1111 */
1112 public void cos(final double[] operand, final int operandOffset,
1113 final double[] result, final int resultOffset) {
1114
1115 // create the function value and derivatives
1116 double[] function = new double[1 + order];
1117 function[0] = FastMath.cos(operand[operandOffset]);
1118 if (order > 0) {
1119 function[1] = -FastMath.sin(operand[operandOffset]);
1120 for (int i = 2; i <= order; ++i) {
1121 function[i] = -function[i - 2];
1122 }
1123 }
1124
1125 // apply function composition
1126 compose(operand, operandOffset, function, result, resultOffset);
1127
1128 }
1129
1130 /** Compute sine of a derivative structure.
1131 * @param operand array holding the operand
1132 * @param operandOffset offset of the operand in its array
1133 * @param result array where result must be stored (for
1134 * sine the result array <em>cannot</em> be the input
1135 * array)
1136 * @param resultOffset offset of the result in its array
1137 */
1138 public void sin(final double[] operand, final int operandOffset,
1139 final double[] result, final int resultOffset) {
1140
1141 // create the function value and derivatives
1142 double[] function = new double[1 + order];
1143 function[0] = FastMath.sin(operand[operandOffset]);
1144 if (order > 0) {
1145 function[1] = FastMath.cos(operand[operandOffset]);
1146 for (int i = 2; i <= order; ++i) {
1147 function[i] = -function[i - 2];
1148 }
1149 }
1150
1151 // apply function composition
1152 compose(operand, operandOffset, function, result, resultOffset);
1153
1154 }
1155
1156 /** Compute tangent of a derivative structure.
1157 * @param operand array holding the operand
1158 * @param operandOffset offset of the operand in its array
1159 * @param result array where result must be stored (for
1160 * tangent the result array <em>cannot</em> be the input
1161 * array)
1162 * @param resultOffset offset of the result in its array
1163 */
1164 public void tan(final double[] operand, final int operandOffset,
1165 final double[] result, final int resultOffset) {
1166
1167 // create the function value and derivatives
1168 final double[] function = new double[1 + order];
1169 final double t = FastMath.tan(operand[operandOffset]);
1170 function[0] = t;
1171
1172 if (order > 0) {
1173
1174 // the nth order derivative of tan has the form:
1175 // dn(tan(x)/dxn = P_n(tan(x))
1176 // where P_n(t) is a degree n+1 polynomial with same parity as n+1
1177 // P_0(t) = t, P_1(t) = 1 + t^2, P_2(t) = 2 t (1 + t^2) ...
1178 // the general recurrence relation for P_n is:
1179 // P_n(x) = (1+t^2) P_(n-1)'(t)
1180 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1181 final double[] p = new double[order + 2];
1182 p[1] = 1;
1183 final double t2 = t * t;
1184 for (int n = 1; n <= order; ++n) {
1185
1186 // update and evaluate polynomial P_n(t)
1187 double v = 0;
1188 p[n + 1] = n * p[n];
1189 for (int k = n + 1; k >= 0; k -= 2) {
1190 v = v * t2 + p[k];
1191 if (k > 2) {
1192 p[k - 2] = (k - 1) * p[k - 1] + (k - 3) * p[k - 3];
1193 } else if (k == 2) {
1194 p[0] = p[1];
1195 }
1196 }
1197 if ((n & 0x1) == 0) {
1198 v *= t;
1199 }
1200
1201 function[n] = v;
1202
1203 }
1204 }
1205
1206 // apply function composition
1207 compose(operand, operandOffset, function, result, resultOffset);
1208
1209 }
1210
1211 /** Compute arc cosine of a derivative structure.
1212 * @param operand array holding the operand
1213 * @param operandOffset offset of the operand in its array
1214 * @param result array where result must be stored (for
1215 * arc cosine the result array <em>cannot</em> be the input
1216 * array)
1217 * @param resultOffset offset of the result in its array
1218 */
1219 public void acos(final double[] operand, final int operandOffset,
1220 final double[] result, final int resultOffset) {
1221
1222 // create the function value and derivatives
1223 double[] function = new double[1 + order];
1224 final double x = operand[operandOffset];
1225 function[0] = FastMath.acos(x);
1226 if (order > 0) {
1227 // the nth order derivative of acos has the form:
1228 // dn(acos(x)/dxn = P_n(x) / [1 - x^2]^((2n-1)/2)
1229 // where P_n(x) is a degree n-1 polynomial with same parity as n-1
1230 // P_1(x) = -1, P_2(x) = -x, P_3(x) = -2x^2 - 1 ...
1231 // the general recurrence relation for P_n is:
1232 // P_n(x) = (1-x^2) P_(n-1)'(x) + (2n-3) x P_(n-1)(x)
1233 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1234 final double[] p = new double[order];
1235 p[0] = -1;
1236 final double x2 = x * x;
1237 final double f = 1.0 / (1 - x2);
1238 double coeff = FastMath.sqrt(f);
1239 function[1] = coeff * p[0];
1240 for (int n = 2; n <= order; ++n) {
1241
1242 // update and evaluate polynomial P_n(x)
1243 double v = 0;
1244 p[n - 1] = (n - 1) * p[n - 2];
1245 for (int k = n - 1; k >= 0; k -= 2) {
1246 v = v * x2 + p[k];
1247 if (k > 2) {
1248 p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
1249 } else if (k == 2) {
1250 p[0] = p[1];
1251 }
1252 }
1253 if ((n & 0x1) == 0) {
1254 v *= x;
1255 }
1256
1257 coeff *= f;
1258 function[n] = coeff * v;
1259
1260 }
1261 }
1262
1263 // apply function composition
1264 compose(operand, operandOffset, function, result, resultOffset);
1265
1266 }
1267
1268 /** Compute arc sine of a derivative structure.
1269 * @param operand array holding the operand
1270 * @param operandOffset offset of the operand in its array
1271 * @param result array where result must be stored (for
1272 * arc sine the result array <em>cannot</em> be the input
1273 * array)
1274 * @param resultOffset offset of the result in its array
1275 */
1276 public void asin(final double[] operand, final int operandOffset,
1277 final double[] result, final int resultOffset) {
1278
1279 // create the function value and derivatives
1280 double[] function = new double[1 + order];
1281 final double x = operand[operandOffset];
1282 function[0] = FastMath.asin(x);
1283 if (order > 0) {
1284 // the nth order derivative of asin has the form:
1285 // dn(asin(x)/dxn = P_n(x) / [1 - x^2]^((2n-1)/2)
1286 // where P_n(x) is a degree n-1 polynomial with same parity as n-1
1287 // P_1(x) = 1, P_2(x) = x, P_3(x) = 2x^2 + 1 ...
1288 // the general recurrence relation for P_n is:
1289 // P_n(x) = (1-x^2) P_(n-1)'(x) + (2n-3) x P_(n-1)(x)
1290 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1291 final double[] p = new double[order];
1292 p[0] = 1;
1293 final double x2 = x * x;
1294 final double f = 1.0 / (1 - x2);
1295 double coeff = FastMath.sqrt(f);
1296 function[1] = coeff * p[0];
1297 for (int n = 2; n <= order; ++n) {
1298
1299 // update and evaluate polynomial P_n(x)
1300 double v = 0;
1301 p[n - 1] = (n - 1) * p[n - 2];
1302 for (int k = n - 1; k >= 0; k -= 2) {
1303 v = v * x2 + p[k];
1304 if (k > 2) {
1305 p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
1306 } else if (k == 2) {
1307 p[0] = p[1];
1308 }
1309 }
1310 if ((n & 0x1) == 0) {
1311 v *= x;
1312 }
1313
1314 coeff *= f;
1315 function[n] = coeff * v;
1316
1317 }
1318 }
1319
1320 // apply function composition
1321 compose(operand, operandOffset, function, result, resultOffset);
1322
1323 }
1324
1325 /** Compute arc tangent of a derivative structure.
1326 * @param operand array holding the operand
1327 * @param operandOffset offset of the operand in its array
1328 * @param result array where result must be stored (for
1329 * arc tangent the result array <em>cannot</em> be the input
1330 * array)
1331 * @param resultOffset offset of the result in its array
1332 */
1333 public void atan(final double[] operand, final int operandOffset,
1334 final double[] result, final int resultOffset) {
1335
1336 // create the function value and derivatives
1337 double[] function = new double[1 + order];
1338 final double x = operand[operandOffset];
1339 function[0] = FastMath.atan(x);
1340 if (order > 0) {
1341 // the nth order derivative of atan has the form:
1342 // dn(atan(x)/dxn = Q_n(x) / (1 + x^2)^n
1343 // where Q_n(x) is a degree n-1 polynomial with same parity as n-1
1344 // Q_1(x) = 1, Q_2(x) = -2x, Q_3(x) = 6x^2 - 2 ...
1345 // the general recurrence relation for Q_n is:
1346 // Q_n(x) = (1+x^2) Q_(n-1)'(x) - 2(n-1) x Q_(n-1)(x)
1347 // as per polynomial parity, we can store coefficients of both Q_(n-1) and Q_n in the same array
1348 final double[] q = new double[order];
1349 q[0] = 1;
1350 final double x2 = x * x;
1351 final double f = 1.0 / (1 + x2);
1352 double coeff = f;
1353 function[1] = coeff * q[0];
1354 for (int n = 2; n <= order; ++n) {
1355
1356 // update and evaluate polynomial Q_n(x)
1357 double v = 0;
1358 q[n - 1] = -n * q[n - 2];
1359 for (int k = n - 1; k >= 0; k -= 2) {
1360 v = v * x2 + q[k];
1361 if (k > 2) {
1362 q[k - 2] = (k - 1) * q[k - 1] + (k - 1 - 2 * n) * q[k - 3];
1363 } else if (k == 2) {
1364 q[0] = q[1];
1365 }
1366 }
1367 if ((n & 0x1) == 0) {
1368 v *= x;
1369 }
1370
1371 coeff *= f;
1372 function[n] = coeff * v;
1373
1374 }
1375 }
1376
1377 // apply function composition
1378 compose(operand, operandOffset, function, result, resultOffset);
1379
1380 }
1381
1382 /** Compute two arguments arc tangent of a derivative structure.
1383 * @param y array holding the first operand
1384 * @param yOffset offset of the first operand in its array
1385 * @param x array holding the second operand
1386 * @param xOffset offset of the second operand in its array
1387 * @param result array where result must be stored (for
1388 * two arguments arc tangent the result array <em>cannot</em>
1389 * be the input array)
1390 * @param resultOffset offset of the result in its array
1391 */
1392 public void atan2(final double[] y, final int yOffset,
1393 final double[] x, final int xOffset,
1394 final double[] result, final int resultOffset) {
1395
1396 // compute r = sqrt(x^2+y^2)
1397 double[] tmp1 = new double[getSize()];
1398 multiply(x, xOffset, x, xOffset, tmp1, 0); // x^2
1399 double[] tmp2 = new double[getSize()];
1400 multiply(y, yOffset, y, yOffset, tmp2, 0); // y^2
1401 add(tmp1, 0, tmp2, 0, tmp2, 0); // x^2 + y^2
1402 rootN(tmp2, 0, 2, tmp1, 0); // r = sqrt(x^2 + y^2)
1403
1404 if (x[xOffset] >= 0) {
1405
1406 // compute atan2(y, x) = 2 atan(y / (r + x))
1407 add(tmp1, 0, x, xOffset, tmp2, 0); // r + x
1408 divide(y, yOffset, tmp2, 0, tmp1, 0); // y /(r + x)
1409 atan(tmp1, 0, tmp2, 0); // atan(y / (r + x))
1410 for (int i = 0; i < tmp2.length; ++i) {
1411 result[resultOffset + i] = 2 * tmp2[i]; // 2 * atan(y / (r + x))
1412 }
1413
1414 } else {
1415
1416 // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
1417 subtract(tmp1, 0, x, xOffset, tmp2, 0); // r - x
1418 divide(y, yOffset, tmp2, 0, tmp1, 0); // y /(r - x)
1419 atan(tmp1, 0, tmp2, 0); // atan(y / (r - x))
1420 result[resultOffset] =
1421 ((tmp2[0] <= 0) ? -FastMath.PI : FastMath.PI) - 2 * tmp2[0]; // +/-pi - 2 * atan(y / (r - x))
1422 for (int i = 1; i < tmp2.length; ++i) {
1423 result[resultOffset + i] = -2 * tmp2[i]; // +/-pi - 2 * atan(y / (r - x))
1424 }
1425
1426 }
1427
1428 // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
1429 result[resultOffset] = FastMath.atan2(y[yOffset], x[xOffset]);
1430
1431 }
1432
1433 /** Compute hyperbolic cosine of a derivative structure.
1434 * @param operand array holding the operand
1435 * @param operandOffset offset of the operand in its array
1436 * @param result array where result must be stored (for
1437 * hyperbolic cosine the result array <em>cannot</em> be the input
1438 * array)
1439 * @param resultOffset offset of the result in its array
1440 */
1441 public void cosh(final double[] operand, final int operandOffset,
1442 final double[] result, final int resultOffset) {
1443
1444 // create the function value and derivatives
1445 double[] function = new double[1 + order];
1446 function[0] = FastMath.cosh(operand[operandOffset]);
1447 if (order > 0) {
1448 function[1] = FastMath.sinh(operand[operandOffset]);
1449 for (int i = 2; i <= order; ++i) {
1450 function[i] = function[i - 2];
1451 }
1452 }
1453
1454 // apply function composition
1455 compose(operand, operandOffset, function, result, resultOffset);
1456
1457 }
1458
1459 /** Compute hyperbolic sine of a derivative structure.
1460 * @param operand array holding the operand
1461 * @param operandOffset offset of the operand in its array
1462 * @param result array where result must be stored (for
1463 * hyperbolic sine the result array <em>cannot</em> be the input
1464 * array)
1465 * @param resultOffset offset of the result in its array
1466 */
1467 public void sinh(final double[] operand, final int operandOffset,
1468 final double[] result, final int resultOffset) {
1469
1470 // create the function value and derivatives
1471 double[] function = new double[1 + order];
1472 function[0] = FastMath.sinh(operand[operandOffset]);
1473 if (order > 0) {
1474 function[1] = FastMath.cosh(operand[operandOffset]);
1475 for (int i = 2; i <= order; ++i) {
1476 function[i] = function[i - 2];
1477 }
1478 }
1479
1480 // apply function composition
1481 compose(operand, operandOffset, function, result, resultOffset);
1482
1483 }
1484
1485 /** Compute hyperbolic tangent of a derivative structure.
1486 * @param operand array holding the operand
1487 * @param operandOffset offset of the operand in its array
1488 * @param result array where result must be stored (for
1489 * hyperbolic tangent the result array <em>cannot</em> be the input
1490 * array)
1491 * @param resultOffset offset of the result in its array
1492 */
1493 public void tanh(final double[] operand, final int operandOffset,
1494 final double[] result, final int resultOffset) {
1495
1496 // create the function value and derivatives
1497 final double[] function = new double[1 + order];
1498 final double t = FastMath.tanh(operand[operandOffset]);
1499 function[0] = t;
1500
1501 if (order > 0) {
1502
1503 // the nth order derivative of tanh has the form:
1504 // dn(tanh(x)/dxn = P_n(tanh(x))
1505 // where P_n(t) is a degree n+1 polynomial with same parity as n+1
1506 // P_0(t) = t, P_1(t) = 1 - t^2, P_2(t) = -2 t (1 - t^2) ...
1507 // the general recurrence relation for P_n is:
1508 // P_n(x) = (1-t^2) P_(n-1)'(t)
1509 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1510 final double[] p = new double[order + 2];
1511 p[1] = 1;
1512 final double t2 = t * t;
1513 for (int n = 1; n <= order; ++n) {
1514
1515 // update and evaluate polynomial P_n(t)
1516 double v = 0;
1517 p[n + 1] = -n * p[n];
1518 for (int k = n + 1; k >= 0; k -= 2) {
1519 v = v * t2 + p[k];
1520 if (k > 2) {
1521 p[k - 2] = (k - 1) * p[k - 1] - (k - 3) * p[k - 3];
1522 } else if (k == 2) {
1523 p[0] = p[1];
1524 }
1525 }
1526 if ((n & 0x1) == 0) {
1527 v *= t;
1528 }
1529
1530 function[n] = v;
1531
1532 }
1533 }
1534
1535 // apply function composition
1536 compose(operand, operandOffset, function, result, resultOffset);
1537
1538 }
1539
1540 /** Compute inverse hyperbolic cosine of a derivative structure.
1541 * @param operand array holding the operand
1542 * @param operandOffset offset of the operand in its array
1543 * @param result array where result must be stored (for
1544 * inverse hyperbolic cosine the result array <em>cannot</em> be the input
1545 * array)
1546 * @param resultOffset offset of the result in its array
1547 */
1548 public void acosh(final double[] operand, final int operandOffset,
1549 final double[] result, final int resultOffset) {
1550
1551 // create the function value and derivatives
1552 double[] function = new double[1 + order];
1553 final double x = operand[operandOffset];
1554 function[0] = FastMath.acosh(x);
1555 if (order > 0) {
1556 // the nth order derivative of acosh has the form:
1557 // dn(acosh(x)/dxn = P_n(x) / [x^2 - 1]^((2n-1)/2)
1558 // where P_n(x) is a degree n-1 polynomial with same parity as n-1
1559 // P_1(x) = 1, P_2(x) = -x, P_3(x) = 2x^2 + 1 ...
1560 // the general recurrence relation for P_n is:
1561 // P_n(x) = (x^2-1) P_(n-1)'(x) - (2n-3) x P_(n-1)(x)
1562 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1563 final double[] p = new double[order];
1564 p[0] = 1;
1565 final double x2 = x * x;
1566 final double f = 1.0 / (x2 - 1);
1567 double coeff = FastMath.sqrt(f);
1568 function[1] = coeff * p[0];
1569 for (int n = 2; n <= order; ++n) {
1570
1571 // update and evaluate polynomial P_n(x)
1572 double v = 0;
1573 p[n - 1] = (1 - n) * p[n - 2];
1574 for (int k = n - 1; k >= 0; k -= 2) {
1575 v = v * x2 + p[k];
1576 if (k > 2) {
1577 p[k - 2] = (1 - k) * p[k - 1] + (k - 2 * n) * p[k - 3];
1578 } else if (k == 2) {
1579 p[0] = -p[1];
1580 }
1581 }
1582 if ((n & 0x1) == 0) {
1583 v *= x;
1584 }
1585
1586 coeff *= f;
1587 function[n] = coeff * v;
1588
1589 }
1590 }
1591
1592 // apply function composition
1593 compose(operand, operandOffset, function, result, resultOffset);
1594
1595 }
1596
1597 /** Compute inverse hyperbolic sine of a derivative structure.
1598 * @param operand array holding the operand
1599 * @param operandOffset offset of the operand in its array
1600 * @param result array where result must be stored (for
1601 * inverse hyperbolic sine the result array <em>cannot</em> be the input
1602 * array)
1603 * @param resultOffset offset of the result in its array
1604 */
1605 public void asinh(final double[] operand, final int operandOffset,
1606 final double[] result, final int resultOffset) {
1607
1608 // create the function value and derivatives
1609 double[] function = new double[1 + order];
1610 final double x = operand[operandOffset];
1611 function[0] = FastMath.asinh(x);
1612 if (order > 0) {
1613 // the nth order derivative of asinh has the form:
1614 // dn(asinh(x)/dxn = P_n(x) / [x^2 + 1]^((2n-1)/2)
1615 // where P_n(x) is a degree n-1 polynomial with same parity as n-1
1616 // P_1(x) = 1, P_2(x) = -x, P_3(x) = 2x^2 - 1 ...
1617 // the general recurrence relation for P_n is:
1618 // P_n(x) = (x^2+1) P_(n-1)'(x) - (2n-3) x P_(n-1)(x)
1619 // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
1620 final double[] p = new double[order];
1621 p[0] = 1;
1622 final double x2 = x * x;
1623 final double f = 1.0 / (1 + x2);
1624 double coeff = FastMath.sqrt(f);
1625 function[1] = coeff * p[0];
1626 for (int n = 2; n <= order; ++n) {
1627
1628 // update and evaluate polynomial P_n(x)
1629 double v = 0;
1630 p[n - 1] = (1 - n) * p[n - 2];
1631 for (int k = n - 1; k >= 0; k -= 2) {
1632 v = v * x2 + p[k];
1633 if (k > 2) {
1634 p[k - 2] = (k - 1) * p[k - 1] + (k - 2 * n) * p[k - 3];
1635 } else if (k == 2) {
1636 p[0] = p[1];
1637 }
1638 }
1639 if ((n & 0x1) == 0) {
1640 v *= x;
1641 }
1642
1643 coeff *= f;
1644 function[n] = coeff * v;
1645
1646 }
1647 }
1648
1649 // apply function composition
1650 compose(operand, operandOffset, function, result, resultOffset);
1651
1652 }
1653
1654 /** Compute inverse hyperbolic tangent of a derivative structure.
1655 * @param operand array holding the operand
1656 * @param operandOffset offset of the operand in its array
1657 * @param result array where result must be stored (for
1658 * inverse hyperbolic tangent the result array <em>cannot</em> be the input
1659 * array)
1660 * @param resultOffset offset of the result in its array
1661 */
1662 public void atanh(final double[] operand, final int operandOffset,
1663 final double[] result, final int resultOffset) {
1664
1665 // create the function value and derivatives
1666 double[] function = new double[1 + order];
1667 final double x = operand[operandOffset];
1668 function[0] = FastMath.atanh(x);
1669 if (order > 0) {
1670 // the nth order derivative of atanh has the form:
1671 // dn(atanh(x)/dxn = Q_n(x) / (1 - x^2)^n
1672 // where Q_n(x) is a degree n-1 polynomial with same parity as n-1
1673 // Q_1(x) = 1, Q_2(x) = 2x, Q_3(x) = 6x^2 + 2 ...
1674 // the general recurrence relation for Q_n is:
1675 // Q_n(x) = (1-x^2) Q_(n-1)'(x) + 2(n-1) x Q_(n-1)(x)
1676 // as per polynomial parity, we can store coefficients of both Q_(n-1) and Q_n in the same array
1677 final double[] q = new double[order];
1678 q[0] = 1;
1679 final double x2 = x * x;
1680 final double f = 1.0 / (1 - x2);
1681 double coeff = f;
1682 function[1] = coeff * q[0];
1683 for (int n = 2; n <= order; ++n) {
1684
1685 // update and evaluate polynomial Q_n(x)
1686 double v = 0;
1687 q[n - 1] = n * q[n - 2];
1688 for (int k = n - 1; k >= 0; k -= 2) {
1689 v = v * x2 + q[k];
1690 if (k > 2) {
1691 q[k - 2] = (k - 1) * q[k - 1] + (2 * n - k + 1) * q[k - 3];
1692 } else if (k == 2) {
1693 q[0] = q[1];
1694 }
1695 }
1696 if ((n & 0x1) == 0) {
1697 v *= x;
1698 }
1699
1700 coeff *= f;
1701 function[n] = coeff * v;
1702
1703 }
1704 }
1705
1706 // apply function composition
1707 compose(operand, operandOffset, function, result, resultOffset);
1708
1709 }
1710
1711 /** Compute composition of a derivative structure by a function.
1712 * @param operand array holding the operand
1713 * @param operandOffset offset of the operand in its array
1714 * @param f array of value and derivatives of the function at
1715 * the current point (i.e. at {@code operand[operandOffset]}).
1716 * @param result array where result must be stored (for
1717 * composition the result array <em>cannot</em> be the input
1718 * array)
1719 * @param resultOffset offset of the result in its array
1720 */
1721 public void compose(final double[] operand, final int operandOffset, final double[] f,
1722 final double[] result, final int resultOffset) {
1723 for (int i = 0; i < compIndirection.length; ++i) {
1724 final int[][] mappingI = compIndirection[i];
1725 double r = 0;
1726 for (int j = 0; j < mappingI.length; ++j) {
1727 final int[] mappingIJ = mappingI[j];
1728 double product = mappingIJ[0] * f[mappingIJ[1]];
1729 for (int k = 2; k < mappingIJ.length; ++k) {
1730 product *= operand[operandOffset + mappingIJ[k]];
1731 }
1732 r += product;
1733 }
1734 result[resultOffset + i] = r;
1735 }
1736 }
1737
1738 /** Evaluate Taylor expansion of a derivative structure.
1739 * @param ds array holding the derivative structure
1740 * @param dsOffset offset of the derivative structure in its array
1741 * @param delta parameters offsets (Δx, Δy, ...)
1742 * @return value of the Taylor expansion at x + Δx, y + Δy, ...
1743 * @throws MathArithmeticException if factorials becomes too large
1744 */
1745 public double taylor(final double[] ds, final int dsOffset, final double ... delta)
1746 throws MathArithmeticException {
1747 double value = 0;
1748 for (int i = getSize() - 1; i >= 0; --i) {
1749 final int[] orders = getPartialDerivativeOrders(i);
1750 double term = ds[dsOffset + i];
1751 for (int k = 0; k < orders.length; ++k) {
1752 if (orders[k] > 0) {
1753 try {
1754 term *= FastMath.pow(delta[k], orders[k]) /
1755 ArithmeticUtils.factorial(orders[k]);
1756 } catch (NotPositiveException e) {
1757 // this cannot happen
1758 throw new MathInternalError(e);
1759 }
1760 }
1761 }
1762 value += term;
1763 }
1764 return value;
1765 }
1766
1767 /** Check rules set compatibility.
1768 * @param compiler other compiler to check against instance
1769 * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
1770 */
1771 public void checkCompatibility(final DSCompiler compiler)
1772 throws DimensionMismatchException {
1773 if (parameters != compiler.parameters) {
1774 throw new DimensionMismatchException(parameters, compiler.parameters);
1775 }
1776 if (order != compiler.order) {
1777 throw new DimensionMismatchException(order, compiler.order);
1778 }
1779 }
1780
1781 }