1use crate::DualNum;
2use nalgebra::allocator::Allocator;
3use nalgebra::constraint::{SameNumberOfRows, ShapeConstraint};
4use nalgebra::*;
5use num_traits::Zero;
6use std::fmt;
7use std::mem::MaybeUninit;
8use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
9
10#[derive(PartialEq, Eq, Clone, Debug)]
12pub struct Derivative<T: Scalar, R: Dim, C: Dim>(pub Option<OMatrix<T, R, C>>)
13where
14 DefaultAllocator: Allocator<R, C>;
15
16impl<T: Scalar + Copy, const R: usize, const C: usize> Copy for Derivative<T, Const<R>, Const<C>> {}
17
18impl<T: DualNum, R: Dim, C: Dim> Derivative<T, R, C>
19where
20 DefaultAllocator: Allocator<R, C>,
21{
22 pub fn new(derivative: Option<OMatrix<T, R, C>>) -> Self {
23 Self(derivative)
24 }
25
26 pub fn some(derivative: OMatrix<T, R, C>) -> Self {
27 Self::new(Some(derivative))
28 }
29
30 pub fn none() -> Self {
31 Self::new(None)
32 }
33
34 pub(crate) fn map<T2>(&self, f: impl FnMut(T) -> T2) -> Derivative<T2, R, C>
35 where
36 T2: DualNum,
37 DefaultAllocator: Allocator<R, C>,
38 {
39 let opt = self.0.as_ref().map(|eps| eps.map(f));
40 Derivative::new(opt)
41 }
42
43 pub(crate) fn map_borrowed<T2>(&self, mut f: impl FnMut(&T) -> T2) -> Derivative<T2, R, C>
50 where
51 T2: DualNum,
52 DefaultAllocator: Allocator<R, C>,
53 {
54 let opt = self.0.as_ref().map(move |eps| {
55 let (nrows, ncols) = eps.shape_generic();
56 let mut res: Matrix<MaybeUninit<T2>, R, C, _> = Matrix::uninit(nrows, ncols);
57
58 for j in 0..ncols.value() {
59 for i in 0..nrows.value() {
60 unsafe {
62 let a = eps.data.get_unchecked(i, j);
63 *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a));
64 }
65 }
66 }
67
68 unsafe { res.assume_init() }
70 });
71 Derivative::new(opt)
72 }
73
74 pub(crate) fn try_map_borrowed<T2>(
76 &self,
77 mut f: impl FnMut(&T) -> Option<T2>,
78 ) -> Option<Derivative<T2, R, C>>
79 where
80 T2: DualNum,
81 DefaultAllocator: Allocator<R, C>,
82 {
83 self.0
84 .as_ref()
85 .and_then(move |eps| {
86 let (nrows, ncols) = eps.shape_generic();
87 let mut res: Matrix<MaybeUninit<T2>, R, C, _> = Matrix::uninit(nrows, ncols);
88
89 for j in 0..ncols.value() {
90 for i in 0..nrows.value() {
91 unsafe {
93 let a = eps.data.get_unchecked(i, j);
94 *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a)?);
95 }
96 }
97 }
98
99 Some(unsafe { res.assume_init() })
101 })
102 .map(Derivative::some)
103 }
104
105 pub fn derivative_generic(r: R, c: C, i: usize) -> Self {
106 let mut m = OMatrix::zeros_generic(r, c);
107 m[i] = T::one();
108 Self::some(m)
109 }
110
111 pub fn unwrap_generic(self, r: R, c: C) -> OMatrix<T, R, C> {
112 self.0.unwrap_or_else(|| OMatrix::zeros_generic(r, c))
113 }
114
115 pub fn fmt(&self, f: &mut fmt::Formatter, symbol: &str) -> fmt::Result {
116 if let Some(m) = self.0.as_ref() {
117 write!(f, " + ")?;
118 match m.shape() {
119 (1, 1) => write!(f, "{}", m[0])?,
120 (1, _) | (_, 1) => {
121 let x: Vec<_> = m.iter().map(T::to_string).collect();
122 write!(f, "[{}]", x.join(", "))?
123 }
124 (_, _) => write!(f, "{m}")?,
125 };
126 write!(f, "{symbol}")?;
127 }
128 write!(f, "")
129 }
130}
131
132impl<T: DualNum> Derivative<T, U1, U1> {
133 #[expect(clippy::self_named_constructors)]
134 pub fn derivative() -> Self {
135 Self::some(SVector::identity())
136 }
137
138 pub fn unwrap(self) -> T {
139 self.0.map_or_else(
140 || T::zero(),
141 |s| {
142 let [[r]] = s.data.0;
143 r
144 },
145 )
146 }
147}
148
149impl<T: DualNum, R: Dim, C: Dim> Mul<T> for Derivative<T, R, C>
150where
151 DefaultAllocator: Allocator<R, C>,
152{
153 type Output = Self;
154
155 fn mul(self, rhs: T) -> Self::Output {
156 Derivative::new(self.0.map(|x| x * rhs))
157 }
158}
159
160impl<T: DualNum, R: Dim, C: Dim> Mul<T> for &Derivative<T, R, C>
161where
162 DefaultAllocator: Allocator<R, C>,
163{
164 type Output = Derivative<T, R, C>;
165
166 fn mul(self, rhs: T) -> Self::Output {
167 Derivative::new(self.0.as_ref().map(|x| x * rhs))
168 }
169}
170
171impl<T: DualNum, R: Dim, C: Dim, R2: Dim, C2: Dim> Mul<&Derivative<T, R2, C2>>
172 for &Derivative<T, R, C>
173where
174 DefaultAllocator: Allocator<R, C> + Allocator<R2, C2> + Allocator<R, C2>,
175 ShapeConstraint: SameNumberOfRows<C, R2>,
176{
177 type Output = Derivative<T, R, C2>;
178
179 fn mul(self, rhs: &Derivative<T, R2, C2>) -> Derivative<T, R, C2> {
180 Derivative::new(self.0.as_ref().zip(rhs.0.as_ref()).map(|(s, r)| s * r))
181 }
182}
183
184impl<T: DualNum, R: Dim, C: Dim> Div<T> for Derivative<T, R, C>
185where
186 DefaultAllocator: Allocator<R, C>,
187{
188 type Output = Self;
189
190 fn div(self, rhs: T) -> Self::Output {
191 Derivative::new(self.0.map(|x| x / rhs))
192 }
193}
194
195impl<T: DualNum, R: Dim, C: Dim> Div<T> for &Derivative<T, R, C>
196where
197 DefaultAllocator: Allocator<R, C>,
198{
199 type Output = Derivative<T, R, C>;
200
201 fn div(self, rhs: T) -> Self::Output {
202 Derivative::new(self.0.as_ref().map(|x| x / rhs))
203 }
204}
205
206impl<T: DualNum, R: Dim, C: Dim> Derivative<T, R, C>
207where
208 DefaultAllocator: Allocator<R, C>,
209{
210 pub fn tr_mul<R2: Dim, C2: Dim>(&self, rhs: &Derivative<T, R2, C2>) -> Derivative<T, C, C2>
211 where
212 DefaultAllocator: Allocator<R2, C2> + Allocator<C, C2>,
213 ShapeConstraint: SameNumberOfRows<R, R2>,
214 {
215 Derivative::new(
216 self.0
217 .as_ref()
218 .zip(rhs.0.as_ref())
219 .map(|(s, r)| s.tr_mul(r)),
220 )
221 }
222}
223
224impl<T: DualNum, R: Dim, C: Dim> Add for Derivative<T, R, C>
225where
226 DefaultAllocator: Allocator<R, C>,
227{
228 type Output = Self;
229
230 fn add(self, rhs: Self) -> Self::Output {
231 Self::new(match (self.0, rhs.0) {
232 (Some(s), Some(r)) => Some(s + r),
233 (Some(s), None) => Some(s),
234 (None, Some(r)) => Some(r),
235 (None, None) => None,
236 })
237 }
238}
239
240impl<T: DualNum, R: Dim, C: Dim> Add<&Derivative<T, R, C>> for Derivative<T, R, C>
241where
242 DefaultAllocator: Allocator<R, C>,
243{
244 type Output = Derivative<T, R, C>;
245
246 fn add(self, rhs: &Derivative<T, R, C>) -> Self::Output {
247 Derivative::new(match (&self.0, &rhs.0) {
248 (Some(s), Some(r)) => Some(s + r),
249 (Some(s), None) => Some(s.clone()),
250 (None, Some(r)) => Some(r.clone()),
251 (None, None) => None,
252 })
253 }
254}
255
256impl<T: DualNum, R: Dim, C: Dim> Add for &Derivative<T, R, C>
257where
258 DefaultAllocator: Allocator<R, C>,
259{
260 type Output = Derivative<T, R, C>;
261
262 fn add(self, rhs: Self) -> Self::Output {
263 Derivative::new(match (&self.0, &rhs.0) {
264 (Some(s), Some(r)) => Some(s + r),
265 (Some(s), None) => Some(s.clone()),
266 (None, Some(r)) => Some(r.clone()),
267 (None, None) => None,
268 })
269 }
270}
271
272impl<T: DualNum, R: Dim, C: Dim> Sub for Derivative<T, R, C>
273where
274 DefaultAllocator: Allocator<R, C>,
275{
276 type Output = Self;
277
278 fn sub(self, rhs: Self) -> Self::Output {
279 Self::new(match (self.0, rhs.0) {
280 (Some(s), Some(r)) => Some(s - r),
281 (Some(s), None) => Some(s),
282 (None, Some(r)) => Some(-r),
283 (None, None) => None,
284 })
285 }
286}
287
288impl<T: DualNum, R: Dim, C: Dim> Sub<&Derivative<T, R, C>> for Derivative<T, R, C>
289where
290 DefaultAllocator: Allocator<R, C>,
291{
292 type Output = Derivative<T, R, C>;
293
294 fn sub(self, rhs: &Derivative<T, R, C>) -> Self::Output {
295 Derivative::new(match (&self.0, &rhs.0) {
296 (Some(s), Some(r)) => Some(s - r),
297 (Some(s), None) => Some(s.clone()),
298 (None, Some(r)) => Some(-r.clone()),
299 (None, None) => None,
300 })
301 }
302}
303
304impl<T: DualNum, R: Dim, C: Dim> Sub for &Derivative<T, R, C>
305where
306 DefaultAllocator: Allocator<R, C>,
307{
308 type Output = Derivative<T, R, C>;
309
310 fn sub(self, rhs: Self) -> Self::Output {
311 Derivative::new(match (&self.0, &rhs.0) {
312 (Some(s), Some(r)) => Some(s - r),
313 (Some(s), None) => Some(s.clone()),
314 (None, Some(r)) => Some(-r),
315 (None, None) => None,
316 })
317 }
318}
319
320impl<T: DualNum, R: Dim, C: Dim> Neg for &Derivative<T, R, C>
321where
322 DefaultAllocator: Allocator<R, C>,
323{
324 type Output = Derivative<T, R, C>;
325
326 fn neg(self) -> Self::Output {
327 Derivative::new(self.0.as_ref().map(|x| -x))
328 }
329}
330
331impl<T: DualNum, R: Dim, C: Dim> Neg for Derivative<T, R, C>
332where
333 DefaultAllocator: Allocator<R, C>,
334{
335 type Output = Self;
336
337 fn neg(self) -> Self::Output {
338 Derivative::new(self.0.map(|x| -x))
339 }
340}
341
342impl<T: DualNum, R: Dim, C: Dim> AddAssign for Derivative<T, R, C>
343where
344 DefaultAllocator: Allocator<R, C>,
345{
346 fn add_assign(&mut self, rhs: Self) {
347 match (&mut self.0, rhs.0) {
348 (Some(s), Some(r)) => *s += &r,
349 (None, Some(r)) => self.0 = Some(r),
350 (_, None) => (),
351 };
352 }
353}
354
355impl<T: DualNum, R: Dim, C: Dim> SubAssign for Derivative<T, R, C>
356where
357 DefaultAllocator: Allocator<R, C>,
358{
359 fn sub_assign(&mut self, rhs: Self) {
360 match (&mut self.0, rhs.0) {
361 (Some(s), Some(r)) => *s -= &r,
362 (None, Some(r)) => self.0 = Some(-&r),
363 (_, None) => (),
364 };
365 }
366}
367
368impl<T: DualNum, R: Dim, C: Dim> MulAssign<T> for Derivative<T, R, C>
369where
370 DefaultAllocator: Allocator<R, C>,
371{
372 fn mul_assign(&mut self, rhs: T) {
373 if let Some(s) = &mut self.0 {
374 *s *= rhs
375 }
376 }
377}
378
379impl<T: DualNum, R: Dim, C: Dim> DivAssign<T> for Derivative<T, R, C>
380where
381 DefaultAllocator: Allocator<R, C>,
382{
383 fn div_assign(&mut self, rhs: T) {
384 if let Some(s) = &mut self.0 {
385 *s /= rhs
386 }
387 }
388}
389
390impl<T, R: Dim, C: Dim> nalgebra::SimdValue for Derivative<T, R, C>
391where
392 DefaultAllocator: Allocator<R, C>,
393 T: DualNum<Primitive = T::Element> + SimdValue + Scalar,
394 T::Element: DualNum<Primitive = T::Element> + Scalar + Zero,
395{
396 type Element = Derivative<T::Element, R, C>;
397
398 type SimdBool = T::SimdBool;
399
400 const LANES: usize = T::LANES;
401
402 #[inline]
403 fn splat(val: Self::Element) -> Self {
404 val.map(|e| T::splat(e))
405 }
406
407 #[inline]
408 fn extract(&self, i: usize) -> Self::Element {
409 self.map_borrowed(|e| T::extract(e, i))
410 }
411
412 #[inline]
413 unsafe fn extract_unchecked(&self, i: usize) -> Self::Element {
414 let opt = self
415 .map_borrowed(|e| unsafe { T::extract_unchecked(e, i) })
416 .0
417 .filter(|x| Iterator::any(&mut x.iter(), |e| !e.is_zero()));
422 Derivative::new(opt)
423 }
424
425 fn replace(&mut self, i: usize, val: Self::Element) {
436 match (&mut self.0, val.0) {
437 (Some(ours), Some(theirs)) => {
438 ours.zip_apply(&theirs, |e, replacement| e.replace(i, replacement));
439 }
440 (ours @ None, Some(theirs)) => {
441 let (r, c) = theirs.shape_generic();
442 let mut init: OMatrix<T, R, C> = OMatrix::zeros_generic(r, c);
443 init.zip_apply(&theirs, |e, replacement| e.replace(i, replacement));
444 *ours = Some(init);
445 }
446 (Some(ours), None) => {
447 ours.apply(|e| e.replace(i, T::Element::zero()));
448 }
449 _ => {}
450 }
451 }
452
453 unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) {
454 match (&mut self.0, val.0) {
455 (Some(ours), Some(theirs)) => {
456 ours.zip_apply(&theirs, |e, replacement| unsafe {
457 e.replace_unchecked(i, replacement)
458 });
459 }
460 (ours @ None, Some(theirs)) => {
461 let (r, c) = theirs.shape_generic();
462 let mut init: OMatrix<T, R, C> = OMatrix::zeros_generic(r, c);
463 init.zip_apply(&theirs, |e, replacement| unsafe {
464 e.replace_unchecked(i, replacement)
465 });
466 *ours = Some(init);
467 }
468 (Some(ours), None) => {
469 ours.apply(|e| unsafe { e.replace_unchecked(i, T::Element::zero()) });
470 }
471 _ => {}
472 }
473 }
474
475 fn select(mut self, cond: Self::SimdBool, other: Self) -> Self {
476 if cond.all() {
479 self
480 } else if cond.none() {
481 other
482 } else {
483 match (&mut self.0, other.0) {
484 (Some(ours), Some(theirs)) => {
485 ours.zip_apply(&theirs, |e, other_e| {
486 let e_ = std::mem::replace(e, T::zero());
488 *e = e_.select(cond, other_e)
489 });
490 self
491 }
492 (Some(ours), None) => {
493 ours.apply(|e| {
494 let e_ = std::mem::replace(e, T::zero());
496 *e = e_.select(cond, T::zero());
497 });
498 self
499 }
500 (ours @ None, Some(mut theirs)) => {
501 use std::ops::Not;
502 let inverted: T::SimdBool = cond.not();
503 theirs.apply(|e| {
504 let e_ = std::mem::replace(e, T::zero());
506 *e = e_.select(inverted, T::zero());
507 });
508 *ours = Some(theirs);
509 self
510 }
511 _ => self,
512 }
513 }
514 }
515}
516
517use simba::scalar::{SubsetOf, SupersetOf};
518
519impl<TSuper, T, R: Dim, C: Dim> SubsetOf<Derivative<TSuper, R, C>> for Derivative<T, R, C>
520where
521 TSuper: DualNum + SupersetOf<T>,
522 T: DualNum,
523 DefaultAllocator: Allocator<R, C>,
524{
525 #[inline(always)]
526 fn to_superset(&self) -> Derivative<TSuper, R, C> {
527 self.map_borrowed(|elem| TSuper::from_subset(elem))
528 }
529 #[inline(always)]
530 fn from_superset(element: &Derivative<TSuper, R, C>) -> Option<Self> {
531 element.try_map_borrowed(|elem| TSuper::to_subset(elem))
532 }
533 #[inline(always)]
534 fn from_superset_unchecked(element: &Derivative<TSuper, R, C>) -> Self {
535 element.map_borrowed(|elem| TSuper::to_subset_unchecked(elem))
536 }
537 #[inline(always)]
538 fn is_in_subset(element: &Derivative<TSuper, R, C>) -> bool {
539 element
540 .0
541 .as_ref()
542 .is_none_or(|matrix| matrix.iter().all(|elem| TSuper::is_in_subset(elem)))
543 }
544}