Skip to main content

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/// Like PartialEq, 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> + approx::AbsDiffEq<Epsilon = T>, F: Float, D: Dim> approx::AbsDiffEq
291    for Dual2Vec<T, F, D>
292where
293    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
294{
295    type Epsilon = Self;
296    #[inline]
297    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
298        self.re.abs_diff_eq(&other.re, epsilon.re)
299    }
300
301    #[inline]
302    fn default_epsilon() -> Self::Epsilon {
303        Self::from_re(T::default_epsilon())
304    }
305}
306/// Like PartialEq, comparisons are only made based on the real part. This allows the code to follow the
307/// same execution path as real-valued code would.
308impl<T: DualNum<F> + approx::RelativeEq<Epsilon = T>, F: Float, D: Dim> approx::RelativeEq
309    for Dual2Vec<T, F, D>
310where
311    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
312{
313    #[inline]
314    fn default_max_relative() -> Self::Epsilon {
315        Self::from_re(T::default_max_relative())
316    }
317
318    #[inline]
319    fn relative_eq(
320        &self,
321        other: &Self,
322        epsilon: Self::Epsilon,
323        max_relative: Self::Epsilon,
324    ) -> bool {
325        self.re.relative_eq(&other.re, epsilon.re, max_relative.re)
326    }
327}
328impl<T: DualNum<F> + UlpsEq<Epsilon = T>, F: Float, D: Dim> UlpsEq for Dual2Vec<T, F, D>
329where
330    DefaultAllocator: Allocator<U1, D> + Allocator<D, D>,
331{
332    #[inline]
333    fn default_max_ulps() -> u32 {
334        T::default_max_ulps()
335    }
336
337    #[inline]
338    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
339        T::ulps_eq(&self.re, &other.re, epsilon.re, max_ulps)
340    }
341}
342
343impl<T, D: Dim> nalgebra::Field for Dual2Vec<T, T::Element, D>
344where
345    T: DualNum<T::Element> + SimdValue,
346    T::Element: DualNum<T::Element> + Scalar + Float,
347    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
348{
349}
350
351use simba::scalar::{SubsetOf, SupersetOf};
352
353impl<TSuper, FSuper, T, F, D: Dim> SubsetOf<Dual2Vec<TSuper, FSuper, D>> for Dual2Vec<T, F, D>
354where
355    TSuper: DualNum<FSuper> + SupersetOf<T>,
356    T: DualNum<F>,
357    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
358{
359    #[inline(always)]
360    fn to_superset(&self) -> Dual2Vec<TSuper, FSuper, D> {
361        let re = TSuper::from_subset(&self.re);
362        let v1 = Derivative::from_subset(&self.v1);
363        let v2 = Derivative::from_subset(&self.v2);
364        Dual2Vec {
365            re,
366            v1,
367            v2,
368            f: PhantomData,
369        }
370    }
371    #[inline(always)]
372    fn from_superset(element: &Dual2Vec<TSuper, FSuper, D>) -> Option<Self> {
373        let re = TSuper::to_subset(&element.re)?;
374        let v1 = Derivative::to_subset(&element.v1)?;
375        let v2 = Derivative::to_subset(&element.v2)?;
376        Some(Self::new(re, v1, v2))
377    }
378    #[inline(always)]
379    fn from_superset_unchecked(element: &Dual2Vec<TSuper, FSuper, D>) -> Self {
380        let re = TSuper::to_subset_unchecked(&element.re);
381        let v1 = Derivative::to_subset_unchecked(&element.v1);
382        let v2 = Derivative::to_subset_unchecked(&element.v2);
383        Self::new(re, v1, v2)
384    }
385    #[inline(always)]
386    fn is_in_subset(element: &Dual2Vec<TSuper, FSuper, D>) -> bool {
387        TSuper::is_in_subset(&element.re)
388            && <Derivative<_, _, _, _> as SupersetOf<Derivative<_, _, _, _>>>::is_in_subset(
389                &element.v1,
390            )
391            && <Derivative<_, _, _, _> as SupersetOf<Derivative<_, _, _, _>>>::is_in_subset(
392                &element.v2,
393            )
394    }
395}
396
397impl<TSuper, FSuper, D: Dim> SupersetOf<f32> for Dual2Vec<TSuper, FSuper, D>
398where
399    TSuper: DualNum<FSuper> + SupersetOf<f32>,
400    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
401{
402    #[inline(always)]
403    fn is_in_subset(&self) -> bool {
404        self.re.is_in_subset()
405    }
406
407    #[inline(always)]
408    fn to_subset_unchecked(&self) -> f32 {
409        self.re.to_subset_unchecked()
410    }
411
412    #[inline(always)]
413    fn from_subset(element: &f32) -> Self {
414        // Interpret as a purely real number
415        let re = TSuper::from_subset(element);
416        let v1 = Derivative::none();
417        let v2 = Derivative::none();
418        Self::new(re, v1, v2)
419    }
420}
421
422impl<TSuper, FSuper, D: Dim> SupersetOf<f64> for Dual2Vec<TSuper, FSuper, D>
423where
424    TSuper: DualNum<FSuper> + SupersetOf<f64>,
425    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
426{
427    #[inline(always)]
428    fn is_in_subset(&self) -> bool {
429        self.re.is_in_subset()
430    }
431
432    #[inline(always)]
433    fn to_subset_unchecked(&self) -> f64 {
434        self.re.to_subset_unchecked()
435    }
436
437    #[inline(always)]
438    fn from_subset(element: &f64) -> Self {
439        // Interpret as a purely real number
440        let re = TSuper::from_subset(element);
441        let v1 = Derivative::none();
442        let v2 = Derivative::none();
443        Self::new(re, v1, v2)
444    }
445}
446
447// We can't do a simd implementation until simba lets us implement SimdPartialOrd
448// using _T_'s SimdBool. The blanket impl gets in the way. So we must constrain
449// T to SimdValue<Element = T, SimdBool = bool>, which is basically the same as
450// saying f32 or f64 only.
451//
452// Limitation of simba. See https://github.com/dimforge/simba/issues/44
453
454use nalgebra::{ComplexField, RealField};
455// This impl is modelled on `impl ComplexField for f32`. The imaginary part is nothing.
456impl<T, D: Dim> ComplexField for Dual2Vec<T, T::Element, D>
457where
458    T: DualNum<T::Element> + SupersetOf<T> + AbsDiffEq<Epsilon = T> + Sync + Send,
459    T::Element: DualNum<T::Element> + Scalar + DualNumFloat + Sync + Send,
460    T: SupersetOf<T::Element>,
461    T: SupersetOf<f32>,
462    T: SupersetOf<f64>,
463    T: SimdPartialOrd + PartialOrd,
464    T: SimdValue<Element = T, SimdBool = bool>,
465    T: RelativeEq + UlpsEq + AbsDiffEq,
466    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
467    <DefaultAllocator as Allocator<D>>::Buffer<T>: Sync + Send,
468    <DefaultAllocator as Allocator<U1, D>>::Buffer<T>: Sync + Send,
469    <DefaultAllocator as Allocator<D, U1>>::Buffer<T>: Sync + Send,
470    <DefaultAllocator as Allocator<D, D>>::Buffer<T>: Sync + Send,
471{
472    type RealField = Self;
473
474    #[inline]
475    fn from_real(re: Self::RealField) -> Self {
476        re
477    }
478
479    #[inline]
480    fn real(self) -> Self::RealField {
481        self
482    }
483
484    #[inline]
485    fn imaginary(self) -> Self::RealField {
486        Self::zero()
487    }
488
489    #[inline]
490    fn modulus(self) -> Self::RealField {
491        self.abs()
492    }
493
494    #[inline]
495    fn modulus_squared(self) -> Self::RealField {
496        &self * &self
497    }
498
499    #[inline]
500    fn argument(self) -> Self::RealField {
501        Self::zero()
502    }
503
504    #[inline]
505    fn norm1(self) -> Self::RealField {
506        self.abs()
507    }
508
509    #[inline]
510    fn scale(self, factor: Self::RealField) -> Self {
511        self * factor
512    }
513
514    #[inline]
515    fn unscale(self, factor: Self::RealField) -> Self {
516        self / factor
517    }
518
519    #[inline]
520    fn floor(self) -> Self {
521        panic!("called floor() on a dual number")
522    }
523
524    #[inline]
525    fn ceil(self) -> Self {
526        panic!("called ceil() on a dual number")
527    }
528
529    #[inline]
530    fn round(self) -> Self {
531        panic!("called round() on a dual number")
532    }
533
534    #[inline]
535    fn trunc(self) -> Self {
536        panic!("called trunc() on a dual number")
537    }
538
539    #[inline]
540    fn fract(self) -> Self {
541        panic!("called fract() on a dual number")
542    }
543
544    #[inline]
545    fn mul_add(self, a: Self, b: Self) -> Self {
546        DualNum::mul_add(&self, a, b)
547    }
548
549    #[inline]
550    fn abs(self) -> Self::RealField {
551        Signed::abs(&self)
552    }
553
554    #[inline]
555    fn hypot(self, other: Self) -> Self::RealField {
556        let sum_sq = self.powi(2) + other.powi(2);
557        DualNum::sqrt(&sum_sq)
558    }
559
560    #[inline]
561    fn recip(self) -> Self {
562        DualNum::recip(&self)
563    }
564
565    #[inline]
566    fn conjugate(self) -> Self {
567        self
568    }
569
570    #[inline]
571    fn sin(self) -> Self {
572        DualNum::sin(&self)
573    }
574
575    #[inline]
576    fn cos(self) -> Self {
577        DualNum::cos(&self)
578    }
579
580    #[inline]
581    fn sin_cos(self) -> (Self, Self) {
582        DualNum::sin_cos(&self)
583    }
584
585    #[inline]
586    fn tan(self) -> Self {
587        DualNum::tan(&self)
588    }
589
590    #[inline]
591    fn asin(self) -> Self {
592        DualNum::asin(&self)
593    }
594
595    #[inline]
596    fn acos(self) -> Self {
597        DualNum::acos(&self)
598    }
599
600    #[inline]
601    fn atan(self) -> Self {
602        DualNum::atan(&self)
603    }
604
605    #[inline]
606    fn sinh(self) -> Self {
607        DualNum::sinh(&self)
608    }
609
610    #[inline]
611    fn cosh(self) -> Self {
612        DualNum::cosh(&self)
613    }
614
615    #[inline]
616    fn tanh(self) -> Self {
617        DualNum::tanh(&self)
618    }
619
620    #[inline]
621    fn asinh(self) -> Self {
622        DualNum::asinh(&self)
623    }
624
625    #[inline]
626    fn acosh(self) -> Self {
627        DualNum::acosh(&self)
628    }
629
630    #[inline]
631    fn atanh(self) -> Self {
632        DualNum::atanh(&self)
633    }
634
635    #[inline]
636    fn log(self, base: Self::RealField) -> Self {
637        DualNum::ln(&self) / DualNum::ln(&base)
638    }
639
640    #[inline]
641    fn log2(self) -> Self {
642        DualNum::log2(&self)
643    }
644
645    #[inline]
646    fn log10(self) -> Self {
647        DualNum::log10(&self)
648    }
649
650    #[inline]
651    fn ln(self) -> Self {
652        DualNum::ln(&self)
653    }
654
655    #[inline]
656    fn ln_1p(self) -> Self {
657        DualNum::ln_1p(&self)
658    }
659
660    #[inline]
661    fn sqrt(self) -> Self {
662        DualNum::sqrt(&self)
663    }
664
665    #[inline]
666    fn exp(self) -> Self {
667        DualNum::exp(&self)
668    }
669
670    #[inline]
671    fn exp2(self) -> Self {
672        DualNum::exp2(&self)
673    }
674
675    #[inline]
676    fn exp_m1(self) -> Self {
677        DualNum::exp_m1(&self)
678    }
679
680    #[inline]
681    fn powi(self, n: i32) -> Self {
682        DualNum::powi(&self, n)
683    }
684
685    #[inline]
686    fn powf(self, n: Self::RealField) -> Self {
687        // n could be a dual.
688        DualNum::powd(&self, n)
689    }
690
691    #[inline]
692    fn powc(self, n: Self) -> Self {
693        // same as powf, Self isn't complex
694        self.powf(n)
695    }
696
697    #[inline]
698    fn cbrt(self) -> Self {
699        DualNum::cbrt(&self)
700    }
701
702    #[inline]
703    fn is_finite(&self) -> bool {
704        self.re.is_finite()
705    }
706
707    #[inline]
708    fn try_sqrt(self) -> Option<Self> {
709        if self > Self::zero() {
710            Some(DualNum::sqrt(&self))
711        } else {
712            None
713        }
714    }
715}
716
717impl<T, D: Dim> RealField for Dual2Vec<T, T::Element, D>
718where
719    T: DualNum<T::Element> + SupersetOf<T> + Sync + Send,
720    T::Element: DualNum<T::Element> + Scalar + DualNumFloat,
721    T: SupersetOf<T::Element>,
722    T: SupersetOf<f32>,
723    T: SupersetOf<f64>,
724    T: SimdPartialOrd + PartialOrd,
725    T: RelativeEq + AbsDiffEq<Epsilon = T>,
726    T: SimdValue<Element = T, SimdBool = bool>,
727    T: UlpsEq,
728    T: AbsDiffEq,
729    DefaultAllocator: Allocator<U1, D> + Allocator<D, U1> + Allocator<D, D>,
730    <DefaultAllocator as Allocator<D>>::Buffer<T>: Sync + Send,
731    <DefaultAllocator as Allocator<U1, D>>::Buffer<T>: Sync + Send,
732    <DefaultAllocator as Allocator<D, U1>>::Buffer<T>: Sync + Send,
733    <DefaultAllocator as Allocator<D, D>>::Buffer<T>: Sync + Send,
734{
735    #[inline]
736    fn copysign(self, sign: Self) -> Self {
737        if sign.re.is_sign_positive() {
738            self.simd_abs()
739        } else {
740            -self.simd_abs()
741        }
742    }
743
744    #[inline]
745    fn atan2(self, other: Self) -> Self {
746        DualNum::atan2(&self, other)
747    }
748
749    #[inline]
750    fn pi() -> Self {
751        Self::from_re(<T as FloatConst>::PI())
752    }
753
754    #[inline]
755    fn two_pi() -> Self {
756        Self::from_re(<T as FloatConst>::TAU())
757    }
758
759    #[inline]
760    fn frac_pi_2() -> Self {
761        Self::from_re(<T as FloatConst>::FRAC_PI_4())
762    }
763
764    #[inline]
765    fn frac_pi_3() -> Self {
766        Self::from_re(<T as FloatConst>::FRAC_PI_3())
767    }
768
769    #[inline]
770    fn frac_pi_4() -> Self {
771        Self::from_re(<T as FloatConst>::FRAC_PI_4())
772    }
773
774    #[inline]
775    fn frac_pi_6() -> Self {
776        Self::from_re(<T as FloatConst>::FRAC_PI_6())
777    }
778
779    #[inline]
780    fn frac_pi_8() -> Self {
781        Self::from_re(<T as FloatConst>::FRAC_PI_8())
782    }
783
784    #[inline]
785    fn frac_1_pi() -> Self {
786        Self::from_re(<T as FloatConst>::FRAC_1_PI())
787    }
788
789    #[inline]
790    fn frac_2_pi() -> Self {
791        Self::from_re(<T as FloatConst>::FRAC_2_PI())
792    }
793
794    #[inline]
795    fn frac_2_sqrt_pi() -> Self {
796        Self::from_re(<T as FloatConst>::FRAC_2_SQRT_PI())
797    }
798
799    #[inline]
800    fn e() -> Self {
801        Self::from_re(<T as FloatConst>::E())
802    }
803
804    #[inline]
805    fn log2_e() -> Self {
806        Self::from_re(<T as FloatConst>::LOG2_E())
807    }
808
809    #[inline]
810    fn log10_e() -> Self {
811        Self::from_re(<T as FloatConst>::LOG10_E())
812    }
813
814    #[inline]
815    fn ln_2() -> Self {
816        Self::from_re(<T as FloatConst>::LN_2())
817    }
818
819    #[inline]
820    fn ln_10() -> Self {
821        Self::from_re(<T as FloatConst>::LN_10())
822    }
823
824    #[inline]
825    fn is_sign_positive(&self) -> bool {
826        self.re.is_sign_positive()
827    }
828
829    #[inline]
830    fn is_sign_negative(&self) -> bool {
831        self.re.is_sign_negative()
832    }
833
834    /// Got to be careful using this, because it throws away the derivatives of the one not chosen
835    #[inline]
836    fn max(self, other: Self) -> Self {
837        if other > self { other } else { self }
838    }
839
840    /// Got to be careful using this, because it throws away the derivatives of the one not chosen
841    #[inline]
842    fn min(self, other: Self) -> Self {
843        if other < self { other } else { self }
844    }
845
846    /// If the min/max values are constants and the clamping has an effect, you lose your gradients.
847    #[inline]
848    fn clamp(self, min: Self, max: Self) -> Self {
849        if self < min {
850            min
851        } else if self > max {
852            max
853        } else {
854            self
855        }
856    }
857
858    #[inline]
859    fn min_value() -> Option<Self> {
860        Some(Self::from_re(T::min_value()))
861    }
862
863    #[inline]
864    fn max_value() -> Option<Self> {
865        Some(Self::from_re(T::max_value()))
866    }
867}