├── .gitignore
├── .project
├── .travis.yml
├── Cargo.toml
├── LICENSE
├── README.md
├── rustfmt.toml
└── src
├── coordinates.rs
├── lib.rs
├── macros.rs
├── metric.rs
├── tensors
├── mod.rs
├── tensor.rs
└── variance.rs
└── tests
├── basic.rs
├── coord_transform.rs
└── mod.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.o
3 | *.so
4 | *.rlib
5 | *.dll
6 |
7 | # Executables
8 | *.exe
9 |
10 | # Generated by Cargo
11 | /target/
12 |
13 | Cargo.lock
14 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | differential-geometry
4 |
5 |
6 |
7 |
8 |
9 | com.github.rustdt.ide.core.Builder
10 |
11 |
12 |
13 |
14 |
15 | com.github.rustdt.ide.core.nature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: rust
2 | after_success: |-
3 | [ $TRAVIS_BRANCH = master ] &&
4 | [ $TRAVIS_PULL_REQUEST = false ] &&
5 | cargo doc &&
6 | echo "" > target/doc/index.html &&
7 | sudo pip install ghp-import &&
8 | ghp-import -n target/doc &&
9 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
10 | env:
11 | global:
12 | secure: NvnxcDYHicHAmxdBKLkmXFL00UXOt3SJGvIBK8GjuPGTFQ5BetCdjrUlVhHj9QeJQ+RTp4KyahexFNRgOTIimAGKV1L+A5v018F6pLUuQrwm8iDDvk4lA2CF+hEx5++nnah+xj2EQfher2dSAU3HCmBhjxIfa7WFNsNBVaNsnsHvMAr1+9zL3Ls8Ee9J2E19hVDXYhXug1sJ84m9LzIEMP1Ywar8mnQ+UNw45TBaN7DXTHTbFBttEjnOaSt3IANwww5rpx/b+Gm7J6f02TsT0QshH8OxODzG9NBMjtd90bTor8MOnADw+X/5Vs3YPMFvc38ahX66gIx5kvdPNirqj4vCx8p5uiDwyaPpF2fTseMEMHc9To7dB8knAJNmn6CvuycqArqaONwAR2mj+RGh+ywDv69bed3FRUgNMoCNZAcJGybuT9P4KOg3N2CTvSPOWFhTI8Cgp2O1ysbLtYlZQbybTSUAkadZV1JP6iecoDZxXIP+67TVfsHmZY+5xRVB/3Jkcjj9m1FEWwym45SmsCHEjw3JFqdjUeUANXZC2l3mbDh+4R2yyd8hk6dpllVY0PWlFsnAxAmX0W032mSCT2wxd5fNCIBvcCZs+y/FIuzhgB0AlI5OStWbGY7nN9tY7nYgnIZV0eeeDkH3mFyI4L1HS2ONsPtWRJWbT07pSOA=
13 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "differential-geometry"
3 | version = "0.3.1"
4 | authors = [ "Bartłomiej Kamiński " ]
5 | description = "A library for differential-geometric calculations"
6 | license = "MIT"
7 | repository = "https://github.com/fizyk20/differential-geometry.git"
8 | edition = "2018"
9 |
10 | [lib]
11 | name = "diffgeom"
12 | crate-type = ["rlib", "dylib"]
13 |
14 | [dependencies]
15 | generic-array = "0.13"
16 |
17 | [dev-dependencies]
18 | rand = "0.5"
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Bartłomiej Kamiński
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://crates.io/crates/differential-geometry)
2 | [](https://travis-ci.org/fizyk20/differential-geometry)
3 | # differential-geometry
4 |
5 | [Crate documentation](https://fizyk20.github.io/differential-geometry/diffgeom)
6 |
7 | This is a crate for differential-geometric calculations, like tensor calculus on manifolds etc.
8 |
9 | Features (version 0.1):
10 | - Defining coordinate systems and conversions between them.
11 | - Defining points on a manifold.
12 | - Defining tensors; tensor addition, subtraction, outer product, inner product, contraction, matrix inversion.
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | reorder_imports = true
2 | use_try_shorthand = true
3 |
--------------------------------------------------------------------------------
/src/coordinates.rs:
--------------------------------------------------------------------------------
1 | //! Module containing basic types representing coordinate systems.
2 |
3 | use super::tensors::{ContravariantIndex, CovariantIndex, Matrix, Tensor};
4 | use crate::typenum::consts::U2;
5 | use crate::typenum::uint::Unsigned;
6 | use crate::typenum::Pow;
7 | use generic_array::{ArrayLength, GenericArray};
8 | use std::fmt;
9 | use std::ops::{Index, IndexMut};
10 |
11 | /// `CoordinateSystem` marks a struct (usually a unit struct) as representing a coordinate system.
12 | pub trait CoordinateSystem: Sized {
13 | /// An associated type representing the dimension of the coordinate system
14 | type Dimension: Unsigned + ArrayLength + ArrayLength;
15 |
16 | /// Function returning a small value for purposes of numerical differentiation.
17 | /// What is considered a small value may depend on the point, hence the parameter.
18 | /// Returns just 0.01 by default.
19 | fn small(_: &Point) -> f64 {
20 | 0.01
21 | }
22 |
23 | /// Function returning the dimension
24 | fn dimension() -> usize {
25 | Self::Dimension::to_usize()
26 | }
27 | }
28 |
29 | /// Struct representing a point on the manifold. The information about the coordinate system
30 | /// is saved in the type parameter, so that only operations on objects belonging to the same
31 | /// coordinate system will be allowed.
32 | pub struct Point {
33 | /// The coordinates of the point.
34 | x: GenericArray,
35 | }
36 |
37 | impl Point
38 | where
39 | T: CoordinateSystem,
40 | {
41 | /// Creates a new point with coordinates described by the array
42 | pub fn new(coords: GenericArray) -> Point {
43 | Point { x: coords }
44 | }
45 |
46 | /// Creates a new point with coordinates passed in the slice
47 | pub fn from_slice(coords: &[f64]) -> Point {
48 | Point {
49 | x: GenericArray::clone_from_slice(coords),
50 | }
51 | }
52 |
53 | /// Returns the point's coordinates as an array
54 | pub fn coords_array(&self) -> &GenericArray {
55 | &self.x
56 | }
57 | }
58 |
59 | impl Clone for Point
60 | where
61 | T: CoordinateSystem,
62 | {
63 | fn clone(&self) -> Point {
64 | Point::new(self.x.clone())
65 | }
66 | }
67 |
68 | impl Copy for Point
69 | where
70 | T: CoordinateSystem,
71 | >::ArrayType: Copy,
72 | {
73 | }
74 |
75 | impl Index for Point
76 | where
77 | T: CoordinateSystem,
78 | {
79 | type Output = f64;
80 |
81 | fn index(&self, idx: usize) -> &f64 {
82 | &self.x[idx]
83 | }
84 | }
85 |
86 | impl IndexMut for Point
87 | where
88 | T: CoordinateSystem,
89 | {
90 | fn index_mut(&mut self, idx: usize) -> &mut f64 {
91 | &mut self.x[idx]
92 | }
93 | }
94 |
95 | impl PartialEq> for Point
96 | where
97 | T: CoordinateSystem,
98 | {
99 | fn eq(&self, rhs: &Point) -> bool {
100 | (0..T::dimension()).all(|i| self[i] == rhs[i])
101 | }
102 | }
103 |
104 | impl Eq for Point where T: CoordinateSystem {}
105 |
106 | impl fmt::Debug for Point
107 | where
108 | T: CoordinateSystem,
109 | {
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 | write!(f, "Point{:?}", &self.x)
112 | }
113 | }
114 |
115 | /// Trait used for conversions between different coordinate systems. Implementing `ConversionTo`
116 | /// for a `CoordinateSystem` will allow objects in that system to be converted to the system `T`
117 | /// (note that `T` also has to be a `CoordinateSystem`).
118 | pub trait ConversionTo: CoordinateSystem
119 | where
120 | T::Dimension: Pow,
121 | >::Output: ArrayLength,
122 | {
123 | /// Function converting the coordinates of a point.
124 | fn convert_point(p: &Point) -> Point;
125 |
126 | /// Function calculating a Jacobian at a point - that is, the matrix of derivatives
127 | /// of the coordinate conversions.
128 | ///
129 | /// This will be contracted with contravariant indices in the tensor.
130 | fn jacobian(p: &Point) -> Matrix {
131 | let d = Self::dimension();
132 | let mut result = Matrix::zero(Self::convert_point(p));
133 | let h = Self::small(p);
134 |
135 | for j in 0..d {
136 | let mut x = p.clone();
137 | x[j] = x[j] - h;
138 | let y1 = Self::convert_point(&x);
139 |
140 | x[j] = x[j] + h * 2.0;
141 | let y2 = Self::convert_point(&x);
142 |
143 | for i in 0..d {
144 | // calculate dyi/dxj
145 | let index = [i, j];
146 | result[&index[..]] = (y2[i] - y1[i]) / (2.0 * h);
147 | }
148 | }
149 |
150 | result
151 | }
152 |
153 | /// The inverse matrix of the Jacobian at a point.
154 | ///
155 | /// In conversions, it will be contracted with covariant indices.
156 | fn inv_jacobian(p: &Point) -> Tensor {
157 | ConversionTo::::jacobian(p).inverse().unwrap()
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | /*!
2 | **diffgeom** is a crate aiming to leverage the Rust type system to provide
3 | a type-safe API for tensor calculus on arbitrary manifolds.
4 |
5 | What is tensor calculus?
6 | ========================
7 |
8 | Tensors are, in a way, a generalized idea similar to vectors and matrices. They are
9 | multidimensional arrays of numbers, but not all such arrays are tensors. What makes
10 | them tensors is how they behave in coordinate transformations. The details are a
11 | topic for a whole academic lecture, so I won't go into them. What's important
12 | is that tensors can be used for describing properties of curved spaces and it is
13 | the intended use case of this crate.
14 |
15 | Problems
16 | ========
17 |
18 | Unfortunately, Rust currently doesn't support generics over static values, so
19 | another representation of type-level numbers is required. In this crate one
20 | provided by [typenum](https://github.com/paholg/typenum) is being used. This
21 | makes it necessary to use a lot of trait bounds, which break the compiler in
22 | a few ways, so some operations require the usage of a pretty cumbersome syntax.
23 |
24 | Example
25 | =======
26 |
27 | Below you can see a code sample presenting some simple operations.
28 |
29 | ```
30 | # extern crate diffgeom;
31 | # extern crate generic_array;
32 | use std::ops::Mul;
33 | use generic_array::{GenericArray, ArrayLength};
34 | use diffgeom::coordinates::{CoordinateSystem, Point};
35 | use diffgeom::tensors::{Vector, Covector, Matrix, InnerProduct};
36 | use generic_array::arr;
37 | use generic_array::typenum::consts::{U0, U1, U2};
38 |
39 | fn main() {
40 | // First, a coordinate system must be defined
41 | struct SomeSystem;
42 | impl CoordinateSystem for SomeSystem {
43 | type Dimension = U2; // a two-dimensional coordinate system
44 | }
45 |
46 | // Each tensor should be anchored at a point, so let's create one
47 | let point = Point::::new(arr![f64; 0.0, 0.0]);
48 |
49 | // A vector can be defined like that:
50 | let vector = Vector::::new(point, arr![f64; 1.0, 2.0]);
51 |
52 | // There are also covectors
53 | let covector = Covector::::new(point, arr![f64; 2.0, 0.5]);
54 |
55 | // They can be multiplied, yielding a matrix
56 | let matrix = as Mul>>::mul(vector, covector);
57 | // Unfortunately this causes infinite recursion in the compiler:
58 | // let matrix = vector * covector;
59 |
60 | // They can be contracted
61 | let scalar = as InnerProduct, U0, U1>>
62 | ::inner_product(vector, covector);
63 |
64 | // scalars returned by tensor functions need to be dereffed to f64
65 | assert_eq!(*scalar, *matrix.trace::());
66 | }
67 | ```
68 | */
69 | pub extern crate generic_array;
70 | pub use generic_array::typenum;
71 |
72 | pub mod coordinates;
73 | pub mod macros;
74 | pub mod metric;
75 | pub mod tensors;
76 |
77 | #[cfg(test)]
78 | mod tests;
79 |
--------------------------------------------------------------------------------
/src/macros.rs:
--------------------------------------------------------------------------------
1 | #[macro_export]
2 | macro_rules! mul {
3 | ($T1: ty, $T2: ty; $op1: expr, $op2: expr) => {{
4 | use std::ops::Mul;
5 | <$T1 as Mul<$T2>>::mul($op1, $op2)
6 | }};
7 | }
8 |
9 | #[macro_export]
10 | macro_rules! inner {
11 | ($T1: ty, $T2: ty; $I1: ty, $I2: ty; $op1: expr, $op2: expr) => {{
12 | use $crate::tensors::InnerProduct;
13 | <$T1 as InnerProduct<$T2, $I1, $I2>>::inner_product($op1, $op2)
14 | }};
15 | }
16 |
--------------------------------------------------------------------------------
/src/metric.rs:
--------------------------------------------------------------------------------
1 | use super::coordinates::{CoordinateSystem, Point};
2 | use super::tensors::{
3 | ContravariantIndex, CovariantIndex, InnerProduct, InvTwoForm, Tensor, TwoForm,
4 | };
5 | use crate::inner;
6 | use crate::typenum::consts::{U0, U1, U2, U3};
7 | use crate::typenum::{Exp, Pow, Unsigned};
8 | use generic_array::ArrayLength;
9 |
10 | /// Trait representing the metric properties of the coordinate system
11 | pub trait MetricSystem: CoordinateSystem
12 | where
13 | ::Dimension: Pow + Pow,
14 | Exp<::Dimension, U2>: ArrayLength,
15 | Exp<::Dimension, U3>: ArrayLength,
16 | {
17 | /// Returns the metric tensor at a given point.
18 | fn g(point: &Point) -> TwoForm;
19 |
20 | /// Returns the inverse metric tensor at a given point.
21 | ///
22 | /// The default implementation calculates the metric and then inverts it. A direct
23 | /// implementation may be desirable for more performance.
24 | fn inv_g(point: &Point) -> InvTwoForm {
25 | Self::g(point).inverse().unwrap()
26 | }
27 |
28 | /// Returns the partial derivatives of the metric at a given point.
29 | ///
30 | /// The default implementation calculates them numerically. A direct implementation
31 | /// may be desirable for performance.
32 | fn dg(point: &Point) -> Tensor {
33 | let d = Self::dimension();
34 | let mut result = Tensor::zero(point.clone());
35 | let h = Self::small(point);
36 |
37 | for j in 0..d {
38 | let mut x = point.clone();
39 | x[j] = x[j] - h;
40 | let g1 = Self::g(&x);
41 |
42 | x[j] = x[j] + h * 2.0;
43 | let g2 = Self::g(&x);
44 |
45 | for coord in g1.iter_coords() {
46 | // calculate dg_i/dx^j
47 | let index = [coord[0], coord[1], j];
48 | result[&index[..]] = (g2[&*coord] - g1[&*coord]) / (2.0 * h);
49 | }
50 | }
51 |
52 | result
53 | }
54 |
55 | /// Returns the covariant Christoffel symbols (with three lower indices).
56 | ///
57 | /// The default implementation calculates them from the metric. A direct implementation
58 | /// may be desirable for performance.
59 | fn covariant_christoffel(
60 | point: &Point,
61 | ) -> Tensor {
62 | let dg = Self::dg(point);
63 | let mut result =
64 | Tensor::::zero(point.clone());
65 |
66 | for i in result.iter_coords() {
67 | result[&*i] =
68 | 0.5 * (dg[&*i] + dg[&[i[0], i[2], i[1]][..]] - dg[&[i[1], i[2], i[0]][..]]);
69 | }
70 |
71 | result
72 | }
73 |
74 | /// Returns the Christoffel symbols.
75 | ///
76 | /// The default implementation calculates them from the metric. A direct implementation
77 | /// may be desirable for performance.
78 | fn christoffel(
79 | point: &Point,
80 | ) -> Tensor {
81 | let ig = Self::inv_g(point);
82 | let gamma = Self::covariant_christoffel(point);
83 |
84 | as InnerProduct<
85 | Tensor,
86 | U1,
87 | U2,
88 | >>::inner_product(ig, gamma)
89 | }
90 | }
91 |
92 | impl Tensor
93 | where
94 | T: MetricSystem,
95 | T::Dimension: Pow + Pow + Pow + Unsigned,
96 | Exp: ArrayLength,
97 | Exp: ArrayLength,
98 | Exp: ArrayLength,
99 | {
100 | pub fn square(&self) -> f64 {
101 | let g = T::g(self.get_point());
102 | let temp = inner!(_, _; U1, U2; g, self.clone());
103 | *inner!(_, _; U0, U1; temp, self.clone())
104 | }
105 |
106 | pub fn normalize(&mut self) {
107 | let len = self.square().abs().sqrt();
108 | for i in 0..T::Dimension::to_usize() {
109 | self[i] /= len;
110 | }
111 | }
112 | }
113 |
114 | impl Tensor
115 | where
116 | T: MetricSystem,
117 | T::Dimension: Pow + Pow + Pow + Unsigned,
118 | Exp: ArrayLength,
119 | Exp: ArrayLength,
120 | Exp: ArrayLength,
121 | {
122 | pub fn square(&self) -> f64 {
123 | let g = T::inv_g(self.get_point());
124 | let temp = inner!(_, _; U1, U2; g, self.clone());
125 | *inner!(_, _; U0, U1; temp, self.clone())
126 | }
127 |
128 | pub fn normalize(&mut self) {
129 | let len = self.square().abs().sqrt();
130 | for i in 0..T::Dimension::to_usize() {
131 | self[i] /= len;
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/tensors/mod.rs:
--------------------------------------------------------------------------------
1 | //! Module containing definitions of tensors and operations on them.
2 | mod tensor;
3 | mod variance;
4 |
5 | pub use self::tensor::{
6 | Covector, InnerProduct, InvTwoForm, Matrix, Scalar, Tensor, TwoForm, Vector,
7 | };
8 | pub use self::variance::{
9 | Concat, Contract, Contracted, ContravariantIndex, CovariantIndex, IndexType, Joined,
10 | OtherIndex, TensorIndex, Variance,
11 | };
12 |
--------------------------------------------------------------------------------
/src/tensors/tensor.rs:
--------------------------------------------------------------------------------
1 | //! This module defines the `Tensor` type and all sorts of operations on it.
2 |
3 | use super::variance::{Concat, Contract, Contracted, Joined, OtherIndex};
4 | use super::{ContravariantIndex, CovariantIndex, IndexType, TensorIndex, Variance};
5 | use crate::coordinates::{ConversionTo, CoordinateSystem, Point};
6 | use crate::typenum::consts::{B1, U2};
7 | use crate::typenum::uint::Unsigned;
8 | use crate::typenum::{Add1, Exp, Pow, Same};
9 | use generic_array::{ArrayLength, GenericArray};
10 | use std::ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
11 | use 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.
24 | pub struct Tensor
25 | where
26 | T::Dimension: Pow,
27 | Exp: ArrayLength,
28 | {
29 | p: Point,
30 | x: GenericArray>,
31 | }
32 |
33 | impl Clone for Tensor
34 | where
35 | T: CoordinateSystem,
36 | U: Variance,
37 | T::Dimension: Pow,
38 | Exp: ArrayLength,
39 | {
40 | fn clone(&self) -> Tensor {
41 | Tensor {
42 | p: self.p.clone(),
43 | x: self.x.clone(),
44 | }
45 | }
46 | }
47 |
48 | impl Copy for Tensor
49 | where
50 | T: CoordinateSystem,
51 | U: Variance,
52 | T::Dimension: Pow,
53 | >::ArrayType: Copy,
54 | Exp: ArrayLength,
55 | as ArrayLength>::ArrayType: Copy,
56 | {
57 | }
58 |
59 | /// A struct for iterating over the coordinates of a tensor.
60 | pub struct CoordIterator
61 | where
62 | U: Variance,
63 | U::Rank: ArrayLength,
64 | {
65 | started: bool,
66 | dimension: usize,
67 | cur_coord: GenericArray,
68 | }
69 |
70 | impl CoordIterator
71 | where
72 | U: Variance,
73 | U::Rank: ArrayLength,
74 | {
75 | pub fn new(dimension: usize) -> CoordIterator {
76 | CoordIterator {
77 | started: false,
78 | dimension: dimension,
79 | cur_coord: GenericArray::default(),
80 | }
81 | }
82 | }
83 |
84 | impl Iterator for CoordIterator
85 | where
86 | U: Variance,
87 | U::Rank: ArrayLength,
88 | {
89 | type Item = GenericArray;
90 |
91 | fn next(&mut self) -> Option {
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 |
119 | impl Tensor
120 | where
121 | T: CoordinateSystem,
122 | V: Variance,
123 | T::Dimension: Pow,
124 | Exp: ArrayLength,
125 | {
126 | /// Returns the point at which the tensor is defined.
127 | pub fn get_point(&self) -> &Point {
128 | &self.p
129 | }
130 |
131 | /// Sets the point at which the tensor is defined.
132 | pub fn set_point(&mut self, p: Point) {
133 | self.p = p;
134 | }
135 |
136 | /// Returns the tensor's coordinates as an array
137 | pub fn coords_array(&self) -> &GenericArray> {
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 {
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 | >::Output::to_usize()
170 | }
171 |
172 | /// Creates a new, zero tensor at a given point
173 | pub fn zero(point: Point) -> Tensor {
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,
191 | coords: GenericArray>,
192 | ) -> Tensor {
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, slice: &[f64]) -> Tensor {
209 | assert_eq!(Tensor::::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(&self) -> Tensor>
220 | where
221 | Ul: Unsigned,
222 | Uh: Unsigned,
223 | V: Contract,
224 | as Variance>::Rank: ArrayLength,
225 | T::Dimension: Pow< as Variance>::Rank>,
226 | Exp as Variance>::Rank>: ArrayLength,
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::>::zero(self.p.clone());
234 | let num_coords_result = Tensor::>::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 |
257 | impl Tensor
258 | where
259 | T: CoordinateSystem,
260 | U: Variance,
261 | U::Rank: ArrayLength,
262 | T::Dimension: Pow,
263 | Exp: ArrayLength,
264 | {
265 | /// Returns an iterator over the coordinates of the tensor.
266 | pub fn iter_coords(&self) -> CoordIterator {
267 | CoordIterator::new(T::dimension())
268 | }
269 | }
270 |
271 | impl<'a, T, U> Index<&'a [usize]> for Tensor
272 | where
273 | T: CoordinateSystem,
274 | U: Variance,
275 | T::Dimension: Pow,
276 | Exp: ArrayLength,
277 | {
278 | type Output = f64;
279 |
280 | fn index(&self, idx: &'a [usize]) -> &f64 {
281 | &self.x[Self::get_coord(idx)]
282 | }
283 | }
284 |
285 | impl<'a, T, U> IndexMut<&'a [usize]> for Tensor
286 | where
287 | T: CoordinateSystem,
288 | U: Variance,
289 | T::Dimension: Pow,
290 | Exp: ArrayLength,
291 | {
292 | fn index_mut(&mut self, idx: &'a [usize]) -> &mut f64 {
293 | &mut self.x[Self::get_coord(idx)]
294 | }
295 | }
296 |
297 | impl<'a, T, U> Index for Tensor
298 | where
299 | T: CoordinateSystem,
300 | U: Variance,
301 | T::Dimension: Pow,
302 | Exp: ArrayLength,
303 | {
304 | type Output = f64;
305 |
306 | fn index(&self, idx: usize) -> &f64 {
307 | &self.x[idx]
308 | }
309 | }
310 |
311 | impl<'a, T, U> IndexMut for Tensor
312 | where
313 | T: CoordinateSystem,
314 | U: Variance,
315 | T::Dimension: Pow,
316 | Exp: ArrayLength,
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`.
326 | pub type Scalar = Tensor;
327 |
328 | /// A vector type (rank 1 contravariant tensor)
329 | pub type Vector = Tensor;
330 |
331 | /// A covector type (rank 1 covariant tensor)
332 | pub type Covector = Tensor;
333 |
334 | /// A matrix type (rank 2 contravariant-covariant tensor)
335 | pub type Matrix = Tensor;
336 |
337 | /// A bilinear form type (rank 2 doubly covariant tensor)
338 | pub type TwoForm = Tensor;
339 |
340 | /// A rank 2 doubly contravariant tensor
341 | pub type InvTwoForm = Tensor;
342 |
343 | impl Deref for Scalar {
344 | type Target = f64;
345 |
346 | fn deref(&self) -> &f64 {
347 | &self.x[0]
348 | }
349 | }
350 |
351 | impl DerefMut for Scalar {
352 | fn deref_mut(&mut self) -> &mut f64 {
353 | &mut self.x[0]
354 | }
355 | }
356 |
357 | // Arithmetic operations
358 |
359 | impl AddAssign> for Tensor
360 | where
361 | T: CoordinateSystem,
362 | U: Variance,
363 | T::Dimension: Pow,
364 | Exp: ArrayLength,
365 | {
366 | fn add_assign(&mut self, rhs: Tensor) {
367 | assert!(self.p == rhs.p);
368 | for i in 0..(Tensor::::get_num_coords()) {
369 | self[i] += rhs[i];
370 | }
371 | }
372 | }
373 |
374 | impl Add> for Tensor
375 | where
376 | T: CoordinateSystem,
377 | U: Variance,
378 | T::Dimension: Pow,
379 | Exp: ArrayLength,
380 | {
381 | type Output = Tensor;
382 |
383 | fn add(mut self, rhs: Tensor) -> Tensor {
384 | self += rhs;
385 | self
386 | }
387 | }
388 |
389 | impl SubAssign> for Tensor
390 | where
391 | T: CoordinateSystem,
392 | U: Variance,
393 | T::Dimension: Pow,
394 | Exp: ArrayLength,
395 | {
396 | fn sub_assign(&mut self, rhs: Tensor) {
397 | assert!(self.p == rhs.p);
398 | for i in 0..(Tensor::::get_num_coords()) {
399 | self[i] -= rhs[i];
400 | }
401 | }
402 | }
403 |
404 | impl Sub> for Tensor
405 | where
406 | T: CoordinateSystem,
407 | U: Variance,
408 | T::Dimension: Pow,
409 | Exp: ArrayLength,
410 | {
411 | type Output = Tensor;
412 |
413 | fn sub(mut self, rhs: Tensor) -> Tensor {
414 | self -= rhs;
415 | self
416 | }
417 | }
418 |
419 | impl MulAssign for Tensor
420 | where
421 | T: CoordinateSystem,
422 | U: Variance,
423 | T::Dimension: Pow,
424 | Exp: ArrayLength,
425 | {
426 | fn mul_assign(&mut self, rhs: f64) {
427 | for i in 0..(Tensor::::get_num_coords()) {
428 | self[i] *= rhs;
429 | }
430 | }
431 | }
432 |
433 | impl Mul for Tensor
434 | where
435 | T: CoordinateSystem,
436 | U: Variance,
437 | T::Dimension: Pow,
438 | Exp: ArrayLength,
439 | {
440 | type Output = Tensor;
441 |
442 | fn mul(mut self, rhs: f64) -> Tensor {
443 | self *= rhs;
444 | self
445 | }
446 | }
447 |
448 | impl Mul> for f64
449 | where
450 | T: CoordinateSystem,
451 | U: Variance,
452 | T::Dimension: Pow,
453 | Exp: ArrayLength,
454 | {
455 | type Output = Tensor;
456 |
457 | fn mul(self, mut rhs: Tensor) -> Tensor {
458 | rhs *= self;
459 | rhs
460 | }
461 | }
462 |
463 | impl DivAssign for Tensor