num_dual/datatypes/
dual2_vec.rs

1use crate::{Derivative, DualNum, DualNumFloat, DualStruct};
2use approx::{AbsDiffEq, RelativeEq, UlpsEq};
3use nalgebra::allocator::Allocator;
4use nalgebra::*;
5use num_traits::{Float, FloatConst, FromPrimitive, Inv, Num, One, Signed, Zero};
6use std::fmt;
7use std::iter::{Product, Sum};
8use std::marker::PhantomData;
9use std::ops::{
10    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
11};
12
13/// A vector second order dual number for the calculation of Hessians.
14#[derive(Clone, Debug)]
15pub struct Dual2Vec<T: DualNum<F>, F, D: Dim>
16where
17    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
18{
19    /// Real part of the second order dual number
20    pub re: T,
21    /// Gradient part of the second order dual number
22    pub v1: Derivative<T, F, U1, D>,
23    /// Hessian part of the second order dual number
24    pub v2: Derivative<T, F, D, D>,
25    f: PhantomData<F>,
26}
27
28impl<T: DualNum<F> + Copy, F: Copy, const N: usize> Copy for Dual2Vec<T, F, Const<N>> {}
29
30#[cfg(feature = "ndarray")]
31impl<T: DualNum<F>, F: DualNumFloat, D: Dim> ndarray::ScalarOperand for Dual2Vec<T, F, D> where
32    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>
33{
34}
35
36pub type Dual2SVec<T, F, const N: usize> = Dual2Vec<T, F, Const<N>>;
37pub type Dual2DVec<T, F> = Dual2Vec<T, F, Dyn>;
38pub type Dual2Vec32<D> = Dual2Vec<f32, f32, D>;
39pub type Dual2Vec64<D> = Dual2Vec<f64, f64, D>;
40pub type Dual2SVec32<const N: usize> = Dual2Vec<f32, f32, Const<N>>;
41pub type Dual2SVec64<const N: usize> = Dual2Vec<f64, f64, Const<N>>;
42pub type Dual2DVec32 = Dual2Vec<f32, f32, Dyn>;
43pub type Dual2DVec64 = Dual2Vec<f64, f64, Dyn>;
44
45impl<T: DualNum<F>, F, D: Dim> Dual2Vec<T, F, D>
46where
47    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
48{
49    /// Create a new second order dual number from its fields.
50    #[inline]
51    pub fn new(re: T, v1: Derivative<T, F, U1, D>, v2: Derivative<T, F, D, D>) -> Self {
52        Self {
53            re,
54            v1,
55            v2,
56            f: PhantomData,
57        }
58    }
59}
60
61impl<T: DualNum<F>, F, const N: usize> Dual2SVec<T, F, N> {
62    /// Set the derivative part of variable `index` to 1.
63    ///
64    /// For most cases, the [`hessian`](crate::hessian) function provides a convenient
65    /// interface to calculate derivatives. This function exists for the more edge cases
66    /// where more control over the variables is required.
67    /// ```
68    /// # use num_dual::Dual2SVec64;
69    /// # use nalgebra::{U1, U2, matrix};
70    /// let x: Dual2SVec64<2> = Dual2SVec64::from_re(5.0).derivative(0);
71    /// let y: Dual2SVec64<2> = Dual2SVec64::from_re(3.0).derivative(1);
72    /// let z = x * x * y;
73    /// assert_eq!(z.re, 75.0);                                                 // x²y
74    /// assert_eq!(z.v1.unwrap_generic(U1, U2), matrix![30.0, 25.0]);           // [2xy, x²]
75    /// assert_eq!(z.v2.unwrap_generic(U2, U2), matrix![6.0, 10.0; 10.0, 0.0]); // [2y, 2x; 2x, 0]
76    /// ```
77    #[inline]
78    pub fn derivative(mut self, index: usize) -> Self {
79        self.v1 = Derivative::derivative_generic(U1, Const::<N>, index);
80        self
81    }
82}
83
84impl<T: DualNum<F>, F> Dual2DVec<T, F> {
85    /// Set the derivative part of variable `index` to 1.
86    ///
87    /// For most cases, the [`hessian`](crate::hessian) function provides a convenient interface
88    /// to calculate derivatives. This function exists for the more edge cases
89    /// where more control over the variables is required.
90    /// ```
91    /// # use num_dual::Dual2DVec64;
92    /// # use nalgebra::{Dyn, U1, dmatrix};
93    /// let x: Dual2DVec64 = Dual2DVec64::from_re(5.0).derivative(2, 0);
94    /// let y: Dual2DVec64 = Dual2DVec64::from_re(3.0).derivative(2, 1);
95    /// let z = &x * &x * y;
96    /// assert_eq!(z.re, 75.0);                                                          // x²y
97    /// assert_eq!(z.v1.unwrap_generic(U1, Dyn(2)), dmatrix![30.0, 25.0]);               // [2xy, x²]
98    /// assert_eq!(z.v2.unwrap_generic(Dyn(2), Dyn(2)), dmatrix![6.0, 10.0; 10.0, 0.0]); // [2y, 2x; 2x, 0]
99    /// ```
100    #[inline]
101    pub fn derivative(mut self, variables: usize, index: usize) -> Self {
102        self.v1 = Derivative::derivative_generic(U1, Dyn(variables), index);
103        self
104    }
105}
106
107impl<T: DualNum<F>, F, D: Dim> Dual2Vec<T, F, D>
108where
109    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
110{
111    /// Create a new second order dual number from the real part.
112    #[inline]
113    pub fn from_re(re: T) -> Self {
114        Self::new(re, Derivative::none(), Derivative::none())
115    }
116}
117
118/* chain rule */
119impl<T: DualNum<F>, F: Float, D: Dim> Dual2Vec<T, F, D>
120where
121    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
122{
123    #[inline]
124    fn chain_rule(&self, f0: T, f1: T, f2: T) -> Self {
125        Self::new(
126            f0,
127            &self.v1 * f1.clone(),
128            &self.v2 * f1 + self.v1.tr_mul(&self.v1) * f2,
129        )
130    }
131}
132
133/* product rule */
134impl<T: DualNum<F>, F: Float, D: Dim> Mul<&Dual2Vec<T, F, D>> for &Dual2Vec<T, F, D>
135where
136    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
137{
138    type Output = Dual2Vec<T, F, D>;
139    #[inline]
140    fn mul(self, other: &Dual2Vec<T, F, D>) -> Dual2Vec<T, F, D> {
141        Dual2Vec::new(
142            self.re.clone() * other.re.clone(),
143            &other.v1 * self.re.clone() + &self.v1 * other.re.clone(),
144            &other.v2 * self.re.clone()
145                + self.v1.tr_mul(&other.v1)
146                + other.v1.tr_mul(&self.v1)
147                + &self.v2 * other.re.clone(),
148        )
149    }
150}
151
152/* quotient rule */
153impl<T: DualNum<F>, F: Float, D: Dim> Div<&Dual2Vec<T, F, D>> for &Dual2Vec<T, F, D>
154where
155    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
156{
157    type Output = Dual2Vec<T, F, D>;
158    #[inline]
159    fn div(self, other: &Dual2Vec<T, F, D>) -> Dual2Vec<T, F, D> {
160        let inv = other.re.recip();
161        let inv2 = inv.clone() * inv.clone();
162        Dual2Vec::new(
163            self.re.clone() * inv.clone(),
164            (&self.v1 * other.re.clone() - &other.v1 * self.re.clone()) * inv2.clone(),
165            &self.v2 * inv.clone()
166                - (&other.v2 * self.re.clone()
167                    + self.v1.tr_mul(&other.v1)
168                    + other.v1.tr_mul(&self.v1))
169                    * inv2.clone()
170                + other.v1.tr_mul(&other.v1)
171                    * ((T::one() + T::one()) * self.re.clone() * inv2 * inv),
172        )
173    }
174}
175
176/* string conversions */
177impl<T: DualNum<F>, F: fmt::Display, D: Dim> fmt::Display for Dual2Vec<T, F, D>
178where
179    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
180{
181    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182        write!(f, "{}", self.re)?;
183        self.v1.fmt(f, "ε1")?;
184        self.v2.fmt(f, "ε1²")
185    }
186}
187
188impl_second_derivatives!(Dual2Vec, [v1, v2], [D], [U1, D], [D, D]);
189impl_dual!(Dual2Vec, [v1, v2], [D], [U1, D], [D, D]);
190
191/**
192 * The SimdValue trait is for rearranging data into a form more suitable for Simd,
193 * and rearranging it back into a usable form. It is not documented particularly well.
194 *
195 * The primary job of this SimdValue impl is to allow people to use `simba::simd::f32x4` etc,
196 * instead of f32/f64. Those types implement nalgebra::SimdRealField/ComplexField, so they
197 * behave like scalars. When we use them, we would have `DualVec<f32x4, f32, N>` etc, with our
198 * F parameter set to `<T as SimdValue>::Element`. We will need to be able to split up that type
199 * into four of DualVec in order to get out of simd-land. That's what the SimdValue trait is for.
200 *
201 * Ultimately, someone will have to to implement SimdRealField on DualVec and call the
202 * simd_ functions of `<T as SimdRealField>`. That's future work for someone who finds
203 * num_dual is not fast enough.
204 *
205 * Unfortunately, doing anything with SIMD is blocked on
206 * <https://github.com/dimforge/simba/issues/44>.
207 *
208 */
209impl<T, D: Dim> nalgebra::SimdValue for Dual2Vec<T, T::Element, D>
210where
211    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
212    T: DualNum<T::Element> + SimdValue + Scalar,
213    T::Element: DualNum<T::Element> + Scalar,
214{
215    // Say T = simba::f32x4. T::Element is f32. T::SimdBool is AutoSimd<[bool; 4]>.
216    // AutoSimd<[f32; 4]> stores an actual [f32; 4], i.e. four floats in one slot.
217    // So our DualVec<AutoSimd<[f32; 4], f32, N> has 4 * (1+N) floats in it, stored in blocks of
218    // four. When we want to do any math on it but ignore its f32x4 storage mode, we need to break
219    // that type into FOUR of DualVec<f32, f32, N>; then we do math on it, then we bring it back
220    // together.
221    //
222    // Hence this definition of Element:
223    type Element = Dual2Vec<T::Element, T::Element, D>;
224    type SimdBool = T::SimdBool;
225
226    const LANES: usize = T::LANES;
227
228    #[inline]
229    fn splat(val: Self::Element) -> Self {
230        // Need to make `lanes` copies of each of:
231        // - the real part
232        // - each of the N epsilon parts
233        let re = T::splat(val.re);
234        let v1 = Derivative::splat(val.v1);
235        let v2 = Derivative::splat(val.v2);
236        Self::new(re, v1, v2)
237    }
238
239    #[inline]
240    fn extract(&self, i: usize) -> Self::Element {
241        let re = self.re.extract(i);
242        let v1 = self.v1.extract(i);
243        let v2 = self.v2.extract(i);
244        Self::Element {
245            re,
246            v1,
247            v2,
248            f: PhantomData,
249        }
250    }
251
252    #[inline]
253    unsafe fn extract_unchecked(&self, i: usize) -> Self::Element {
254        let re = unsafe { self.re.extract_unchecked(i) };
255        let v1 = unsafe { self.v1.extract_unchecked(i) };
256        let v2 = unsafe { self.v2.extract_unchecked(i) };
257        Self::Element {
258            re,
259            v1,
260            v2,
261            f: PhantomData,
262        }
263    }
264
265    #[inline]
266    fn replace(&mut self, i: usize, val: Self::Element) {
267        self.re.replace(i, val.re);
268        self.v1.replace(i, val.v1);
269        self.v2.replace(i, val.v2);
270    }
271
272    #[inline]
273    unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) {
274        unsafe { self.re.replace_unchecked(i, val.re) };
275        unsafe { self.v1.replace_unchecked(i, val.v1) };
276        unsafe { self.v2.replace_unchecked(i, val.v2) };
277    }
278
279    #[inline]
280    fn select(self, cond: Self::SimdBool, other: Self) -> Self {
281        let re = self.re.select(cond, other.re);
282        let v1 = self.v1.select(cond, other.v1);
283        let v2 = self.v2.select(cond, other.v2);
284        Self::new(re, v1, v2)
285    }
286}
287
288/// Comparisons are only made based on the real part. This allows the code to follow the
289/// same execution path as real-valued code would.
290impl<T: DualNum<F> + PartialEq, F: Float, D: Dim> PartialEq for Dual2Vec<T, F, D>
291where
292    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
293{
294    #[inline]
295    fn eq(&self, other: &Self) -> bool {
296        self.re.eq(&other.re)
297    }
298}
299/// Like PartialEq, comparisons are only made based on the real part. This allows the code to follow the
300/// same execution path as real-valued code would.
301impl<T: DualNum<F> + PartialOrd, F: Float, D: Dim> PartialOrd for Dual2Vec<T, F, D>
302where
303    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
304{
305    #[inline]
306    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
307        self.re.partial_cmp(&other.re)
308    }
309}
310/// Like PartialEq, comparisons are only made based on the real part. This allows the code to follow the
311/// same execution path as real-valued code would.
312impl<T: DualNum<F> + approx::AbsDiffEq<Epsilon = T>, F: Float, D: Dim> approx::AbsDiffEq
313    for Dual2Vec<T, F, D>
314where
315    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
316{
317    type Epsilon = Self;
318    #[inline]
319    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
320        self.re.abs_diff_eq(&other.re, epsilon.re)
321    }
322
323    #[inline]
324    fn default_epsilon() -> Self::Epsilon {
325        Self::from_re(T::default_epsilon())
326    }
327}
328/// Like PartialEq, comparisons are only made based on the real part. This allows the code to follow the
329/// same execution path as real-valued code would.
330impl<T: DualNum<F> + approx::RelativeEq<Epsilon = T>, F: Float, D: Dim> approx::RelativeEq
331    for Dual2Vec<T, F, D>
332where
333    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
334{
335    #[inline]
336    fn default_max_relative() -> Self::Epsilon {
337        Self::from_re(T::default_max_relative())
338    }
339
340    #[inline]
341    fn relative_eq(
342        &self,
343        other: &Self,
344        epsilon: Self::Epsilon,
345        max_relative: Self::Epsilon,
346    ) -> bool {
347        self.re.relative_eq(&other.re, epsilon.re, max_relative.re)
348    }
349}
350impl<T: DualNum<F> + UlpsEq<Epsilon = T>, F: Float, D: Dim> UlpsEq for Dual2Vec<T, F, D>
351where
352    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
353{
354    #[inline]
355    fn default_max_ulps() -> u32 {
356        T::default_max_ulps()
357    }
358
359    #[inline]
360    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
361        T::ulps_eq(&self.re, &other.re, epsilon.re, max_ulps)
362    }
363}
364
365impl<T, D: Dim> nalgebra::Field for Dual2Vec<T, T::Element, D>
366where
367    T: DualNum<T::Element> + SimdValue,
368    T::Element: DualNum<T::Element> + Scalar + Float,
369    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
370{
371}
372
373use simba::scalar::{SubsetOf, SupersetOf};
374
375impl<TSuper, FSuper, T, F, D: Dim> SubsetOf<Dual2Vec<TSuper, FSuper, D>> for Dual2Vec<T, F, D>
376where
377    TSuper: DualNum<FSuper> + SupersetOf<T>,
378    T: DualNum<F>,
379    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
380{
381    #[inline(always)]
382    fn to_superset(&self) -> Dual2Vec<TSuper, FSuper, D> {
383        let re = TSuper::from_subset(&self.re);
384        let v1 = Derivative::from_subset(&self.v1);
385        let v2 = Derivative::from_subset(&self.v2);
386        Dual2Vec {
387            re,
388            v1,
389            v2,
390            f: PhantomData,
391        }
392    }
393    #[inline(always)]
394    fn from_superset(element: &Dual2Vec<TSuper, FSuper, D>) -> Option<Self> {
395        let re = TSuper::to_subset(&element.re)?;
396        let v1 = Derivative::to_subset(&element.v1)?;
397        let v2 = Derivative::to_subset(&element.v2)?;
398        Some(Self::new(re, v1, v2))
399    }
400    #[inline(always)]
401    fn from_superset_unchecked(element: &Dual2Vec<TSuper, FSuper, D>) -> Self {
402        let re = TSuper::to_subset_unchecked(&element.re);
403        let v1 = Derivative::to_subset_unchecked(&element.v1);
404        let v2 = Derivative::to_subset_unchecked(&element.v2);
405        Self::new(re, v1, v2)
406    }
407    #[inline(always)]
408    fn is_in_subset(element: &Dual2Vec<TSuper, FSuper, D>) -> bool {
409        TSuper::is_in_subset(&element.re)
410            && <Derivative<_, _, _, _> as SupersetOf<Derivative<_, _, _, _>>>::is_in_subset(
411                &element.v1,
412            )
413            && <Derivative<_, _, _, _> as SupersetOf<Derivative<_, _, _, _>>>::is_in_subset(
414                &element.v2,
415            )
416    }
417}
418
419impl<TSuper, FSuper, D: Dim> SupersetOf<f32> for Dual2Vec<TSuper, FSuper, D>
420where
421    TSuper: DualNum<FSuper> + SupersetOf<f32>,
422    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
423{
424    #[inline(always)]
425    fn is_in_subset(&self) -> bool {
426        self.re.is_in_subset()
427    }
428
429    #[inline(always)]
430    fn to_subset_unchecked(&self) -> f32 {
431        self.re.to_subset_unchecked()
432    }
433
434    #[inline(always)]
435    fn from_subset(element: &f32) -> Self {
436        // Interpret as a purely real number
437        let re = TSuper::from_subset(element);
438        let v1 = Derivative::none();
439        let v2 = Derivative::none();
440        Self::new(re, v1, v2)
441    }
442}
443
444impl<TSuper, FSuper, D: Dim> SupersetOf<f64> for Dual2Vec<TSuper, FSuper, D>
445where
446    TSuper: DualNum<FSuper> + SupersetOf<f64>,
447    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
448{
449    #[inline(always)]
450    fn is_in_subset(&self) -> bool {
451        self.re.is_in_subset()
452    }
453
454    #[inline(always)]
455    fn to_subset_unchecked(&self) -> f64 {
456        self.re.to_subset_unchecked()
457    }
458
459    #[inline(always)]
460    fn from_subset(element: &f64) -> Self {
461        // Interpret as a purely real number
462        let re = TSuper::from_subset(element);
463        let v1 = Derivative::none();
464        let v2 = Derivative::none();
465        Self::new(re, v1, v2)
466    }
467}
468
469// We can't do a simd implementation until simba lets us implement SimdPartialOrd
470// using _T_'s SimdBool. The blanket impl gets in the way. So we must constrain
471// T to SimdValue<Element = T, SimdBool = bool>, which is basically the same as
472// saying f32 or f64 only.
473//
474// Limitation of simba. See https://github.com/dimforge/simba/issues/44
475
476use nalgebra::{ComplexField, RealField};
477// This impl is modelled on `impl ComplexField for f32`. The imaginary part is nothing.
478impl<T, D: Dim> ComplexField for Dual2Vec<T, T::Element, D>
479where
480    T: DualNum<T::Element> + SupersetOf<T> + AbsDiffEq<Epsilon = T> + Sync + Send,
481    T::Element: DualNum<T::Element> + Scalar + DualNumFloat + Sync + Send,
482    T: SupersetOf<T::Element>,
483    T: SupersetOf<f32>,
484    T: SupersetOf<f64>,
485    T: SimdPartialOrd + PartialOrd,
486    T: SimdValue<Element = T, SimdBool = bool>,
487    T: RelativeEq + UlpsEq + AbsDiffEq,
488    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
489    <DefaultAllocator as Allocator<D>>::Buffer<T>: Sync + Send,
490    <DefaultAllocator as Allocator<U1, D>>::Buffer<T>: Sync + Send,
491    <DefaultAllocator as Allocator<D, U1>>::Buffer<T>: Sync + Send,
492    <DefaultAllocator as Allocator<D, D>>::Buffer<T>: Sync + Send,
493{
494    type RealField = Self;
495
496    #[inline]
497    fn from_real(re: Self::RealField) -> Self {
498        re
499    }
500
501    #[inline]
502    fn real(self) -> Self::RealField {
503        self
504    }
505
506    #[inline]
507    fn imaginary(self) -> Self::RealField {
508        Self::zero()
509    }
510
511    #[inline]
512    fn modulus(self) -> Self::RealField {
513        self.abs()
514    }
515
516    #[inline]
517    fn modulus_squared(self) -> Self::RealField {
518        &self * &self
519    }
520
521    #[inline]
522    fn argument(self) -> Self::RealField {
523        Self::zero()
524    }
525
526    #[inline]
527    fn norm1(self) -> Self::RealField {
528        self.abs()
529    }
530
531    #[inline]
532    fn scale(self, factor: Self::RealField) -> Self {
533        self * factor
534    }
535
536    #[inline]
537    fn unscale(self, factor: Self::RealField) -> Self {
538        self / factor
539    }
540
541    #[inline]
542    fn floor(self) -> Self {
543        panic!("called floor() on a dual number")
544    }
545
546    #[inline]
547    fn ceil(self) -> Self {
548        panic!("called ceil() on a dual number")
549    }
550
551    #[inline]
552    fn round(self) -> Self {
553        panic!("called round() on a dual number")
554    }
555
556    #[inline]
557    fn trunc(self) -> Self {
558        panic!("called trunc() on a dual number")
559    }
560
561    #[inline]
562    fn fract(self) -> Self {
563        panic!("called fract() on a dual number")
564    }
565
566    #[inline]
567    fn mul_add(self, a: Self, b: Self) -> Self {
568        DualNum::mul_add(&self, a, b)
569    }
570
571    #[inline]
572    fn abs(self) -> Self::RealField {
573        Signed::abs(&self)
574    }
575
576    #[inline]
577    fn hypot(self, other: Self) -> Self::RealField {
578        let sum_sq = self.powi(2) + other.powi(2);
579        DualNum::sqrt(&sum_sq)
580    }
581
582    #[inline]
583    fn recip(self) -> Self {
584        DualNum::recip(&self)
585    }
586
587    #[inline]
588    fn conjugate(self) -> Self {
589        self
590    }
591
592    #[inline]
593    fn sin(self) -> Self {
594        DualNum::sin(&self)
595    }
596
597    #[inline]
598    fn cos(self) -> Self {
599        DualNum::cos(&self)
600    }
601
602    #[inline]
603    fn sin_cos(self) -> (Self, Self) {
604        DualNum::sin_cos(&self)
605    }
606
607    #[inline]
608    fn tan(self) -> Self {
609        DualNum::tan(&self)
610    }
611
612    #[inline]
613    fn asin(self) -> Self {
614        DualNum::asin(&self)
615    }
616
617    #[inline]
618    fn acos(self) -> Self {
619        DualNum::acos(&self)
620    }
621
622    #[inline]
623    fn atan(self) -> Self {
624        DualNum::atan(&self)
625    }
626
627    #[inline]
628    fn sinh(self) -> Self {
629        DualNum::sinh(&self)
630    }
631
632    #[inline]
633    fn cosh(self) -> Self {
634        DualNum::cosh(&self)
635    }
636
637    #[inline]
638    fn tanh(self) -> Self {
639        DualNum::tanh(&self)
640    }
641
642    #[inline]
643    fn asinh(self) -> Self {
644        DualNum::asinh(&self)
645    }
646
647    #[inline]
648    fn acosh(self) -> Self {
649        DualNum::acosh(&self)
650    }
651
652    #[inline]
653    fn atanh(self) -> Self {
654        DualNum::atanh(&self)
655    }
656
657    #[inline]
658    fn log(self, base: Self::RealField) -> Self {
659        DualNum::ln(&self) / DualNum::ln(&base)
660    }
661
662    #[inline]
663    fn log2(self) -> Self {
664        DualNum::log2(&self)
665    }
666
667    #[inline]
668    fn log10(self) -> Self {
669        DualNum::log10(&self)
670    }
671
672    #[inline]
673    fn ln(self) -> Self {
674        DualNum::ln(&self)
675    }
676
677    #[inline]
678    fn ln_1p(self) -> Self {
679        DualNum::ln_1p(&self)
680    }
681
682    #[inline]
683    fn sqrt(self) -> Self {
684        DualNum::sqrt(&self)
685    }
686
687    #[inline]
688    fn exp(self) -> Self {
689        DualNum::exp(&self)
690    }
691
692    #[inline]
693    fn exp2(self) -> Self {
694        DualNum::exp2(&self)
695    }
696
697    #[inline]
698    fn exp_m1(self) -> Self {
699        DualNum::exp_m1(&self)
700    }
701
702    #[inline]
703    fn powi(self, n: i32) -> Self {
704        DualNum::powi(&self, n)
705    }
706
707    #[inline]
708    fn powf(self, n: Self::RealField) -> Self {
709        // n could be a dual.
710        DualNum::powd(&self, n)
711    }
712
713    #[inline]
714    fn powc(self, n: Self) -> Self {
715        // same as powf, Self isn't complex
716        self.powf(n)
717    }
718
719    #[inline]
720    fn cbrt(self) -> Self {
721        DualNum::cbrt(&self)
722    }
723
724    #[inline]
725    fn is_finite(&self) -> bool {
726        self.re.is_finite()
727    }
728
729    #[inline]
730    fn try_sqrt(self) -> Option<Self> {
731        if self > Self::zero() {
732            Some(DualNum::sqrt(&self))
733        } else {
734            None
735        }
736    }
737}
738
739impl<T, D: Dim> RealField for Dual2Vec<T, T::Element, D>
740where
741    T: DualNum<T::Element> + SupersetOf<T> + Sync + Send,
742    T::Element: DualNum<T::Element> + Scalar + DualNumFloat,
743    T: SupersetOf<T::Element>,
744    T: SupersetOf<f32>,
745    T: SupersetOf<f64>,
746    T: SimdPartialOrd + PartialOrd,
747    T: RelativeEq + AbsDiffEq<Epsilon = T>,
748    T: SimdValue<Element = T, SimdBool = bool>,
749    T: UlpsEq,
750    T: AbsDiffEq,
751    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
752    <DefaultAllocator as Allocator<D>>::Buffer<T>: Sync + Send,
753    <DefaultAllocator as Allocator<U1, D>>::Buffer<T>: Sync + Send,
754    <DefaultAllocator as Allocator<D, U1>>::Buffer<T>: Sync + Send,
755    <DefaultAllocator as Allocator<D, D>>::Buffer<T>: Sync + Send,
756{
757    #[inline]
758    fn copysign(self, sign: Self) -> Self {
759        if sign.re.is_sign_positive() {
760            self.simd_abs()
761        } else {
762            -self.simd_abs()
763        }
764    }
765
766    #[inline]
767    fn atan2(self, other: Self) -> Self {
768        DualNum::atan2(&self, other)
769    }
770
771    #[inline]
772    fn pi() -> Self {
773        Self::from_re(<T as FloatConst>::PI())
774    }
775
776    #[inline]
777    fn two_pi() -> Self {
778        Self::from_re(<T as FloatConst>::TAU())
779    }
780
781    #[inline]
782    fn frac_pi_2() -> Self {
783        Self::from_re(<T as FloatConst>::FRAC_PI_4())
784    }
785
786    #[inline]
787    fn frac_pi_3() -> Self {
788        Self::from_re(<T as FloatConst>::FRAC_PI_3())
789    }
790
791    #[inline]
792    fn frac_pi_4() -> Self {
793        Self::from_re(<T as FloatConst>::FRAC_PI_4())
794    }
795
796    #[inline]
797    fn frac_pi_6() -> Self {
798        Self::from_re(<T as FloatConst>::FRAC_PI_6())
799    }
800
801    #[inline]
802    fn frac_pi_8() -> Self {
803        Self::from_re(<T as FloatConst>::FRAC_PI_8())
804    }
805
806    #[inline]
807    fn frac_1_pi() -> Self {
808        Self::from_re(<T as FloatConst>::FRAC_1_PI())
809    }
810
811    #[inline]
812    fn frac_2_pi() -> Self {
813        Self::from_re(<T as FloatConst>::FRAC_2_PI())
814    }
815
816    #[inline]
817    fn frac_2_sqrt_pi() -> Self {
818        Self::from_re(<T as FloatConst>::FRAC_2_SQRT_PI())
819    }
820
821    #[inline]
822    fn e() -> Self {
823        Self::from_re(<T as FloatConst>::E())
824    }
825
826    #[inline]
827    fn log2_e() -> Self {
828        Self::from_re(<T as FloatConst>::LOG2_E())
829    }
830
831    #[inline]
832    fn log10_e() -> Self {
833        Self::from_re(<T as FloatConst>::LOG10_E())
834    }
835
836    #[inline]
837    fn ln_2() -> Self {
838        Self::from_re(<T as FloatConst>::LN_2())
839    }
840
841    #[inline]
842    fn ln_10() -> Self {
843        Self::from_re(<T as FloatConst>::LN_10())
844    }
845
846    #[inline]
847    fn is_sign_positive(&self) -> bool {
848        self.re.is_sign_positive()
849    }
850
851    #[inline]
852    fn is_sign_negative(&self) -> bool {
853        self.re.is_sign_negative()
854    }
855
856    /// Got to be careful using this, because it throws away the derivatives of the one not chosen
857    #[inline]
858    fn max(self, other: Self) -> Self {
859        if other > self { other } else { self }
860    }
861
862    /// Got to be careful using this, because it throws away the derivatives of the one not chosen
863    #[inline]
864    fn min(self, other: Self) -> Self {
865        if other < self { other } else { self }
866    }
867
868    /// If the min/max values are constants and the clamping has an effect, you lose your gradients.
869    #[inline]
870    fn clamp(self, min: Self, max: Self) -> Self {
871        if self < min {
872            min
873        } else if self > max {
874            max
875        } else {
876            self
877        }
878    }
879
880    #[inline]
881    fn min_value() -> Option<Self> {
882        Some(Self::from_re(T::min_value()))
883    }
884
885    #[inline]
886    fn max_value() -> Option<Self> {
887        Some(Self::from_re(T::max_value()))
888    }
889}