Skip to content

Commit 2bae625

Browse files
committed
Expose lax::eig, update documents
1 parent a94bd45 commit 2bae625

File tree

2 files changed

+80
-22
lines changed

2 files changed

+80
-22
lines changed

lax/src/eig.rs

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
1+
//! Eigenvalue problem for general matricies
2+
13
use crate::{error::*, layout::MatrixLayout, *};
24
use cauchy::*;
35
use num_traits::{ToPrimitive, Zero};
46

57
#[cfg_attr(doc, katexit::katexit)]
68
/// Eigenvalue problem for general matrix
79
///
8-
/// LAPACK assumes a column-major input. A row-major input can
9-
/// be interpreted as the transpose of a column-major input. So,
10-
/// for row-major inputs, we we want to solve the following,
11-
/// given the column-major input `A`:
10+
/// To manage memory more strictly, use [EigWork].
11+
///
12+
/// Right and Left eigenvalue problem
13+
/// ----------------------------------
14+
/// LAPACK can solve both right eigenvalue problem
15+
/// $$
16+
/// AV_R = V_R \Lambda
17+
/// $$
18+
/// where $V_R = \left( v_R^1, \cdots, v_R^n \right)$ are right eigenvectors
19+
/// and left eigenvalue problem
20+
/// $$
21+
/// V_L^\dagger A = V_L^\dagger \Lambda
22+
/// $$
23+
/// where $V_L = \left( v_L^1, \cdots, v_L^n \right)$ are left eigenvectors
24+
/// and eigenvalues
25+
/// $$
26+
/// \Lambda = \begin{pmatrix}
27+
/// \lambda_1 & & 0 \\\\
28+
/// & \ddots & \\\\
29+
/// 0 & & \lambda_n
30+
/// \end{pmatrix}
31+
/// $$
32+
/// which satisfies $A v_R^i = \lambda_i v_R^i$ and
33+
/// $\left(v_L^i\right)^\dagger A = \lambda_i \left(v_L^i\right)^\dagger$
34+
/// for column-major matrices, although row-major matrices are not supported.
35+
/// Since a row-major matrix can be interpreted
36+
/// as a transpose of a column-major matrix,
37+
/// this transforms right eigenvalue problem to left one:
1238
///
13-
/// A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H
39+
/// $$
40+
/// A^\dagger V = V Λ ⟺ V^\dagger A = Λ V^\dagger
41+
/// $$
1442
///
15-
/// So, in this case, the right eigenvectors are the conjugates
16-
/// of the left eigenvectors computed with `A`, and the
17-
/// eigenvalues are the eigenvalues computed with `A`.
1843
pub trait Eig_: Scalar {
1944
/// Compute right eigenvalue and eigenvectors $Ax = \lambda x$
2045
///
@@ -41,7 +66,7 @@ macro_rules! impl_eig {
4166
a: &mut [Self],
4267
) -> Result<(Vec<Self::Complex>, Vec<Self::Complex>)> {
4368
let work = EigWork::<$s>::new(calc_v, l)?;
44-
let Eig { eigs, vr, vl } = work.eval(a)?;
69+
let EigOwned { eigs, vr, vl } = work.eval(a)?;
4570
Ok((eigs, vr.or(vl).unwrap_or_default()))
4671
}
4772
}
@@ -53,13 +78,16 @@ impl_eig!(f64);
5378
impl_eig!(f32);
5479

5580
/// Working memory for [Eig_]
56-
#[derive(Debug, Clone)]
81+
#[non_exhaustive]
5782
pub struct EigWork<T: Scalar> {
83+
/// Problem size
5884
pub n: i32,
85+
/// Compute right eigenvectors or not
5986
pub jobvr: JobEv,
87+
/// Compute left eigenvectors or not
6088
pub jobvl: JobEv,
6189

62-
/// Eigenvalues used in complex routines
90+
/// Eigenvalues
6391
pub eigs: Vec<MaybeUninit<T::Complex>>,
6492
/// Real part of eigenvalues used in real routines
6593
pub eigs_re: Option<Vec<MaybeUninit<T::Real>>>,
@@ -68,9 +96,11 @@ pub struct EigWork<T: Scalar> {
6896

6997
/// Left eigenvectors
7098
pub vc_l: Option<Vec<MaybeUninit<T::Complex>>>,
99+
/// Left eigenvectors used in real routines
71100
pub vr_l: Option<Vec<MaybeUninit<T::Real>>>,
72101
/// Right eigenvectors
73102
pub vc_r: Option<Vec<MaybeUninit<T::Complex>>>,
103+
/// Right eigenvectors used in real routines
74104
pub vr_r: Option<Vec<MaybeUninit<T::Real>>>,
75105

76106
/// Working memory
@@ -79,28 +109,55 @@ pub struct EigWork<T: Scalar> {
79109
pub rwork: Option<Vec<MaybeUninit<T::Real>>>,
80110
}
81111

112+
impl<T> EigWork<T>
113+
where
114+
T: Scalar,
115+
EigWork<T>: EigWorkImpl<Elem = T>,
116+
{
117+
/// Create new working memory for eigenvalues compution.
118+
pub fn new(calc_v: bool, l: MatrixLayout) -> Result<Self> {
119+
EigWorkImpl::new(calc_v, l)
120+
}
121+
122+
/// Compute eigenvalues and vectors on this working memory.
123+
pub fn calc(&mut self, a: &mut [T]) -> Result<EigRef<T>> {
124+
EigWorkImpl::calc(self, a)
125+
}
126+
127+
/// Compute eigenvalues and vectors by consuming this working memory.
128+
pub fn eval(self, a: &mut [T]) -> Result<EigOwned<T>> {
129+
EigWorkImpl::eval(self, a)
130+
}
131+
}
132+
133+
/// Owned result of eigenvalue problem by [EigWork::eval]
82134
#[derive(Debug, Clone, PartialEq)]
83-
pub struct Eig<T: Scalar> {
135+
pub struct EigOwned<T: Scalar> {
136+
/// Eigenvalues
84137
pub eigs: Vec<T::Complex>,
138+
/// Right eigenvectors
85139
pub vr: Option<Vec<T::Complex>>,
140+
/// Left eigenvectors
86141
pub vl: Option<Vec<T::Complex>>,
87142
}
88143

144+
/// Reference result of eigenvalue problem by [EigWork::calc]
89145
#[derive(Debug, Clone, PartialEq)]
90146
pub struct EigRef<'work, T: Scalar> {
147+
/// Eigenvalues
91148
pub eigs: &'work [T::Complex],
149+
/// Right eigenvectors
92150
pub vr: Option<&'work [T::Complex]>,
151+
/// Left eigenvectors
93152
pub vl: Option<&'work [T::Complex]>,
94153
}
95154

155+
/// Helper trait for implementing [EigWork] methods
96156
pub trait EigWorkImpl: Sized {
97157
type Elem: Scalar;
98-
/// Create new working memory for eigenvalues compution.
99158
fn new(calc_v: bool, l: MatrixLayout) -> Result<Self>;
100-
/// Compute eigenvalues and vectors on this working memory.
101159
fn calc<'work>(&'work mut self, a: &mut [Self::Elem]) -> Result<EigRef<'work, Self::Elem>>;
102-
/// Compute eigenvalues and vectors by consuming this working memory.
103-
fn eval(self, a: &mut [Self::Elem]) -> Result<Eig<Self::Elem>>;
160+
fn eval(self, a: &mut [Self::Elem]) -> Result<EigOwned<Self::Elem>>;
104161
}
105162

106163
macro_rules! impl_eig_work_c {
@@ -210,7 +267,7 @@ macro_rules! impl_eig_work_c {
210267
})
211268
}
212269

213-
fn eval(mut self, a: &mut [Self::Elem]) -> Result<Eig<Self::Elem>> {
270+
fn eval(mut self, a: &mut [Self::Elem]) -> Result<EigOwned<Self::Elem>> {
214271
let lwork = self.work.len().to_i32().unwrap();
215272
let mut info = 0;
216273
unsafe {
@@ -239,7 +296,7 @@ macro_rules! impl_eig_work_c {
239296
value.im = -value.im;
240297
}
241298
}
242-
Ok(Eig {
299+
Ok(EigOwned {
243300
eigs: unsafe { self.eigs.assume_init() },
244301
vl: self.vc_l.map(|v| unsafe { v.assume_init() }),
245302
vr: self.vc_r.map(|v| unsafe { v.assume_init() }),
@@ -377,7 +434,7 @@ macro_rules! impl_eig_work_r {
377434
})
378435
}
379436

380-
fn eval(mut self, a: &mut [Self::Elem]) -> Result<Eig<Self::Elem>> {
437+
fn eval(mut self, a: &mut [Self::Elem]) -> Result<EigOwned<Self::Elem>> {
381438
let lwork = self.work.len().to_i32().unwrap();
382439
let mut info = 0;
383440
unsafe {
@@ -421,7 +478,7 @@ macro_rules! impl_eig_work_r {
421478
reconstruct_eigenvectors(false, eigs_im, v, self.vc_r.as_mut().unwrap());
422479
}
423480

424-
Ok(Eig {
481+
Ok(EigOwned {
425482
eigs: unsafe { self.eigs.assume_init() },
426483
vl: self.vc_l.map(|v| unsafe { v.assume_init() }),
427484
vr: self.vc_r.map(|v| unsafe { v.assume_init() }),

lax/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ pub mod error;
8484
pub mod flags;
8585
pub mod layout;
8686

87+
pub mod eig;
88+
8789
mod alloc;
8890
mod cholesky;
89-
mod eig;
9091
mod eigh;
9192
mod least_squares;
9293
mod opnorm;
@@ -100,7 +101,7 @@ mod triangular;
100101
mod tridiagonal;
101102

102103
pub use self::cholesky::*;
103-
pub use self::eig::*;
104+
pub use self::eig::Eig_;
104105
pub use self::eigh::*;
105106
pub use self::flags::*;
106107
pub use self::least_squares::*;

0 commit comments

Comments
 (0)