diffgeom/tensors/
tensor.rs

1//! This module defines the `Tensor` type and all sorts of operations on it.
2
3use super::variance::{Concat, Contract, Contracted, Joined, OtherIndex};
4use super::{ContravariantIndex, CovariantIndex, IndexType, TensorIndex, Variance};
5use crate::coordinates::{ConversionTo, CoordinateSystem, Point};
6use crate::typenum::consts::{B1, U2};
7use crate::typenum::uint::Unsigned;
8use crate::typenum::{Add1, Exp, Pow, Same};
9use generic_array::{ArrayLength, GenericArray};
10use std::ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
11use std::ops::{Index, IndexMut};
12
13/// Struct representing a tensor.
14///
15/// A tensor is anchored at a given point and has coordinates
16/// represented in the system defined by the generic parameter
17/// `T`. The variance of the tensor (meaning its rank and types
18/// of its indices) is defined by `V`. This allows Rust
19/// to decide at compile time whether two tensors are legal
20/// to be added / multiplied / etc.
21///
22/// It is only OK to perform an operation on two tensors if
23/// they belong to the same coordinate system.
24pub struct Tensor<T: CoordinateSystem, U: Variance>
25where
26    T::Dimension: Pow<U::Rank>,
27    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
28{
29    p: Point<T>,
30    x: GenericArray<f64, Exp<T::Dimension, U::Rank>>,
31}
32
33impl<T, U> Clone for Tensor<T, U>
34where
35    T: CoordinateSystem,
36    U: Variance,
37    T::Dimension: Pow<U::Rank>,
38    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
39{
40    fn clone(&self) -> Tensor<T, U> {
41        Tensor {
42            p: self.p.clone(),
43            x: self.x.clone(),
44        }
45    }
46}
47
48impl<T, U> Copy for Tensor<T, U>
49where
50    T: CoordinateSystem,
51    U: Variance,
52    T::Dimension: Pow<U::Rank>,
53    <T::Dimension as ArrayLength<f64>>::ArrayType: Copy,
54    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
55    <Exp<T::Dimension, U::Rank> as ArrayLength<f64>>::ArrayType: Copy,
56{
57}
58
59/// A struct for iterating over the coordinates of a tensor.
60pub struct CoordIterator<U>
61where
62    U: Variance,
63    U::Rank: ArrayLength<usize>,
64{
65    started: bool,
66    dimension: usize,
67    cur_coord: GenericArray<usize, U::Rank>,
68}
69
70impl<U> CoordIterator<U>
71where
72    U: Variance,
73    U::Rank: ArrayLength<usize>,
74{
75    pub fn new(dimension: usize) -> CoordIterator<U> {
76        CoordIterator {
77            started: false,
78            dimension: dimension,
79            cur_coord: GenericArray::default(),
80        }
81    }
82}
83
84impl<U> Iterator for CoordIterator<U>
85where
86    U: Variance,
87    U::Rank: ArrayLength<usize>,
88{
89    type Item = GenericArray<usize, U::Rank>;
90
91    fn next(&mut self) -> Option<Self::Item> {
92        if !self.started {
93            self.started = true;
94            return Some(self.cur_coord.clone());
95        }
96
97        // handle scalars
98        if self.cur_coord.len() < 1 {
99            return None;
100        }
101
102        let mut i = self.cur_coord.len() - 1;
103        loop {
104            self.cur_coord[i] += 1;
105            if self.cur_coord[i] < self.dimension {
106                break;
107            }
108            self.cur_coord[i] = 0;
109            if i == 0 {
110                return None;
111            }
112            i -= 1;
113        }
114
115        Some(self.cur_coord.clone())
116    }
117}
118
119impl<T, V> Tensor<T, V>
120where
121    T: CoordinateSystem,
122    V: Variance,
123    T::Dimension: Pow<V::Rank>,
124    Exp<T::Dimension, V::Rank>: ArrayLength<f64>,
125{
126    /// Returns the point at which the tensor is defined.
127    pub fn get_point(&self) -> &Point<T> {
128        &self.p
129    }
130
131    /// Sets the point at which the tensor is defined.
132    pub fn set_point(&mut self, p: Point<T>) {
133        self.p = p;
134    }
135
136    /// Returns the tensor's coordinates as an array
137    pub fn coords_array(&self) -> &GenericArray<f64, Exp<T::Dimension, V::Rank>> {
138        &self.x
139    }
140
141    /// Converts a set of tensor indices passed as a slice into a single index
142    /// for the internal array.
143    ///
144    /// The length of the slice (the number of indices) has to be compatible
145    /// with the rank of the tensor.
146    pub fn get_coord(i: &[usize]) -> usize {
147        assert_eq!(i.len(), V::rank());
148        let dim = T::dimension();
149        let index = i.into_iter().fold(0, |res, idx| {
150            assert!(*idx < dim);
151            res * dim + idx
152        });
153        index
154    }
155
156    /// Returns the variance of the tensor, that is, the list of the index types.
157    /// A vector would return vec![Contravariant], a metric tensor: vec![Covariant, Covariant].
158    pub fn get_variance() -> Vec<IndexType> {
159        V::variance()
160    }
161
162    /// Returns the rank of the tensor
163    pub fn get_rank() -> usize {
164        V::rank()
165    }
166
167    /// Returns the number of coordinates of the tensor (equal to [Dimension]^[Rank])
168    pub fn get_num_coords() -> usize {
169        <T::Dimension as Pow<V::Rank>>::Output::to_usize()
170    }
171
172    /// Creates a new, zero tensor at a given point
173    pub fn zero(point: Point<T>) -> Tensor<T, V> {
174        Tensor {
175            p: point,
176            x: GenericArray::default(),
177        }
178    }
179
180    /// Creates a tensor at a given point with the coordinates defined by the array.
181    ///
182    /// The number of elements in the array must be equal to the number of coordinates
183    /// of the tensor.
184    ///
185    /// One-dimensional array represents an n-dimensional tensor in such a way, that
186    /// the last index is the one that is changing the most often, i.e. the sequence is
187    /// as follows:
188    /// (0,0,...,0), (0,0,...,1), (0,0,...,2), ..., (0,0,...,1,0), (0,0,...,1,1), ... etc.
189    pub fn new(
190        point: Point<T>,
191        coords: GenericArray<f64, Exp<T::Dimension, V::Rank>>,
192    ) -> Tensor<T, V> {
193        Tensor {
194            p: point,
195            x: coords,
196        }
197    }
198
199    /// Creates a tensor at a given point with the coordinates defined by the slice.
200    ///
201    /// The number of elements in the slice must be equal to the number of coordinates
202    /// of the tensor.
203    ///
204    /// One-dimensional slice represents an n-dimensional tensor in such a way, that
205    /// the last index is the one that is changing the most often, i.e. the sequence is
206    /// as follows:
207    /// (0,0,...,0), (0,0,...,1), (0,0,...,2), ..., (0,0,...,1,0), (0,0,...,1,1), ... etc.
208    pub fn from_slice(point: Point<T>, slice: &[f64]) -> Tensor<T, V> {
209        assert_eq!(Tensor::<T, V>::get_num_coords(), slice.len());
210        Tensor {
211            p: point,
212            x: GenericArray::clone_from_slice(slice),
213        }
214    }
215
216    /// Contracts two indices
217    ///
218    /// The indices must be of opposite types. This is checked at compile time.
219    pub fn trace<Ul, Uh>(&self) -> Tensor<T, Contracted<V, Ul, Uh>>
220    where
221        Ul: Unsigned,
222        Uh: Unsigned,
223        V: Contract<Ul, Uh>,
224        <Contracted<V, Ul, Uh> as Variance>::Rank: ArrayLength<usize>,
225        T::Dimension: Pow<<Contracted<V, Ul, Uh> as Variance>::Rank>,
226        Exp<T::Dimension, <Contracted<V, Ul, Uh> as Variance>::Rank>: ArrayLength<f64>,
227    {
228        let index1 = Ul::to_usize();
229        let index2 = Uh::to_usize();
230        let rank = V::Rank::to_usize();
231        let dim = T::Dimension::to_usize();
232
233        let mut result = Tensor::<T, Contracted<V, Ul, Uh>>::zero(self.p.clone());
234        let num_coords_result = Tensor::<T, Contracted<V, Ul, Uh>>::get_num_coords();
235        let modh = dim.pow((rank - 1 - index2) as u32);
236        let modl = dim.pow((rank - 2 - index1) as u32);
237
238        for coord in 0..num_coords_result {
239            let coord1 = coord / modl;
240            let coord1rest = coord % modl;
241            let coord2 = coord1rest / modh;
242            let coord2rest = coord1rest % modh;
243            let coord_template = coord1 * modl * dim * dim + coord2 * modh * dim + coord2rest;
244            let mut sum = 0.0;
245
246            for i in 0..T::dimension() {
247                sum += self[coord_template + i * modl * dim + i * modh];
248            }
249
250            result[coord] = sum;
251        }
252
253        result
254    }
255}
256
257impl<T, U> Tensor<T, U>
258where
259    T: CoordinateSystem,
260    U: Variance,
261    U::Rank: ArrayLength<usize>,
262    T::Dimension: Pow<U::Rank>,
263    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
264{
265    /// Returns an iterator over the coordinates of the tensor.
266    pub fn iter_coords(&self) -> CoordIterator<U> {
267        CoordIterator::new(T::dimension())
268    }
269}
270
271impl<'a, T, U> Index<&'a [usize]> for Tensor<T, U>
272where
273    T: CoordinateSystem,
274    U: Variance,
275    T::Dimension: Pow<U::Rank>,
276    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
277{
278    type Output = f64;
279
280    fn index(&self, idx: &'a [usize]) -> &f64 {
281        &self.x[Self::get_coord(idx)]
282    }
283}
284
285impl<'a, T, U> IndexMut<&'a [usize]> for Tensor<T, U>
286where
287    T: CoordinateSystem,
288    U: Variance,
289    T::Dimension: Pow<U::Rank>,
290    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
291{
292    fn index_mut(&mut self, idx: &'a [usize]) -> &mut f64 {
293        &mut self.x[Self::get_coord(idx)]
294    }
295}
296
297impl<'a, T, U> Index<usize> for Tensor<T, U>
298where
299    T: CoordinateSystem,
300    U: Variance,
301    T::Dimension: Pow<U::Rank>,
302    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
303{
304    type Output = f64;
305
306    fn index(&self, idx: usize) -> &f64 {
307        &self.x[idx]
308    }
309}
310
311impl<'a, T, U> IndexMut<usize> for Tensor<T, U>
312where
313    T: CoordinateSystem,
314    U: Variance,
315    T::Dimension: Pow<U::Rank>,
316    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
317{
318    fn index_mut(&mut self, idx: usize) -> &mut f64 {
319        &mut self.x[idx]
320    }
321}
322
323/// A scalar type, which is a tensor with rank 0.
324///
325/// This is de facto just a number, so it implements `Deref` and `DerefMut` into `f64`.
326pub type Scalar<T> = Tensor<T, ()>;
327
328/// A vector type (rank 1 contravariant tensor)
329pub type Vector<T> = Tensor<T, ContravariantIndex>;
330
331/// A covector type (rank 1 covariant tensor)
332pub type Covector<T> = Tensor<T, CovariantIndex>;
333
334/// A matrix type (rank 2 contravariant-covariant tensor)
335pub type Matrix<T> = Tensor<T, (ContravariantIndex, CovariantIndex)>;
336
337/// A bilinear form type (rank 2 doubly covariant tensor)
338pub type TwoForm<T> = Tensor<T, (CovariantIndex, CovariantIndex)>;
339
340/// A rank 2 doubly contravariant tensor
341pub type InvTwoForm<T> = Tensor<T, (ContravariantIndex, ContravariantIndex)>;
342
343impl<T: CoordinateSystem> Deref for Scalar<T> {
344    type Target = f64;
345
346    fn deref(&self) -> &f64 {
347        &self.x[0]
348    }
349}
350
351impl<T: CoordinateSystem> DerefMut for Scalar<T> {
352    fn deref_mut(&mut self) -> &mut f64 {
353        &mut self.x[0]
354    }
355}
356
357// Arithmetic operations
358
359impl<T, U> AddAssign<Tensor<T, U>> for Tensor<T, U>
360where
361    T: CoordinateSystem,
362    U: Variance,
363    T::Dimension: Pow<U::Rank>,
364    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
365{
366    fn add_assign(&mut self, rhs: Tensor<T, U>) {
367        assert!(self.p == rhs.p);
368        for i in 0..(Tensor::<T, U>::get_num_coords()) {
369            self[i] += rhs[i];
370        }
371    }
372}
373
374impl<T, U> Add<Tensor<T, U>> for Tensor<T, U>
375where
376    T: CoordinateSystem,
377    U: Variance,
378    T::Dimension: Pow<U::Rank>,
379    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
380{
381    type Output = Tensor<T, U>;
382
383    fn add(mut self, rhs: Tensor<T, U>) -> Tensor<T, U> {
384        self += rhs;
385        self
386    }
387}
388
389impl<T, U> SubAssign<Tensor<T, U>> for Tensor<T, U>
390where
391    T: CoordinateSystem,
392    U: Variance,
393    T::Dimension: Pow<U::Rank>,
394    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
395{
396    fn sub_assign(&mut self, rhs: Tensor<T, U>) {
397        assert!(self.p == rhs.p);
398        for i in 0..(Tensor::<T, U>::get_num_coords()) {
399            self[i] -= rhs[i];
400        }
401    }
402}
403
404impl<T, U> Sub<Tensor<T, U>> for Tensor<T, U>
405where
406    T: CoordinateSystem,
407    U: Variance,
408    T::Dimension: Pow<U::Rank>,
409    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
410{
411    type Output = Tensor<T, U>;
412
413    fn sub(mut self, rhs: Tensor<T, U>) -> Tensor<T, U> {
414        self -= rhs;
415        self
416    }
417}
418
419impl<T, U> MulAssign<f64> for Tensor<T, U>
420where
421    T: CoordinateSystem,
422    U: Variance,
423    T::Dimension: Pow<U::Rank>,
424    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
425{
426    fn mul_assign(&mut self, rhs: f64) {
427        for i in 0..(Tensor::<T, U>::get_num_coords()) {
428            self[i] *= rhs;
429        }
430    }
431}
432
433impl<T, U> Mul<f64> for Tensor<T, U>
434where
435    T: CoordinateSystem,
436    U: Variance,
437    T::Dimension: Pow<U::Rank>,
438    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
439{
440    type Output = Tensor<T, U>;
441
442    fn mul(mut self, rhs: f64) -> Tensor<T, U> {
443        self *= rhs;
444        self
445    }
446}
447
448impl<T, U> Mul<Tensor<T, U>> for f64
449where
450    T: CoordinateSystem,
451    U: Variance,
452    T::Dimension: Pow<U::Rank>,
453    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
454{
455    type Output = Tensor<T, U>;
456
457    fn mul(self, mut rhs: Tensor<T, U>) -> Tensor<T, U> {
458        rhs *= self;
459        rhs
460    }
461}
462
463impl<T, U> DivAssign<f64> for Tensor<T, U>
464where
465    T: CoordinateSystem,
466    U: Variance,
467    T::Dimension: Pow<U::Rank>,
468    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
469{
470    fn div_assign(&mut self, rhs: f64) {
471        for i in 0..(Tensor::<T, U>::get_num_coords()) {
472            self[i] /= rhs;
473        }
474    }
475}
476
477impl<T, U> Div<f64> for Tensor<T, U>
478where
479    T: CoordinateSystem,
480    U: Variance,
481    T::Dimension: Pow<U::Rank>,
482    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
483{
484    type Output = Tensor<T, U>;
485
486    fn div(mut self, rhs: f64) -> Tensor<T, U> {
487        self /= rhs;
488        self
489    }
490}
491
492// Tensor multiplication
493
494// For some reason this triggers recursion overflow when tested - to be investigated
495impl<T, U, V> Mul<Tensor<T, V>> for Tensor<T, U>
496where
497    T: CoordinateSystem,
498    U: Variance,
499    V: Variance,
500    U::Rank: ArrayLength<usize>,
501    V::Rank: ArrayLength<usize>,
502    T::Dimension: Pow<U::Rank> + Pow<V::Rank>,
503    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
504    Exp<T::Dimension, V::Rank>: ArrayLength<f64>,
505    U: Concat<V>,
506    Joined<U, V>: Variance,
507    T::Dimension: Pow<<Joined<U, V> as Variance>::Rank>,
508    Exp<T::Dimension, <Joined<U, V> as Variance>::Rank>: ArrayLength<f64>,
509{
510    type Output = Tensor<T, Joined<U, V>>;
511
512    fn mul(self, rhs: Tensor<T, V>) -> Tensor<T, Joined<U, V>> {
513        assert!(self.p == rhs.p);
514        let mut result = Tensor::zero(self.p.clone());
515        let num_coords2 = Tensor::<T, V>::get_num_coords();
516        let num_coords_result = Tensor::<T, Joined<U, V>>::get_num_coords();
517        for coord in 0..num_coords_result {
518            let coord1 = coord / num_coords2;
519            let coord2 = coord % num_coords2;
520            result[coord] = self[coord1] * rhs[coord2];
521        }
522        result
523    }
524}
525
526/// Trait representing the inner product of two tensors.
527///
528/// The inner product is just a multiplication followed by a contraction.
529/// The contraction is defined by type parameters `Ul` and `Uh`. `Ul` has to
530/// be less than `Uh` and the indices at those positions must be of opposite types
531/// (checked at compile time)
532pub trait InnerProduct<Rhs, Ul: Unsigned, Uh: Unsigned> {
533    type Output;
534
535    fn inner_product(self, rhs: Rhs) -> Self::Output;
536}
537
538impl<T, U, V, Ul, Uh> InnerProduct<Tensor<T, V>, Ul, Uh> for Tensor<T, U>
539where
540    T: CoordinateSystem,
541    U: Variance,
542    V: Variance,
543    Ul: Unsigned,
544    Uh: Unsigned,
545    T::Dimension: Pow<U::Rank> + Pow<V::Rank>,
546    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
547    Exp<T::Dimension, V::Rank>: ArrayLength<f64>,
548    U: Concat<V>,
549    Joined<U, V>: Contract<Ul, Uh>,
550    <Contracted<Joined<U, V>, Ul, Uh> as Variance>::Rank: ArrayLength<usize>,
551    T::Dimension: Pow<<Contracted<Joined<U, V>, Ul, Uh> as Variance>::Rank>,
552    Exp<T::Dimension, <Contracted<Joined<U, V>, Ul, Uh> as Variance>::Rank>: ArrayLength<f64>,
553{
554    type Output = Tensor<T, Contracted<Joined<U, V>, Ul, Uh>>;
555
556    fn inner_product(self, rhs: Tensor<T, V>) -> Tensor<T, Contracted<Joined<U, V>, Ul, Uh>> {
557        assert_eq!(self.p, rhs.p);
558        let indexl = Ul::to_usize();
559        let indexh = Uh::to_usize();
560        let num_coords_result = Tensor::<T, Contracted<Joined<U, V>, Ul, Uh>>::get_num_coords();
561        let u_rank = U::Rank::to_usize();
562        let v_rank = V::Rank::to_usize();
563        let dim = T::Dimension::to_usize();
564
565        let mut result = Tensor::<T, Contracted<Joined<U, V>, Ul, Uh>>::zero(self.p.clone());
566        let (modl, modh, modv) = match (indexl < u_rank, indexh < u_rank) {
567            (true, true) => (
568                dim.pow((u_rank - 2 - indexl) as u32),
569                dim.pow((u_rank - 1 - indexh) as u32),
570                dim.pow(v_rank as u32),
571            ),
572            (true, false) => (
573                dim.pow((u_rank - 1 - indexl) as u32),
574                dim.pow((u_rank + v_rank - 1 - indexh) as u32),
575                dim.pow((v_rank - 1) as u32),
576            ),
577            (false, false) => (
578                dim.pow((u_rank + v_rank - 2 - indexl) as u32),
579                dim.pow((u_rank + v_rank - 1 - indexh) as u32),
580                dim.pow(v_rank as u32),
581            ),
582            _ => unreachable!(),
583        };
584
585        let to_templates_both1 = |coord| {
586            let coords1 = coord / modv;
587            let coords2 = coord % modv;
588            let coords1part1 = coords1 / modl;
589            let coords1part2 = (coords1 % modl) / modh;
590            let coords1part3 = coords1 % modh;
591            (
592                coords1part1 * modl * dim * dim + coords1part2 * modh * dim + coords1part3,
593                coords2,
594                modl + modh,
595                0,
596            )
597        };
598
599        let to_templates_both2 = |coord| {
600            let coords1 = coord / modv;
601            let coords2 = coord % modv;
602            let coords2part1 = coords2 / modl;
603            let coords2part2 = (coords2 % modl) / modh;
604            let coords2part3 = coords2 % modh;
605            (
606                coords1,
607                coords2part1 * modl * dim * dim + coords2part2 * modh * dim + coords2part3,
608                0,
609                modl + modh,
610            )
611        };
612
613        let to_templates = |coord| {
614            let coords1 = coord / modv;
615            let coords2 = coord % modv;
616            let coords1part1 = coords1 / modl;
617            let coords1part2 = coords1 % modl;
618            let coords2part1 = coords2 / modh;
619            let coords2part2 = coords2 % modh;
620            (
621                coords1part1 * modl * dim + coords1part2,
622                coords2part1 * modh * dim + coords2part2,
623                modl,
624                modh,
625            )
626        };
627
628        let templates: &Fn(usize) -> (usize, usize, usize, usize) =
629            match (indexl < u_rank, indexh < u_rank) {
630                (false, false) => &to_templates_both2,
631                (true, false) => &to_templates,
632                (true, true) => &to_templates_both1,
633                _ => unreachable!(),
634            };
635
636        for coord in 0..num_coords_result {
637            let mut sum = 0.0;
638            let (mut coord1, mut coord2, step1, step2) = templates(coord);
639            for _ in 0..dim {
640                sum += self[coord1] * rhs[coord2];
641                coord1 += step1;
642                coord2 += step2;
643            }
644            result[coord] = sum;
645        }
646
647        result
648    }
649}
650
651impl<T, Ul, Ur> Tensor<T, (Ul, Ur)>
652where
653    T: CoordinateSystem,
654    Ul: TensorIndex + OtherIndex,
655    Ur: TensorIndex + OtherIndex,
656    Add1<Ul::Rank>: Unsigned + Add<B1>,
657    Add1<Ur::Rank>: Unsigned + Add<B1>,
658    Add1<<<Ul as OtherIndex>::Output as Variance>::Rank>: Unsigned + Add<B1>,
659    Add1<<<Ur as OtherIndex>::Output as Variance>::Rank>: Unsigned + Add<B1>,
660    <(Ul, Ur) as Variance>::Rank: ArrayLength<usize>,
661    T::Dimension: Pow<Add1<Ul::Rank>> + Pow<Add1<Ur::Rank>> + ArrayLength<usize>,
662    T::Dimension: Pow<Add1<<<Ul as OtherIndex>::Output as Variance>::Rank>>,
663    T::Dimension: Pow<Add1<<<Ur as OtherIndex>::Output as Variance>::Rank>>,
664    Exp<T::Dimension, Add1<Ul::Rank>>: ArrayLength<f64>,
665    Exp<T::Dimension, Add1<Ur::Rank>>: ArrayLength<f64>,
666    Exp<T::Dimension, Add1<<<Ul as OtherIndex>::Output as Variance>::Rank>>: ArrayLength<f64>,
667    Exp<T::Dimension, Add1<<<Ur as OtherIndex>::Output as Variance>::Rank>>: ArrayLength<f64>,
668{
669    /// Returns a unit matrix (1 on the diagonal, 0 everywhere else)
670    pub fn unit(p: Point<T>) -> Tensor<T, (Ul, Ur)> {
671        let mut result = Tensor::<T, (Ul, Ur)>::zero(p);
672
673        for i in 0..T::dimension() {
674            let coords: &[usize] = &[i, i];
675            result[coords] = 1.0;
676        }
677
678        result
679    }
680
681    /// Transposes the matrix
682    pub fn transpose(&self) -> Tensor<T, (Ur, Ul)> {
683        let mut result = Tensor::<T, (Ur, Ul)>::zero(self.p.clone());
684
685        for coords in self.iter_coords() {
686            let coords2: &[usize] = &[coords[1], coords[0]];
687            result[coords2] = self[&*coords];
688        }
689
690        result
691    }
692
693    // Function calculating the LU decomposition of a matrix - found in the internet
694    // The decomposition is done in-place and a permutation vector is returned (or None
695    // if the matrix was singular)
696    fn lu_decompose(&mut self) -> Option<GenericArray<usize, T::Dimension>> {
697        let n = T::dimension();
698        let absmin = 1.0e-30_f64;
699        let mut result = GenericArray::default();
700        let mut row_norm = GenericArray::<f64, T::Dimension>::default();
701
702        let mut max_row = 0;
703
704        for i in 0..n {
705            let mut absmax = 0.0;
706
707            for j in 0..n {
708                let coord: &[usize] = &[i, j];
709                let maxtemp = self[coord].abs();
710                absmax = if maxtemp > absmax { maxtemp } else { absmax };
711            }
712
713            if absmax == 0.0 {
714                return None;
715            }
716
717            row_norm[i] = 1.0 / absmax;
718        }
719
720        for j in 0..n {
721            for i in 0..j {
722                for k in 0..i {
723                    let coord1: &[usize] = &[i, j];
724                    let coord2: &[usize] = &[i, k];
725                    let coord3: &[usize] = &[k, j];
726
727                    self[coord1] -= self[coord2] * self[coord3];
728                }
729            }
730
731            let mut absmax = 0.0;
732
733            for i in j..n {
734                let coord1: &[usize] = &[i, j];
735
736                for k in 0..j {
737                    let coord2: &[usize] = &[i, k];
738                    let coord3: &[usize] = &[k, j];
739
740                    self[coord1] -= self[coord2] * self[coord3];
741                }
742
743                let maxtemp = self[coord1].abs() * row_norm[i];
744
745                if maxtemp > absmax {
746                    absmax = maxtemp;
747                    max_row = i;
748                }
749            }
750
751            if max_row != j {
752                if (j == n - 2) && self[&[j, j + 1] as &[usize]] == 0.0 {
753                    max_row = j;
754                } else {
755                    for k in 0..n {
756                        let jk: &[usize] = &[j, k];
757                        let maxrow_k: &[usize] = &[max_row, k];
758                        let maxtemp = self[jk];
759                        self[jk] = self[maxrow_k];
760                        self[maxrow_k] = maxtemp;
761                    }
762
763                    row_norm[max_row] = row_norm[j];
764                }
765            }
766
767            result[j] = max_row;
768
769            let jj: &[usize] = &[j, j];
770
771            if self[jj] == 0.0 {
772                self[jj] = absmin;
773            }
774
775            if j != n - 1 {
776                let maxtemp = 1.0 / self[jj];
777                for i in j + 1..n {
778                    self[&[i, j] as &[usize]] *= maxtemp;
779                }
780            }
781        }
782
783        Some(result)
784    }
785
786    // Function solving a linear system of equations (self*x = b) using the LU decomposition
787    fn lu_substitution(
788        &self,
789        b: &GenericArray<f64, T::Dimension>,
790        permute: &GenericArray<usize, T::Dimension>,
791    ) -> GenericArray<f64, T::Dimension> {
792        let mut result = b.clone();
793        let n = T::dimension();
794
795        for i in 0..n {
796            let mut tmp = result[permute[i]];
797            result[permute[i]] = result[i];
798            for j in (0..i).rev() {
799                tmp -= self[&[i, j] as &[usize]] * result[j];
800            }
801            result[i] = tmp;
802        }
803
804        for i in (0..n).rev() {
805            for j in i + 1..n {
806                result[i] -= self[&[i, j] as &[usize]] * result[j];
807            }
808            result[i] /= self[&[i, i] as &[usize]];
809        }
810
811        result
812    }
813
814    /// Function calculating the inverse of `self` using the LU ddecomposition.
815    ///
816    /// The return value is an `Option`, since `self` may be non-invertible -
817    /// in such a case, None is returned
818    pub fn inverse(
819        &self,
820    ) -> Option<Tensor<T, (<Ul as OtherIndex>::Output, <Ur as OtherIndex>::Output)>> {
821        let mut result =
822            Tensor::<T, (<Ul as OtherIndex>::Output, <Ur as OtherIndex>::Output)>::zero(
823                self.p.clone(),
824            );
825
826        let mut tmp = self.clone();
827
828        let permute = match tmp.lu_decompose() {
829            Some(p) => p,
830            None => return None,
831        };
832
833        for i in 0..T::dimension() {
834            let mut dxm = GenericArray::<f64, T::Dimension>::default();
835            dxm[i] = 1.0;
836
837            let x = tmp.lu_substitution(&dxm, &permute);
838
839            for k in 0..T::dimension() {
840                result[&[k, i] as &[usize]] = x[k];
841            }
842        }
843
844        Some(result)
845    }
846}
847
848impl<T, U> Tensor<T, U>
849where
850    T: CoordinateSystem,
851    U: Variance,
852    U::Rank: ArrayLength<usize>,
853    T::Dimension: Pow<U::Rank>,
854    Exp<T::Dimension, U::Rank>: ArrayLength<f64>,
855{
856    pub fn convert<T2>(&self) -> Tensor<T2, U>
857    where
858        T2: CoordinateSystem + 'static,
859        T2::Dimension: Pow<U::Rank> + Pow<U2> + Same<T::Dimension>,
860        Exp<T2::Dimension, U::Rank>: ArrayLength<f64>,
861        Exp<T2::Dimension, U2>: ArrayLength<f64>,
862        T: ConversionTo<T2>,
863    {
864        let mut result = Tensor::<T2, U>::zero(<T as ConversionTo<T2>>::convert_point(&self.p));
865
866        let jacobian = <T as ConversionTo<T2>>::jacobian(&self.p);
867        let inv_jacobian = <T as ConversionTo<T2>>::inv_jacobian(&self.p);
868        let variance = <U as Variance>::variance();
869
870        for i in result.iter_coords() {
871            let mut temp = 0.0;
872            for j in self.iter_coords() {
873                let mut temp2 = self[&*j];
874                for (k, v) in variance.iter().enumerate() {
875                    let coords = [i[k], j[k]];
876                    temp2 *= match *v {
877                        IndexType::Covariant => inv_jacobian[&coords[..]],
878                        IndexType::Contravariant => jacobian[&coords[..]],
879                    };
880                }
881                temp += temp2;
882            }
883            result[&*i] = temp;
884        }
885
886        result
887    }
888}