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.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 (&Delta;x, &Delta;y, ...)
1742      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;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 }