Skip to main content

karpal_algebra/
vector_space.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::field::Field;
5use crate::module::Module;
6
7/// A `Module` over a `Field` — a vector space.
8pub trait VectorSpace<F: Field>: Module<F> {}
9
10impl VectorSpace<f32> for f32 {}
11impl VectorSpace<f64> for f64 {}
12
13impl<F: Field + crate::abelian::AbelianGroup> VectorSpace<F> for (F, F) {}
14
15#[cfg(test)]
16mod tests {
17    use super::*;
18    use crate::module::Module;
19    use crate::semiring::Semiring;
20    use karpal_core::Semigroup;
21
22    #[test]
23    fn f64_is_vector_space() {
24        fn use_vs<V: VectorSpace<f64>>(v: V, s: f64) -> V {
25            v.scale(s)
26        }
27        assert!((use_vs(3.0f64, 2.0) - 6.0).abs() < 1e-10);
28    }
29
30    #[test]
31    fn tuple_is_vector_space() {
32        fn add_scaled<V: VectorSpace<f64> + Semigroup>(a: V, b: V, s: f64) -> V {
33            a.combine(b.scale(s))
34        }
35        let result = add_scaled((1.0f64, 0.0), (0.0, 1.0f64), 2.0);
36        assert!((result.0 - 1.0).abs() < 1e-10);
37        assert!((result.1 - 2.0).abs() < 1e-10);
38    }
39
40    #[test]
41    fn tuple_linear_combination() {
42        let e1 = (1.0f64, 0.0);
43        let e2 = (0.0f64, 1.0);
44        let v = e1.scale(3.0).combine(e2.scale(4.0));
45        assert!((v.0 - 3.0).abs() < 1e-10);
46        assert!((v.1 - 4.0).abs() < 1e-10);
47    }
48
49    #[test]
50    fn scalar_field_is_one_dimensional() {
51        // Every field is a vector space over itself
52        let v: f64 = 5.0;
53        let scaled = v.scale(f64::one());
54        assert!((scaled - 5.0).abs() < 1e-10);
55    }
56}