1use crate::{DualNum, DualNumFloat, DualStruct};
2use num_traits::{Float, FloatConst, FromPrimitive, Inv, Num, One, Signed, Zero};
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::iter::{Product, Sum};
7use std::marker::PhantomData;
8use std::ops::*;
9
10#[derive(PartialEq, Eq, Copy, Clone, Debug)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct HyperHyperDual<T, F = T> {
14 pub re: T,
16 pub eps1: T,
18 pub eps2: T,
20 pub eps3: T,
22 pub eps1eps2: T,
24 pub eps1eps3: T,
26 pub eps2eps3: T,
28 pub eps1eps2eps3: T,
30 #[cfg_attr(feature = "serde", serde(skip))]
31 f: PhantomData<F>,
32}
33
34#[cfg(feature = "ndarray")]
35impl<T: DualNum<F>, F: DualNumFloat> ndarray::ScalarOperand for HyperHyperDual<T, F> {}
36
37pub type HyperHyperDual32 = HyperHyperDual<f32>;
38pub type HyperHyperDual64 = HyperHyperDual<f64>;
39
40impl<T: DualNum<F>, F> HyperHyperDual<T, F> {
41 #[inline]
43 #[expect(clippy::too_many_arguments)]
44 pub fn new(
45 re: T,
46 eps1: T,
47 eps2: T,
48 eps3: T,
49 eps1eps2: T,
50 eps1eps3: T,
51 eps2eps3: T,
52 eps1eps2eps3: T,
53 ) -> Self {
54 Self {
55 re,
56 eps1,
57 eps2,
58 eps3,
59 eps1eps2,
60 eps1eps3,
61 eps2eps3,
62 eps1eps2eps3,
63 f: PhantomData,
64 }
65 }
66
67 #[inline]
69 pub fn derivative1(mut self) -> Self {
70 self.eps1 = T::one();
71 self
72 }
73
74 #[inline]
76 pub fn derivative2(mut self) -> Self {
77 self.eps2 = T::one();
78 self
79 }
80
81 #[inline]
83 pub fn derivative3(mut self) -> Self {
84 self.eps3 = T::one();
85 self
86 }
87
88 #[inline]
90 pub fn from_re(re: T) -> Self {
91 Self::new(
92 re,
93 T::zero(),
94 T::zero(),
95 T::zero(),
96 T::zero(),
97 T::zero(),
98 T::zero(),
99 T::zero(),
100 )
101 }
102}
103
104impl<T: DualNum<F>, F: Float> HyperHyperDual<T, F> {
105 #[inline]
106 fn chain_rule(&self, f0: T, f1: T, f2: T, f3: T) -> Self {
107 Self::new(
108 f0,
109 f1.clone() * &self.eps1,
110 f1.clone() * &self.eps2,
111 f1.clone() * &self.eps3,
112 f1.clone() * &self.eps1eps2 + f2.clone() * &self.eps1 * &self.eps2,
113 f1.clone() * &self.eps1eps3 + f2.clone() * &self.eps1 * &self.eps3,
114 f1.clone() * &self.eps2eps3 + f2.clone() * &self.eps2 * &self.eps3,
115 f1 * &self.eps1eps2eps3
116 + f2 * (self.eps1.clone() * &self.eps2eps3
117 + self.eps2.clone() * &self.eps1eps3
118 + self.eps3.clone() * &self.eps1eps2)
119 + f3 * self.eps1.clone() * &self.eps2 * &self.eps3,
120 )
121 }
122}
123
124impl<T: DualNum<F>, F: Float> Mul<&HyperHyperDual<T, F>> for &HyperHyperDual<T, F> {
125 type Output = HyperHyperDual<T, F>;
126 #[inline]
127 fn mul(self, rhs: &HyperHyperDual<T, F>) -> HyperHyperDual<T, F> {
128 HyperHyperDual::new(
129 self.re.clone() * &rhs.re,
130 self.eps1.clone() * &rhs.re + self.re.clone() * &rhs.eps1,
131 self.eps2.clone() * &rhs.re + self.re.clone() * &rhs.eps2,
132 self.eps3.clone() * &rhs.re + self.re.clone() * &rhs.eps3,
133 self.eps1eps2.clone() * &rhs.re
134 + self.eps1.clone() * &rhs.eps2
135 + self.eps2.clone() * &rhs.eps1
136 + self.re.clone() * &rhs.eps1eps2,
137 self.eps1eps3.clone() * &rhs.re
138 + self.eps1.clone() * &rhs.eps3
139 + self.eps3.clone() * &rhs.eps1
140 + self.re.clone() * &rhs.eps1eps3,
141 self.eps2eps3.clone() * &rhs.re
142 + self.eps2.clone() * &rhs.eps3
143 + self.eps3.clone() * &rhs.eps2
144 + self.re.clone() * &rhs.eps2eps3,
145 self.eps1eps2eps3.clone() * &rhs.re
146 + self.eps1.clone() * &rhs.eps2eps3
147 + self.eps2.clone() * &rhs.eps1eps3
148 + self.eps3.clone() * &rhs.eps1eps2
149 + self.eps2eps3.clone() * &rhs.eps1
150 + self.eps1eps3.clone() * &rhs.eps2
151 + self.eps1eps2.clone() * &rhs.eps3
152 + self.re.clone() * &rhs.eps1eps2eps3,
153 )
154 }
155}
156
157impl<T: DualNum<F>, F: Float> Div<&HyperHyperDual<T, F>> for &HyperHyperDual<T, F> {
158 type Output = HyperHyperDual<T, F>;
159 #[inline]
160 fn div(self, rhs: &HyperHyperDual<T, F>) -> HyperHyperDual<T, F> {
161 let rec = T::one() / &rhs.re;
162 let f0 = rec.clone();
163 let f1 = -f0.clone() * &rec;
164 let f2 = f1.clone() * &rec * F::from(-2.0).unwrap();
165 let f3 = f2.clone() * rec * F::from(-3.0).unwrap();
166 self * rhs.chain_rule(f0, f1, f2, f3)
167 }
168}
169
170impl<T: fmt::Display, F> fmt::Display for HyperHyperDual<T, F> {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 write!(
174 f,
175 "{} + {}ε1 + {}ε2 + {}ε3 + {}ε1ε2 + {}ε1ε3 + {}ε2ε3 + {}ε1ε2ε3",
176 self.re,
177 self.eps1,
178 self.eps2,
179 self.eps3,
180 self.eps1eps2,
181 self.eps1eps3,
182 self.eps2eps3,
183 self.eps1eps2eps3
184 )
185 }
186}
187
188impl_third_derivatives!(
189 HyperHyperDual,
190 [eps1, eps2, eps3, eps1eps2, eps1eps3, eps2eps3, eps1eps2eps3]
191);
192impl_dual!(
193 HyperHyperDual,
194 [eps1, eps2, eps3, eps1eps2, eps1eps3, eps2eps3, eps1eps2eps3]
195);