001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.analysis.differentiation;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import java.util.concurrent.atomic.AtomicReference;
023
024import org.apache.commons.math3.exception.DimensionMismatchException;
025import org.apache.commons.math3.exception.MathArithmeticException;
026import org.apache.commons.math3.exception.MathInternalError;
027import org.apache.commons.math3.exception.NotPositiveException;
028import org.apache.commons.math3.exception.NumberIsTooLargeException;
029import org.apache.commons.math3.util.CombinatoricsUtils;
030import org.apache.commons.math3.util.FastMath;
031import org.apache.commons.math3.util.MathArrays;
032
033/** Class holding "compiled" computation rules for derivative structures.
034 * <p>This class implements the computation rules described in Dan Kalman's paper <a
035 * href="http://www1.american.edu/cas/mathstat/People/kalman/pdffiles/mmgautodiff.pdf">Doubly
036 * Recursive Multivariate Automatic Differentiation</a>, Mathematics Magazine, vol. 75,
037 * no. 3, June 2002. However, in order to avoid performances bottlenecks, the recursive
038 * rules are "compiled" once in an unfold form. This class does this recursion unrolling
039 * and stores the computation rules as simple loops with pre-computed indirection arrays.</p>
040 * <p>
041 * This class maps all derivative computation into single dimension arrays that hold the
042 * value and partial derivatives. The class does not hold these arrays, which remains under
043 * the responsibility of the caller. For each combination of number of free parameters and
044 * derivation order, only one compiler is necessary, and this compiler will be used to
045 * perform computations on all arrays provided to it, which can represent hundreds or
046 * thousands of different parameters kept together with all theur partial derivatives.
047 * </p>
048 * <p>
049 * The arrays on which compilers operate contain only the partial derivatives together
050 * with the 0<sup>th</sup> derivative, i.e. the value. The partial derivatives are stored in
051 * a compiler-specific order, which can be retrieved using methods {@link
052 * #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} and {@link
053 * #getPartialDerivativeOrders(int)}. The value is guaranteed to be stored as the first element
054 * (i.e. the {@link #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} method returns
055 * 0 when called with 0 for all derivation orders and {@link #getPartialDerivativeOrders(int)
056 * getPartialDerivativeOrders} returns an array filled with 0 when called with 0 as the index).
057 * </p>
058 * <p>
059 * Note that the ordering changes with number of parameters and derivation order. For example
060 * given 2 parameters x and y, df/dy is stored at index 2 when derivation order is set to 1 (in
061 * this case the array has three elements: f, df/dx and df/dy). If derivation order is set to
062 * 2, then df/dy will be stored at index 3 (in this case the array has six elements: f, df/dx,
063 * df/dxdx, df/dy, df/dxdy and df/dydy).
064 * </p>
065 * <p>
066 * Given this structure, users can perform some simple operations like adding, subtracting
067 * or multiplying constants and negating the elements by themselves, knowing if they want to
068 * mutate their array or create a new array. These simple operations are not provided by
069 * the compiler. The compiler provides only the more complex operations between several arrays.
070 * </p>
071 * <p>This class is mainly used as the engine for scalar variable {@link DerivativeStructure}.
072 * It can also be used directly to hold several variables in arrays for more complex data
073 * structures. User can for example store a vector of n variables depending on three x, y
074 * and z free parameters in one array as follows:
075 * <pre>
076 *   // parameter 0 is x, parameter 1 is y, parameter 2 is z
077 *   int parameters = 3;
078 *   DSCompiler compiler = DSCompiler.getCompiler(parameters, order);
079 *   int size = compiler.getSize();
080 *
081 *   // pack all elements in a single array
082 *   double[] array = new double[n * size];
083 *   for (int i = 0; i < n; ++i) {
084 *
085 *     // we know value is guaranteed to be the first element
086 *     array[i * size] = v[i];
087 *
088 *     // we don't know where first derivatives are stored, so we ask the compiler
089 *     array[i * size + compiler.getPartialDerivativeIndex(1, 0, 0) = dvOnDx[i][0];
090 *     array[i * size + compiler.getPartialDerivativeIndex(0, 1, 0) = dvOnDy[i][0];
091 *     array[i * size + compiler.getPartialDerivativeIndex(0, 0, 1) = dvOnDz[i][0];
092 *
093 *     // we let all higher order derivatives set to 0
094 *
095 *   }
096 * </pre>
097 * Then in another function, user can perform some operations on all elements stored
098 * in the single array, such as a simple product of all variables:
099 * <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 */
127public 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}