Skip to main content

conspire/math/tensor/rank_1/
mod.rs

1#[cfg(test)]
2mod test;
3use super::{ContractWith, Differentiable, Erase};
4use crate::math::{Current, Projection, Reference};
5
6pub(crate) mod cross;
7pub(crate) mod list;
8pub(crate) mod list_2d;
9pub(crate) mod vec;
10pub(crate) mod vec_2d;
11
12use std::{
13    array::from_fn,
14    fmt::{self, Debug, Display, Formatter},
15    iter::Sum,
16    marker::PhantomData,
17    ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
18};
19
20use crate::units::{UnitDiv, UnitMul};
21use crate::{
22    ABS_TOL,
23    math::{
24        matrix::vector::Vector,
25        tensor::{
26            HessianBlock, Jacobian, Quantity, Solution, Tensor, TensorArray, rank_0::TensorRank0,
27            rank_1::list::TensorRank1List, rank_2::TensorRank2,
28        },
29        write_tensor_rank_0,
30    },
31    units::Dimensionless,
32};
33
34use crate::math::assert::FiniteDifference;
35
36/// A *d*-dimensional tensor of rank 1.
37///
38/// `D` is the dimension, `I` is the configuration.
39#[repr(transparent)]
40pub struct TensorRank1<const D: usize, I, U = Dimensionless>(
41    pub(super) [Quantity<U>; D],
42    pub(super) PhantomData<I>,
43);
44
45impl<const D: usize, I, U> Clone for TensorRank1<D, I, U> {
46    fn clone(&self) -> Self {
47        Self(self.0, PhantomData)
48    }
49}
50
51impl<const D: usize, I, U> Debug for TensorRank1<D, I, U> {
52    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53        self.0.fmt(f)
54    }
55}
56
57impl<const D: usize, I, U> PartialEq for TensorRank1<D, I, U> {
58    fn eq(&self, other: &Self) -> bool {
59        self.0 == other.0
60    }
61}
62
63impl<const D: usize, I, U> TensorRank1<D, I, U> {
64    pub(super) fn canonical(&self) -> &TensorRank1<D, Reference, Dimensionless> {
65        unsafe { &*(self as *const Self as *const TensorRank1<D, Reference, Dimensionless>) }
66    }
67    /// Asserts that the tensor carries the given unit.
68    pub fn with_unit<V>(self) -> TensorRank1<D, I, V> {
69        relabel(self.into_canonical())
70    }
71    /// Returns the direction the tensor points in.
72    pub fn normalized(self) -> TensorRank1<D, I, Dimensionless> {
73        let norm = self.norm().value();
74        (self / norm).with_unit()
75    }
76    fn into_canonical(self) -> TensorRank1<D, Reference, Dimensionless> {
77        unsafe {
78            (&self as *const Self)
79                .cast::<TensorRank1<D, Reference, Dimensionless>>()
80                .read()
81        }
82    }
83}
84
85pub(super) fn relabel<const D: usize, I, U>(
86    tensor: TensorRank1<D, Reference, Dimensionless>,
87) -> TensorRank1<D, I, U> {
88    unsafe {
89        (&tensor as *const TensorRank1<D, Reference, Dimensionless>)
90            .cast::<TensorRank1<D, I, U>>()
91            .read()
92    }
93}
94
95impl<const D: usize, I, U> TensorRank1<D, I, U> {
96    /// Associated function for const type conversion.
97    pub const fn const_from(array: [TensorRank0; D]) -> Self {
98        let mut entries = [Quantity::new(0.0); D];
99        let mut i = 0;
100        while i < D {
101            entries[i] = Quantity::new(array[i]);
102            i += 1;
103        }
104        Self(entries, PhantomData)
105    }
106}
107
108impl<const D: usize, I, U> Default for TensorRank1<D, I, U> {
109    fn default() -> Self {
110        Self::zero()
111    }
112}
113
114impl<const D: usize, U> From<TensorRank1<D, Reference, U>> for TensorRank1<D, Current, U> {
115    fn from(tensor_rank_1: TensorRank1<D, Reference, U>) -> Self {
116        Self(tensor_rank_1.0, PhantomData)
117    }
118}
119
120impl<const D: usize, U> From<&TensorRank1<D, Reference, U>> for TensorRank1<D, Current, U> {
121    fn from(tensor_rank_1: &TensorRank1<D, Reference, U>) -> Self {
122        Self(tensor_rank_1.0, PhantomData)
123    }
124}
125
126impl<const D: usize, U> From<TensorRank1<D, Current, U>> for TensorRank1<D, Reference, U> {
127    fn from(tensor_rank_1: TensorRank1<D, Current, U>) -> Self {
128        Self(tensor_rank_1.0, PhantomData)
129    }
130}
131
132impl<const D: usize, U> From<&TensorRank1<D, Current, U>> for TensorRank1<D, Reference, U> {
133    fn from(tensor_rank_1: &TensorRank1<D, Current, U>) -> Self {
134        Self(tensor_rank_1.0, PhantomData)
135    }
136}
137
138impl<const D: usize, U> From<TensorRank1<D, Projection, U>> for TensorRank1<D, Reference, U> {
139    fn from(tensor_rank_1: TensorRank1<D, Projection, U>) -> Self {
140        Self(tensor_rank_1.0, PhantomData)
141    }
142}
143
144impl<const D: usize, U> From<&TensorRank1<D, Projection, U>> for TensorRank1<D, Reference, U> {
145    fn from(tensor_rank_1: &TensorRank1<D, Projection, U>) -> Self {
146        Self(tensor_rank_1.0, PhantomData)
147    }
148}
149
150impl<const D: usize, I, U> Display for TensorRank1<D, I, U> {
151    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
152        write!(f, "\x1B[s")?;
153        write!(f, "[")?;
154        self.iter()
155            .try_for_each(|entry| write_tensor_rank_0(f, &entry.value()))?;
156        write!(f, "\x1B[2D]")
157    }
158}
159
160impl<const D: usize, I, U> TensorRank1<D, I, U> {
161    /// Returns a raw pointer to the slice’s buffer.
162    pub const fn as_ptr(&self) -> *const TensorRank0 {
163        self.0.as_ptr().cast()
164    }
165    /// Returns an orthonormal basis whose first vector is this one's direction.
166    pub fn orthonormal_basis(&self) -> TensorRank1List<D, I, D, Dimensionless> {
167        let norm = self.norm().value();
168        assert!(
169            norm > ABS_TOL,
170            "Cannot build an orthonormal basis from the zero vector"
171        );
172        let mut basis = TensorRank1List::zero();
173        basis[0] = (self / norm).with_unit();
174        let mut filled = 1;
175        for i in 0..D {
176            if filled == D {
177                break;
178            }
179            let mut v: TensorRank1<D, I, Dimensionless> = zero();
180            v[i] = Quantity::new(1.0);
181            basis.iter().take(filled).for_each(|q| v -= q * (&v * q));
182            let v_norm = v.norm().value();
183            if v_norm > ABS_TOL {
184                basis[filled] = v / v_norm;
185                filled += 1;
186            }
187        }
188        assert!(filled == D, "Failed to construct full orthonormal basis");
189        basis
190    }
191}
192
193impl<const D: usize, I, U> FiniteDifference for TensorRank1<D, I, U> {
194    fn error_fd(&self, comparator: &Self, epsilon: TensorRank0) -> Option<(bool, usize)> {
195        let error_count = self
196            .iter()
197            .zip(comparator.iter())
198            .filter(|&(&self_i, &comparator_i)| self_i.differs(comparator_i, epsilon))
199            .count();
200        if error_count > 0 {
201            Some((true, error_count))
202        } else {
203            None
204        }
205    }
206}
207
208impl<const D: usize, I, U> Solution for TensorRank1<D, I, U> {
209    fn decrement_from(&mut self, other: &Vector) {
210        self.iter_mut()
211            .zip(other.iter())
212            .for_each(|(self_i, vector_i)| *self_i -= Quantity::new(*vector_i))
213    }
214    fn decrement_from_chained(&mut self, other: &mut Vector, vector: &Vector) {
215        let mut values = vector.iter();
216        self.iter_mut()
217            .zip(values.by_ref())
218            .for_each(|(entry_i, vector_i)| *entry_i -= Quantity::new(*vector_i));
219        other
220            .iter_mut()
221            .zip(values)
222            .for_each(|(entry_i, vector_i)| *entry_i -= vector_i)
223    }
224}
225
226impl<const D: usize, I, U> Jacobian for TensorRank1<D, I, U> {
227    fn fill_into(&self, vector: &mut Vector) {
228        self.iter()
229            .zip(vector.iter_mut())
230            .for_each(|(self_i, vector_i)| *vector_i = self_i.value())
231    }
232    fn fill_into_chained(self, other: Vector, vector: &mut Vector) {
233        self.into_iter()
234            .map(|entry| entry.value())
235            .chain(other)
236            .zip(vector.iter_mut())
237            .for_each(|(self_i, vector_i)| *vector_i = self_i)
238    }
239}
240
241impl<const D: usize, I, U> HessianBlock for TensorRank1<D, I, U> {
242    fn entry(&self, row: usize, _column: usize) -> TensorRank0 {
243        self[row].value()
244    }
245    fn height(&self) -> usize {
246        D
247    }
248    fn width(&self) -> usize {
249        1
250    }
251    fn fill_into_block<M>(&self, matrix: &mut M, row: usize, column: usize)
252    where
253        M: IndexMut<usize, Output = Vector>,
254    {
255        self.iter()
256            .enumerate()
257            .for_each(|(i, self_i)| matrix[row + i][column] = self_i.value())
258    }
259}
260
261impl<const D: usize, I, U> Sub<Vector> for TensorRank1<D, I, U> {
262    type Output = Self;
263    fn sub(mut self, vector: Vector) -> Self::Output {
264        self.iter_mut()
265            .enumerate()
266            .for_each(|(i, self_i)| *self_i -= Quantity::new(vector[i]));
267        self
268    }
269}
270
271impl<const D: usize, I, U> Sub<&Vector> for TensorRank1<D, I, U> {
272    type Output = Self;
273    fn sub(mut self, vector: &Vector) -> Self::Output {
274        self.iter_mut()
275            .enumerate()
276            .for_each(|(i, self_i)| *self_i -= Quantity::new(vector[i]));
277        self
278    }
279}
280
281impl<const D: usize, I, U> Erase for TensorRank1<D, I, U> {
282    type Erased = TensorRank1<D, Reference, Dimensionless>;
283    fn erase(&self) -> &Self::Erased {
284        self.canonical()
285    }
286}
287
288impl<const D: usize, I, U, V> Mul<Quantity<V>> for TensorRank1<D, I, U>
289where
290    U: UnitMul<V>,
291{
292    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
293    fn mul(self, quantity: Quantity<V>) -> Self::Output {
294        relabel(self.into_canonical() * quantity.value())
295    }
296}
297
298impl<const D: usize, I, U, V> Mul<Quantity<V>> for &TensorRank1<D, I, U>
299where
300    U: UnitMul<V>,
301{
302    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
303    fn mul(self, quantity: Quantity<V>) -> Self::Output {
304        relabel(self.canonical() * quantity.value())
305    }
306}
307
308impl<const D: usize, I, U, V> Mul<&Quantity<V>> for TensorRank1<D, I, U>
309where
310    U: UnitMul<V>,
311{
312    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
313    fn mul(self, quantity: &Quantity<V>) -> Self::Output {
314        self * *quantity
315    }
316}
317
318impl<const D: usize, I, U, V> Mul<&Quantity<V>> for &TensorRank1<D, I, U>
319where
320    U: UnitMul<V>,
321{
322    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
323    fn mul(self, quantity: &Quantity<V>) -> Self::Output {
324        self * *quantity
325    }
326}
327
328impl<const D: usize, I, U, V> Div<Quantity<V>> for TensorRank1<D, I, U>
329where
330    U: UnitDiv<V>,
331{
332    type Output = TensorRank1<D, I, <U as UnitDiv<V>>::Output>;
333    fn div(self, quantity: Quantity<V>) -> Self::Output {
334        relabel(self.into_canonical() / quantity.value())
335    }
336}
337
338impl<const D: usize, I, U, V> Div<Quantity<V>> for &TensorRank1<D, I, U>
339where
340    U: UnitDiv<V>,
341{
342    type Output = TensorRank1<D, I, <U as UnitDiv<V>>::Output>;
343    fn div(self, quantity: Quantity<V>) -> Self::Output {
344        relabel(self.canonical() / quantity.value())
345    }
346}
347
348impl<const D: usize, I, U> Tensor for TensorRank1<D, I, U> {
349    type Item = Quantity<U>;
350    type Unit = U;
351    fn full_contraction(&self, tensor_rank_1: &Self) -> TensorRank0 {
352        self.iter()
353            .zip(tensor_rank_1.iter())
354            .map(|(self_i, other_i)| self_i.value().algebraic_mul(other_i.value()))
355            .fold(0.0, f64::algebraic_add)
356    }
357    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
358        self.0.iter()
359    }
360    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> {
361        self.0.iter_mut()
362    }
363    fn len(&self) -> usize {
364        D
365    }
366    fn size(&self) -> usize {
367        D
368    }
369}
370
371impl<const D: usize, I, U> IntoIterator for TensorRank1<D, I, U> {
372    type Item = Quantity<U>;
373    type IntoIter = std::array::IntoIter<Self::Item, D>;
374    fn into_iter(self) -> Self::IntoIter {
375        self.0.into_iter()
376    }
377}
378
379impl<const D: usize, I, U> TensorArray for TensorRank1<D, I, U> {
380    type Array = [Quantity<U>; D];
381    type Item = Quantity<U>;
382    fn as_array(&self) -> Self::Array {
383        self.0
384    }
385    fn identity() -> Self {
386        ones()
387    }
388    fn zero() -> Self {
389        zero()
390    }
391}
392
393/// Returns the rank-1 tensor of ones as a constant.
394pub(crate) const fn ones<const D: usize, I, U>() -> TensorRank1<D, I, U> {
395    TensorRank1([Quantity::new(1.0); D], PhantomData)
396}
397
398/// Returns the rank-1 zero tensor as a constant.
399pub const fn zero<const D: usize, I, U>() -> TensorRank1<D, I, U> {
400    TensorRank1([Quantity::new(0.0); D], PhantomData)
401}
402
403impl<const D: usize, I, U> From<[Quantity<U>; D]> for TensorRank1<D, I, U> {
404    fn from(array: [Quantity<U>; D]) -> Self {
405        Self(array, PhantomData)
406    }
407}
408
409impl<const D: usize, I, U> From<[TensorRank0; D]> for TensorRank1<D, I, U> {
410    fn from(array: [TensorRank0; D]) -> Self {
411        Self(array.map(Quantity::new), PhantomData)
412    }
413}
414
415impl<const D: usize, I, U> From<TensorRank1<D, I, U>> for [TensorRank0; D] {
416    fn from(tensor_rank_1: TensorRank1<D, I, U>) -> Self {
417        tensor_rank_1.0.map(|entry| entry.value())
418    }
419}
420
421impl<const D: usize, I, U> From<Vec<TensorRank0>> for TensorRank1<D, I, U> {
422    fn from(vec: Vec<TensorRank0>) -> Self {
423        Self(
424            TryInto::<[TensorRank0; D]>::try_into(vec)
425                .unwrap()
426                .map(Quantity::new),
427            PhantomData,
428        )
429    }
430}
431
432impl<const D: usize, I, U> From<TensorRank1<D, I, U>> for Vec<TensorRank0> {
433    fn from(tensor_rank_1: TensorRank1<D, I, U>) -> Self {
434        tensor_rank_1.0.iter().map(|entry| entry.value()).collect()
435    }
436}
437
438impl<const D: usize, I, U> From<Vector> for TensorRank1<D, I, U> {
439    fn from(vector: Vector) -> Self {
440        vector.into_iter().take(D).collect()
441    }
442}
443
444impl<const D: usize, I, U> FromIterator<TensorRank0> for TensorRank1<D, I, U> {
445    fn from_iter<Ii: IntoIterator<Item = TensorRank0>>(into_iterator: Ii) -> Self {
446        into_iterator.into_iter().map(Quantity::new).collect()
447    }
448}
449
450impl<const D: usize, I, U> FromIterator<Quantity<U>> for TensorRank1<D, I, U> {
451    fn from_iter<Ii: IntoIterator<Item = Quantity<U>>>(into_iterator: Ii) -> Self {
452        let mut tensor_rank_1 = zero();
453        tensor_rank_1
454            .iter_mut()
455            .zip(into_iterator)
456            .for_each(|(tensor_rank_1_i, value_i)| *tensor_rank_1_i = value_i);
457        tensor_rank_1
458    }
459}
460
461impl<const D: usize, I, U> Index<usize> for TensorRank1<D, I, U> {
462    type Output = Quantity<U>;
463    fn index(&self, index: usize) -> &Self::Output {
464        &self.0[index]
465    }
466}
467
468impl<const D: usize, I, U> IndexMut<usize> for TensorRank1<D, I, U> {
469    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
470        &mut self.0[index]
471    }
472}
473
474impl<const D: usize, I, U> Sum for TensorRank1<D, I, U> {
475    fn sum<Ii>(iter: Ii) -> Self
476    where
477        Ii: Iterator<Item = Self>,
478    {
479        iter.reduce(|mut acc, item| {
480            acc += item;
481            acc
482        })
483        .unwrap_or_else(Self::default)
484    }
485}
486
487impl<'a, const D: usize, I, U> Sum<&'a Self> for TensorRank1<D, I, U> {
488    fn sum<Ii>(iter: Ii) -> Self
489    where
490        Ii: Iterator<Item = &'a Self>,
491    {
492        iter.fold(Self::default(), |mut acc, item| {
493            acc += item;
494            acc
495        })
496    }
497}
498
499impl<const D: usize, I, U> Neg for TensorRank1<D, I, U> {
500    type Output = Self;
501    fn neg(self) -> Self::Output {
502        from_fn(|i| -self[i]).into()
503    }
504}
505
506impl<const D: usize, I, U> Neg for &TensorRank1<D, I, U> {
507    type Output = TensorRank1<D, I, U>;
508    fn neg(self) -> Self::Output {
509        from_fn(|i| -self[i]).into()
510    }
511}
512
513impl<const D: usize, I, U> Div<TensorRank0> for TensorRank1<D, I, U> {
514    type Output = Self;
515    fn div(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
516        self /= tensor_rank_0;
517        self
518    }
519}
520
521impl<const D: usize, I, U> Div<TensorRank0> for &TensorRank1<D, I, U> {
522    type Output = TensorRank1<D, I, U>;
523    fn div(self, tensor_rank_0: TensorRank0) -> Self::Output {
524        self.iter().map(|self_i| self_i / tensor_rank_0).collect()
525    }
526}
527
528impl<const D: usize, I, U> Div<&TensorRank0> for TensorRank1<D, I, U> {
529    type Output = Self;
530    fn div(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
531        self /= tensor_rank_0;
532        self
533    }
534}
535
536impl<const D: usize, I, U> Div<&TensorRank0> for &TensorRank1<D, I, U> {
537    type Output = TensorRank1<D, I, U>;
538    fn div(self, tensor_rank_0: &TensorRank0) -> Self::Output {
539        self.iter().map(|self_i| self_i / tensor_rank_0).collect()
540    }
541}
542
543impl<const D: usize, I, U> DivAssign<TensorRank0> for TensorRank1<D, I, U> {
544    fn div_assign(&mut self, tensor_rank_0: TensorRank0) {
545        self.iter_mut().for_each(|self_i| *self_i /= &tensor_rank_0);
546    }
547}
548
549impl<const D: usize, I, U> DivAssign<&TensorRank0> for TensorRank1<D, I, U> {
550    fn div_assign(&mut self, tensor_rank_0: &TensorRank0) {
551        self.iter_mut().for_each(|self_i| *self_i /= tensor_rank_0);
552    }
553}
554
555impl<const D: usize, I, U> Mul<TensorRank0> for TensorRank1<D, I, U> {
556    type Output = Self;
557    fn mul(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
558        self *= tensor_rank_0;
559        self
560    }
561}
562
563impl<const D: usize, I, U> Mul<TensorRank0> for &TensorRank1<D, I, U> {
564    type Output = TensorRank1<D, I, U>;
565    fn mul(self, tensor_rank_0: TensorRank0) -> Self::Output {
566        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
567    }
568}
569
570impl<const D: usize, I, U> Mul<&TensorRank0> for TensorRank1<D, I, U> {
571    type Output = Self;
572    fn mul(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
573        self *= tensor_rank_0;
574        self
575    }
576}
577
578impl<const D: usize, I, U> Mul<&TensorRank0> for &TensorRank1<D, I, U> {
579    type Output = TensorRank1<D, I, U>;
580    fn mul(self, tensor_rank_0: &TensorRank0) -> Self::Output {
581        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
582    }
583}
584
585impl<const D: usize, I, U> MulAssign<TensorRank0> for TensorRank1<D, I, U> {
586    fn mul_assign(&mut self, tensor_rank_0: TensorRank0) {
587        self.iter_mut().for_each(|self_i| *self_i *= &tensor_rank_0);
588    }
589}
590
591impl<const D: usize, I, U> MulAssign<&TensorRank0> for TensorRank1<D, I, U> {
592    fn mul_assign(&mut self, tensor_rank_0: &TensorRank0) {
593        self.iter_mut().for_each(|self_i| *self_i *= tensor_rank_0);
594    }
595}
596
597impl<const D: usize, I, U> Add for TensorRank1<D, I, U> {
598    type Output = Self;
599    fn add(mut self, tensor_rank_1: Self) -> Self::Output {
600        self += tensor_rank_1;
601        self
602    }
603}
604
605impl<const D: usize, I, U> Add<&Self> for TensorRank1<D, I, U> {
606    type Output = Self;
607    fn add(mut self, tensor_rank_1: &Self) -> Self::Output {
608        self += tensor_rank_1;
609        self
610    }
611}
612
613impl<const D: usize, I, U> Add<TensorRank1<D, I, U>> for &TensorRank1<D, I, U> {
614    type Output = TensorRank1<D, I, U>;
615    fn add(self, mut tensor_rank_1: TensorRank1<D, I, U>) -> Self::Output {
616        tensor_rank_1 += self;
617        tensor_rank_1
618    }
619}
620
621impl<const D: usize, I, U> Add<Self> for &TensorRank1<D, I, U> {
622    type Output = TensorRank1<D, I, U>;
623    fn add(self, tensor_rank_1: Self) -> Self::Output {
624        tensor_rank_1
625            .iter()
626            .zip(self.iter())
627            .map(|(tensor_rank_1_i, self_i)| self_i + *tensor_rank_1_i)
628            .collect()
629    }
630}
631
632impl<const D: usize, I, U> AddAssign for TensorRank1<D, I, U> {
633    fn add_assign(&mut self, tensor_rank_1: Self) {
634        self.iter_mut()
635            .zip(tensor_rank_1)
636            .for_each(|(self_i, tensor_rank_1_i)| *self_i += tensor_rank_1_i);
637    }
638}
639
640impl<const D: usize, I, U> AddAssign<&Self> for TensorRank1<D, I, U> {
641    fn add_assign(&mut self, tensor_rank_1: &Self) {
642        self.iter_mut()
643            .zip(tensor_rank_1.iter())
644            .for_each(|(self_i, tensor_rank_1_i)| *self_i += tensor_rank_1_i);
645    }
646}
647
648impl<const D: usize, I, U> Sub for TensorRank1<D, I, U> {
649    type Output = Self;
650    fn sub(mut self, tensor_rank_1: Self) -> Self::Output {
651        self -= tensor_rank_1;
652        self
653    }
654}
655
656impl<const D: usize, I, U> Sub<&Self> for TensorRank1<D, I, U> {
657    type Output = Self;
658    fn sub(mut self, tensor_rank_1: &Self) -> Self::Output {
659        self -= tensor_rank_1;
660        self
661    }
662}
663
664impl<const D: usize, I, U> Sub<TensorRank1<D, I, U>> for &TensorRank1<D, I, U> {
665    type Output = TensorRank1<D, I, U>;
666    fn sub(self, mut tensor_rank_1: TensorRank1<D, I, U>) -> Self::Output {
667        tensor_rank_1
668            .iter_mut()
669            .zip(self.iter())
670            .for_each(|(tensor_rank_1_i, self_i)| *tensor_rank_1_i = self_i - *tensor_rank_1_i);
671        tensor_rank_1
672    }
673}
674
675impl<const D: usize, I, U> Sub<Self> for &TensorRank1<D, I, U> {
676    type Output = TensorRank1<D, I, U>;
677    fn sub(self, tensor_rank_1: Self) -> Self::Output {
678        tensor_rank_1
679            .iter()
680            .zip(self.iter())
681            .map(|(tensor_rank_1_i, self_i)| self_i - *tensor_rank_1_i)
682            .collect()
683    }
684}
685
686impl<const D: usize, I, U> SubAssign for TensorRank1<D, I, U> {
687    fn sub_assign(&mut self, tensor_rank_1: Self) {
688        self.iter_mut()
689            .zip(tensor_rank_1)
690            .for_each(|(self_i, tensor_rank_1_i)| *self_i -= tensor_rank_1_i);
691    }
692}
693
694impl<const D: usize, I, U> SubAssign<&Self> for TensorRank1<D, I, U> {
695    fn sub_assign(&mut self, tensor_rank_1: &Self) {
696        self.iter_mut()
697            .zip(tensor_rank_1.iter())
698            .for_each(|(self_i, tensor_rank_1_i)| *self_i -= tensor_rank_1_i);
699    }
700}
701
702impl<const D: usize, I, U> Mul for TensorRank1<D, I, U> {
703    type Output = TensorRank0;
704    fn mul(self, tensor_rank_1: Self) -> Self::Output {
705        self.into_iter()
706            .zip(tensor_rank_1)
707            .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
708            .sum()
709    }
710}
711
712impl<const D: usize, I, U, V> Mul<&TensorRank1<D, I, V>> for TensorRank1<D, I, U>
713where
714    U: UnitMul<V>,
715{
716    type Output = Quantity<<U as UnitMul<V>>::Output>;
717    fn mul(self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
718        Quantity::new(
719            self.into_iter()
720                .zip(tensor_rank_1.iter())
721                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
722                .sum(),
723        )
724    }
725}
726
727impl<const D: usize, I, U, V> Mul<TensorRank1<D, I, V>> for &TensorRank1<D, I, U>
728where
729    U: UnitMul<V>,
730{
731    type Output = Quantity<<U as UnitMul<V>>::Output>;
732    fn mul(self, tensor_rank_1: TensorRank1<D, I, V>) -> Self::Output {
733        Quantity::new(
734            self.iter()
735                .zip(tensor_rank_1)
736                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
737                .sum(),
738        )
739    }
740}
741
742impl<const D: usize, I, U, V> Mul<&TensorRank1<D, I, V>> for &TensorRank1<D, I, U>
743where
744    U: UnitMul<V>,
745{
746    type Output = Quantity<<U as UnitMul<V>>::Output>;
747    fn mul(self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
748        Quantity::new(
749            self.iter()
750                .zip(tensor_rank_1.iter())
751                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
752                .sum(),
753        )
754    }
755}
756
757#[expect(clippy::suspicious_arithmetic_impl)]
758impl<const D: usize, I, J, U, V> Div<TensorRank2<D, I, J, V>> for &TensorRank1<D, I, U>
759where
760    U: UnitDiv<V>,
761{
762    type Output = TensorRank1<D, J, <U as UnitDiv<V>>::Output>;
763    fn div(self, tensor_rank_2: TensorRank2<D, I, J, V>) -> Self::Output {
764        relabel(tensor_rank_2.canonical().clone().inverse() * self.canonical())
765    }
766}
767
768impl<const D: usize, I, U, V> ContractWith<TensorRank1<D, I, V>> for TensorRank1<D, I, U>
769where
770    U: UnitMul<V>,
771{
772    type Output = Quantity<<U as UnitMul<V>>::Output>;
773    fn contract_with(&self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
774        Quantity::new(self.canonical().full_contraction(tensor_rank_1.canonical()))
775    }
776}
777
778impl<const D: usize, I, U, T> Differentiable<T> for TensorRank1<D, I, U>
779where
780    U: UnitDiv<T>,
781{
782    type Derivative = TensorRank1<D, I, <U as UnitDiv<T>>::Output>;
783}