num_dual/lib.rs
1//! Generalized, recursive, scalar and vector (hyper) dual numbers for the automatic and exact calculation of (partial) derivatives.
2//!
3//! # Example
4//! This example defines a generic scalar and a generic vector function that can be called using any (hyper-) dual number and automatically calculates derivatives.
5//! ```
6//! # #[cfg(feature = "nalgebra")]
7//! # {
8//! use num_dual::*;
9//! use nalgebra::SVector;
10//!
11//! fn foo<D: DualNum>(x: D) -> D {
12//! x.powi(3)
13//! }
14//!
15//! fn bar<D: DualNum, const N: usize>(x: SVector<D, N>) -> D {
16//! x.dot(&x).sqrt()
17//! }
18//!
19//! fn main() {
20//! // Calculate a simple derivative
21//! let (f, df) = first_derivative(foo, 5.0);
22//! assert_eq!(f, 125.0);
23//! assert_eq!(df, 75.0);
24//!
25//! // Manually construct the dual number
26//! let x = Dual64::new(5.0, 1.0);
27//! println!("{}", foo(x)); // 125 + 75ε
28//!
29//! // Calculate a gradient
30//! let (f, g) = gradient(bar, &SVector::from([4.0, 3.0]));
31//! assert_eq!(f, 5.0);
32//! assert_eq!(g[0], 0.8);
33//!
34//! // Calculate a Hessian
35//! let (f, g, h) = hessian(bar, &SVector::from([4.0, 3.0]));
36//! println!("{h}"); // [[0.072, -0.096], [-0.096, 0.128]]
37//!
38//! // for x=cos(t) calculate the third derivative of foo w.r.t. t
39//! let (f0, f1, f2, f3) = third_derivative(|t| foo(t.cos()), 1.0);
40//! println!("{f3}"); // 1.5836632930100278
41//! }
42//! # }
43//! ```
44//!
45//! # Usage
46//! There are two ways to use the data structures and functions provided in this crate:
47//! 1. (recommended) Using the provided functions for explicit ([`first_derivative`], [`gradient`], ...) and
48//! implicit ([`implicit_derivative`], [`implicit_derivative_binary`], [`implicit_derivative_vec`]) functions.
49//! 2. (for experienced users) Using the different dual number types ([`Dual`], [`HyperDual`], [`DualVec`], ...) directly.
50//!
51//! The following examples and explanations focus on the first way.
52//!
53//! # Derivatives of explicit functions
54//! To be able to calculate the derivative of a function, it needs to be generic over the type of dual number used.
55//! Most commonly this would look like this:
56//! ```compile_fail
57//! fn foo<D: DualNum + Copy>(x: X) -> O {...}
58//! ```
59//! Of course, the function could also use single precision ([`f32`]) or be generic over the precision (`F:` [`DualNumFloat`]).
60//! For now, [`Copy`] is not a supertrait of [`DualNum`] to enable the calculation of derivatives with respect
61//! to a dynamic number of variables. However, in practice, using the [`Copy`] trait bound leads to an
62//! implementation that is more similar to one not using AD and there could be severe performance ramifications
63//! when using dynamically allocated dual numbers.
64//!
65//! The type `X` above is `D` for univariate functions, [`&OVector`](nalgebra::OVector) for multivariate
66//! functions, and `(D, D)` or `(&OVector, &OVector)` for partial derivatives. In the simplest case, the output
67//! `O` is a scalar `D`. However, it is generalized using the [`Mappable`] trait to also include types like
68//! [`Option<D>`] or [`Result<D, E>`], collections like [`Vec<D>`] or [`HashMap<K, D>`], or custom structs that
69//! implement the [`Mappable`] trait. Therefore, it is, e.g., possible to calculate the derivative of a fallible
70//! function:
71//!
72//! ```no_run
73//! # use num_dual::{DualNum, first_derivative};
74//! # type E = ();
75//! fn foo<D: DualNum + Copy>(x: D) -> Result<D, E> { todo!() }
76//!
77//! fn main() -> Result<(), E> {
78//! let (val, deriv) = first_derivative(foo, 2.0)?;
79//! // ...
80//! Ok(())
81//! }
82//! ```
83//! All dual number types can contain other dual numbers as inner types. Therefore, it is also possible to
84//! use the different derivative functions inside of each other.
85//!
86//! ## Extra arguments
87//! The [`partial`] and [`partial2`] functions are used to pass additional arguments to the function, e.g.:
88//! ```no_run
89//! # use num_dual::{DualNum, first_derivative, partial};
90//! fn foo<D: DualNum + Copy>(x: D, args: &(D, D)) -> D { todo!() }
91//!
92//! fn main() {
93//! let (val, deriv) = first_derivative(partial(foo, &(3.0, 4.0)), 5.0);
94//! }
95//! ```
96//! All types that implement the [`DualStruct`] trait can be used as additional function arguments. The
97//! only difference between using the [`partial`] and [`partial2`] functions compared to passing the extra
98//! arguments via a closure, is that the type of the extra arguments is automatically adjusted to the correct
99//! dual number type used for the automatic differentiation. Note that the following code would not compile:
100//! ```compile_fail
101//! # use num_dual::{DualNum, first_derivative};
102//! # fn foo<D: DualNum + Copy>(x: D, args: &(D, D)) -> D { todo!() }
103//! fn main() {
104//! let (val, deriv) = first_derivative(|x| foo(x, &(3.0, 4.0)), 5.0);
105//! }
106//! ```
107//! The code created by [`partial`] essentially translates to:
108//! ```no_run
109//! # use num_dual::{DualNum, first_derivative, Dual, DualStruct};
110//! # fn foo<D: DualNum + Copy>(x: D, args: &(D, D)) -> D { todo!() }
111//! fn main() {
112//! let (val, deriv) = first_derivative(|x| foo(x, &(Dual::from_re(3.0), Dual::from_re(4.0))), 5.0);
113//! }
114//! ```
115//!
116//! ## The [`Gradients`] trait
117//! The functions [`gradient`], [`hessian`], [`partial_hessian`] and [`jacobian`] are generic over the dimensionality
118//! of the variable vector. However, to use the functions in a generic context requires not using the [`Copy`] trait
119//! bound on the dual number type, because the dynamically sized dual numbers can by construction not implement
120//! [`Copy`]. Also, due to frequent heap allocations, the performance of the automatic differentiation could
121//! suffer significantly for dynamically sized dual numbers compared to statically sized dual numbers. The
122//! [`Gradients`] trait is introduced to overcome these limitations.
123//! ```
124//! # #[cfg(feature = "nalgebra")]
125//! # {
126//! # use num_dual::{DualNum, Gradients};
127//! # use nalgebra::{OVector, DefaultAllocator, allocator::Allocator, vector, dvector};
128//! # use approx::assert_relative_eq;
129//! fn foo<D: DualNum + Copy, N: Gradients>(x: OVector<D, N>, n: &D) -> D where DefaultAllocator: Allocator<N> {
130//! x.dot(&x).sqrt() - n
131//! }
132//!
133//! fn main() {
134//! let x = vector![1.0, 5.0, 5.0, 7.0];
135//! let (f, grad) = Gradients::gradient(foo, &x, &10.0);
136//! assert_eq!(f, 0.0);
137//! assert_relative_eq!(grad, vector![0.1, 0.5, 0.5, 0.7]);
138//!
139//! let x = dvector![1.0, 5.0, 5.0, 7.0];
140//! let (f, grad) = Gradients::gradient(foo, &x, &10.0);
141//! assert_eq!(f, 0.0);
142//! assert_relative_eq!(grad, dvector![0.1, 0.5, 0.5, 0.7]);
143//! }
144//! # }
145//! ```
146//! For dynamically sized input arrays, the [`Gradients`] trait evaluates gradients or higher-order derivatives
147//! by iteratively evaluating scalar derivatives. For functions that do not rely on the [`Copy`] trait bound,
148//! only benchmarking can reveal Whether the increased performance through the avoidance of heap allocations
149//! can overcome the overhead of repeated function evaluations, i.e., if [`Gradients`] outperforms directly
150//! calling [`gradient`], [`hessian`], [`partial_hessian`] or [`jacobian`].
151//!
152//! # Derivatives of implicit functions
153//! Implicit differentiation is used to determine the derivative `dy/dx` where the output `y` is only related
154//! implicitly to the input `x` via the equation `f(x,y)=0`. Automatic implicit differentiation generalizes the
155//! idea to determining the output `y` with full derivative information. Note that the first step in calculating
156//! an implicit derivative is always determining the "real" part (i.e., neglecting all derivatives) of the equation
157//! `f(x,y)=0`. The `num-dual` library is focused on automatic differentiation and not nonlinear equation
158//! solving. Therefore, this first step needs to be done with your own custom solutions, or Rust crates for
159//! nonlinear equation solving and optimization like, e.g., [argmin](https://argmin-rs.org/).
160//!
161//! The following example implements a square root for generic dual numbers using implicit differentiation. Of
162//! course, the derivatives of the square root can also be determined explicitly using the chain rule, so the
163//! example serves mostly as illustration. `x.re()` provides the "real" part of the dual number which is a [`f64`]
164//! and therefore, we can use all the functionalities from the std library (including the square root).
165//! ```
166//! # use num_dual::{DualNum, implicit_derivative, first_derivative};
167//! fn implicit_sqrt<D: DualNum + Copy>(x: D) -> D {
168//! implicit_derivative(|s, x| s * s - x, x.re().sqrt(), &x)
169//! }
170//!
171//! fn main() {
172//! // sanity check, not actually calculating any derivative
173//! assert_eq!(implicit_sqrt(25.0), 5.0);
174//!
175//! let (sq, deriv) = first_derivative(implicit_sqrt, 25.0);
176//! assert_eq!(sq, 5.0);
177//! // The derivative of sqrt(x) is 1/(2*sqrt(x)) which should evaluate to 0.1
178//! assert_eq!(deriv, 0.1);
179//! }
180//! ```
181//! The `implicit_sqrt` or any likewise defined function is generic over the dual type `D`
182//! and can, therefore, be used anywhere as a part of an arbitrary complex computation. The functions
183//! [`implicit_derivative_binary`] and [`implicit_derivative_vec`] can be used for implicit functions
184//! with more than one variable.
185//!
186//! For implicit functions that contain complex models and a large number of parameters, the [`ImplicitDerivative`]
187//! interface might come in handy. The idea is to define the implicit function using the [`ImplicitFunction`] trait
188//! and feeding it into the [`ImplicitDerivative`] struct, which internally stores the parameters as dual numbers
189//! and their real parts. The [`ImplicitDerivative`] then provides methods for the evaluation of the real part
190//! of the residual (which can be passed to a nonlinear solver) and the implicit derivative which can be called
191//! after solving for the real part of the solution to reconstruct all the derivatives.
192//! ```
193//! # use num_dual::{ImplicitFunction, DualNum, Dual, ImplicitDerivative};
194//! struct ImplicitSqrt;
195//! impl ImplicitFunction for ImplicitSqrt {
196//! type Parameters<D> = D;
197//! type Variable<D> = D;
198//! fn residual<D: DualNum + Copy>(x: D, square: &D) -> D {
199//! *square - x * x
200//! }
201//! }
202//!
203//! fn main() {
204//! let x = Dual::from_re(25.0).derivative();
205//! let func = ImplicitDerivative::new(ImplicitSqrt, x);
206//! assert_eq!(func.residual(5.0), 0.0);
207//! assert_eq!(x.sqrt(), func.implicit_derivative(5.0));
208//! }
209//! ```
210//!
211//! ## Combination with nonlinear solver libraries
212//! As mentioned previously, this crate does not contain any algorithms for nonlinear optimization or root finding.
213//! However, combining the capabilities of automatic differentiation with nonlinear solving can be very fruitful.
214//! Most importantly, the calculation of Jacobians or Hessians can be completely automated, if the model can be
215//! expressed within the functionalities of the [`DualNum`] trait. On top of that implicit derivatives can be of
216//! interest, if derivatives of the result of the optimization itself are relevant (e.g., in a bilevel
217//! optimization). The synergy is exploited in the [`ipopt-ad`](https://github.com/prehner/ipopt-ad) crate that
218//! turns the NLP solver [IPOPT](https://github.com/coin-or/Ipopt) into a black-box optimization algorithm (i.e.,
219//! it only requires a function that returns the values of the optimization variable and constraints), without
220//! any repercussions regarding the robustness or speed of convergence of the solver.
221//!
222//! If you are developing nonlinear optimization algorithms in Rust, feel free to reach out to us. We are happy to
223//! discuss how to enhance your algorithms with the automatic differentiation capabilities of this crate.
224
225#![warn(clippy::all)]
226#![warn(clippy::allow_attributes)]
227
228#[cfg(feature = "nalgebra")]
229use nalgebra::{DefaultAllocator, Dim, OMatrix, Scalar, allocator::Allocator};
230#[cfg(feature = "ndarray")]
231use ndarray::ScalarOperand;
232use num_traits::{Float, FloatConst, FromPrimitive, Inv, NumAssignOps, NumOps, Signed};
233use std::collections::HashMap;
234use std::fmt;
235use std::hash::Hash;
236use std::iter::{Product, Sum};
237
238#[macro_use]
239mod macros;
240#[macro_use]
241#[cfg(feature = "nalgebra")]
242mod nalgebra_macros;
243#[macro_use]
244mod impl_derivatives;
245
246mod bessel;
247mod datatypes;
248mod explicit;
249mod implicit;
250pub use bessel::BesselDual;
251#[cfg(feature = "nalgebra")]
252pub use datatypes::derivative::Derivative;
253pub use datatypes::dual::{Dual, Dual32, Dual64};
254#[cfg(feature = "nalgebra")]
255pub use datatypes::dual_vec::{
256 DualDVec32, DualDVec64, DualSVec, DualSVec32, DualSVec64, DualVec, DualVec32, DualVec64,
257};
258pub use datatypes::dual2::{Dual2, Dual2_32, Dual2_64};
259#[cfg(feature = "nalgebra")]
260pub use datatypes::dual2_vec::{
261 Dual2DVec, Dual2DVec32, Dual2DVec64, Dual2SVec, Dual2SVec32, Dual2SVec64, Dual2Vec, Dual2Vec32,
262 Dual2Vec64,
263};
264pub use datatypes::dual3::{Dual3, Dual3_32, Dual3_64};
265pub use datatypes::hyperdual::{HyperDual, HyperDual32, HyperDual64};
266#[cfg(feature = "nalgebra")]
267pub use datatypes::hyperdual_vec::{
268 HyperDualDVec32, HyperDualDVec64, HyperDualSVec32, HyperDualSVec64, HyperDualVec,
269 HyperDualVec32, HyperDualVec64,
270};
271pub use datatypes::hyperhyperdual::{HyperHyperDual, HyperHyperDual32, HyperHyperDual64};
272pub use datatypes::real::Real;
273#[cfg(feature = "nalgebra")]
274pub use explicit::{Gradients, gradient, hessian, jacobian, partial_hessian};
275pub use explicit::{
276 first_derivative, partial, partial2, partial3, second_derivative, second_partial_derivative,
277 third_derivative, third_partial_derivative, third_partial_derivative_vec, zeroth_derivative,
278};
279pub use implicit::{ImplicitDerivative, ImplicitFunction, implicit_derivative};
280#[cfg(feature = "nalgebra")]
281pub use implicit::{implicit_derivative_binary, implicit_derivative_sp, implicit_derivative_vec};
282
283#[cfg(feature = "nalgebra")]
284pub mod linalg;
285
286#[cfg(feature = "python")]
287pub mod python;
288
289#[cfg(feature = "python_macro")]
290mod python_macro;
291
292/// A generalized (hyper) dual number.
293#[cfg(feature = "ndarray")]
294pub trait DualNum:
295 NumOps
296 + for<'r> NumOps<&'r Self>
297 + Signed
298 + NumOps<Self::Primitive>
299 + NumAssignOps
300 + NumAssignOps<Self::Primitive>
301 + Clone
302 + Inv<Output = Self>
303 + Sum
304 + Product
305 + FromPrimitive
306 + From<Self::Primitive>
307 + DualStruct<Real = Self::Primitive>
308 + Mappable<Self>
309 + fmt::Display
310 + PartialOrd
311 + PartialOrd<Self::Primitive>
312 + fmt::Debug
313 + ScalarOperand
314 + 'static
315{
316 /// The underlying primitive data type (mostly f64 or f32)
317 type Primitive: DualNumFloat;
318
319 /// Highest derivative that can be calculated with this struct
320 const NDERIV: usize;
321
322 /// The type of the individual elements of this dual number
323 type InnerDual: DualNum;
324
325 /// Build a dual number from its real part, setting all other values to 0
326 fn from_re(re: Self::InnerDual) -> Self;
327
328 /// Reciprocal (inverse) of a number `1/x`
329 fn recip(&self) -> Self;
330
331 /// Power with integer exponent `x^n`
332 fn powi(&self, n: i32) -> Self;
333
334 /// Power with real exponent `x^n`
335 fn powf(&self, n: Self::Primitive) -> Self;
336
337 /// Square root
338 fn sqrt(&self) -> Self;
339
340 /// Cubic root
341 fn cbrt(&self) -> Self;
342
343 /// Exponential `e^x`
344 fn exp(&self) -> Self;
345
346 /// Exponential with base 2 `2^x`
347 fn exp2(&self) -> Self;
348
349 /// Exponential minus 1 `e^x-1`
350 fn exp_m1(&self) -> Self;
351
352 /// Natural logarithm
353 fn ln(&self) -> Self;
354
355 /// Logarithm with arbitrary base
356 fn log(&self, base: Self::Primitive) -> Self;
357
358 /// Logarithm with base 2
359 fn log2(&self) -> Self;
360
361 /// Logarithm with base 10
362 fn log10(&self) -> Self;
363
364 /// Logarithm on x plus one `ln(1+x)`
365 fn ln_1p(&self) -> Self;
366
367 /// Sine
368 fn sin(&self) -> Self;
369
370 /// Cosine
371 fn cos(&self) -> Self;
372
373 /// Tangent
374 fn tan(&self) -> Self;
375
376 /// Calculate sine and cosine simultaneously
377 fn sin_cos(&self) -> (Self, Self);
378
379 /// Arcsine
380 fn asin(&self) -> Self;
381
382 /// Arccosine
383 fn acos(&self) -> Self;
384
385 /// Arctangent
386 fn atan(&self) -> Self;
387
388 /// Arctangent
389 fn atan2(&self, other: Self) -> Self;
390
391 /// Hyperbolic sine
392 fn sinh(&self) -> Self;
393
394 /// Hyperbolic cosine
395 fn cosh(&self) -> Self;
396
397 /// Hyperbolic tangent
398 fn tanh(&self) -> Self;
399
400 /// Area hyperbolic sine
401 fn asinh(&self) -> Self;
402
403 /// Area hyperbolic cosine
404 fn acosh(&self) -> Self;
405
406 /// Area hyperbolic tangent
407 fn atanh(&self) -> Self;
408
409 /// 0th order spherical Bessel function of the first kind
410 fn sph_j0(&self) -> Self;
411
412 /// 1st order spherical Bessel function of the first kind
413 fn sph_j1(&self) -> Self;
414
415 /// 2nd order spherical Bessel function of the first kind
416 fn sph_j2(&self) -> Self;
417
418 /// Fused multiply-add
419 #[inline]
420 fn mul_add(&self, a: Self, b: Self) -> Self {
421 self.clone() * a + b
422 }
423
424 /// Power with dual exponent `x^n`
425 #[inline]
426 fn powd(&self, exp: Self) -> Self {
427 (self.ln() * exp).exp()
428 }
429}
430
431/// A generalized (hyper) dual number.
432#[cfg(not(feature = "ndarray"))]
433pub trait DualNum:
434 NumOps
435 + for<'r> NumOps<&'r Self>
436 + Signed
437 + NumOps<Self::Primitive>
438 + NumAssignOps
439 + NumAssignOps<Self::Primitive>
440 + Clone
441 + Inv<Output = Self>
442 + Sum
443 + Product
444 + FromPrimitive
445 + From<Self::Primitive>
446 + DualStruct<Real = Self::Primitive>
447 + Mappable<Self>
448 + fmt::Display
449 + PartialOrd
450 + PartialOrd<Self::Primitive>
451 + fmt::Debug
452 + 'static
453{
454 /// The underlying primitive data type (mostly f64 or f32)
455 type Primitive: DualNumFloat;
456
457 /// Highest derivative that can be calculated with this struct
458 const NDERIV: usize;
459
460 /// The type of the individual elements of this dual number
461 type InnerDual: DualNum;
462
463 /// Build a dual number from its real part, setting all other values to 0
464 fn from_re(re: Self::InnerDual) -> Self;
465
466 /// Reciprocal (inverse) of a number `1/x`
467 fn recip(&self) -> Self;
468
469 /// Power with integer exponent `x^n`
470 fn powi(&self, n: i32) -> Self;
471
472 /// Power with real exponent `x^n`
473 fn powf(&self, n: Self::Primitive) -> Self;
474
475 /// Square root
476 fn sqrt(&self) -> Self;
477
478 /// Cubic root
479 fn cbrt(&self) -> Self;
480
481 /// Exponential `e^x`
482 fn exp(&self) -> Self;
483
484 /// Exponential with base 2 `2^x`
485 fn exp2(&self) -> Self;
486
487 /// Exponential minus 1 `e^x-1`
488 fn exp_m1(&self) -> Self;
489
490 /// Natural logarithm
491 fn ln(&self) -> Self;
492
493 /// Logarithm with arbitrary base
494 fn log(&self, base: Self::Primitive) -> Self;
495
496 /// Logarithm with base 2
497 fn log2(&self) -> Self;
498
499 /// Logarithm with base 10
500 fn log10(&self) -> Self;
501
502 /// Logarithm on x plus one `ln(1+x)`
503 fn ln_1p(&self) -> Self;
504
505 /// Sine
506 fn sin(&self) -> Self;
507
508 /// Cosine
509 fn cos(&self) -> Self;
510
511 /// Tangent
512 fn tan(&self) -> Self;
513
514 /// Calculate sine and cosine simultaneously
515 fn sin_cos(&self) -> (Self, Self);
516
517 /// Arcsine
518 fn asin(&self) -> Self;
519
520 /// Arccosine
521 fn acos(&self) -> Self;
522
523 /// Arctangent
524 fn atan(&self) -> Self;
525
526 /// Arctangent
527 fn atan2(&self, other: Self) -> Self;
528
529 /// Hyperbolic sine
530 fn sinh(&self) -> Self;
531
532 /// Hyperbolic cosine
533 fn cosh(&self) -> Self;
534
535 /// Hyperbolic tangent
536 fn tanh(&self) -> Self;
537
538 /// Area hyperbolic sine
539 fn asinh(&self) -> Self;
540
541 /// Area hyperbolic cosine
542 fn acosh(&self) -> Self;
543
544 /// Area hyperbolic tangent
545 fn atanh(&self) -> Self;
546
547 /// 0th order spherical Bessel function of the first kind
548 fn sph_j0(&self) -> Self;
549
550 /// 1st order spherical Bessel function of the first kind
551 fn sph_j1(&self) -> Self;
552
553 /// 2nd order spherical Bessel function of the first kind
554 fn sph_j2(&self) -> Self;
555
556 /// Fused multiply-add
557 #[inline]
558 fn mul_add(&self, a: Self, b: Self) -> Self {
559 self.clone() * a + b
560 }
561
562 /// Power with dual exponent `x^n`
563 #[inline]
564 fn powd(&self, exp: Self) -> Self {
565 (self.ln() * exp).exp()
566 }
567}
568
569/// A generalized (hyper) dual number that has a static size.
570pub trait DualNumCopy: DualNum + Copy + Send + Sync {}
571impl<T: DualNum + Copy + Send + Sync> DualNumCopy for T {}
572
573/// The underlying data type of individual derivatives. Implemented for f32 or f64.
574pub trait DualNumFloat: DualNumCopy + Float + FloatConst {
575 const THIRD: Self;
576 const HALF: Self;
577 const TWO: Self;
578 const THREE: Self;
579 const FOUR: Self;
580 const SIX: Self;
581 const TEN: Self;
582 const FIFTEEN: Self;
583}
584
585macro_rules! impl_dual_num_float {
586 ($float:ty) => {
587 impl DualNum for $float {
588 type Primitive = $float;
589
590 const NDERIV: usize = 0;
591
592 type InnerDual = $float;
593 fn from_re(re: $float) -> Self {
594 re
595 }
596
597 fn mul_add(&self, a: Self, b: Self) -> Self {
598 <$float>::mul_add(*self, a, b)
599 }
600 fn recip(&self) -> Self {
601 <$float>::recip(*self)
602 }
603 fn powi(&self, n: i32) -> Self {
604 <$float>::powi(*self, n)
605 }
606 fn powf(&self, n: Self) -> Self {
607 <$float>::powf(*self, n)
608 }
609 fn powd(&self, n: Self) -> Self {
610 <$float>::powf(*self, n)
611 }
612 fn sqrt(&self) -> Self {
613 <$float>::sqrt(*self)
614 }
615 fn exp(&self) -> Self {
616 <$float>::exp(*self)
617 }
618 fn exp2(&self) -> Self {
619 <$float>::exp2(*self)
620 }
621 fn ln(&self) -> Self {
622 <$float>::ln(*self)
623 }
624 fn log(&self, base: Self) -> Self {
625 <$float>::log(*self, base)
626 }
627 fn log2(&self) -> Self {
628 <$float>::log2(*self)
629 }
630 fn log10(&self) -> Self {
631 <$float>::log10(*self)
632 }
633 fn cbrt(&self) -> Self {
634 <$float>::cbrt(*self)
635 }
636 fn sin(&self) -> Self {
637 <$float>::sin(*self)
638 }
639 fn cos(&self) -> Self {
640 <$float>::cos(*self)
641 }
642 fn tan(&self) -> Self {
643 <$float>::tan(*self)
644 }
645 fn asin(&self) -> Self {
646 <$float>::asin(*self)
647 }
648 fn acos(&self) -> Self {
649 <$float>::acos(*self)
650 }
651 fn atan(&self) -> Self {
652 <$float>::atan(*self)
653 }
654 fn atan2(&self, other: $float) -> Self {
655 <$float>::atan2(*self, other)
656 }
657 fn sin_cos(&self) -> (Self, Self) {
658 <$float>::sin_cos(*self)
659 }
660 fn exp_m1(&self) -> Self {
661 <$float>::exp_m1(*self)
662 }
663 fn ln_1p(&self) -> Self {
664 <$float>::ln_1p(*self)
665 }
666 fn sinh(&self) -> Self {
667 <$float>::sinh(*self)
668 }
669 fn cosh(&self) -> Self {
670 <$float>::cosh(*self)
671 }
672 fn tanh(&self) -> Self {
673 <$float>::tanh(*self)
674 }
675 fn asinh(&self) -> Self {
676 <$float>::asinh(*self)
677 }
678 fn acosh(&self) -> Self {
679 <$float>::acosh(*self)
680 }
681 fn atanh(&self) -> Self {
682 <$float>::atanh(*self)
683 }
684 fn sph_j0(&self) -> Self {
685 if self.abs() < <$float>::EPSILON {
686 1.0 - self * self / 6.0
687 } else {
688 self.sin() / self
689 }
690 }
691 fn sph_j1(&self) -> Self {
692 if self.abs() < <$float>::EPSILON {
693 self / 3.0
694 } else {
695 let sc = self.sin_cos();
696 let rec = self.recip();
697 (sc.0 * rec - sc.1) * rec
698 }
699 }
700 fn sph_j2(&self) -> Self {
701 if self.abs() < <$float>::EPSILON {
702 self * self / 15.0
703 } else {
704 let sc = self.sin_cos();
705 let s2 = self * self;
706 ((3.0 - s2) * sc.0 - 3.0 * self * sc.1) / (self * s2)
707 }
708 }
709 }
710
711 impl DualNumFloat for $float {
712 const THIRD: Self = 1.0 / 3.0;
713 const HALF: Self = 0.5;
714 const TWO: Self = 2.0;
715 const THREE: Self = 3.0;
716 const FOUR: Self = 4.0;
717 const SIX: Self = 6.0;
718 const TEN: Self = 10.0;
719 const FIFTEEN: Self = 15.0;
720 }
721 };
722}
723
724impl_dual_num_float!(f32);
725impl_dual_num_float!(f64);
726
727/// A struct that contains dual numbers. Needed for arbitrary arguments in [ImplicitFunction].
728///
729/// The trait is implemented for all dual types themselves, and common data types (tuple, vec,
730/// array, ...) and can be implemented for custom data types to achieve full flexibility.
731pub trait DualStruct {
732 type Real;
733 type Inner: DualStruct;
734 fn re(&self) -> Self::Real;
735 fn from_inner(inner: &Self::Inner) -> Self;
736}
737
738/// Trait for structs used as an output of functions for which derivatives are calculated.
739///
740/// The main intention is to generalize the calculation of derivatives to fallible functions, but
741/// other use cases might also appear in the future.
742pub trait Mappable<D> {
743 type Output<O>;
744 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O>;
745}
746
747impl DualStruct for () {
748 type Real = ();
749 type Inner = ();
750 fn re(&self) {}
751 fn from_inner(_: &Self::Inner) -> Self {}
752}
753
754impl<D> Mappable<D> for () {
755 type Output<O> = ();
756 fn map_dual<M: FnOnce(D) -> O, O>(self, _: M) {}
757}
758
759impl DualStruct for f32 {
760 type Real = f32;
761 type Inner = f32;
762 fn re(&self) -> f32 {
763 *self
764 }
765 fn from_inner(inner: &Self::Inner) -> Self {
766 *inner
767 }
768}
769
770impl Mappable<f32> for f32 {
771 type Output<O> = O;
772 fn map_dual<M: FnOnce(f32) -> O, O>(self, f: M) -> Self::Output<O> {
773 f(self)
774 }
775}
776
777impl DualStruct for f64 {
778 type Real = f64;
779 type Inner = f64;
780 fn re(&self) -> f64 {
781 *self
782 }
783 fn from_inner(inner: &Self::Inner) -> Self {
784 *inner
785 }
786}
787
788impl Mappable<f64> for f64 {
789 type Output<O> = O;
790 fn map_dual<M: FnOnce(f64) -> O, O>(self, f: M) -> Self::Output<O> {
791 f(self)
792 }
793}
794
795impl<T1: DualStruct, T2: DualStruct> DualStruct for (T1, T2) {
796 type Real = (T1::Real, T2::Real);
797 type Inner = (T1::Inner, T2::Inner);
798 fn re(&self) -> Self::Real {
799 let (s1, s2) = self;
800 (s1.re(), s2.re())
801 }
802 fn from_inner(re: &Self::Inner) -> Self {
803 let (r1, r2) = re;
804 (T1::from_inner(r1), T2::from_inner(r2))
805 }
806}
807
808impl<D, T1: Mappable<D>, T2: Mappable<D>> Mappable<D> for (T1, T2) {
809 type Output<O> = (T1::Output<O>, T2::Output<O>);
810 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
811 let (s1, s2) = self;
812 (s1.map_dual(&f), s2.map_dual(&f))
813 }
814}
815
816impl<T1: DualStruct, T2: DualStruct, T3: DualStruct> DualStruct for (T1, T2, T3) {
817 type Real = (T1::Real, T2::Real, T3::Real);
818 type Inner = (T1::Inner, T2::Inner, T3::Inner);
819 fn re(&self) -> Self::Real {
820 let (s1, s2, s3) = self;
821 (s1.re(), s2.re(), s3.re())
822 }
823 fn from_inner(inner: &Self::Inner) -> Self {
824 let (r1, r2, r3) = inner;
825 (T1::from_inner(r1), T2::from_inner(r2), T3::from_inner(r3))
826 }
827}
828
829impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>> Mappable<D> for (T1, T2, T3) {
830 type Output<O> = (T1::Output<O>, T2::Output<O>, T3::Output<O>);
831 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
832 let (s1, s2, s3) = self;
833 (s1.map_dual(&f), s2.map_dual(&f), s3.map_dual(&f))
834 }
835}
836
837impl<T1: DualStruct, T2: DualStruct, T3: DualStruct, T4: DualStruct> DualStruct
838 for (T1, T2, T3, T4)
839{
840 type Real = (T1::Real, T2::Real, T3::Real, T4::Real);
841 type Inner = (T1::Inner, T2::Inner, T3::Inner, T4::Inner);
842 fn re(&self) -> Self::Real {
843 let (s1, s2, s3, s4) = self;
844 (s1.re(), s2.re(), s3.re(), s4.re())
845 }
846 fn from_inner(inner: &Self::Inner) -> Self {
847 let (r1, r2, r3, r4) = inner;
848 (
849 T1::from_inner(r1),
850 T2::from_inner(r2),
851 T3::from_inner(r3),
852 T4::from_inner(r4),
853 )
854 }
855}
856
857impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>, T4: Mappable<D>> Mappable<D>
858 for (T1, T2, T3, T4)
859{
860 type Output<O> = (T1::Output<O>, T2::Output<O>, T3::Output<O>, T4::Output<O>);
861 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
862 let (s1, s2, s3, s4) = self;
863 (
864 s1.map_dual(&f),
865 s2.map_dual(&f),
866 s3.map_dual(&f),
867 s4.map_dual(&f),
868 )
869 }
870}
871
872impl<T1: DualStruct, T2: DualStruct, T3: DualStruct, T4: DualStruct, T5: DualStruct> DualStruct
873 for (T1, T2, T3, T4, T5)
874{
875 type Real = (T1::Real, T2::Real, T3::Real, T4::Real, T5::Real);
876 type Inner = (T1::Inner, T2::Inner, T3::Inner, T4::Inner, T5::Inner);
877 fn re(&self) -> Self::Real {
878 let (s1, s2, s3, s4, s5) = self;
879 (s1.re(), s2.re(), s3.re(), s4.re(), s5.re())
880 }
881 fn from_inner(inner: &Self::Inner) -> Self {
882 let (r1, r2, r3, r4, r5) = inner;
883 (
884 T1::from_inner(r1),
885 T2::from_inner(r2),
886 T3::from_inner(r3),
887 T4::from_inner(r4),
888 T5::from_inner(r5),
889 )
890 }
891}
892
893impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>, T4: Mappable<D>, T5: Mappable<D>>
894 Mappable<D> for (T1, T2, T3, T4, T5)
895{
896 type Output<O> = (
897 T1::Output<O>,
898 T2::Output<O>,
899 T3::Output<O>,
900 T4::Output<O>,
901 T5::Output<O>,
902 );
903 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
904 let (s1, s2, s3, s4, s5) = self;
905 (
906 s1.map_dual(&f),
907 s2.map_dual(&f),
908 s3.map_dual(&f),
909 s4.map_dual(&f),
910 s5.map_dual(&f),
911 )
912 }
913}
914
915impl<T: DualStruct, const N: usize> DualStruct for [T; N] {
916 type Real = [T::Real; N];
917 type Inner = [T::Inner; N];
918 fn re(&self) -> Self::Real {
919 self.each_ref().map(|x| x.re())
920 }
921 fn from_inner(re: &Self::Inner) -> Self {
922 re.each_ref().map(T::from_inner)
923 }
924}
925
926impl<D, T: Mappable<D>, const N: usize> Mappable<D> for [T; N] {
927 type Output<O> = [T::Output<O>; N];
928 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
929 self.map(|x| x.map_dual(&f))
930 }
931}
932
933impl<T: DualStruct> DualStruct for Option<T> {
934 type Real = Option<T::Real>;
935 type Inner = Option<T::Inner>;
936 fn re(&self) -> Self::Real {
937 self.as_ref().map(|x| x.re())
938 }
939 fn from_inner(inner: &Self::Inner) -> Self {
940 inner.as_ref().map(|x| T::from_inner(x))
941 }
942}
943
944impl<D, T: Mappable<D>> Mappable<D> for Option<T> {
945 type Output<O> = Option<T::Output<O>>;
946 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
947 self.map(|x| x.map_dual(f))
948 }
949}
950
951impl<D, T: Mappable<D>, E> Mappable<D> for Result<T, E> {
952 type Output<O> = Result<T::Output<O>, E>;
953 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
954 self.map(|x| x.map_dual(f))
955 }
956}
957
958impl<T: DualStruct> DualStruct for Vec<T> {
959 type Real = Vec<T::Real>;
960 type Inner = Vec<T::Inner>;
961 fn re(&self) -> Self::Real {
962 self.iter().map(|x| x.re()).collect()
963 }
964 fn from_inner(inner: &Self::Inner) -> Self {
965 inner.iter().map(|x| T::from_inner(x)).collect()
966 }
967}
968
969impl<D, T: Mappable<D>> Mappable<D> for Vec<T> {
970 type Output<O> = Vec<T::Output<O>>;
971 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
972 self.into_iter().map(|x| x.map_dual(&f)).collect()
973 }
974}
975
976impl<T: DualStruct, K: Clone + Eq + Hash> DualStruct for HashMap<K, T> {
977 type Real = HashMap<K, T::Real>;
978 type Inner = HashMap<K, T::Inner>;
979 fn re(&self) -> Self::Real {
980 self.iter().map(|(k, x)| (k.clone(), x.re())).collect()
981 }
982 fn from_inner(inner: &Self::Inner) -> Self {
983 inner
984 .iter()
985 .map(|(k, x)| (k.clone(), T::from_inner(x)))
986 .collect()
987 }
988}
989
990impl<D, T: Mappable<D>, K: Eq + Hash> Mappable<D> for HashMap<K, T> {
991 type Output<O> = HashMap<K, T::Output<O>>;
992 fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
993 self.into_iter().map(|(k, x)| (k, x.map_dual(&f))).collect()
994 }
995}
996
997#[cfg(feature = "nalgebra")]
998impl<D: DualNum, R: Dim, C: Dim> DualStruct for OMatrix<D, R, C>
999where
1000 DefaultAllocator: Allocator<R, C>,
1001{
1002 type Real = OMatrix<D::Real, R, C>;
1003 type Inner = OMatrix<D::InnerDual, R, C>;
1004 fn re(&self) -> Self::Real {
1005 self.map(|x| x.re())
1006 }
1007 fn from_inner(inner: &Self::Inner) -> Self {
1008 inner.map(|x| DualNum::from_re(x))
1009 }
1010}
1011
1012#[cfg(feature = "nalgebra")]
1013impl<D: Scalar, R: Dim, C: Dim> Mappable<Self> for OMatrix<D, R, C>
1014where
1015 DefaultAllocator: Allocator<R, C>,
1016{
1017 type Output<O> = O;
1018 fn map_dual<M: Fn(Self) -> O, O>(self, f: M) -> O {
1019 f(self)
1020 }
1021}