1#![doc = include_str!("../README.md")]
2#![no_std]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign};
6
7use thermite::vector::ops::{MulAddAssignExt, MulAddExt, Square};
8
9pub mod ad;
10pub mod math;
11pub mod vector;
12
13#[cfg(feature = "special")]
14pub mod special;
15
16pub use ad::AutoDiff;
17pub use vector::DualFloatVector;
18
19pub trait DualValue:
31 Copy
32 + Add<Output = Self>
33 + Sub<Output = Self>
34 + Mul<Output = Self>
35 + Div<Output = Self>
36 + Neg<Output = Self>
37 + MulAddExt<Self, Self, Output = Self>
38{
39 const VAL_ZERO: Self;
41 const VAL_ONE: Self;
43
44 fn val_trunc(self) -> Self;
46}
47
48impl DualValue for f32 {
49 const VAL_ZERO: Self = 0.0;
50 const VAL_ONE: Self = 1.0;
51
52 #[inline(always)]
53 fn val_trunc(self) -> Self {
54 thermite::register::FloatElement::trunc(self)
55 }
56}
57
58impl DualValue for f64 {
59 const VAL_ZERO: Self = 0.0;
60 const VAL_ONE: Self = 1.0;
61
62 #[inline(always)]
63 fn val_trunc(self) -> Self {
64 thermite::register::FloatElement::trunc(self)
65 }
66}
67
68impl<R: thermite::register::FloatRegister> DualValue for thermite::prelude::Vector<R> {
69 const VAL_ZERO: Self = <Self as thermite::prelude::NumericVector>::ZERO;
70 const VAL_ONE: Self = <Self as thermite::prelude::NumericVector>::ONE;
71
72 #[inline(always)]
73 fn val_trunc(self) -> Self {
74 thermite::prelude::FloatVector::trunc(self)
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
84#[repr(C)]
85pub struct Dual<V, const N: usize> {
86 pub re: V,
88 pub dual: [V; N],
90}
91
92impl<V: DualValue, const N: usize> Default for Dual<V, N> {
93 #[inline(always)]
94 fn default() -> Self {
95 Self::ZERO
96 }
97}
98
99impl<V: DualValue, const N: usize> Dual<V, N> {
100 pub const ZERO: Self = Self {
102 re: V::VAL_ZERO,
103 dual: [V::VAL_ZERO; N],
104 };
105
106 pub const ONE: Self = Self {
108 re: V::VAL_ONE,
109 dual: [V::VAL_ZERO; N],
110 };
111
112 #[inline(always)]
117 pub const fn constant(re: V) -> Self {
118 Self {
119 re,
120 dual: [V::VAL_ZERO; N],
121 }
122 }
123
124 #[inline(always)]
126 pub const fn new(re: V, dual: [V; N]) -> Self {
127 Self { re, dual }
128 }
129
130 #[inline(always)]
140 pub fn variable(re: V, i: usize) -> Self {
141 assert!(i < N, "dual variable index {i} out of range for N = {N}");
142 let mut dual = [V::VAL_ZERO; N];
143 dual[i] = V::VAL_ONE;
144 Self { re, dual }
145 }
146
147 #[inline(always)]
149 pub const fn value(self) -> V {
150 self.re
151 }
152
153 #[inline(always)]
155 pub const fn gradient(self) -> [V; N] {
156 self.dual
157 }
158
159 #[inline(always)]
163 pub fn chain(self, new_re: V, factor: V) -> Self {
164 let mut dual = self.dual;
165 let mut i = 0;
166 while i < N {
167 dual[i] = factor * dual[i];
168 i += 1;
169 }
170 Self { re: new_re, dual }
171 }
172}
173
174impl<V: DualValue, const N: usize> Neg for Dual<V, N> {
177 type Output = Self;
178
179 #[inline(always)]
180 fn neg(self) -> Self {
181 let mut dual = self.dual;
182 let mut i = 0;
183 while i < N {
184 dual[i] = -dual[i];
185 i += 1;
186 }
187 Self { re: -self.re, dual }
188 }
189}
190
191impl<V: DualValue, const N: usize> Add for Dual<V, N> {
192 type Output = Self;
193
194 #[inline(always)]
195 fn add(self, rhs: Self) -> Self {
196 let mut dual = self.dual;
197 let mut i = 0;
198 while i < N {
199 dual[i] = dual[i] + rhs.dual[i];
200 i += 1;
201 }
202 Self {
203 re: self.re + rhs.re,
204 dual,
205 }
206 }
207}
208
209impl<V: DualValue, const N: usize> Sub for Dual<V, N> {
210 type Output = Self;
211
212 #[inline(always)]
213 fn sub(self, rhs: Self) -> Self {
214 let mut dual = self.dual;
215 let mut i = 0;
216 while i < N {
217 dual[i] = dual[i] - rhs.dual[i];
218 i += 1;
219 }
220 Self {
221 re: self.re - rhs.re,
222 dual,
223 }
224 }
225}
226
227impl<V: DualValue, const N: usize> Mul for Dual<V, N> {
228 type Output = Self;
229
230 #[allow(clippy::suspicious_arithmetic_impl)]
232 #[inline(always)]
233 fn mul(self, rhs: Self) -> Self {
234 let mut dual = self.dual;
235 let mut i = 0;
236 while i < N {
237 dual[i] = self.re.mul_adde(rhs.dual[i], self.dual[i] * rhs.re);
239 i += 1;
240 }
241 Self {
242 re: self.re * rhs.re,
243 dual,
244 }
245 }
246}
247
248impl<V: DualValue, const N: usize> Div for Dual<V, N> {
249 type Output = Self;
250
251 #[allow(clippy::suspicious_arithmetic_impl)]
253 #[inline(always)]
254 fn div(self, rhs: Self) -> Self {
255 let q = self.re / rhs.re;
256 let inv = V::VAL_ONE / rhs.re;
258 let mut dual = self.dual;
259 let mut i = 0;
260 while i < N {
261 dual[i] = q.nmul_adde(rhs.dual[i], self.dual[i]) * inv;
263 i += 1;
264 }
265 Self { re: q, dual }
266 }
267}
268
269impl<V: DualValue, const N: usize> Add<V> for Dual<V, N> {
272 type Output = Self;
273
274 #[inline(always)]
275 fn add(self, rhs: V) -> Self {
276 Self {
277 re: self.re + rhs,
278 dual: self.dual,
279 }
280 }
281}
282
283impl<V: DualValue, const N: usize> Sub<V> for Dual<V, N> {
284 type Output = Self;
285
286 #[inline(always)]
287 fn sub(self, rhs: V) -> Self {
288 Self {
289 re: self.re - rhs,
290 dual: self.dual,
291 }
292 }
293}
294
295impl<V: DualValue, const N: usize> Mul<V> for Dual<V, N> {
296 type Output = Self;
297
298 #[inline(always)]
299 fn mul(self, rhs: V) -> Self {
300 let mut dual = self.dual;
301 let mut i = 0;
302 while i < N {
303 dual[i] = dual[i] * rhs;
304 i += 1;
305 }
306 Self {
307 re: self.re * rhs,
308 dual,
309 }
310 }
311}
312
313impl<V: DualValue, const N: usize> Div<V> for Dual<V, N> {
314 type Output = Self;
315
316 #[inline(always)]
317 fn div(self, rhs: V) -> Self {
318 let inv = V::VAL_ONE / rhs;
320 let mut dual = self.dual;
321 let mut i = 0;
322 while i < N {
323 dual[i] = dual[i] * inv;
324 i += 1;
325 }
326 Self {
327 re: self.re / rhs,
328 dual,
329 }
330 }
331}
332
333impl<V: DualValue, const N: usize, T> AddAssign<T> for Dual<V, N>
336where
337 Self: Add<T, Output = Self>,
338{
339 #[inline(always)]
340 fn add_assign(&mut self, rhs: T) {
341 *self = *self + rhs;
342 }
343}
344
345impl<V: DualValue, const N: usize, T> SubAssign<T> for Dual<V, N>
346where
347 Self: Sub<T, Output = Self>,
348{
349 #[inline(always)]
350 fn sub_assign(&mut self, rhs: T) {
351 *self = *self - rhs;
352 }
353}
354
355impl<V: DualValue, const N: usize, T> MulAssign<T> for Dual<V, N>
356where
357 Self: Mul<T, Output = Self>,
358{
359 #[inline(always)]
360 fn mul_assign(&mut self, rhs: T) {
361 *self = *self * rhs;
362 }
363}
364
365impl<V: DualValue, const N: usize, T> DivAssign<T> for Dual<V, N>
366where
367 Self: Div<T, Output = Self>,
368{
369 #[inline(always)]
370 fn div_assign(&mut self, rhs: T) {
371 *self = *self / rhs;
372 }
373}
374
375impl<V: DualValue, const N: usize> Rem for Dual<V, N> {
382 type Output = Self;
383
384 #[inline(always)]
387 fn rem(self, rhs: Self) -> Self {
388 let k = (self.re / rhs.re).val_trunc();
389 let mut dual = self.dual;
390 let mut i = 0;
391 while i < N {
392 dual[i] = k.nmul_adde(rhs.dual[i], self.dual[i]); i += 1;
394 }
395 Self {
396 re: k.nmul_adde(rhs.re, self.re), dual,
398 }
399 }
400}
401
402#[allow(clippy::suspicious_arithmetic_impl)]
403impl<V: DualValue, const N: usize> Rem<V> for Dual<V, N> {
404 type Output = Self;
405
406 #[inline(always)]
407 fn rem(self, rhs: V) -> Self {
408 let k = (self.re / rhs).val_trunc();
409 Self {
410 re: k.nmul_adde(rhs, self.re), dual: self.dual,
412 }
413 }
414}
415
416impl<V: DualValue, const N: usize, T> RemAssign<T> for Dual<V, N>
417where
418 Self: Rem<T, Output = Self>,
419{
420 #[inline(always)]
421 fn rem_assign(&mut self, rhs: T) {
422 *self = *self % rhs;
423 }
424}
425
426macro_rules! dual_fma {
439 ($($name:ident => $re_op:ident, $outer:ident, $inner:ident);* $(;)?) => {
440 $(
441 #[inline(always)]
442 fn $name(self, a: Self, b: Self) -> Self {
443 let re = self.re.$re_op(a.re, b.re);
444 let mut dual = self.dual;
445 let mut i = 0;
446 while i < N {
447 dual[i] = self.re.$outer(a.dual[i], self.dual[i].$inner(a.re, b.dual[i]));
448 i += 1;
449 }
450 Self { re, dual }
451 }
452 )*
453 };
454}
455
456#[rustfmt::skip]
457impl<V: DualValue, const N: usize> MulAddExt<Self, Self> for Dual<V, N> {
458 type Output = Self;
459
460 const HAS_TRUE_FMA: bool = false;
461
462 dual_fma! {
463 mul_add => mul_add, mul_add, mul_add;
464 mul_sub => mul_sub, mul_add, mul_sub;
465 nmul_add => nmul_add, nmul_add, nmul_add;
466 nmul_sub => nmul_sub, nmul_sub, mul_add;
467 mul_adde => mul_adde, mul_adde, mul_adde;
468 mul_sube => mul_sube, mul_adde, mul_sube;
469 nmul_adde => nmul_adde, nmul_adde, nmul_adde;
470 nmul_sube => nmul_sube, nmul_sube, mul_adde;
471 }
472}
473
474#[rustfmt::skip]
475impl<V: DualValue, const N: usize, A, B> MulAddAssignExt<A, B> for Dual<V, N>
476where
477 Self: MulAddExt<A, B, Output = Self>,
478{
479 #[inline(always)] fn mul_add_assign(&mut self, a: A, b: B) { *self = self.mul_add(a, b); }
480 #[inline(always)] fn mul_sub_assign(&mut self, a: A, b: B) { *self = self.mul_sub(a, b); }
481 #[inline(always)] fn nmul_add_assign(&mut self, a: A, b: B) { *self = self.nmul_add(a, b); }
482 #[inline(always)] fn nmul_sub_assign(&mut self, a: A, b: B) { *self = self.nmul_sub(a, b); }
483 #[inline(always)] fn mul_adde_assign(&mut self, a: A, b: B) { *self = self.mul_adde(a, b); }
484 #[inline(always)] fn mul_sube_assign(&mut self, a: A, b: B) { *self = self.mul_sube(a, b); }
485 #[inline(always)] fn nmul_adde_assign(&mut self, a: A, b: B) { *self = self.nmul_adde(a, b); }
486 #[inline(always)] fn nmul_sube_assign(&mut self, a: A, b: B) { *self = self.nmul_sube(a, b); }
487}
488
489impl<V: DualValue, const N: usize> Square for Dual<V, N> {
490 type Output = Self;
491
492 #[inline(always)]
495 fn square(self) -> Self {
496 let two_re = self.re + self.re;
497 let mut dual = self.dual;
498 let mut i = 0;
499 while i < N {
500 dual[i] = two_re * dual[i];
501 i += 1;
502 }
503 Self {
504 re: self.re * self.re,
505 dual,
506 }
507 }
508}
509
510impl<V: DualValue, const N: usize> DualValue for Dual<V, N> {
513 const VAL_ZERO: Self = Self::ZERO;
514 const VAL_ONE: Self = Self::ONE;
515
516 #[inline(always)]
517 fn val_trunc(self) -> Self {
518 Self {
519 re: self.re.val_trunc(),
520 dual: self.dual,
521 }
522 }
523}