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
272                .field()
273                .ring_cmp(self.coordinate(i), other.coordinate(i))
274            {
275                std::cmp::Ordering::Less => {
276                    return std::cmp::Ordering::Less;
277                }
278                std::cmp::Ordering::Equal => {}
279                std::cmp::Ordering::Greater => {
280                    return std::cmp::Ordering::Greater;
281                }
282            }
283        }
284        std::cmp::Ordering::Equal
285    }
286}
287
288pub trait DotProduct<Other> {
289    type Output;
290
291    fn dot(self, other: Other) -> Self::Output;
292}
293
294// &vector . &vector
295impl<'f, FS: FieldSignature> DotProduct<&Vector<'f, FS>> for &Vector<'f, FS> {
296    type Output = FS::Set;
297
298    fn dot(self, other: &Vector<'f, FS>) -> Self::Output {
299        match common_space(self.ambient_space, other.ambient_space) {
300            Some(space) => {
301                let n = space.linear_dimension().unwrap();
302                space.field().sum(
303                    (0..n)
304                        .map(|i| space.field().mul(self.coordinate(i), other.coordinate(i)))
305                        .collect(),
306                )
307            }
308            None => panic!("Can't add vectors belonging to different spaces"),
309        }
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use algebraeon_nzq::Rational;
317
318    #[test]
319    fn vector_from_mat() {
320        let space = AffineSpace::new_linear(Rational::structure_ref(), 2);
321        let mat = Matrix::<Rational>::from_rows(vec![
322            vec![Rational::from(1), Rational::from(2)],
323            vec![Rational::from(3), Rational::from(4)],
324        ]);
325
326        mat.pprint();
327
328        let mut vecs = space.vectors_from_rows(&mat);
329        let v2 = vecs.pop().unwrap();
330        let v1 = vecs.pop().unwrap();
331        println!("v1 = {v1:?}");
332        println!("v2 = {v2:?}");
333
334        assert_eq!(
335            v1,
336            Vector::new(space, vec![Rational::from(1), Rational::from(2)])
337        );
338        assert_eq!(
339            v2,
340            Vector::new(space, vec![Rational::from(3), Rational::from(4)])
341        );
342    }
343
344    #[test]
345    fn det() {
346        let space = AffineSpace::new_linear(Rational::structure_ref(), 2);
347        let v1 = Vector::new(space, vec![Rational::from(3), Rational::from(2)]);
348        let v2 = Vector::new(space, vec![Rational::from(5), Rational::from(7)]);
349        assert_eq!(space.determinant(vec![&v1, &v2]), Rational::from(11));
350    }
351
352    #[test]
353    fn test_abgroup() {
354        let space_ab = AffineSpace::new_linear(Rational::structure_ref(), 2);
355        let a = Vector::new(space_ab, vec![Rational::from(1), Rational::from(2)]);
356        let b = Vector::new(space_ab, vec![Rational::from(6), Rational::from(3)]);
357        let c = Vector::new(space_ab, vec![Rational::from(7), Rational::from(5)]);
358
359        let space_xy = AffineSpace::new_linear(Rational::structure_ref(), 2);
360        let x = Vector::new(space_xy, vec![Rational::from(1), Rational::from(2)]);
361        let y = Vector::new(space_xy, vec![Rational::from(6), Rational::from(3)]);
362        let z = Vector::new(space_xy, vec![Rational::from(7), Rational::from(5)]);
363        let w = Vector::new(space_xy, vec![Rational::from(-2), Rational::from(-4)]);
364
365        assert_eq!(c, &a + &b);
366        assert_eq!(z, &x + &y);
367        assert_eq!(a, a);
368        assert_ne!(a, b);
369        assert_ne!(a, x); //same coordinates but different space
370        assert_ne!(x.scalar_mul(&Rational::from(-2)), z);
371        assert_eq!(x.scalar_mul(&Rational::from(-2)), w);
372    }
373}