scirs2_linalg/structured/mod.rs
1//! Structured matrices support (Toeplitz, circulant) for efficient representations
2//!
3//! This module provides implementations of various structured matrices that have
4//! special properties allowing for efficient storage and operations.
5//!
6//! # Overview
7//!
8//! Structured matrices are matrices with special patterns that allow them to be
9//! represented using far fewer parameters than general matrices. This provides
10//! significant memory savings and computational advantages for large matrices.
11//!
12//! ## Types of Structured Matrices
13//!
14//! * **Toeplitz matrices**: Matrices where each descending diagonal from left to right is constant
15//! * **Circulant matrices**: Special Toeplitz matrices where each row is a cyclic shift of the first row
16//! * **Hankel matrices**: Matrices where each ascending diagonal from left to right is constant
17//!
18//! # Examples
19//!
20//! ```
21//! use scirs2_core::ndarray::{Array1, array};
22//! use scirs2_linalg::structured::{ToeplitzMatrix, StructuredMatrix};
23//!
24//! // Create a Toeplitz matrix from its first row and column
25//! let first_row = array![1.0, 2.0, 3.0];
26//! let first_col = array![1.0, 4.0, 5.0];
27//!
28//! let toeplitz = ToeplitzMatrix::new(first_row.view(), first_col.view()).unwrap();
29//!
30//! // The matrix represented is:
31//! // [1.0, 2.0, 3.0]
32//! // [4.0, 1.0, 2.0]
33//! // [5.0, 4.0, 1.0]
34//!
35//! // Apply to a vector efficiently without forming the full matrix
36//! let x = array![1.0, 2.0, 3.0];
37//! let y = toeplitz.matvec(&x.view()).unwrap();
38//! ```
39
40use crate::error::LinalgResult;
41use crate::matrixfree::LinearOperator;
42use scirs2_core::ndarray::ScalarOperand;
43use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
44use scirs2_core::numeric::{Float, NumAssign, One, Zero};
45use std::{fmt::Debug, iter::Sum};
46
47mod circulant;
48mod hankel;
49mod toeplitz;
50mod utils;
51
52pub use circulant::CirculantMatrix;
53pub use hankel::HankelMatrix;
54pub use toeplitz::ToeplitzMatrix;
55pub use utils::{
56 circulant_determinant, circulant_eigenvalues, circulant_inverse_fft, circulant_matvec_direct,
57 circulant_matvec_fft, dftmatrix_multiply, fast_toeplitz_inverse, gohberg_semencul_inverse,
58 hadamard_transform, hankel_determinant, hankel_matvec, hankel_matvec_fft, hankel_svd,
59 levinson_durbin, solve_circulant, solve_circulant_fft, solve_toeplitz, solve_tridiagonal_lu,
60 solve_tridiagonal_thomas, tridiagonal_determinant, tridiagonal_eigenvalues,
61 tridiagonal_eigenvectors, tridiagonal_matvec, yule_walker,
62};
63
64/// A trait for structured matrices that can be represented efficiently
65///
66/// This trait defines common operations for all structured matrices.
67pub trait StructuredMatrix<A>
68where
69 A: Float + NumAssign + Zero + Sum + One + ScalarOperand + Send + Sync + Debug,
70{
71 /// Returns the number of rows in the matrix
72 fn nrows(&self) -> usize;
73
74 /// Returns the number of columns in the matrix
75 fn ncols(&self) -> usize;
76
77 /// Returns the shape of the matrix as (rows, cols)
78 fn shape(&self) -> (usize, usize) {
79 (self.nrows(), self.ncols())
80 }
81
82 /// Returns the element at position (i, j)
83 fn get(&self, i: usize, j: usize) -> LinalgResult<A>;
84
85 /// Multiply the matrix by a vector (matrix-vector product)
86 fn matvec(&self, x: &ArrayView1<A>) -> LinalgResult<Array1<A>>;
87
88 /// Multiply the transpose of the matrix by a vector
89 fn matvec_transpose(&self, x: &ArrayView1<A>) -> LinalgResult<Array1<A>>;
90
91 /// Convert the structured matrix to a dense ndarray representation
92 fn to_dense(&self) -> LinalgResult<Array2<A>> {
93 let (rows, cols) = self.shape();
94 let mut result = Array2::zeros((rows, cols));
95
96 for i in 0..rows {
97 for j in 0..cols {
98 result[[i, j]] = self.get(i, j)?;
99 }
100 }
101
102 Ok(result)
103 }
104
105 /// Create a matrix-free operator from this structured matrix
106 fn to_operator(&self) -> LinearOperator<A>
107 where
108 Self: Clone + Send + Sync + 'static,
109 {
110 let matrix = self.clone();
111 let (rows, cols) = self.shape();
112
113 LinearOperator::new_rectangular(rows, cols, move |x: &ArrayView1<A>| {
114 matrix.matvec(x).unwrap()
115 })
116 }
117}
118
119/// Convert a structured matrix to a matrix-free operator
120#[allow(dead_code)]
121pub fn structured_to_operator<A, M>(matrix: &M) -> LinearOperator<A>
122where
123 A: Float + NumAssign + Zero + Sum + One + ScalarOperand + Send + Sync + Debug + 'static,
124 M: StructuredMatrix<A> + Clone + Send + Sync + 'static,
125{
126 let matrix_clone = matrix.clone();
127 let (rows, cols) = matrix.shape();
128
129 LinearOperator::new_rectangular(rows, cols, move |x: &ArrayView1<A>| {
130 matrix_clone.matvec(x).unwrap()
131 })
132}
133
134#[cfg(test)]
135mod tests {
136 // Tests are implemented in individual module test files
137}