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