1use geop_core_math::{
19 geop_error::GeopResult,
20 scalars::{Field, Ring, Scalar},
21};
22
23pub const MAX_LOCAL_VARS: usize = 10;
27
28#[derive(Clone, Copy, Debug)]
31pub struct Dual<S: Scalar> {
32 pub v: S,
33 pub d: [S; MAX_LOCAL_VARS],
34}
35
36impl<S: Scalar> core::fmt::Display for Dual<S> {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 core::fmt::Display::fmt(&self.v, f)
39 }
40}
41
42impl<S: Scalar> Default for Dual<S> {
43 fn default() -> Self {
44 Dual::cst(S::default())
45 }
46}
47
48impl<S: Scalar> Dual<S> {
49 pub fn cst(v: S) -> Self {
51 Dual {
52 v,
53 d: [S::ZERO; MAX_LOCAL_VARS],
54 }
55 }
56
57 pub fn var(v: S, slot: usize) -> Self {
59 let mut d = [S::ZERO; MAX_LOCAL_VARS];
60 d[slot] = S::ONE;
61 Dual { v, d }
62 }
63
64 fn chain(self, v: S, df: S) -> Self {
67 let mut d = self.d;
68 for x in &mut d {
69 *x = x.mul(df);
70 }
71 Dual { v, d }
72 }
73}
74
75impl<S: Scalar> Ring for Dual<S> {
76 fn add(self, o: Self) -> Self {
77 let mut d = self.d;
78 for (x, y) in d.iter_mut().zip(o.d) {
79 *x = x.add(y);
80 }
81 Dual {
82 v: self.v.add(o.v),
83 d,
84 }
85 }
86
87 fn sub(self, o: Self) -> Self {
88 let mut d = self.d;
89 for (x, y) in d.iter_mut().zip(o.d) {
90 *x = x.sub(y);
91 }
92 Dual {
93 v: self.v.sub(o.v),
94 d,
95 }
96 }
97
98 fn mul(self, o: Self) -> Self {
99 let mut d = [S::ZERO; MAX_LOCAL_VARS];
100 for (i, x) in d.iter_mut().enumerate() {
101 *x = self.d[i].mul(o.v).add(o.d[i].mul(self.v));
102 }
103 Dual {
104 v: self.v.mul(o.v),
105 d,
106 }
107 }
108
109 fn neg(self) -> Self {
110 self.chain(self.v.neg(), S::ZERO.sub(S::ONE))
111 }
112}
113
114impl<S: Scalar> Field for Dual<S> {
115 fn div(self, o: Self) -> GeopResult<Self> {
116 let v = self.v.div(o.v)?;
117 let mut d = [S::ZERO; MAX_LOCAL_VARS];
118 for (i, x) in d.iter_mut().enumerate() {
119 *x = self.d[i].sub(v.mul(o.d[i])).div(o.v)?;
121 }
122 Ok(Dual { v, d })
123 }
124}
125
126impl<S: Scalar> Scalar for Dual<S> {
134 const ZERO: Self = Dual {
135 v: S::ZERO,
136 d: [S::ZERO; MAX_LOCAL_VARS],
137 };
138 const ONE: Self = Dual {
139 v: S::ONE,
140 d: [S::ZERO; MAX_LOCAL_VARS],
141 };
142 const TWO: Self = Dual {
143 v: S::TWO,
144 d: [S::ZERO; MAX_LOCAL_VARS],
145 };
146 const E: Self = Dual {
147 v: S::E,
148 d: [S::ZERO; MAX_LOCAL_VARS],
149 };
150 const PI: Self = Dual {
151 v: S::PI,
152 d: [S::ZERO; MAX_LOCAL_VARS],
153 };
154 const INFINITY: Self = Dual {
155 v: S::INFINITY,
156 d: [S::ZERO; MAX_LOCAL_VARS],
157 };
158 const ENTIRE: Self = Dual {
159 v: S::ENTIRE,
160 d: [S::ZERO; MAX_LOCAL_VARS],
161 };
162
163 fn from_i64(v: i64) -> Self {
164 Dual::cst(S::from_i64(v))
165 }
166
167 fn from_f64(v: f64) -> Self {
168 Dual::cst(S::from_f64(v))
169 }
170
171 fn from_ratio(num: i64, den: i64) -> GeopResult<Self> {
172 Ok(Dual::cst(S::from_ratio(num, den)?))
173 }
174
175 fn to_f64(self) -> f64 {
176 self.v.to_f64()
177 }
178
179 fn abs(self) -> Self {
180 if self.v.definitely_less(S::ZERO) {
184 self.chain(self.v.abs(), S::ZERO.sub(S::ONE))
185 } else {
186 self.chain(self.v.abs(), S::ONE)
187 }
188 }
189
190 fn sqrt(self) -> GeopResult<Self> {
191 let s = self.v.sqrt()?;
192 let df = if s.definitely_greater(S::ZERO) {
196 S::ONE.div(S::TWO.mul(s))?
197 } else {
198 S::ZERO
199 };
200 Ok(self.chain(s, df))
201 }
202
203 fn sin(self) -> Self {
204 self.chain(self.v.sin(), self.v.cos())
205 }
206
207 fn cos(self) -> Self {
208 let df = self.v.sin().neg();
209 self.chain(self.v.cos(), df)
210 }
211
212 fn could_be_equal(self, other: Self) -> bool {
213 self.v.could_be_equal(other.v)
214 }
215
216 fn definitely_not_equal(self, other: Self) -> bool {
217 self.v.definitely_not_equal(other.v)
218 }
219
220 fn could_be_greater(self, other: Self) -> bool {
221 self.v.could_be_greater(other.v)
222 }
223
224 fn definitely_greater(self, other: Self) -> bool {
225 self.v.definitely_greater(other.v)
226 }
227
228 fn could_be_less(self, other: Self) -> bool {
229 self.v.could_be_less(other.v)
230 }
231
232 fn definitely_less(self, other: Self) -> bool {
233 self.v.definitely_less(other.v)
234 }
235
236 fn is_infinite(self) -> bool {
237 self.v.is_infinite()
238 }
239
240 fn is_finite(self) -> bool {
241 self.v.is_finite()
242 }
243
244 fn midpoint(self) -> Self {
245 Dual {
246 v: self.v.midpoint(),
247 d: self.d,
248 }
249 }
250
251 fn is_sharp(self) -> bool {
252 self.v.is_sharp()
253 }
254
255 fn width(self) -> Self {
256 Dual::cst(self.v.width())
257 }
258
259 fn lower(self) -> Self {
260 Dual {
261 v: self.v.lower(),
262 d: self.d,
263 }
264 }
265
266 fn upper(self) -> Self {
267 Dual {
268 v: self.v.upper(),
269 d: self.d,
270 }
271 }
272
273 fn intersect(self, other: Self) -> Self {
274 Dual {
275 v: self.v.intersect(other.v),
276 d: self.d,
277 }
278 }
279
280 fn union(self, other: Self) -> Self {
281 Dual {
282 v: self.v.union(other.v),
283 d: self.d,
284 }
285 }
286
287 fn is_subset_of(self, other: Self) -> bool {
288 self.v.is_subset_of(other.v)
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use geop_core_math::scalars::scal_in_f64::ScalInF64;
296
297 #[test]
299 fn dual_matches_finite_differences() {
300 fn f<S: Scalar>(x: S, y: S) -> S {
301 let numerator = x.mul(y).add(x.sin().mul(y.cos()));
302 let denom = x.mul(x).add(S::ONE).sqrt().unwrap();
303 numerator.div(denom).unwrap().sub(y.sub(x).abs())
304 }
305 let (x, y) = (0.7, -1.3);
306 let d = f(
307 Dual::<ScalInF64>::var(ScalInF64::from_f64(x), 0),
308 Dual::<ScalInF64>::var(ScalInF64::from_f64(y), 1),
309 );
310 let h = 1e-6;
311 let fv = |x: f64, y: f64| f(ScalInF64::from_f64(x), ScalInF64::from_f64(y)).to_f64();
312 let dx = (fv(x + h, y) - fv(x - h, y)) / (2.0 * h);
313 let dy = (fv(x, y + h) - fv(x, y - h)) / (2.0 * h);
314 assert!((d.v.to_f64() - fv(x, y)).abs() < 1e-15);
315 assert!((d.d[0].to_f64() - dx).abs() < 1e-8, "{} vs {dx}", d.d[0]);
316 assert!((d.d[1].to_f64() - dy).abs() < 1e-8, "{} vs {dy}", d.d[1]);
317 }
318}