1#[allow(non_camel_case_types)]
36#[derive(Clone, Copy, Debug)]
37pub struct dual32 {
38 pub x: f32,
39 pub d: f32,
40}
41
42impl dual32 {
43 pub const ZERO: dual32 = dual32 { x: 0.0, d: 0.0 };
44 pub const ONE: dual32 = dual32 { x: 1.0, d: 0.0 };
45
46 pub const J: dual32 = dual32 { x: 0.0, d: 1.0 };
48
49 #[inline]
50 pub const fn new(x: f32, d: f32) -> dual32 {
51 Self { x, d }
52 }
53}
54
55impl core::ops::Add<dual32> for dual32 {
56 type Output = dual32;
57
58 #[inline]
59 fn add(self, rhs: dual32) -> Self::Output {
60 Self::Output::new(self.x + rhs.x, self.d + rhs.d)
61 }
62}
63
64impl core::ops::Add<f32> for dual32 {
65 type Output = dual32;
66
67 #[inline]
68 fn add(self, rhs: f32) -> Self::Output {
69 Self::Output::new(self.x + rhs, self.d)
70 }
71}
72
73impl core::ops::Add<dual32> for f32 {
74 type Output = dual32;
75
76 #[inline]
77 fn add(self, rhs: dual32) -> Self::Output {
78 Self::Output::new(self + rhs.x, rhs.d)
79 }
80}
81
82impl core::ops::Sub<dual32> for dual32 {
83 type Output = dual32;
84
85 #[inline]
86 fn sub(self, rhs: dual32) -> Self::Output {
87 Self::Output::new(self.x - rhs.x, self.d - rhs.d)
88 }
89}
90
91impl core::ops::Sub<f32> for dual32 {
92 type Output = dual32;
93
94 #[inline]
95 fn sub(self, rhs: f32) -> Self::Output {
96 Self::Output::new(self.x - rhs, self.d)
97 }
98}
99
100impl core::ops::Sub<dual32> for f32 {
101 type Output = dual32;
102
103 #[inline]
104 fn sub(self, rhs: dual32) -> Self::Output {
105 Self::Output::new(self - rhs.x, rhs.d)
106 }
107}
108
109impl core::ops::Mul<dual32> for dual32 {
110 type Output = dual32;
111
112 #[inline]
113 fn mul(self, rhs: dual32) -> Self::Output {
114 Self::Output::new(self.x * rhs.x, self.x * rhs.d + self.d * rhs.x)
115 }
116}
117
118impl core::ops::Mul<f32> for dual32 {
119 type Output = dual32;
120
121 #[inline]
122 fn mul(self, rhs: f32) -> Self::Output {
123 Self::Output::new(self.x * rhs, self.x * rhs)
124 }
125}
126
127impl core::ops::Mul<dual32> for f32 {
128 type Output = dual32;
129
130 #[inline]
131 fn mul(self, rhs: dual32) -> Self::Output {
132 Self::Output::new(self * rhs.x, self * rhs.d)
133 }
134}
135
136impl core::ops::Div<dual32> for dual32 {
137 type Output = dual32;
138
139 #[inline]
140 fn div(self, rhs: dual32) -> Self::Output {
141 Self::Output::new(
142 self.x / rhs.x,
143 (self.d * rhs.x - self.x * rhs.d) / (rhs.x * rhs.x),
144 )
145 }
146}
147
148impl core::ops::Div<f32> for dual32 {
149 type Output = dual32;
150
151 #[inline]
152 fn div(self, rhs: f32) -> Self::Output {
153 Self::Output::new(self.x / rhs, self.x / rhs)
154 }
155}
156
157impl core::ops::Div<dual32> for f32 {
158 type Output = dual32;
159
160 #[inline]
161 fn div(self, rhs: dual32) -> Self::Output {
162 Self::Output::new(self / rhs.x, (-self * rhs.d) / (rhs.x * rhs.x))
163 }
164}
165
166impl dual32 {
168 #[inline]
169 pub fn recip(self) -> dual32 {
170 Self::new(self.x.recip(), -self.d * self.x.recip().powi(2))
171 }
172
173 #[inline]
174 pub fn powf(self, n: f32) -> dual32 {
175 Self::new(self.x.powf(n), self.d * n * self.x.powf(n - 1.0))
176 }
177
178 #[inline]
179 pub fn powi(self, n: i32) -> dual32 {
180 Self::new(self.x.powi(n), self.d * n as f32 * self.x.powi(n - 1))
181 }
182
183 #[inline]
184 pub fn sqrt(self) -> dual32 {
185 Self::new(self.x.sqrt(), self.d * 0.5 / self.x.sqrt())
186 }
187
188 #[inline]
189 pub fn cbrt(self) -> dual32 {
190 Self::new(self.x.cbrt(), self.d * (1.0 / 3.0) / self.x.cbrt().powi(2))
191 }
192}
193
194impl dual32 {
196 #[inline]
197 pub fn exp(self) -> dual32 {
198 Self::new(self.x.exp(), self.d * self.x.exp())
199 }
200
201 #[inline]
202 pub fn exp2(self) -> dual32 {
203 Self::new(
204 self.x.exp2(),
205 self.d * core::f32::consts::LN_2 * self.x.exp2(),
206 )
207 }
208
209 #[inline]
210 pub fn exp_m1(self) -> dual32 {
211 Self::new(self.x.exp_m1(), self.d * self.x.exp())
212 }
213
214 #[inline]
215 pub fn ln(self) -> dual32 {
216 Self::new(self.x.ln(), self.d / self.x)
217 }
218
219 #[inline]
220 pub fn ln_1p(self) -> dual32 {
221 Self::new(self.x.ln_1p(), self.d / (1.0 + self.x))
222 }
223
224 #[inline]
225 pub fn log(self, base: f32) -> dual32 {
226 Self::new(self.x.log(base), self.d / (base.ln() * self.x))
227 }
228
229 #[inline]
230 pub fn log10(self) -> dual32 {
231 Self::new(self.x.log10(), self.d / (core::f32::consts::LN_10 * self.x))
232 }
233
234 #[inline]
235 pub fn log2(self) -> dual32 {
236 Self::new(self.x.log2(), self.d / (core::f32::consts::LN_2 * self.x))
237 }
238}
239
240impl dual32 {
244 #[inline]
245 pub fn cos(self) -> dual32 {
246 Self::new(self.x.cos(), -self.d * self.x.sin())
247 }
248
249 #[inline]
250 pub fn sin(self) -> dual32 {
251 Self::new(self.x.sin(), self.d * self.x.cos())
252 }
253
254 #[inline]
255 pub fn tan(self) -> dual32 {
256 Self::new(self.x.tan(), self.d / self.x.cos().powi(2))
257 }
258
259 #[inline]
260 pub fn acos(self) -> dual32 {
261 Self::new(self.x.acos(), -self.d / (1.0 - self.x * self.x).sqrt())
262 }
263
264 #[inline]
265 pub fn asin(self) -> dual32 {
266 Self::new(self.x.asin(), self.d / (1.0 - self.x * self.x).sqrt())
267 }
268
269 #[inline]
270 pub fn atan(self) -> dual32 {
271 Self::new(self.x.tan(), self.d / (1.0 + self.x * self.x))
272 }
273}
274
275impl dual32 {
277 #[inline]
278 pub fn cosh(self) -> dual32 {
279 Self::new(self.x.cosh(), self.d * self.x.sinh())
280 }
281
282 #[inline]
283 pub fn sinh(self) -> dual32 {
284 Self::new(self.x.sinh(), self.d * self.x.cosh())
285 }
286
287 #[inline]
288 pub fn tanh(self) -> dual32 {
289 Self::new(self.x.tanh(), self.d / self.x.cosh().powi(2))
290 }
291
292 #[inline]
293 pub fn acosh(self) -> dual32 {
294 Self::new(self.x.acosh(), self.d / (self.x * self.x - 1.0).sqrt())
295 }
296
297 #[inline]
298 pub fn asinh(self) -> dual32 {
299 Self::new(self.x.asinh(), self.d / (self.x * self.x + 1.0).sqrt())
300 }
301
302 #[inline]
303 pub fn atanh(self) -> dual32 {
304 Self::new(self.x.atanh(), self.d / (1.0 - self.x * self.x))
305 }
306}
307
308impl dual32 {
310 #[inline]
311 pub fn abs(self) -> dual32 {
312 Self::new(self.x.abs(), self.d * self.x.signum())
313 }
314
315 #[inline]
316 pub fn min(self, other: f32) -> dual32 {
317 Self::new(
318 self.x.min(other),
319 if self.x < other { self.d * 1.0 } else { 0.0 },
320 )
321 }
322
323 #[inline]
324 pub fn max(self, other: f32) -> dual32 {
325 Self::new(
326 self.x.max(other),
327 if other < self.x { self.d * 1.0 } else { 0.0 },
328 )
329 }
330
331 #[inline]
332 pub fn clamp(self, min: f32, max: f32) -> dual32 {
333 Self::new(
334 self.x.clamp(min, max),
335 if min < self.x && self.x < max {
336 self.d * 1.0
337 } else {
338 0.0
339 },
340 )
341 }
342}
343
344impl dual32 {
346 #[inline]
347 pub fn ceil(self) -> dual32 {
348 Self::new(self.x.ceil(), 0.0)
349 }
350
351 #[inline]
352 pub fn floor(self) -> dual32 {
353 Self::new(self.x.floor(), 0.0)
354 }
355
356 #[inline]
358 pub fn fract(self) -> dual32 {
359 Self::new(self.x.fract(), self.d)
360 }
361
362 #[inline]
363 pub fn round(self) -> dual32 {
364 Self::new(self.x.round(), 0.0)
365 }
366
367 #[inline]
368 pub fn signum(self) -> dual32 {
369 Self::new(self.x.signum(), 0.0)
370 }
371
372 #[inline]
373 pub fn trunc(self) -> dual32 {
374 Self::new(self.x.trunc(), 0.0)
375 }
376}
377
378impl dual32 {
380 #[inline]
381 pub fn mul_add(self, a: f32, b: f32) -> dual32 {
382 Self::new(self.x.mul_add(a, b), self.d * a * self.x)
383 }
384}
385
386#[allow(non_camel_case_types)]
388#[derive(Clone, Copy, Debug)]
389pub struct dual64 {
390 pub x: f64,
391 pub d: f64,
392}
393
394impl dual64 {
395 pub const ZERO: dual64 = dual64 { x: 0.0, d: 0.0 };
396 pub const ONE: dual64 = dual64 { x: 1.0, d: 0.0 };
397
398 pub const J: dual64 = dual64 { x: 0.0, d: 1.0 };
400
401 #[inline]
402 pub const fn new(x: f64, d: f64) -> dual64 {
403 Self { x, d }
404 }
405}
406
407impl core::ops::Add<dual64> for dual64 {
408 type Output = dual64;
409
410 #[inline]
411 fn add(self, rhs: dual64) -> Self::Output {
412 Self::Output::new(self.x + rhs.x, self.d + rhs.d)
413 }
414}
415
416impl core::ops::Add<f64> for dual64 {
417 type Output = dual64;
418
419 #[inline]
420 fn add(self, rhs: f64) -> Self::Output {
421 Self::Output::new(self.x + rhs, self.d)
422 }
423}
424
425impl core::ops::Add<dual64> for f64 {
426 type Output = dual64;
427
428 #[inline]
429 fn add(self, rhs: dual64) -> Self::Output {
430 Self::Output::new(self + rhs.x, rhs.d)
431 }
432}
433
434impl core::ops::Sub<dual64> for dual64 {
435 type Output = dual64;
436
437 #[inline]
438 fn sub(self, rhs: dual64) -> Self::Output {
439 Self::Output::new(self.x - rhs.x, self.d - rhs.d)
440 }
441}
442
443impl core::ops::Sub<f64> for dual64 {
444 type Output = dual64;
445
446 #[inline]
447 fn sub(self, rhs: f64) -> Self::Output {
448 Self::Output::new(self.x - rhs, self.d)
449 }
450}
451
452impl core::ops::Sub<dual64> for f64 {
453 type Output = dual64;
454
455 #[inline]
456 fn sub(self, rhs: dual64) -> Self::Output {
457 Self::Output::new(self - rhs.x, rhs.d)
458 }
459}
460
461impl core::ops::Mul<dual64> for dual64 {
462 type Output = dual64;
463
464 #[inline]
465 fn mul(self, rhs: dual64) -> Self::Output {
466 Self::Output::new(self.x * rhs.x, self.x * rhs.d + self.d * rhs.x)
467 }
468}
469
470impl core::ops::Mul<f64> for dual64 {
471 type Output = dual64;
472
473 #[inline]
474 fn mul(self, rhs: f64) -> Self::Output {
475 Self::Output::new(self.x * rhs, self.x * rhs)
476 }
477}
478
479impl core::ops::Mul<dual64> for f64 {
480 type Output = dual64;
481
482 #[inline]
483 fn mul(self, rhs: dual64) -> Self::Output {
484 Self::Output::new(self * rhs.x, self * rhs.d)
485 }
486}
487
488impl core::ops::Div<dual64> for dual64 {
489 type Output = dual64;
490
491 #[inline]
492 fn div(self, rhs: dual64) -> Self::Output {
493 Self::Output::new(
494 self.x / rhs.x,
495 (self.d * rhs.x - self.x * rhs.d) / (rhs.x * rhs.x),
496 )
497 }
498}
499
500impl core::ops::Div<f64> for dual64 {
501 type Output = dual64;
502
503 #[inline]
504 fn div(self, rhs: f64) -> Self::Output {
505 Self::Output::new(self.x / rhs, self.x / rhs)
506 }
507}
508
509impl core::ops::Div<dual64> for f64 {
510 type Output = dual64;
511
512 #[inline]
513 fn div(self, rhs: dual64) -> Self::Output {
514 Self::Output::new(self / rhs.x, (-self * rhs.d) / (rhs.x * rhs.x))
515 }
516}
517
518impl dual64 {
520 #[inline]
521 pub fn recip(self) -> dual64 {
522 Self::new(self.x.recip(), -self.d * self.x.recip().powi(2))
523 }
524
525 #[inline]
526 pub fn powf(self, n: f64) -> dual64 {
527 Self::new(self.x.powf(n), self.d * n * self.x.powf(n - 1.0))
528 }
529
530 #[inline]
531 pub fn powi(self, n: i32) -> dual64 {
532 Self::new(self.x.powi(n), self.d * n as f64 * self.x.powi(n - 1))
533 }
534
535 #[inline]
536 pub fn sqrt(self) -> dual64 {
537 Self::new(self.x.sqrt(), self.d * 0.5 / self.x.sqrt())
538 }
539
540 #[inline]
541 pub fn cbrt(self) -> dual64 {
542 Self::new(self.x.cbrt(), self.d * (1.0 / 3.0) / self.x.cbrt().powi(2))
543 }
544}
545
546impl dual64 {
548 #[inline]
549 pub fn exp(self) -> dual64 {
550 Self::new(self.x.exp(), self.d * self.x.exp())
551 }
552
553 #[inline]
554 pub fn exp2(self) -> dual64 {
555 Self::new(
556 self.x.exp2(),
557 self.d * core::f64::consts::LN_2 * self.x.exp2(),
558 )
559 }
560
561 #[inline]
562 pub fn exp_m1(self) -> dual64 {
563 Self::new(self.x.exp_m1(), self.d * self.x.exp())
564 }
565
566 #[inline]
567 pub fn ln(self) -> dual64 {
568 Self::new(self.x.ln(), self.d / self.x)
569 }
570
571 #[inline]
572 pub fn ln_1p(self) -> dual64 {
573 Self::new(self.x.ln_1p(), self.d / (1.0 + self.x))
574 }
575
576 #[inline]
577 pub fn log(self, base: f64) -> dual64 {
578 Self::new(self.x.log(base), self.d / (base.ln() * self.x))
579 }
580
581 #[inline]
582 pub fn log10(self) -> dual64 {
583 Self::new(self.x.log10(), self.d / (core::f64::consts::LN_10 * self.x))
584 }
585
586 #[inline]
587 pub fn log2(self) -> dual64 {
588 Self::new(self.x.log2(), self.d / (core::f64::consts::LN_2 * self.x))
589 }
590}
591
592impl dual64 {
596 #[inline]
597 pub fn cos(self) -> dual64 {
598 Self::new(self.x.cos(), -self.d * self.x.sin())
599 }
600
601 #[inline]
602 pub fn sin(self) -> dual64 {
603 Self::new(self.x.sin(), self.d * self.x.cos())
604 }
605
606 #[inline]
607 pub fn tan(self) -> dual64 {
608 Self::new(self.x.tan(), self.d / self.x.cos().powi(2))
609 }
610
611 #[inline]
612 pub fn acos(self) -> dual64 {
613 Self::new(self.x.acos(), -self.d / (1.0 - self.x * self.x).sqrt())
614 }
615
616 #[inline]
617 pub fn asin(self) -> dual64 {
618 Self::new(self.x.asin(), self.d / (1.0 - self.x * self.x).sqrt())
619 }
620
621 #[inline]
622 pub fn atan(self) -> dual64 {
623 Self::new(self.x.tan(), self.d / (1.0 + self.x * self.x))
624 }
625}
626
627impl dual64 {
629 #[inline]
630 pub fn cosh(self) -> dual64 {
631 Self::new(self.x.cosh(), self.d * self.x.sinh())
632 }
633
634 #[inline]
635 pub fn sinh(self) -> dual64 {
636 Self::new(self.x.sinh(), self.d * self.x.cosh())
637 }
638
639 #[inline]
640 pub fn tanh(self) -> dual64 {
641 Self::new(self.x.tanh(), self.d / self.x.cosh().powi(2))
642 }
643
644 #[inline]
645 pub fn acosh(self) -> dual64 {
646 Self::new(self.x.acosh(), self.d / (self.x * self.x - 1.0).sqrt())
647 }
648
649 #[inline]
650 pub fn asinh(self) -> dual64 {
651 Self::new(self.x.asinh(), self.d / (self.x * self.x + 1.0).sqrt())
652 }
653
654 #[inline]
655 pub fn atanh(self) -> dual64 {
656 Self::new(self.x.atanh(), self.d / (1.0 - self.x * self.x))
657 }
658}
659
660impl dual64 {
662 #[inline]
663 pub fn abs(self) -> dual64 {
664 Self::new(self.x.abs(), self.d * self.x.signum())
665 }
666
667 #[inline]
668 pub fn min(self, other: f64) -> dual64 {
669 Self::new(
670 self.x.min(other),
671 if self.x < other { self.d * 1.0 } else { 0.0 },
672 )
673 }
674
675 #[inline]
676 pub fn max(self, other: f64) -> dual64 {
677 Self::new(
678 self.x.max(other),
679 if other < self.x { self.d * 1.0 } else { 0.0 },
680 )
681 }
682
683 #[inline]
684 pub fn clamp(self, min: f64, max: f64) -> dual64 {
685 Self::new(
686 self.x.clamp(min, max),
687 if min < self.x && self.x < max {
688 self.d * 1.0
689 } else {
690 0.0
691 },
692 )
693 }
694}
695
696impl dual64 {
698 #[inline]
699 pub fn ceil(self) -> dual64 {
700 Self::new(self.x.ceil(), 0.0)
701 }
702
703 #[inline]
704 pub fn floor(self) -> dual64 {
705 Self::new(self.x.floor(), 0.0)
706 }
707
708 #[inline]
710 pub fn fract(self) -> dual64 {
711 Self::new(self.x.fract(), self.d)
712 }
713
714 #[inline]
715 pub fn round(self) -> dual64 {
716 Self::new(self.x.round(), 0.0)
717 }
718
719 #[inline]
720 pub fn signum(self) -> dual64 {
721 Self::new(self.x.signum(), 0.0)
722 }
723
724 #[inline]
725 pub fn trunc(self) -> dual64 {
726 Self::new(self.x.trunc(), 0.0)
727 }
728}
729
730impl dual64 {
732 #[inline]
733 pub fn mul_add(self, a: f64, b: f64) -> dual64 {
734 Self::new(self.x.mul_add(a, b), self.d * a * self.x)
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741
742 #[test]
743 fn identity32() {
744 let dual32 { x, d } = dual32::J.powi(2);
745 assert!(x.abs() < f32::EPSILON);
746 assert!(d.abs() < f32::EPSILON);
747 }
748
749 #[test]
750 fn identity64() {
751 let dual64 { x, d } = dual64::J.powi(2);
752 assert!(x.abs() < f64::EPSILON);
753 assert!(d.abs() < f64::EPSILON);
754 }
755
756 #[test]
757 fn polynomial32() {
758 fn p(x: dual32) -> dual32 {
759 4.0 * x * x - 3.0 * x + 3.0
760 }
761 let x = 3.0;
762 let dual32 { x: px, d: pdx } = p(x + dual32::J);
763
764 assert!((px - 30.0).abs() <= f32::EPSILON);
765 assert!((pdx - 21.0).abs() <= f32::EPSILON);
766 }
767
768 #[test]
769 fn polynomial64() {
770 fn p(x: dual64) -> dual64 {
771 4.0 * x * x - 3.0 * x + 3.0
772 }
773 let x = 3.0;
774 let dual64 { x: px, d: pdx } = p(x + dual64::J);
775
776 assert!((px - 30.0).abs() <= f64::EPSILON);
777 assert!((pdx - 21.0).abs() <= f64::EPSILON);
778 }
779}
780
781pub trait Dual<F> {
785 fn real(self) -> F;
786 fn imag(self) -> F;
787}
788
789impl Dual<f32> for dual32 {
790 fn real(self) -> f32 {
791 self.x
792 }
793
794 fn imag(self) -> f32 {
795 self.d
796 }
797}
798
799impl Dual<f64> for dual64 {
800 fn real(self) -> f64 {
801 self.x
802 }
803
804 fn imag(self) -> f64 {
805 self.d
806 }
807}
808
809pub trait AsDualVariable<Dual> {
812 fn as_dual_variable(self) -> Dual;
813 fn from_dual(z: Dual) -> Self;
814}
815
816impl AsDualVariable<dual32> for f32 {
817 fn as_dual_variable(self) -> dual32 {
818 dual32::new(self, 1.0)
819 }
820
821 fn from_dual(z: dual32) -> Self {
822 z.x
823 }
824}
825
826impl AsDualVariable<dual32> for dual32 {
827 fn as_dual_variable(self) -> dual32 {
828 self
829 }
830
831 fn from_dual(z: dual32) -> Self {
832 z
833 }
834}
835
836impl AsDualVariable<dual64> for f64 {
837 fn as_dual_variable(self) -> dual64 {
838 dual64::new(self, 1.0)
839 }
840
841 fn from_dual(z: dual64) -> Self {
842 z.x
843 }
844}
845
846impl AsDualVariable<dual64> for dual64 {
847 fn as_dual_variable(self) -> dual64 {
848 self
849 }
850
851 fn from_dual(z: dual64) -> Self {
852 z
853 }
854}
855
856pub fn derivative<F, D>(f: impl Fn(D) -> D, x: F) -> F
858where
859 D: Dual<F>,
860 F: AsDualVariable<D>,
861{
862 f(x.as_dual_variable()).imag()
863}