Skip to content

Commit 7f03e30

Browse files
authored
refactor(algebra): matrix implementation (#80)
* cleanup * cleanup * lint * Update homology.rs
1 parent 971adbf commit 7f03e30

File tree

9 files changed

+1576
-2693
lines changed

9 files changed

+1576
-2693
lines changed

cova-algebra/src/tensors/dynamic/block.rs

Lines changed: 0 additions & 702 deletions
This file was deleted.

cova-algebra/src/tensors/dynamic/matrix.rs

Lines changed: 1324 additions & 1605 deletions
Large diffs are not rendered by default.

cova-algebra/src/tensors/dynamic/mod.rs

Lines changed: 54 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,48 @@
77
//!
88
//! The dynamic tensors module includes:
99
//!
10-
//! - `DynamicVector`: A flexible vector implementation with arbitrary dimension
11-
//! - `DynamicDenseMatrix`: A matrix implementation with two storage orientation options:
12-
//! - Row-major: Efficient for row operations
13-
//! - Column-major: Efficient for column operations
10+
//! - `Vector`: A flexible vector implementation with arbitrary dimension
11+
//! - `Matrix`: A clean, ergonomic matrix implementation with block construction support
1412
//!
1513
//! ## Mathematical Foundation
1614
//!
1715
//! Tensors are mathematical objects that generalize vectors and matrices to higher dimensions.
1816
//! The implementations in this module adhere to the algebraic properties of vector spaces
1917
//! and linear transformations over arbitrary fields.
2018
//!
21-
//! ## Performance Considerations
22-
//!
23-
//! - Choose storage orientation based on the dominant operation pattern:
24-
//! - Use row-major matrices when mostly operating on rows
25-
//! - Use column-major matrices when mostly operating on columns
26-
//! - Matrix-vector operations automatically use the most efficient implementation based on the
27-
//! storage orientation
28-
//!
2919
//! ## Examples
3020
//!
3121
//! ```
3222
//! use cova_algebra::{
3323
//! prelude::*,
34-
//! tensors::dynamic::{
35-
//! matrix::{DynamicDenseMatrix, RowMajor},
36-
//! vector::DynamicVector,
37-
//! },
24+
//! tensors::dynamic::{matrix::Matrix, vector::Vector},
3825
//! };
3926
//!
4027
//! // Create vectors
41-
//! let v1 = DynamicVector::from([1.0, 2.0, 3.0]);
42-
//! let v2 = DynamicVector::from([4.0, 5.0, 6.0]);
28+
//! let v1 = Vector::from([1.0, 2.0, 3.0]);
29+
//! let v2 = Vector::from([4.0, 5.0, 6.0]);
4330
//!
4431
//! // Create a matrix from rows
45-
//! let mut matrix = DynamicDenseMatrix::<f64, RowMajor>::new();
46-
//! matrix.append_row(v1);
47-
//! matrix.append_row(v2);
32+
//! let matrix = Matrix::from_rows([v1, v2]);
33+
//!
34+
//! // Or using builder pattern
35+
//! let matrix = Matrix::<f64>::builder().row([1.0, 2.0, 3.0]).row([4.0, 5.0, 6.0]).build();
36+
//!
37+
//! // Block matrix construction
38+
//! let block_matrix = Matrix::<f64>::from_blocks(vec![
39+
//! vec![Matrix::identity(2), Matrix::zeros(2, 3)],
40+
//! vec![Matrix::zeros(3, 2), Matrix::identity(3)],
41+
//! ]);
4842
//!
4943
//! // Perform Gaussian elimination to row echelon form
50-
//! let result = matrix.row_echelon_form();
44+
//! let (rref, output) = matrix.into_row_echelon_form();
5145
//! ```
5246
53-
use matrix::{DynamicDenseMatrix, RowMajor};
54-
use vector::DynamicVector;
47+
pub use matrix::Matrix;
48+
pub use vector::Vector;
5549

5650
use super::*;
5751

58-
pub mod block;
5952
pub mod matrix;
6053
pub mod vector;
6154

@@ -66,9 +59,9 @@ pub mod vector;
6659
/// this method returns a basis for the quotient space V/U.
6760
/// The input vectors are treated as column vectors.
6861
pub fn compute_quotient_basis<F: Field + Copy>(
69-
subspace_vectors: &[DynamicVector<F>],
70-
space_vectors: &[DynamicVector<F>],
71-
) -> Vec<DynamicVector<F>> {
62+
subspace_vectors: &[Vector<F>],
63+
space_vectors: &[Vector<F>],
64+
) -> Vec<Vector<F>> {
7265
if space_vectors.is_empty() {
7366
return Vec::new();
7467
}
@@ -98,19 +91,15 @@ pub fn compute_quotient_basis<F: Field + Copy>(
9891
);
9992
}
10093

101-
let mut matrix = DynamicDenseMatrix::<F, RowMajor>::new();
102-
103-
for vec in subspace_vectors {
104-
matrix.append_column(vec);
105-
}
106-
107-
for vec in space_vectors {
108-
matrix.append_column(vec);
109-
}
94+
// Create matrix from columns
95+
let mut all_columns = Vec::new();
96+
all_columns.extend_from_slice(subspace_vectors);
97+
all_columns.extend_from_slice(space_vectors);
11098

111-
let echelon_output = matrix.row_echelon_form();
99+
let matrix = Matrix::from_cols(all_columns);
100+
let (_, echelon_output) = matrix.into_row_echelon_form();
112101

113-
let mut quotient_basis: Vec<DynamicVector<F>> = Vec::new();
102+
let mut quotient_basis: Vec<Vector<F>> = Vec::new();
114103
let num_subspace_cols = subspace_vectors.len();
115104

116105
let pivot_cols_set: std::collections::HashSet<usize> =
@@ -129,15 +118,15 @@ pub fn compute_quotient_basis<F: Field + Copy>(
129118
#[cfg(test)]
130119
mod tests {
131120
use super::compute_quotient_basis;
132-
use crate::{fixtures::Mod7, tensors::dynamic::vector::DynamicVector};
121+
use crate::{fixtures::Mod7, tensors::dynamic::vector::Vector};
133122

134123
#[test]
135124
fn test_quotient_simple_span() {
136125
// V = span{[1,0,0], [0,1,0]}, U = span{[1,0,0]}
137126
// V/U should be span{[0,1,0]}
138-
let u1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
139-
let v_in_u = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
140-
let v_new = DynamicVector::from(vec![Mod7::new(0), Mod7::new(1), Mod7::new(0)]);
127+
let u1 = Vector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
128+
let v_in_u = Vector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
129+
let v_new = Vector::from(vec![Mod7::new(0), Mod7::new(1), Mod7::new(0)]);
141130

142131
let subspace_vectors = vec![u1];
143132
// Order of space_vectors: putting v_in_u first
@@ -157,8 +146,8 @@ mod tests {
157146
fn test_quotient_subspace_equals_space() {
158147
// V = span{[1,0], [0,1]}, U = span{[1,0], [0,1]}
159148
// V/U should be empty
160-
let u1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0)]);
161-
let u2 = DynamicVector::from(vec![Mod7::new(0), Mod7::new(1)]);
149+
let u1 = Vector::from(vec![Mod7::new(1), Mod7::new(0)]);
150+
let u2 = Vector::from(vec![Mod7::new(0), Mod7::new(1)]);
162151
// space_vectors are the same as subspace_vectors
163152
let space_vectors = vec![u1.clone(), u2.clone()];
164153
let subspace_vectors = vec![u1.clone(), u2.clone()];
@@ -175,10 +164,10 @@ mod tests {
175164
fn test_quotient_trivial_subspace() {
176165
// V = span{[1,0], [0,1]}, U = {} (trivial subspace)
177166
// V/U should be span{[1,0], [0,1]}
178-
let v1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0)]);
179-
let v2 = DynamicVector::from(vec![Mod7::new(0), Mod7::new(1)]);
167+
let v1 = Vector::from(vec![Mod7::new(1), Mod7::new(0)]);
168+
let v2 = Vector::from(vec![Mod7::new(0), Mod7::new(1)]);
180169

181-
let subspace_vectors: Vec<DynamicVector<Mod7>> = vec![];
170+
let subspace_vectors: Vec<Vector<Mod7>> = vec![];
182171
let space_vectors = vec![v1.clone(), v2.clone()];
183172

184173
let quotient_basis = compute_quotient_basis(&subspace_vectors, &space_vectors);
@@ -192,10 +181,10 @@ mod tests {
192181
// V = span{[1,0], [2,0], [0,1]}, U = span{[1,0]}
193182
// [2,0] is dependent on [1,0].
194183
// V/U should be span{[0,1]}
195-
let u1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0)]);
196-
let v_in_u = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0)]); // Effectively in U
197-
let v_dependent_on_u = DynamicVector::from(vec![Mod7::new(2), Mod7::new(0)]); // 2*u1
198-
let v_new_independent = DynamicVector::from(vec![Mod7::new(0), Mod7::new(1)]);
184+
let u1 = Vector::from(vec![Mod7::new(1), Mod7::new(0)]);
185+
let v_in_u = Vector::from(vec![Mod7::new(1), Mod7::new(0)]); // Effectively in U
186+
let v_dependent_on_u = Vector::from(vec![Mod7::new(2), Mod7::new(0)]); // 2*u1
187+
let v_new_independent = Vector::from(vec![Mod7::new(0), Mod7::new(1)]);
199188

200189
let subspace_vectors = vec![u1.clone()];
201190
let space_vectors = vec![v_in_u.clone(), v_dependent_on_u.clone(), v_new_independent.clone()];
@@ -223,11 +212,11 @@ mod tests {
223212
// Expected quotient basis: a basis for span{[0,1,0], [0,0,1]}, chosen from original space
224213
// vectors. So, should be [[0,1,0], [0,0,1]] if [0,2,0] is correctly identified as dependent
225214
// on [0,1,0] in context of quotient.
226-
let u1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
215+
let u1 = Vector::from(vec![Mod7::new(1), Mod7::new(0), Mod7::new(0)]);
227216

228-
let v1_new = DynamicVector::from(vec![Mod7::new(0), Mod7::new(1), Mod7::new(0)]);
229-
let v2_dependent_on_v1 = DynamicVector::from(vec![Mod7::new(0), Mod7::new(2), Mod7::new(0)]);
230-
let v3_new = DynamicVector::from(vec![Mod7::new(0), Mod7::new(0), Mod7::new(1)]);
217+
let v1_new = Vector::from(vec![Mod7::new(0), Mod7::new(1), Mod7::new(0)]);
218+
let v2_dependent_on_v1 = Vector::from(vec![Mod7::new(0), Mod7::new(2), Mod7::new(0)]);
219+
let v3_new = Vector::from(vec![Mod7::new(0), Mod7::new(0), Mod7::new(1)]);
231220

232221
let subspace_vectors = vec![u1];
233222
// Order: v1_new, then its dependent v2_dependent_on_v1, then independent v3_new
@@ -250,19 +239,19 @@ mod tests {
250239

251240
#[test]
252241
fn test_quotient_empty_space_vectors() {
253-
let u1 = DynamicVector::from(vec![Mod7::new(1), Mod7::new(0)]);
242+
let u1 = Vector::from(vec![Mod7::new(1), Mod7::new(0)]);
254243
let subspace_vectors = vec![u1];
255-
let space_vectors: Vec<DynamicVector<Mod7>> = vec![];
244+
let space_vectors: Vec<Vector<Mod7>> = vec![];
256245

257246
let quotient_basis = compute_quotient_basis(&subspace_vectors, &space_vectors);
258247
assert!(quotient_basis.is_empty(), "Quotient basis should be empty if space_vectors is empty");
259248
}
260249

261250
#[test]
262251
fn test_quotient_zero_dimensional_vectors() {
263-
let u1_zero_dim = DynamicVector::<Mod7>::new(vec![]);
264-
let v1_zero_dim = DynamicVector::<Mod7>::new(vec![]);
265-
let v2_zero_dim = DynamicVector::<Mod7>::new(vec![]);
252+
let u1_zero_dim = Vector::<Mod7>::new(vec![]);
253+
let v1_zero_dim = Vector::<Mod7>::new(vec![]);
254+
let v2_zero_dim = Vector::<Mod7>::new(vec![]);
266255

267256
let subspace_vectors = vec![u1_zero_dim];
268257
let space_vectors = vec![v1_zero_dim, v2_zero_dim];
@@ -274,7 +263,7 @@ mod tests {
274263
);
275264

276265
// Case: subspace empty, space has 0-dim vectors
277-
let subspace_vectors_empty: Vec<DynamicVector<Mod7>> = vec![];
266+
let subspace_vectors_empty: Vec<Vector<Mod7>> = vec![];
278267
let quotient_basis_empty_sub = compute_quotient_basis(&subspace_vectors_empty, &space_vectors);
279268
assert!(
280269
quotient_basis_empty_sub.is_empty(),
@@ -286,9 +275,9 @@ mod tests {
286275
fn test_quotient_all_zero_vectors_of_some_dimension() {
287276
// V = span{[0,0], [0,0]}, U = span{[0,0]}
288277
// V/U should be empty
289-
let u1_zero_vec = DynamicVector::from(vec![Mod7::new(0), Mod7::new(0)]);
290-
let v1_zero_vec = DynamicVector::from(vec![Mod7::new(0), Mod7::new(0)]);
291-
let v2_zero_vec = DynamicVector::from(vec![Mod7::new(0), Mod7::new(0)]);
278+
let u1_zero_vec = Vector::from(vec![Mod7::new(0), Mod7::new(0)]);
279+
let v1_zero_vec = Vector::from(vec![Mod7::new(0), Mod7::new(0)]);
280+
let v2_zero_vec = Vector::from(vec![Mod7::new(0), Mod7::new(0)]);
292281

293282
let subspace_vectors = vec![u1_zero_vec.clone()];
294283
let space_vectors = vec![v1_zero_vec.clone(), v2_zero_vec.clone()];

0 commit comments

Comments
 (0)