Skip to main content

mdarray_linalg/
matvec.rs

1//! Basic vector and matrix-vector operations, including `Ax`, `Ax + βy`, Givens rotations, argmax, and rank-1 updates
2//!
3//! # Matrix-Vector Operations
4//!
5//! ```rust
6//! use mdarray::tensor;
7//! use mdarray_linalg::prelude::*;
8//! use mdarray_linalg::Naive;
9//!
10//! // Create a 3x3 matrix and a vector
11//! let a = tensor![[1., 2., 3.],
12//!                 [4., 5., 6.],
13//!                 [7., 8., 9.]];
14//! let x = tensor![1., 1., 1.];
15//!
16//! // Basic matrix-vector multiplication: y = A·x
17//! let y = Naive.matvec(&a, &x).eval();
18//! assert_eq!(y, tensor![6., 15., 24.]);
19//!
20//! // Scaled operation: y = 2·A·x
21//! let y_scaled = Naive.matvec(&a, &x).scale(2.).eval();
22//! assert_eq!(y_scaled, tensor![12., 30., 48.]);
23//!
24//! // Write result to existing vector: y := α·A·x
25//! let mut y_write = tensor![0., 0., 0.];
26//! Naive.matvec(&a, &x).scale(2.).write(&mut y_write);
27//! assert_eq!(y_write, tensor![12., 30., 48.]);
28//!
29//! // Add to vector: y := A·x + y
30//! let mut y_add = tensor![1., 1., 1.];
31//! Naive.matvec(&a, &x).add_to_vec(&mut y_add);
32//! assert_eq!(y_add, tensor![7., 16., 25.]);
33//!
34//! // Scaled addition: y := α·A·x + β·y
35//! let mut y_axpy = tensor![1., 1., 1.];
36//! Naive.matvec(&a, &x).add_to_scaled_vec(&mut y_axpy, 2.);
37//! assert_eq!(y_axpy, tensor![8., 17., 26.]);
38//! ```
39//!
40//! # Outer Products and Rank-1 Updates
41//!
42//! ```rust
43//! use mdarray::tensor;
44//! use mdarray_linalg::prelude::*;
45//! use mdarray_linalg::Naive;
46//!
47//! // Create two vectors
48//! let x = tensor![1., 2.];
49//! let y = tensor![1., 10., 100.];
50//!
51//! // Basic outer product: A = x ⊗ y
52//! let a = Naive.outer(&x, &y).eval();
53//! assert_eq!(a, tensor![[1., 10., 100.],
54//!                       [2., 20., 200.]]);
55//!
56//! // Scaled outer product: A = β·(x ⊗ y)
57//! let a_scaled = Naive.outer(&x, &y).scale(2.).eval();
58//! assert_eq!(a_scaled, tensor![[2., 20., 200.],
59//!                              [4., 40., 400.]]);
60//!
61//! // Write to existing matrix: A := β·(x ⊗ y)
62//! let mut a_write = tensor![[0., 0., 0.],
63//!                           [0., 0., 0.]];
64//! Naive.outer(&x, &y).scale(2.).write(&mut a_write);
65//! assert_eq!(a_write, tensor![[2., 20., 200.],
66//!                             [4., 40., 400.]]);
67//!
68//! // Rank-1 update: A := β·(x ⊗ y) + A
69//! let mut a_update = tensor![[1., 1., 1.],
70//!                            [1., 1., 1.]];
71//! Naive.outer(&x, &y).scale(2.).add_to(&mut a_update);
72//! assert_eq!(a_update, tensor![[3., 21., 201.],
73//!                              [5., 41., 401.]]);
74//! ```
75//!
76//! # Complex Number Support
77//!
78//! ```rust
79//! use mdarray::tensor;
80//! use mdarray_linalg::prelude::*;
81//! use mdarray_linalg::Naive;
82//! use num_complex::Complex64;
83//!
84//! // Complex outer product
85//! let x = tensor![Complex64::new(1., 1.), Complex64::new(2., 0.)];
86//! let y = tensor![Complex64::new(1., 0.), Complex64::new(0., 1.)];
87//! let a = Naive.outer(&x, &y).eval();
88//! assert_eq!(a[[0, 0]], Complex64::new(1., 1.));
89//! assert_eq!(a[[0, 1]], Complex64::new(-1., 1.));
90//!
91//! ```
92//! # Argmax
93//!
94//! ```rust
95//! use mdarray::tensor;
96//! use mdarray_linalg::prelude::*;
97//! use mdarray_linalg::Naive;
98//!
99//! // Find index of maximum value in 1D array
100//! let x = tensor![1., 5., 3., 8., 2.];
101//! let idx = Naive.argmax(&x).unwrap();
102//! assert_eq!(idx, vec![3]);  // Maximum is at index 3
103//!
104//! // Find index in 2D array (returns multi-dimensional index)
105//! let a = tensor![[0., 1., 2.],
106//!                 [3., 4., 5.]];
107//! let idx = Naive.argmax(&a.view(.., ..).into_dyn()).unwrap();
108//! assert_eq!(idx, vec![1, 2]);  // Maximum is at position [1, 2]
109//!
110//! // Find element with largest absolute value
111//! let y = tensor![1., -6., 3., -2., 5.];
112//! let idx = Naive.argmax_abs(&y).unwrap();
113//! assert_eq!(idx, vec![1]);  // -6 has largest absolute value
114//!
115//! // Write result to reusable buffer
116//! let mut output = Vec::new();
117//! let success = Naive.argmax_write(&x, &mut output);
118//! assert!(success);
119//! assert_eq!(output, vec![3]);
120//! ```
121//! # Vector Operations
122//!
123//! ```rust
124//! use mdarray::tensor;
125//! use mdarray_linalg::prelude::*;
126//! use mdarray_linalg::Naive;
127//! use num_complex::Complex64;
128//!
129//! // Scaled vector addition: y := α·x + y
130//! let x = tensor![1., 2., 3.];
131//! let mut y = tensor![1., 1., 1.];
132//! Naive.add_to_scaled(2.0, &x, &mut y);
133//! assert_eq!(y, tensor![3., 5., 7.]);  // y = 2·x + y
134//!
135//! // Dot product: ∑xᵢyᵢ
136//! let x = tensor![1., 2., 3.];
137//! let y = tensor![2., 4., 6.];
138//! let result = Naive.dot(&x, &y);
139//! assert_eq!(result, 28.0);  // 1*2 + 2*4 + 3*6 = 28
140//!
141//! // Conjugated dot product with complex numbers: ∑(conj(xᵢ)·yᵢ)
142//! let x = tensor![Complex64::new(1., 2.), Complex64::new(2., 3.)];
143//! let y = tensor![Complex64::new(3., 4.), Complex64::new(4., 5.)];
144//! let result = Naive.dotc(&x, &y);
145//! // conj(1+2i)*(3+4i) + conj(2+3i)*(4+5i)
146//! let expected = x[[0]].conj() * y[[0]] + x[[1]].conj() * y[[1]];
147//! assert_eq!(result, expected);
148//!
149//! // L2 norm (Euclidean): √(∑|xᵢ|²)
150//! let x = tensor![3., 4.];
151//! let norm = Naive.norm2(&x);
152//! assert_eq!(norm, 5.0);  // √(9 + 16) = 5
153//!
154//! // L1 norm (Manhattan): ∑|xᵢ|
155//! let x = tensor![Complex64::new(1., 2.), Complex64::new(2., 3.)];
156//! let norm = Naive.norm1(&x);
157//! // |1+2i| + |2+3i| = (|1|+|2|) + (|2|+|3|) = 8
158//! assert_eq!(norm, 8.0);
159//! ```
160use mdarray::{Array, Dim, Layout, Shape, Slice};
161
162/// Matrix-vector multiplication and transformations
163pub trait MatVec<T, D0: Dim, D1: Dim> {
164    fn matvec<'a, La, Lx>(
165        &self,
166        a: &'a Slice<T, (D0, D1), La>,
167        x: &'a Slice<T, (D1,), Lx>,
168    ) -> impl MatVecBuilder<'a, T, La, Lx, D0, D1>
169    where
170        La: Layout,
171        Lx: Layout;
172}
173
174/// Builder interface for configuring matrix-vector operations
175pub trait MatVecBuilder<'a, T, La, Lx, D0: Dim, D1: Dim>
176where
177    La: Layout,
178    Lx: Layout,
179    T: 'a,
180    La: 'a,
181    Lx: 'a,
182{
183    /// `α := α·α'`
184    fn scale(self, alpha: T) -> Self;
185
186    /// Returns `α·A·x`
187    fn eval(self) -> Array<T, (D0,)>;
188
189    /// `y := α·A·x`
190    fn write<Ly: Layout>(self, y: &mut Slice<T, (D0,), Ly>);
191
192    /// `y := α·A·x + y`
193    fn add_to_vec<Ly: Layout>(self, y: &mut Slice<T, (D0,), Ly>);
194
195    /// `y := α·A·x + β·y`
196    fn add_to_scaled_vec<Ly: Layout>(self, y: &mut Slice<T, (D0,), Ly>, beta: T);
197}
198
199/// Vector operations and basic linear algebra utilities
200pub trait VecOps<T, D1: Dim> {
201    /// Real scalar type used for norm results and real rotation coefficients.
202    ///
203    /// This type is chosen by the backend implementation for the scalar type `T`.
204    type Real;
205
206    /// Accumulate a scaled vector: `y := α·x + y`
207    fn add_to_scaled<Lx: Layout, Ly: Layout>(
208        &self,
209        alpha: T,
210        x: &Slice<T, (D1,), Lx>,
211        y: &mut Slice<T, (D1,), Ly>,
212    );
213
214    /// Dot product: `∑xᵢyᵢ`
215    fn dot<Lx: Layout, Ly: Layout>(&self, x: &Slice<T, (D1,), Lx>, y: &Slice<T, (D1,), Ly>) -> T;
216
217    /// Conjugated dot product: `∑conj(xᵢ) * yᵢ` (BLAS convention)
218    fn dotc<Lx: Layout, Ly: Layout>(&self, x: &Slice<T, (D1,), Lx>, y: &Slice<T, (D1,), Ly>) -> T;
219
220    /// L2 norm: `√(∑|xᵢ|²)`
221    fn norm2<Lx: Layout>(&self, x: &Slice<T, (D1,), Lx>) -> Self::Real;
222
223    /// L1 norm (Manhattan) for complex numbers: `∑(|re(xᵢ)| + |im(xᵢ)|)`.
224    /// For real numbers this reduces to `∑|xᵢ|`.
225    fn norm1<Lx: Layout>(&self, x: &Slice<T, (D1,), Lx>) -> Self::Real;
226
227    /// Givens rotation
228    fn rot<Lx: Layout, Ly: Layout>(
229        &self,
230        x: &mut Slice<T, (D1,), Lx>,
231        y: &mut Slice<T, (D1,), Ly>,
232        c: Self::Real,
233        s: T,
234    );
235}
236
237/// Argmax for tensors of any rank.
238///
239/// Implementations define the ordering and magnitude semantics they support for `T`;
240/// backend impl bounds should state any Rust scalar traits they rely on.
241pub trait Argmax<T> {
242    fn argmax_write<Lx: Layout, S: Shape>(
243        &self,
244        x: &Slice<T, S, Lx>,
245        output: &mut Vec<usize>,
246    ) -> bool;
247
248    fn argmax_abs_write<Lx: Layout, S: Shape>(
249        &self,
250        x: &Slice<T, S, Lx>,
251        output: &mut Vec<usize>,
252    ) -> bool;
253
254    /// Index of max xᵢ (argmaxᵢ xᵢ)
255    fn argmax<Lx: Layout, S: Shape>(&self, x: &Slice<T, S, Lx>) -> Option<Vec<usize>>;
256
257    /// Index of max |xᵢ| (argmaxᵢ |xᵢ|)
258    fn argmax_abs<Lx: Layout, S: Shape>(&self, x: &Slice<T, S, Lx>) -> Option<Vec<usize>>;
259}
260
261/// Outer product and rank-1 update
262pub trait Outer<T, Dx: Dim, Dy: Dim> {
263    fn outer<'a, Lx, Ly>(
264        &self,
265        x: &'a Slice<T, (Dx,), Lx>,
266        y: &'a Slice<T, (Dy,), Ly>,
267    ) -> impl OuterBuilder<'a, T, Lx, Ly, Dx, Dy>
268    where
269        Lx: Layout,
270        Ly: Layout;
271}
272
273/// Builder interface for configuring outer product and rank-1 update
274pub trait OuterBuilder<'a, T, Lx, Ly, Dx: Dim, Dy: Dim>
275where
276    Lx: Layout,
277    Ly: Layout,
278    T: 'a,
279    Lx: 'a,
280    Ly: 'a,
281{
282    /// `α := α·α'`
283    fn scale(self, alpha: T) -> Self;
284
285    /// Returns `α·xy`
286    fn eval(self) -> Array<T, (Dx, Dy)>;
287
288    /// `a := α·xy`
289    fn write<La: Layout>(self, a: &mut Slice<T, (Dx, Dy), La>);
290
291    /// Rank-1 update: `A := α·x·yᵀ + A`
292    fn add_to<La: Layout>(self, a: &mut Slice<T, (Dx, Dy), La>);
293}