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