blob: 66c52b7975cf8839d1c842cd66abc451b5dff39c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.analysis.differentiation;
import java.util.Arrays;
import java.io.Serializable;
import org.apache.commons.numbers.core.Sum;
import org.apache.commons.math4.legacy.core.Field;
import org.apache.commons.math4.legacy.core.FieldElement;
import org.apache.commons.math4.legacy.core.RealFieldElement;
import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
import org.apache.commons.math4.legacy.exception.MathArithmeticException;
import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
import org.apache.commons.math4.core.jdkmath.JdkMath;
import org.apache.commons.math4.legacy.core.MathArrays;
/** Class representing both the value and the differentials of a function.
* <p>This class is the workhorse of the differentiation package.</p>
* <p>This class is an implementation of the extension to Rall's
* numbers described in Dan Kalman's paper <a
* href="http://www1.american.edu/cas/mathstat/People/kalman/pdffiles/mmgautodiff.pdf">Doubly
* Recursive Multivariate Automatic Differentiation</a>, Mathematics Magazine, vol. 75,
* no. 3, June 2002. Rall's numbers are an extension to the real numbers used
* throughout mathematical expressions; they hold the derivative together with the
* value of a function. Dan Kalman's derivative structures hold all partial derivatives
* up to any specified order, with respect to any number of free parameters. Rall's
* numbers therefore can be seen as derivative structures for order one derivative and
* one free parameter, and real numbers can be seen as derivative structures with zero
* order derivative and no free parameters.</p>
* <p>{@link DerivativeStructure} instances can be used directly thanks to
* the arithmetic operators to the mathematical functions provided as
* methods by this class (+, -, *, /, %, sin, cos ...).</p>
* <p>Implementing complex expressions by hand using these classes is
* a tedious and error-prone task but has the advantage of having no limitation
* on the derivation order despite not requiring users to compute the derivatives by
* themselves. Implementing complex expression can also be done by developing computation
* code using standard primitive double values and to use {@link
* UnivariateFunctionDifferentiator differentiators} to create the {@link
* DerivativeStructure}-based instances. This method is simpler but may be limited in
* the accuracy and derivation orders and may be computationally intensive (this is
* typically the case for {@link FiniteDifferencesDifferentiator finite differences
* differentiator}.</p>
* <p>Instances of this class are guaranteed to be immutable.</p>
* @see DSCompiler
* @since 3.1
*/
public class DerivativeStructure implements RealFieldElement<DerivativeStructure>, Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20120730L;
/** Compiler for the current dimensions. */
private transient DSCompiler compiler;
/** Combined array holding all values. */
private final double[] data;
/** Build an instance with all values and derivatives set to 0.
* @param compiler compiler to use for computation
*/
private DerivativeStructure(final DSCompiler compiler) {
this.compiler = compiler;
this.data = new double[compiler.getSize()];
}
/** Build an instance with all values and derivatives set to 0.
* @param parameters number of free parameters
* @param order derivation order
* @throws NumberIsTooLargeException if order is too large
*/
public DerivativeStructure(final int parameters, final int order)
throws NumberIsTooLargeException {
this(DSCompiler.getCompiler(parameters, order));
}
/** Build an instance representing a constant value.
* @param parameters number of free parameters
* @param order derivation order
* @param value value of the constant
* @throws NumberIsTooLargeException if order is too large
* @see #DerivativeStructure(int, int, int, double)
*/
public DerivativeStructure(final int parameters, final int order, final double value)
throws NumberIsTooLargeException {
this(parameters, order);
this.data[0] = value;
}
/** Build an instance representing a variable.
* <p>Instances built using this constructor are considered
* to be the free variables with respect to which differentials
* are computed. As such, their differential with respect to
* themselves is +1.</p>
* @param parameters number of free parameters
* @param order derivation order
* @param index index of the variable (from 0 to {@code parameters - 1})
* @param value value of the variable
* @exception NumberIsTooLargeException if {@code index &ge; parameters}.
* @see #DerivativeStructure(int, int, double)
*/
public DerivativeStructure(final int parameters, final int order,
final int index, final double value)
throws NumberIsTooLargeException {
this(parameters, order, value);
if (index >= parameters) {
throw new NumberIsTooLargeException(index, parameters, false);
}
if (order > 0) {
// the derivative of the variable with respect to itself is 1.
data[DSCompiler.getCompiler(index, order).getSize()] = 1.0;
}
}
/** Linear combination constructor.
* The derivative structure built will be a1 * ds1 + a2 * ds2
* @param a1 first scale factor
* @param ds1 first base (unscaled) derivative structure
* @param a2 second scale factor
* @param ds2 second base (unscaled) derivative structure
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
*/
public DerivativeStructure(final double a1, final DerivativeStructure ds1,
final double a2, final DerivativeStructure ds2)
throws DimensionMismatchException {
this(ds1.compiler);
compiler.checkCompatibility(ds2.compiler);
compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0, data, 0);
}
/** Linear combination constructor.
* The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3
* @param a1 first scale factor
* @param ds1 first base (unscaled) derivative structure
* @param a2 second scale factor
* @param ds2 second base (unscaled) derivative structure
* @param a3 third scale factor
* @param ds3 third base (unscaled) derivative structure
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
*/
public DerivativeStructure(final double a1, final DerivativeStructure ds1,
final double a2, final DerivativeStructure ds2,
final double a3, final DerivativeStructure ds3)
throws DimensionMismatchException {
this(ds1.compiler);
compiler.checkCompatibility(ds2.compiler);
compiler.checkCompatibility(ds3.compiler);
compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0, a3, ds3.data, 0, data, 0);
}
/** Linear combination constructor.
* The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
* @param a1 first scale factor
* @param ds1 first base (unscaled) derivative structure
* @param a2 second scale factor
* @param ds2 second base (unscaled) derivative structure
* @param a3 third scale factor
* @param ds3 third base (unscaled) derivative structure
* @param a4 fourth scale factor
* @param ds4 fourth base (unscaled) derivative structure
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
*/
public DerivativeStructure(final double a1, final DerivativeStructure ds1,
final double a2, final DerivativeStructure ds2,
final double a3, final DerivativeStructure ds3,
final double a4, final DerivativeStructure ds4)
throws DimensionMismatchException {
this(ds1.compiler);
compiler.checkCompatibility(ds2.compiler);
compiler.checkCompatibility(ds3.compiler);
compiler.checkCompatibility(ds4.compiler);
compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0,
a3, ds3.data, 0, a4, ds4.data, 0,
data, 0);
}
/** Build an instance from all its derivatives.
* @param parameters number of free parameters
* @param order derivation order
* @param derivatives derivatives sorted according to
* {@link DSCompiler#getPartialDerivativeIndex(int...)}
* @exception DimensionMismatchException if derivatives array does not match the
* {@link DSCompiler#getSize() size} expected by the compiler
* @throws NumberIsTooLargeException if order is too large
* @see #getAllDerivatives()
*/
public DerivativeStructure(final int parameters, final int order, final double ... derivatives)
throws DimensionMismatchException, NumberIsTooLargeException {
this(parameters, order);
if (derivatives.length != data.length) {
throw new DimensionMismatchException(derivatives.length, data.length);
}
System.arraycopy(derivatives, 0, data, 0, data.length);
}
/** Copy constructor.
* @param ds instance to copy
*/
private DerivativeStructure(final DerivativeStructure ds) {
this.compiler = ds.compiler;
this.data = ds.data.clone();
}
/** Get the number of free parameters.
* @return number of free parameters
*/
public int getFreeParameters() {
return compiler.getFreeParameters();
}
/** Get the derivation order.
* @return derivation order
*/
public int getOrder() {
return compiler.getOrder();
}
/** Create a constant compatible with instance order and number of parameters.
* <p>
* This method is a convenience factory method, it simply calls
* {@code new DerivativeStructure(getFreeParameters(), getOrder(), c)}
* </p>
* @param c value of the constant
* @return a constant compatible with instance order and number of parameters
* @see #DerivativeStructure(int, int, double)
* @since 3.3
*/
public DerivativeStructure createConstant(final double c) {
return new DerivativeStructure(getFreeParameters(), getOrder(), c);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public double getReal() {
return data[0];
}
/** Get the value part of the derivative structure.
* @return value part of the derivative structure
* @see #getPartialDerivative(int...)
*/
public double getValue() {
return data[0];
}
/** Get a partial derivative.
* @param orders derivation orders with respect to each variable (if all orders are 0,
* the value is returned)
* @return partial derivative
* @see #getValue()
* @exception DimensionMismatchException if the numbers of variables does not
* match the instance
* @exception NumberIsTooLargeException if sum of derivation orders is larger
* than the instance limits
*/
public double getPartialDerivative(final int ... orders)
throws DimensionMismatchException, NumberIsTooLargeException {
return data[compiler.getPartialDerivativeIndex(orders)];
}
/** Get all partial derivatives.
* @return a fresh copy of partial derivatives, in an array sorted according to
* {@link DSCompiler#getPartialDerivativeIndex(int...)}
*/
public double[] getAllDerivatives() {
return data.clone();
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure add(final double a) {
final DerivativeStructure ds = new DerivativeStructure(this);
ds.data[0] += a;
return ds;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
*/
@Override
public DerivativeStructure add(final DerivativeStructure a)
throws DimensionMismatchException {
compiler.checkCompatibility(a.compiler);
final DerivativeStructure ds = new DerivativeStructure(this);
compiler.add(data, 0, a.data, 0, ds.data, 0);
return ds;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure subtract(final double a) {
return add(-a);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
*/
@Override
public DerivativeStructure subtract(final DerivativeStructure a)
throws DimensionMismatchException {
compiler.checkCompatibility(a.compiler);
final DerivativeStructure ds = new DerivativeStructure(this);
compiler.subtract(data, 0, a.data, 0, ds.data, 0);
return ds;
}
/** {@inheritDoc} */
@Override
public DerivativeStructure multiply(final int n) {
return multiply((double) n);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure multiply(final double a) {
final DerivativeStructure ds = new DerivativeStructure(this);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] *= a;
}
return ds;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
*/
@Override
public DerivativeStructure multiply(final DerivativeStructure a)
throws DimensionMismatchException {
compiler.checkCompatibility(a.compiler);
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.multiply(data, 0, a.data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure divide(final double a) {
final DerivativeStructure ds = new DerivativeStructure(this);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] /= a;
}
return ds;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
*/
@Override
public DerivativeStructure divide(final DerivativeStructure a)
throws DimensionMismatchException {
compiler.checkCompatibility(a.compiler);
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.divide(data, 0, a.data, 0, result.data, 0);
return result;
}
/** {@inheritDoc} */
@Override
public DerivativeStructure remainder(final double a) {
final DerivativeStructure ds = new DerivativeStructure(this);
ds.data[0] = JdkMath.IEEEremainder(ds.data[0], a);
return ds;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure remainder(final DerivativeStructure a)
throws DimensionMismatchException {
compiler.checkCompatibility(a.compiler);
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.remainder(data, 0, a.data, 0, result.data, 0);
return result;
}
/** {@inheritDoc} */
@Override
public DerivativeStructure negate() {
final DerivativeStructure ds = new DerivativeStructure(compiler);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] = -data[i];
}
return ds;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure abs() {
if (Double.doubleToLongBits(data[0]) < 0) {
// we use the bits representation to also handle -0.0
return negate();
} else {
return this;
}
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure ceil() {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getOrder(),
JdkMath.ceil(data[0]));
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure floor() {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getOrder(),
JdkMath.floor(data[0]));
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure rint() {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getOrder(),
JdkMath.rint(data[0]));
}
/** {@inheritDoc} */
@Override
public long round() {
return JdkMath.round(data[0]);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure signum() {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getOrder(),
JdkMath.signum(data[0]));
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure copySign(final DerivativeStructure sign){
long m = Double.doubleToLongBits(data[0]);
long s = Double.doubleToLongBits(sign.data[0]);
if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
return this;
}
return negate(); // flip sign
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure copySign(final double sign) {
long m = Double.doubleToLongBits(data[0]);
long s = Double.doubleToLongBits(sign);
if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
return this;
}
return negate(); // flip sign
}
/**
* Return the exponent of the instance value, removing the bias.
* <p>
* For double numbers of the form 2<sup>x</sup>, the unbiased
* exponent is exactly x.
* </p>
* @return exponent for instance in IEEE754 representation, without bias
*/
public int getExponent() {
return JdkMath.getExponent(data[0]);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure scalb(final int n) {
final DerivativeStructure ds = new DerivativeStructure(compiler);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] = JdkMath.scalb(data[i], n);
}
return ds;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure hypot(final DerivativeStructure y)
throws DimensionMismatchException {
compiler.checkCompatibility(y.compiler);
if (Double.isInfinite(data[0]) || Double.isInfinite(y.data[0])) {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getFreeParameters(),
Double.POSITIVE_INFINITY);
} else if (Double.isNaN(data[0]) || Double.isNaN(y.data[0])) {
return new DerivativeStructure(compiler.getFreeParameters(),
compiler.getFreeParameters(),
Double.NaN);
} else {
final int expX = getExponent();
final int expY = y.getExponent();
if (expX > expY + 27) {
// y is neglectible with respect to x
return abs();
} else if (expY > expX + 27) {
// x is neglectible with respect to y
return y.abs();
} else {
// find an intermediate scale to avoid both overflow and underflow
final int middleExp = (expX + expY) / 2;
// scale parameters without losing precision
final DerivativeStructure scaledX = scalb(-middleExp);
final DerivativeStructure scaledY = y.scalb(-middleExp);
// compute scaled hypotenuse
final DerivativeStructure scaledH =
scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
// remove scaling
return scaledH.scalb(middleExp);
}
}
}
/**
* Returns the hypotenuse of a triangle with sides {@code x} and {@code y}
* - sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
* avoiding intermediate overflow or underflow.
*
* <ul>
* <li> If either argument is infinite, then the result is positive infinity.</li>
* <li> else, if either argument is NaN then the result is NaN.</li>
* </ul>
*
* @param x a value
* @param y a value
* @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
public static DerivativeStructure hypot(final DerivativeStructure x, final DerivativeStructure y)
throws DimensionMismatchException {
return x.hypot(y);
}
/** Compute composition of the instance by a univariate function.
* @param f array of value and derivatives of the function at
* the current point (i.e. [f({@link #getValue()}),
* f'({@link #getValue()}), f''({@link #getValue()})...]).
* @return f(this)
* @exception DimensionMismatchException if the number of derivatives
* in the array is not equal to {@link #getOrder() order} + 1
*/
public DerivativeStructure compose(final double ... f)
throws DimensionMismatchException {
if (f.length != getOrder() + 1) {
throw new DimensionMismatchException(f.length, getOrder() + 1);
}
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.compose(data, 0, f, result.data, 0);
return result;
}
/** {@inheritDoc} */
@Override
public DerivativeStructure reciprocal() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.pow(data, 0, -1, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure sqrt() {
return rootN(2);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure cbrt() {
return rootN(3);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure rootN(final int n) {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.rootN(data, 0, n, result.data, 0);
return result;
}
/** {@inheritDoc} */
@Override
public Field<DerivativeStructure> getField() {
return new Field<DerivativeStructure>() {
/** {@inheritDoc} */
@Override
public DerivativeStructure getZero() {
return new DerivativeStructure(compiler.getFreeParameters(), compiler.getOrder(), 0.0);
}
/** {@inheritDoc} */
@Override
public DerivativeStructure getOne() {
return new DerivativeStructure(compiler.getFreeParameters(), compiler.getOrder(), 1.0);
}
/** {@inheritDoc} */
@Override
public Class<? extends FieldElement<DerivativeStructure>> getRuntimeClass() {
return DerivativeStructure.class;
}
};
}
/** Compute a<sup>x</sup> where a is a double and x a {@link DerivativeStructure}.
* @param a number to exponentiate
* @param x power to apply
* @return a<sup>x</sup>
* @since 3.3
*/
public static DerivativeStructure pow(final double a, final DerivativeStructure x) {
final DerivativeStructure result = new DerivativeStructure(x.compiler);
x.compiler.pow(a, x.data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure pow(final double p) {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.pow(data, 0, p, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure pow(final int n) {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.pow(data, 0, n, result.data, 0);
return result;
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure pow(final DerivativeStructure e)
throws DimensionMismatchException {
compiler.checkCompatibility(e.compiler);
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.pow(data, 0, e.data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure exp() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.exp(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure expm1() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.expm1(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure log() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.log(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure log1p() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.log1p(data, 0, result.data, 0);
return result;
}
/** Base 10 logarithm.
* @return base 10 logarithm of the instance
*/
@Override
public DerivativeStructure log10() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.log10(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure cos() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.cos(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure sin() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.sin(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure tan() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.tan(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure acos() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.acos(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure asin() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.asin(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure atan() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.atan(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure atan2(final DerivativeStructure x)
throws DimensionMismatchException {
compiler.checkCompatibility(x.compiler);
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.atan2(data, 0, x.data, 0, result.data, 0);
return result;
}
/** Two arguments arc tangent operation.
* @param y first argument of the arc tangent
* @param x second argument of the arc tangent
* @return atan2(y, x)
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
public static DerivativeStructure atan2(final DerivativeStructure y, final DerivativeStructure x)
throws DimensionMismatchException {
return y.atan2(x);
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure cosh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.cosh(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure sinh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.sinh(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure tanh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.tanh(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure acosh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.acosh(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure asinh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.asinh(data, 0, result.data, 0);
return result;
}
/** {@inheritDoc}
* @since 3.2
*/
@Override
public DerivativeStructure atanh() {
final DerivativeStructure result = new DerivativeStructure(compiler);
compiler.atanh(data, 0, result.data, 0);
return result;
}
/** Convert radians to degrees, with error of less than 0.5 ULP.
* @return instance converted into degrees
*/
public DerivativeStructure toDegrees() {
final DerivativeStructure ds = new DerivativeStructure(compiler);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] = JdkMath.toDegrees(data[i]);
}
return ds;
}
/** Convert degrees to radians, with error of less than 0.5 ULP.
* @return instance converted into radians
*/
public DerivativeStructure toRadians() {
final DerivativeStructure ds = new DerivativeStructure(compiler);
for (int i = 0; i < ds.data.length; ++i) {
ds.data[i] = JdkMath.toRadians(data[i]);
}
return ds;
}
/** Evaluate Taylor expansion a derivative structure.
* @param delta parameters offsets (&Delta;x, &Delta;y, ...)
* @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
* @throws MathArithmeticException if factorials becomes too large
*/
public double taylor(final double ... delta) throws MathArithmeticException {
return compiler.taylor(data, 0, delta);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final DerivativeStructure[] a, final DerivativeStructure[] b)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double[] aDouble = new double[a.length];
for (int i = 0; i < a.length; ++i) {
aDouble[i] = a[i].getValue();
}
final double[] bDouble = new double[b.length];
for (int i = 0; i < b.length; ++i) {
bDouble[i] = b[i].getValue();
}
final double accurateValue = Sum.ofProducts(aDouble, bDouble).getAsDouble();
// compute a simple value, with all partial derivatives
DerivativeStructure simpleValue = a[0].getField().getZero();
for (int i = 0; i < a.length; ++i) {
simpleValue = simpleValue.add(a[i].multiply(b[i]));
}
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(simpleValue.getFreeParameters(), simpleValue.getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final double[] a, final DerivativeStructure[] b)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double[] bDouble = new double[b.length];
for (int i = 0; i < b.length; ++i) {
bDouble[i] = b[i].getValue();
}
final double accurateValue = Sum.ofProducts(a, bDouble).getAsDouble();
// compute a simple value, with all partial derivatives
DerivativeStructure simpleValue = b[0].getField().getZero();
for (int i = 0; i < a.length; ++i) {
simpleValue = simpleValue.add(b[i].multiply(a[i]));
}
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(simpleValue.getFreeParameters(), simpleValue.getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
final DerivativeStructure a2, final DerivativeStructure b2)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1.getValue(), b1.getValue())
.addProduct(a2.getValue(), b2.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
final double a2, final DerivativeStructure b2)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1, b1.getValue())
.addProduct(a2, b2.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
final DerivativeStructure a2, final DerivativeStructure b2,
final DerivativeStructure a3, final DerivativeStructure b3)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1.getValue(), b1.getValue())
.addProduct(a2.getValue(), b2.getValue())
.addProduct(a3.getValue(), b3.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
final double a2, final DerivativeStructure b2,
final double a3, final DerivativeStructure b3)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1, b1.getValue())
.addProduct(a2, b2.getValue())
.addProduct(a3, b3.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
final DerivativeStructure a2, final DerivativeStructure b2,
final DerivativeStructure a3, final DerivativeStructure b3,
final DerivativeStructure a4, final DerivativeStructure b4)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1.getValue(), b1.getValue())
.addProduct(a2.getValue(), b2.getValue())
.addProduct(a3.getValue(), b3.getValue())
.addProduct(a4.getValue(), b4.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/** {@inheritDoc}
* @exception DimensionMismatchException if number of free parameters
* or orders do not match
* @since 3.2
*/
@Override
public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
final double a2, final DerivativeStructure b2,
final double a3, final DerivativeStructure b3,
final double a4, final DerivativeStructure b4)
throws DimensionMismatchException {
// compute an accurate value, taking care of cancellations
final double accurateValue = Sum.create()
.addProduct(a1, b1.getValue())
.addProduct(a2, b2.getValue())
.addProduct(a3, b3.getValue())
.addProduct(a4, b4.getValue()).getAsDouble();
// compute a simple value, with all partial derivatives
final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));
// create a result with accurate value and all derivatives (not necessarily as accurate as the value)
final double[] all = simpleValue.getAllDerivatives();
all[0] = accurateValue;
return new DerivativeStructure(getFreeParameters(), getOrder(), all);
}
/**
* Test for the equality of two derivative structures.
* <p>
* Derivative structures are considered equal if they have the same number
* of free parameters, the same derivation order, and the same derivatives.
* </p>
* @param other Object to test for equality to this
* @return true if two derivative structures are equal
* @since 3.2
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof DerivativeStructure) {
final DerivativeStructure rhs = (DerivativeStructure)other;
return (getFreeParameters() == rhs.getFreeParameters()) &&
(getOrder() == rhs.getOrder()) &&
MathArrays.equals(data, rhs.data);
}
return false;
}
/**
* Get a hashCode for the derivative structure.
* @return a hash code value for this object
* @since 3.2
*/
@Override
public int hashCode() {
return 227 + 229 * getFreeParameters() + 233 * getOrder() + 239 * Arrays.hashCode(data);
}
/**
* Replace the instance with a data transfer object for serialization.
* @return data transfer object that will be serialized
*/
private Object writeReplace() {
return new DataTransferObject(compiler.getFreeParameters(), compiler.getOrder(), data);
}
/** Internal class used only for serialization. */
private static class DataTransferObject implements Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20120730L;
/** Number of variables.
* @serial
*/
private final int variables;
/** Derivation order.
* @serial
*/
private final int order;
/** Partial derivatives.
* @serial
*/
private final double[] data;
/** Simple constructor.
* @param variables number of variables
* @param order derivation order
* @param data partial derivatives
*/
DataTransferObject(final int variables, final int order, final double[] data) {
this.variables = variables;
this.order = order;
this.data = data;
}
/** Replace the deserialized data transfer object with a {@link DerivativeStructure}.
* @return replacement {@link DerivativeStructure}
*/
private Object readResolve() {
return new DerivativeStructure(variables, order, data);
}
}
}