View Javadoc
1   /*
2    * Licensed to the Hipparchus project under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.hipparchus.analysis.differentiation;
18  
19  import org.hipparchus.exception.LocalizedCoreFormats;
20  import org.hipparchus.exception.MathIllegalArgumentException;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.MathArrays;
23  import org.hipparchus.util.MathUtils;
24  
25  /** Class representing both the value and the differentials of a function.
26   * <p>This class is a stripped-down version of {@link DerivativeStructure}
27   * with only one {@link DerivativeStructure#getFreeParameters() free parameter}
28   * and {@link DerivativeStructure#getOrder() derivation order} also limited to one.
29   * It should have less overhead than {@link DerivativeStructure} in its domain.</p>
30   * <p>This class is an implementation of Rall's numbers. Rall's numbers are an
31   * extension to the real numbers used throughout mathematical expressions; they hold
32   * the derivative together with the value of a function.</p>
33   * <p>{@link UnivariateDerivative1} instances can be used directly thanks to
34   * the arithmetic operators to the mathematical functions provided as
35   * methods by this class (+, -, *, /, %, sin, cos ...).</p>
36   * <p>Implementing complex expressions by hand using {@link Derivative}-based
37   * classes (or in fact any {@link org.hipparchus.CalculusFieldElement} class) is
38   * a tedious and error-prone task but has the advantage of not requiring users
39   * to compute the derivatives by themselves and allowing to switch for one
40   * derivative implementation to another as they all share the same filed API.</p>
41   * <p>Instances of this class are guaranteed to be immutable.</p>
42   * @see DerivativeStructure
43   * @see UnivariateDerivative2
44   * @see Gradient
45   * @see FieldDerivativeStructure
46   * @see FieldUnivariateDerivative1
47   * @see FieldUnivariateDerivative2
48   * @see FieldGradient
49   * @since 1.7
50   */
51  public class UnivariateDerivative1 extends UnivariateDerivative<UnivariateDerivative1>
52          implements Derivative1<UnivariateDerivative1> {
53  
54      /** The constant value of π as a {@code UnivariateDerivative1}.
55       * @since 2.0
56       */
57      public static final UnivariateDerivative1 PI = new UnivariateDerivative1(FastMath.PI, 0.0);
58  
59      /** Serializable UID. */
60      private static final long serialVersionUID = 20200519L;
61  
62      /** Value of the function. */
63      private final double f0;
64  
65      /** First derivative of the function. */
66      private final double f1;
67  
68      /** Build an instance with values and derivative.
69       * @param f0 value of the function
70       * @param f1 first derivative of the function
71       */
72      public UnivariateDerivative1(final double f0, final double f1) {
73          this.f0 = f0;
74          this.f1 = f1;
75      }
76  
77      /** Build an instance from a {@link DerivativeStructure}.
78       * @param ds derivative structure
79       * @exception MathIllegalArgumentException if either {@code ds} parameters
80       * is not 1 or {@code ds} order is not 1
81       */
82      public UnivariateDerivative1(final DerivativeStructure ds) throws MathIllegalArgumentException {
83          MathUtils.checkDimension(ds.getFreeParameters(), 1);
84          MathUtils.checkDimension(ds.getOrder(), 1);
85          this.f0 = ds.getValue();
86          this.f1 = ds.getPartialDerivative(1);
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public UnivariateDerivative1 newInstance(final double value) {
92          return new UnivariateDerivative1(value, 0.0);
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public UnivariateDerivative1 withValue(final double value) {
98          return new UnivariateDerivative1(value, f1);
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     public UnivariateDerivative1 getAddendum() {
104         return new UnivariateDerivative1(0, f1);
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public double getValue() {
110         return f0;
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public double getDerivative(final int n) {
116         switch (n) {
117             case 0 :
118                 return f0;
119             case 1 :
120                 return f1;
121             default :
122                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DERIVATION_ORDER_NOT_ALLOWED, n);
123         }
124     }
125 
126     /** Get the first derivative.
127      * @return first derivative
128      * @see #getValue()
129      */
130     public double getFirstDerivative() {
131         return f1;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public DerivativeStructure toDerivativeStructure() {
137         return getField().getConversionFactory().build(f0, f1);
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public UnivariateDerivative1 add(final UnivariateDerivative1 a) {
143         return new UnivariateDerivative1(f0 + a.f0, f1 + a.f1);
144     }
145 
146     /** {@inheritDoc} */
147     @Override
148     public UnivariateDerivative1 subtract(final UnivariateDerivative1 a) {
149         return new UnivariateDerivative1(f0 - a.f0, f1 - a.f1);
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public UnivariateDerivative1 multiply(final int n) {
155         return new UnivariateDerivative1(f0 * n, f1 * n);
156     }
157 
158     /** {@inheritDoc} */
159     @Override
160     public UnivariateDerivative1 multiply(final double a) {
161         return new UnivariateDerivative1(f0 * a, f1 * a);
162     }
163 
164     /** {@inheritDoc} */
165     @Override
166     public UnivariateDerivative1 multiply(final UnivariateDerivative1 a) {
167         return new UnivariateDerivative1(f0 * a.f0,
168                                          MathArrays.linearCombination(f1, a.f0, f0, a.f1));
169     }
170 
171     /** {@inheritDoc} */
172     @Override
173     public UnivariateDerivative1 divide(final double a) {
174         final double inv1 = 1.0 / a;
175         return new UnivariateDerivative1(f0 * inv1, f1 * inv1);
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public UnivariateDerivative1 divide(final UnivariateDerivative1 a) {
181         final double inv1 = 1.0 / a.f0;
182         final double inv2 = inv1 * inv1;
183         return new UnivariateDerivative1(f0 * inv1,
184                                          MathArrays.linearCombination(f1, a.f0, -f0, a.f1) * inv2);
185     }
186 
187     /** {@inheritDoc} */
188     @Override
189     public UnivariateDerivative1 remainder(final UnivariateDerivative1 a) {
190 
191         // compute k such that lhs % rhs = lhs - k rhs
192         final double rem = FastMath.IEEEremainder(f0, a.f0);
193         final double k   = FastMath.rint((f0 - rem) / a.f0);
194 
195         return new UnivariateDerivative1(rem, f1 - k * a.f1);
196 
197     }
198 
199     /** {@inheritDoc} */
200     @Override
201     public UnivariateDerivative1 negate() {
202         return new UnivariateDerivative1(-f0, -f1);
203     }
204 
205     /** {@inheritDoc} */
206     @Override
207     public UnivariateDerivative1 abs() {
208         if (Double.doubleToLongBits(f0) < 0) {
209             // we use the bits representation to also handle -0.0
210             return negate();
211         } else {
212             return this;
213         }
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     public UnivariateDerivative1 copySign(final UnivariateDerivative1 sign) {
219         long m = Double.doubleToLongBits(f0);
220         long s = Double.doubleToLongBits(sign.f0);
221         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
222             return this;
223         }
224         return negate(); // flip sign
225     }
226 
227     /** {@inheritDoc} */
228     @Override
229     public UnivariateDerivative1 copySign(final double sign) {
230         long m = Double.doubleToLongBits(f0);
231         long s = Double.doubleToLongBits(sign);
232         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
233             return this;
234         }
235         return negate(); // flip sign
236     }
237 
238     /** {@inheritDoc} */
239     @Override
240     public UnivariateDerivative1 scalb(final int n) {
241         return new UnivariateDerivative1(FastMath.scalb(f0, n), FastMath.scalb(f1, n));
242     }
243 
244     /** {@inheritDoc} */
245     @Override
246     public UnivariateDerivative1 hypot(final UnivariateDerivative1 y) {
247 
248         if (Double.isInfinite(f0) || Double.isInfinite(y.f0)) {
249             return new UnivariateDerivative1(Double.POSITIVE_INFINITY, 0.0);
250         } else if (Double.isNaN(f0) || Double.isNaN(y.f0)) {
251             return new UnivariateDerivative1(Double.NaN, 0.0);
252         } else {
253 
254             final int expX = getExponent();
255             final int expY = y.getExponent();
256             if (expX > expY + 27) {
257                 // y is negligible with respect to x
258                 return abs();
259             } else if (expY > expX + 27) {
260                 // x is negligible with respect to y
261                 return y.abs();
262             } else {
263 
264                 // find an intermediate scale to avoid both overflow and underflow
265                 final int middleExp = (expX + expY) / 2;
266 
267                 // scale parameters without losing precision
268                 final UnivariateDerivative1 scaledX = scalb(-middleExp);
269                 final UnivariateDerivative1 scaledY = y.scalb(-middleExp);
270 
271                 // compute scaled hypotenuse
272                 final UnivariateDerivative1 scaledH =
273                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
274 
275                 // remove scaling
276                 return scaledH.scalb(middleExp);
277 
278             }
279 
280         }
281     }
282 
283     /** {@inheritDoc} */
284     @Override
285     public UnivariateDerivative1 compose(final double... f) {
286         MathUtils.checkDimension(f.length, getOrder() + 1);
287         return compose(f[0], f[1]);
288     }
289 
290     /** {@inheritDoc} */
291     @Override
292     public UnivariateDerivative1 compose(final double ff0, final double ff1) {
293         return new UnivariateDerivative1(ff0, this.f1 * ff1);
294     }
295 
296     /** {@inheritDoc} */
297     @Override
298     public UnivariateDerivative1Field getField() {
299         return UnivariateDerivative1Field.getInstance();
300     }
301 
302     /** Compute a<sup>x</sup> where a is a double and x a {@link UnivariateDerivative1}
303      * @param a number to exponentiate
304      * @param x power to apply
305      * @return a<sup>x</sup>
306      */
307     public static UnivariateDerivative1 pow(final double a, final UnivariateDerivative1 x) {
308         if (a == 0) {
309             return x.getField().getZero();
310         } else {
311             final double aX = FastMath.pow(a, x.f0);
312             return new UnivariateDerivative1(aX, FastMath.log(a) * aX * x.f1);
313         }
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     public UnivariateDerivative1 pow(final double p) {
319         if (p == 0) {
320             return getField().getOne();
321         } else {
322             final double f0Pm1 = FastMath.pow(f0, p - 1);
323             return compose(f0Pm1 * f0, p * f0Pm1);
324         }
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public UnivariateDerivative1 pow(final int n) {
330         if (n == 0) {
331             return getField().getOne();
332         } else {
333             final double f0Nm1 = FastMath.pow(f0, n - 1);
334             return compose(f0Nm1 * f0, n * f0Nm1);
335         }
336     }
337 
338     /** {@inheritDoc} */
339     @Override
340     public UnivariateDerivative1 atan2(final UnivariateDerivative1 x) {
341         final double inv = 1.0 / (f0 * f0 + x.f0 * x.f0);
342         return new UnivariateDerivative1(FastMath.atan2(f0, x.f0),
343                                          MathArrays.linearCombination(x.f0, f1, -x.f1, f0) * inv);
344     }
345 
346     /** {@inheritDoc} */
347     @Override
348     public UnivariateDerivative1 toDegrees() {
349         return new UnivariateDerivative1(FastMath.toDegrees(f0), FastMath.toDegrees(f1));
350     }
351 
352     /** {@inheritDoc} */
353     @Override
354     public UnivariateDerivative1 toRadians() {
355         return new UnivariateDerivative1(FastMath.toRadians(f0), FastMath.toRadians(f1));
356     }
357 
358     /** Evaluate Taylor expansion a univariate derivative.
359      * @param delta parameter offset Δx
360      * @return value of the Taylor expansion at x + Δx
361      */
362     public double taylor(final double delta) {
363         return f0 + delta * f1;
364     }
365 
366     /** {@inheritDoc} */
367     @Override
368     public UnivariateDerivative1 linearCombination(final UnivariateDerivative1[] a, final UnivariateDerivative1[] b) {
369 
370         // extract values and first derivatives
371         final int      n  = a.length;
372         final double[] a0 = new double[n];
373         final double[] b0 = new double[n];
374         final double[] a1 = new double[2 * n];
375         final double[] b1 = new double[2 * n];
376         for (int i = 0; i < n; ++i) {
377             final UnivariateDerivative1 ai = a[i];
378             final UnivariateDerivative1 bi = b[i];
379             a0[i]         = ai.f0;
380             b0[i]         = bi.f0;
381             a1[2 * i]     = ai.f0;
382             a1[2 * i + 1] = ai.f1;
383             b1[2 * i]     = bi.f1;
384             b1[2 * i + 1] = bi.f0;
385         }
386 
387         return new UnivariateDerivative1(MathArrays.linearCombination(a0, b0),
388                                          MathArrays.linearCombination(a1, b1));
389 
390     }
391 
392     /** {@inheritDoc} */
393     @Override
394     public UnivariateDerivative1 linearCombination(final double[] a, final UnivariateDerivative1[] b) {
395 
396         // extract values and first derivatives
397         final int      n  = b.length;
398         final double[] b0 = new double[n];
399         final double[] b1 = new double[n];
400         for (int i = 0; i < n; ++i) {
401             b0[i] = b[i].f0;
402             b1[i] = b[i].f1;
403         }
404 
405         return new UnivariateDerivative1(MathArrays.linearCombination(a, b0),
406                                          MathArrays.linearCombination(a, b1));
407 
408     }
409 
410     /** {@inheritDoc} */
411     @Override
412     public UnivariateDerivative1 linearCombination(final UnivariateDerivative1 a1, final UnivariateDerivative1 b1,
413                                                    final UnivariateDerivative1 a2, final UnivariateDerivative1 b2) {
414         return new UnivariateDerivative1(MathArrays.linearCombination(a1.f0, b1.f0,
415                                                                       a2.f0, b2.f0),
416                                          MathArrays.linearCombination(a1.f0, b1.f1,
417                                                                       a1.f1, b1.f0,
418                                                                       a2.f0, b2.f1,
419                                                                       a2.f1, b2.f0));
420     }
421 
422     /** {@inheritDoc} */
423     @Override
424     public UnivariateDerivative1 linearCombination(final double a1, final UnivariateDerivative1 b1,
425                                                    final double a2, final UnivariateDerivative1 b2) {
426         return new UnivariateDerivative1(MathArrays.linearCombination(a1, b1.f0,
427                                                                       a2, b2.f0),
428                                          MathArrays.linearCombination(a1, b1.f1,
429                                                                       a2, b2.f1));
430     }
431 
432     /** {@inheritDoc} */
433     @Override
434     public UnivariateDerivative1 linearCombination(final UnivariateDerivative1 a1, final UnivariateDerivative1 b1,
435                                                    final UnivariateDerivative1 a2, final UnivariateDerivative1 b2,
436                                                    final UnivariateDerivative1 a3, final UnivariateDerivative1 b3) {
437         return new UnivariateDerivative1(MathArrays.linearCombination(a1.f0, b1.f0,
438                                                                       a2.f0, b2.f0,
439                                                                       a3.f0, b3.f0),
440                                          MathArrays.linearCombination(new double[] {
441                                                                           a1.f0, a1.f1,
442                                                                           a2.f0, a2.f1,
443                                                                           a3.f0, a3.f1
444                                                                       }, new double[] {
445                                                                           b1.f1, b1.f0,
446                                                                           b2.f1, b2.f0,
447                                                                           b3.f1, b3.f0
448                                                                       }));
449     }
450 
451     /** {@inheritDoc} */
452     @Override
453     public UnivariateDerivative1 linearCombination(final double a1, final UnivariateDerivative1 b1,
454                                                    final double a2, final UnivariateDerivative1 b2,
455                                                    final double a3, final UnivariateDerivative1 b3) {
456         return new UnivariateDerivative1(MathArrays.linearCombination(a1, b1.f0,
457                                                                       a2, b2.f0,
458                                                                       a3, b3.f0),
459                                          MathArrays.linearCombination(a1, b1.f1,
460                                                                       a2, b2.f1,
461                                                                       a3, b3.f1));
462     }
463 
464     /** {@inheritDoc} */
465     @Override
466     public UnivariateDerivative1 linearCombination(final UnivariateDerivative1 a1, final UnivariateDerivative1 b1,
467                                                    final UnivariateDerivative1 a2, final UnivariateDerivative1 b2,
468                                                    final UnivariateDerivative1 a3, final UnivariateDerivative1 b3,
469                                                    final UnivariateDerivative1 a4, final UnivariateDerivative1 b4) {
470         return new UnivariateDerivative1(MathArrays.linearCombination(a1.f0, b1.f0,
471                                                                       a2.f0, b2.f0,
472                                                                       a3.f0, b3.f0,
473                                                                       a4.f0, b4.f0),
474                                          MathArrays.linearCombination(new double[] {
475                                                                           a1.f0, a1.f1,
476                                                                           a2.f0, a2.f1,
477                                                                           a3.f0, a3.f1,
478                                                                           a4.f0, a4.f1
479                                                                       }, new double[] {
480                                                                           b1.f1, b1.f0,
481                                                                           b2.f1, b2.f0,
482                                                                           b3.f1, b3.f0,
483                                                                           b4.f1, b4.f0
484                                                                       }));
485     }
486 
487     /** {@inheritDoc} */
488     @Override
489     public UnivariateDerivative1 linearCombination(final double a1, final UnivariateDerivative1 b1,
490                                                    final double a2, final UnivariateDerivative1 b2,
491                                                    final double a3, final UnivariateDerivative1 b3,
492                                                    final double a4, final UnivariateDerivative1 b4) {
493         return new UnivariateDerivative1(MathArrays.linearCombination(a1, b1.f0,
494                                                                       a2, b2.f0,
495                                                                       a3, b3.f0,
496                                                                       a4, b4.f0),
497                                          MathArrays.linearCombination(a1, b1.f1,
498                                                                       a2, b2.f1,
499                                                                       a3, b3.f1,
500                                                                       a4, b4.f1));
501     }
502 
503     /** {@inheritDoc} */
504     @Override
505     public UnivariateDerivative1 getPi() {
506         return PI;
507     }
508 
509     /** Test for the equality of two univariate derivatives.
510      * <p>
511      * univariate derivatives are considered equal if they have the same derivatives.
512      * </p>
513      * @param other Object to test for equality to this
514      * @return true if two univariate derivatives are equal
515      */
516     @Override
517     public boolean equals(Object other) {
518 
519         if (this == other) {
520             return true;
521         }
522 
523         if (other instanceof UnivariateDerivative1) {
524             final UnivariateDerivative1 rhs = (UnivariateDerivative1) other;
525             return f0 == rhs.f0 && f1 == rhs.f1;
526         }
527 
528         return false;
529 
530     }
531 
532     /** Get a hashCode for the univariate derivative.
533      * @return a hash code value for this object
534      */
535     @Override
536     public int hashCode() {
537         return 453 - 19 * Double.hashCode(f0) + 37 * Double.hashCode(f1);
538     }
539 
540     /** {@inheritDoc}
541      * <p>
542      * Comparison performed considering that derivatives are intrinsically linked to monomials in the corresponding
543      * Taylor expansion and that the higher the degree, the smaller the term.
544      * </p>
545      * @since 3.0
546      */
547     @Override
548     public int compareTo(final UnivariateDerivative1 o) {
549         final int cF0 = Double.compare(f0, o.getReal());
550         if (cF0 == 0) {
551             return Double.compare(f1, o.getFirstDerivative());
552         } else {
553             return cF0;
554         }
555     }
556 
557 }