Skip to main content

algebraeon_geometry/
vector.rs

1use crate::ambient_space::{AffineSpace, common_space};
2
3use super::*;
4use algebraeon_rings::matrix::{Matrix, MatrixStructure};
5use std::borrow::Borrow;
6use std::hash::Hash;
7
8#[derive(Clone)]
9pub struct Vector<'f, FS: FieldSignature + 'f> {
10    ambient_space: AffineSpace<'f, FS>,
11    coordinates: Vec<FS::Set>, //length equal to ambient_space.dimension()
12}
13
14impl<'f, FS: FieldSignature> std::fmt::Debug for Vector<'f, FS> {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        f.debug_struct("Vector")
17            .field("coordinates", &self.coordinates)
18            .finish()
19    }
20}
21
22impl<'f, FS: FieldSignature> PartialEq for Vector<'f, FS> {
23    fn eq(&self, other: &Self) -> bool {
24        match common_space(self.ambient_space, other.ambient_space) {
25            Some(space) => {
26                let n = space.linear_dimension().unwrap();
27                (0..n).all(|i| space.field().equal(self.coordinate(i), other.coordinate(i)))
28            }
29            None => false,
30        }
31    }
32}
33
34impl<'f, FS: FieldSignature> Eq for Vector<'f, FS> {}
35
36impl<'f, FS: FieldSignature> Hash for Vector<'f, FS>
37where
38    FS::Set: Hash,
39{
40    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
41        // self.ambient_space.borrow().hash(state);
42        self.coordinates.hash(state);
43    }
44}
45impl<'f, FS: FieldSignature> Vector<'f, FS> {
46    pub fn ambient_space(&self) -> AffineSpace<'f, FS> {
47        self.ambient_space
48    }
49
50    fn new(
51        ambient_space: AffineSpace<'f, FS>,
52        coordinates: impl IntoIterator<Item = impl Into<FS::Set>>,
53    ) -> Self {
54        let coordinates = coordinates
55            .into_iter()
56            .map(|c| c.into())
57            .collect::<Vec<_>>();
58        assert_eq!(ambient_space.linear_dimension().unwrap(), coordinates.len());
59        Self {
60            ambient_space,
61            coordinates,
62        }
63    }
64
65    pub fn construct(
66        ambient_space: AffineSpace<'f, FS>,
67        coordinate_func: impl FnMut(usize) -> FS::Set,
68    ) -> Self {
69        let coordinates = (0..ambient_space.borrow().linear_dimension().unwrap())
70            .map(coordinate_func)
71            .collect();
72        Self {
73            ambient_space,
74            coordinates,
75        }
76    }
77
78    pub fn zero(ambient_space: AffineSpace<'f, FS>) -> Self {
79        let field = ambient_space.borrow().field().clone();
80        Self::construct(ambient_space, |_i| field.zero())
81    }
82
83    pub fn coordinate(&self, i: usize) -> &FS::Set {
84        self.coordinates.get(i).unwrap()
85    }
86
87    pub fn coordinate_mut(&mut self, i: usize) -> &mut FS::Set {
88        self.coordinates.get_mut(i).unwrap()
89    }
90
91    pub fn into_coordinates(self) -> Vec<FS::Set> {
92        self.coordinates
93    }
94
95    pub fn into_row(&self) -> Matrix<FS::Set> {
96        Matrix::construct(
97            1,
98            self.ambient_space().linear_dimension().unwrap(),
99            |_r, c| self.coordinate(c).clone(),
100        )
101    }
102
103    pub fn into_col(&self) -> Matrix<FS::Set> {
104        Matrix::construct(
105            self.ambient_space().linear_dimension().unwrap(),
106            1,
107            |r, _c| self.coordinate(r).clone(),
108        )
109    }
110}
111
112impl<'f, FS: FieldSignature> AffineSpace<'f, FS> {
113    pub fn vector(
114        self,
115        coordinates: impl IntoIterator<Item = impl Into<FS::Set>>,
116    ) -> Vector<'f, FS> {
117        Vector::new(self, coordinates)
118    }
119
120    pub fn rows_from_vectors(&self, vecs: Vec<&Vector<'f, FS>>) -> Matrix<FS::Set> {
121        for vec in &vecs {
122            assert_eq!(*self, vec.ambient_space());
123        }
124        Matrix::construct(vecs.len(), self.linear_dimension().unwrap(), |r, c| {
125            vecs[r].coordinate(c).clone()
126        })
127    }
128
129    pub fn cols_from_vectors(&self, vecs: Vec<&Vector<'f, FS>>) -> Matrix<FS::Set> {
130        self.rows_from_vectors(vecs).transpose()
131    }
132
133    pub fn vectors_from_rows(self, mat: &Matrix<FS::Set>) -> Vec<Vector<'f, FS>> {
134        assert_eq!(mat.cols(), self.linear_dimension().unwrap());
135        (0..mat.rows())
136            .map(|r| Vector::new(self, (0..mat.cols()).map(|c| mat.at(r, c).unwrap().clone())))
137            .collect()
138    }
139
140    pub fn vectors_from_cols(self, mat: &Matrix<FS::Set>) -> Vec<Vector<'f, FS>> {
141        assert_eq!(mat.rows(), self.linear_dimension().unwrap());
142        self.vectors_from_rows(&mat.transpose_ref())
143    }
144
145    pub fn vector_from_row(self, mat: &Matrix<FS::Set>) -> Vector<'f, FS> {
146        assert_eq!(mat.rows(), 1);
147        assert_eq!(mat.cols(), self.linear_dimension().unwrap());
148        self.vectors_from_rows(mat).pop().unwrap()
149    }
150
151    pub fn vector_from_col(self, mat: &Matrix<FS::Set>) -> Vector<'f, FS> {
152        assert_eq!(mat.rows(), self.linear_dimension().unwrap());
153        assert_eq!(mat.cols(), 1);
154        self.vector_from_row(&mat.transpose_ref())
155    }
156
157    pub fn are_points_affine_independent(&self, points: Vec<&Vector<'f, FS>>) -> bool {
158        for point in &points {
159            assert_eq!(*self, point.ambient_space());
160        }
161        if points.is_empty() {
162            true
163        } else {
164            let vecs = (1..points.len())
165                .map(|i| points[i] - points[0])
166                .collect::<Vec<_>>();
167            let mat = self.rows_from_vectors(vecs.iter().collect());
168            MatrixStructure::new(self.field().clone()).rank(mat) == vecs.len()
169        }
170    }
171
172    pub fn determinant(&self, vecs: Vec<&Vector<'f, FS>>) -> FS::Set {
173        MatrixStructure::new(self.field().clone())
174            .det(self.rows_from_vectors(vecs))
175            .unwrap()
176    }
177
178    pub fn rank(&self, vecs: Vec<&Vector<'f, FS>>) -> usize {
179        MatrixStructure::new(self.field().clone()).rank(self.rows_from_vectors(vecs))
180    }
181}
182
183// -&vector
184impl<'f, FS: FieldSignature> std::ops::Neg for &Vector<'f, FS> {
185    type Output = Vector<'f, FS>;
186
187    fn neg(self) -> Self::Output {
188        Vector {
189            ambient_space: self.ambient_space,
190            coordinates: self
191                .coordinates
192                .iter()
193                .map(|x| self.ambient_space().field().neg(x))
194                .collect(),
195        }
196    }
197}
198
199// &vector + &vector
200impl<'f, FS: FieldSignature> std::ops::Add<&Vector<'f, FS>> for &Vector<'f, FS> {
201    type Output = Vector<'f, FS>;
202
203    fn add(self, other: &Vector<'f, FS>) -> Self::Output {
204        match common_space(self.ambient_space, other.ambient_space) {
205            Some(space) => {
206                let n = space.linear_dimension().unwrap();
207                let coordinates = (0..n)
208                    .map(|i| space.field().add(self.coordinate(i), other.coordinate(i)))
209                    .collect();
210                Vector {
211                    ambient_space: space,
212                    coordinates,
213                }
214            }
215            None => panic!("Can't add vectors belonging to different spaces"),
216        }
217    }
218}
219
220// mut vector += &vector
221impl<'f, FS: FieldSignature> std::ops::AddAssign<&Vector<'f, FS>> for Vector<'f, FS> {
222    fn add_assign(&mut self, other: &Vector<'f, FS>) {
223        match common_space(self.ambient_space, other.ambient_space) {
224            Some(space) => {
225                let n = space.borrow().linear_dimension().unwrap();
226                for i in 0..n {
227                    space
228                        .field()
229                        .add_mut(self.coordinate_mut(i), other.coordinate(i));
230                }
231            }
232            None => panic!("Can't add vectors belonging to different spaces"),
233        }
234    }
235}
236
237// &vector - &vector
238impl<'f, FS: FieldSignature> std::ops::Sub<&Vector<'f, FS>> for &Vector<'f, FS> {
239    type Output = Vector<'f, FS>;
240
241    fn sub(self, other: &Vector<'f, FS>) -> Self::Output {
242        self + &(-other)
243    }
244}
245
246// &vector * &scalar
247impl<'f, FS: FieldSignature> Vector<'f, FS> {
248    pub fn scalar_mul(&self, other: &FS::Set) -> Vector<'f, FS> {
249        Vector {
250            ambient_space: self.ambient_space,
251            coordinates: self
252                .coordinates
253                .iter()
254                .map(|x| self.ambient_space().field().mul(x, other))
255                .collect(),
256        }
257    }
258}
259
260// It is helpful for computational reasons to put an ordering on the vectors
261// so that the points of a simplex can be ordered
262impl<'f, FS: OrderedRingSignature + FieldSignature> PartialOrd for Vector<'f, FS> {
263    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
264        Some(self.cmp(other))
265    }
266}
267impl<'f, FS: OrderedRingSignature + FieldSignature> Ord for Vector<'f, FS> {
268    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
269        let space = common_space(self.ambient_space(), other.ambient_space()).unwrap();
270        for i in 0..space.linear_dimension().unwrap() {
271            match space.field().cmp(self.coordinate(i), other.coordinate(i)) {
272                std::cmp::Ordering::Less => {
273                    return std::cmp::Ordering::Less;
274                }
275                std::cmp::Ordering::Equal => {}
276                std::cmp::Ordering::Greater => {
277                    return std::cmp::Ordering::Greater;
278                }
279            }
280        }
281        std::cmp::Ordering::Equal
282    }
283}
284
285pub trait DotProduct<Other> {
286    type Output;
287
288    fn dot(self, other: Other) -> Self::Output;
289}
290
291// &vector . &vector
292impl<'f, FS: FieldSignature> DotProduct<&Vector<'f, FS>> for &Vector<'f, FS> {
293    type Output = FS::Set;
294
295    fn dot(self, other: &Vector<'f, FS>) -> Self::Output {
296        match common_space(self.ambient_space, other.ambient_space) {
297            Some(space) => {
298                let n = space.linear_dimension().unwrap();
299                space.field().sum(
300                    (0..n)
301                        .map(|i| space.field().mul(self.coordinate(i), other.coordinate(i)))
302                        .collect(),
303                )
304            }
305            None => panic!("Can't add vectors belonging to different spaces"),
306        }
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313    use algebraeon_nzq::Rational;
314
315    #[test]
316    fn vector_from_mat() {
317        let space = AffineSpace::new_linear(Rational::structure_ref(), 2);
318        let mat = Matrix::<Rational>::from_rows(vec![
319            vec![Rational::from(1), Rational::from(2)],
320            vec![Rational::from(3), Rational::from(4)],
321        ]);
322
323        mat.pprint();
324
325        let mut vecs = space.vectors_from_rows(&mat);
326        let v2 = vecs.pop().unwrap();
327        let v1 = vecs.pop().unwrap();
328        println!("v1 = {v1:?}");
329        println!("v2 = {v2:?}");
330
331        assert_eq!(
332            v1,
333            Vector::new(space, vec![Rational::from(1), Rational::from(2)])
334        );
335        assert_eq!(
336            v2,
337            Vector::new(space, vec![Rational::from(3), Rational::from(4)])
338        );
339    }
340
341    #[test]
342    fn det() {
343        let space = AffineSpace::new_linear(Rational::structure_ref(), 2);
344        let v1 = Vector::new(space, vec![Rational::from(3), Rational::from(2)]);
345        let v2 = Vector::new(space, vec![Rational::from(5), Rational::from(7)]);
346        assert_eq!(space.determinant(vec![&v1, &v2]), Rational::from(11));
347    }
348
349    #[test]
350    fn test_abgroup() {
351        let space_ab = AffineSpace::new_linear(Rational::structure_ref(), 2);
352        let a = Vector::new(space_ab, vec![Rational::from(1), Rational::from(2)]);
353        let b = Vector::new(space_ab, vec![Rational::from(6), Rational::from(3)]);
354        let c = Vector::new(space_ab, vec![Rational::from(7), Rational::from(5)]);
355
356        let space_xy = AffineSpace::new_linear(Rational::structure_ref(), 2);
357        let x = Vector::new(space_xy, vec![Rational::from(1), Rational::from(2)]);
358        let y = Vector::new(space_xy, vec![Rational::from(6), Rational::from(3)]);
359        let z = Vector::new(space_xy, vec![Rational::from(7), Rational::from(5)]);
360        let w = Vector::new(space_xy, vec![Rational::from(-2), Rational::from(-4)]);
361
362        assert_eq!(c, &a + &b);
363        assert_eq!(z, &x + &y);
364        assert_eq!(a, a);
365        assert_ne!(a, b);
366        assert_ne!(a, x); //same coordinates but different space
367        assert_ne!(x.scalar_mul(&Rational::from(-2)), z);
368        assert_eq!(x.scalar_mul(&Rational::from(-2)), w);
369    }
370}