From 429d14e03d82fda9255f1af2e11c788c0f25c30e Mon Sep 17 00:00:00 2001 From: Benjamin Kay Date: Tue, 27 Jul 2021 16:49:10 -0500 Subject: [PATCH 001/165] Relax requirement for DataMut on SVDCC --- ndarray-linalg/src/svddc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/src/svddc.rs b/ndarray-linalg/src/svddc.rs index 017354c2..ff73407f 100644 --- a/ndarray-linalg/src/svddc.rs +++ b/ndarray-linalg/src/svddc.rs @@ -38,7 +38,7 @@ pub trait SVDDCInplace { impl SVDDC for ArrayBase where A: Scalar + Lapack, - S: DataMut, + S: Data, { type U = Array2; type VT = Array2; From 70b9227a188d938b45261a4d2f6c37da9a1c711e Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Tue, 7 Sep 2021 13:35:03 +0200 Subject: [PATCH 002/165] Fix typo in lax error message --- lax/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/src/error.rs b/lax/src/error.rs index fb4b9838..e1c314ee 100644 --- a/lax/src/error.rs +++ b/lax/src/error.rs @@ -11,7 +11,7 @@ pub enum Error { LapackInvalidValue { return_code: i32 }, #[error( - "Comutational failure in LAPACK subroutine: return_code = {}", + "Computational failure in LAPACK subroutine: return_code = {}", return_code )] LapackComputationalFailure { return_code: i32 }, From 3ee97c60847bc2e0e9ded3353e8c918b8a71ce5c Mon Sep 17 00:00:00 2001 From: Mike Seddon Date: Sat, 6 Aug 2022 09:41:59 +1000 Subject: [PATCH 003/165] bump lax intel-mkl-src --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 53bde27f..77b2d955 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -35,7 +35,7 @@ num-traits = "0.2.14" lapack = "0.18.0" [dependencies.intel-mkl-src] -version = "0.6.0" +version = "0.7.0" default-features = false optional = true From 194dae97b727709625459acc81ab397fdfffee1d Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 28 Aug 2022 21:58:15 +0900 Subject: [PATCH 004/165] Use new container by https://github.com/rust-math/rust-mkl-container --- .github/workflows/intel-mkl.yml | 3 ++- .github/workflows/rust.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/intel-mkl.yml b/.github/workflows/intel-mkl.yml index 2144049f..d6bf8905 100644 --- a/.github/workflows/intel-mkl.yml +++ b/.github/workflows/intel-mkl.yml @@ -34,7 +34,8 @@ jobs: linux-container: runs-on: ubuntu-18.04 - container: ghcr.io/rust-math/intel-mkl-src/mkl-rust:1.49.0 + container: + image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f4aa30ae..c58670b1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: coverage: runs-on: ubuntu-18.04 container: - image: ghcr.io/rust-math/intel-mkl-src/mkl-rust:1.49.0 + image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 options: --security-opt seccomp=unconfined steps: - uses: actions/checkout@v2 From e7fe82447ac856727de325f29ec096c20702d2d3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 28 Aug 2022 21:59:32 +0900 Subject: [PATCH 005/165] Use ubuntu-22.04 runner --- .github/workflows/gh-pages.yml | 2 +- .github/workflows/intel-mkl.yml | 4 ++-- .github/workflows/netlib.yml | 2 +- .github/workflows/openblas.yml | 2 +- .github/workflows/rust.yml | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index fce128da..1c910568 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -7,7 +7,7 @@ on: jobs: pages: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - name: Generate code coverage diff --git a/.github/workflows/intel-mkl.yml b/.github/workflows/intel-mkl.yml index d6bf8905..fcb8c845 100644 --- a/.github/workflows/intel-mkl.yml +++ b/.github/workflows/intel-mkl.yml @@ -20,7 +20,7 @@ jobs: --features=intel-mkl-static linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 @@ -33,7 +33,7 @@ jobs: --features=intel-mkl-static linux-container: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 container: image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 steps: diff --git a/.github/workflows/netlib.yml b/.github/workflows/netlib.yml index f278f0ca..d4df49a5 100644 --- a/.github/workflows/netlib.yml +++ b/.github/workflows/netlib.yml @@ -13,7 +13,7 @@ jobs: matrix: feature: - static - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: apt install gfortran diff --git a/.github/workflows/openblas.yml b/.github/workflows/openblas.yml index 644bf313..4e717ba9 100644 --- a/.github/workflows/openblas.yml +++ b/.github/workflows/openblas.yml @@ -8,7 +8,7 @@ on: jobs: linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 container: image: rust strategy: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c58670b1..4f80c808 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,7 +8,7 @@ on: jobs: check-format: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 @@ -17,7 +17,7 @@ jobs: args: -- --check clippy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 @@ -25,7 +25,7 @@ jobs: command: clippy coverage: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 container: image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 options: --security-opt seccomp=unconfined From 51b65d4feea586564ec2f370f639041a35005919 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 28 Aug 2022 20:32:59 +0900 Subject: [PATCH 006/165] clippy fix --- lax/src/tridiagonal.rs | 2 +- ndarray-linalg/examples/eig.rs | 2 +- ndarray-linalg/examples/eigh.rs | 2 +- ndarray-linalg/src/krylov/householder.rs | 2 +- ndarray-linalg/src/krylov/mgs.rs | 6 +++--- ndarray-linalg/src/layout.rs | 10 ++++------ ndarray-linalg/src/least_squares.rs | 6 +++--- ndarray-linalg/src/lobpcg/lobpcg.rs | 3 +-- ndarray-linalg/src/lobpcg/svd.rs | 2 +- ndarray-linalg/src/tridiagonal.rs | 10 +++++----- 10 files changed, 21 insertions(+), 24 deletions(-) diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index f6cbfc7e..2b1d98e2 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -15,7 +15,7 @@ use std::ops::{Index, IndexMut}; /// ... ..., u{n-1}, /// 0, ..., l{n-1}, d{n-1},] /// ``` -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] pub struct Tridiagonal { /// layout of raw matrix pub l: MatrixLayout, diff --git a/ndarray-linalg/examples/eig.rs b/ndarray-linalg/examples/eig.rs index 3e41556a..746ec2e4 100644 --- a/ndarray-linalg/examples/eig.rs +++ b/ndarray-linalg/examples/eig.rs @@ -3,7 +3,7 @@ use ndarray_linalg::*; fn main() { let a = arr2(&[[2.0, 1.0, 2.0], [-2.0, 2.0, 1.0], [1.0, 2.0, -2.0]]); - let (e, vecs) = a.clone().eig().unwrap(); + let (e, vecs) = a.eig().unwrap(); println!("eigenvalues = \n{:?}", e); println!("V = \n{:?}", vecs); let a_c: Array2 = a.map(|f| c64::new(*f, 0.0)); diff --git a/ndarray-linalg/examples/eigh.rs b/ndarray-linalg/examples/eigh.rs index c9bcc941..f4f36841 100644 --- a/ndarray-linalg/examples/eigh.rs +++ b/ndarray-linalg/examples/eigh.rs @@ -6,7 +6,7 @@ use ndarray_linalg::*; fn main() { let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0]]); - let (e, vecs) = a.clone().eigh(UPLO::Upper).unwrap(); + let (e, vecs) = a.eigh(UPLO::Upper).unwrap(); println!("eigenvalues = \n{:?}", e); println!("V = \n{:?}", vecs); let av = a.dot(&vecs); diff --git a/ndarray-linalg/src/krylov/householder.rs b/ndarray-linalg/src/krylov/householder.rs index 844d0e7e..c04d9049 100644 --- a/ndarray-linalg/src/krylov/householder.rs +++ b/ndarray-linalg/src/krylov/householder.rs @@ -34,7 +34,7 @@ where { assert_eq!(w.len(), a.len()); let n = a.len(); - let c = A::from(2.0).unwrap() * w.inner(&a); + let c = A::from(2.0).unwrap() * w.inner(a); for l in 0..n { a[l] -= c * w[l]; } diff --git a/ndarray-linalg/src/krylov/mgs.rs b/ndarray-linalg/src/krylov/mgs.rs index dc0dfba6..67806a2c 100644 --- a/ndarray-linalg/src/krylov/mgs.rs +++ b/ndarray-linalg/src/krylov/mgs.rs @@ -50,7 +50,7 @@ impl Orthogonalizer for MGS { let mut coef = Array1::zeros(self.len() + 1); for i in 0..self.len() { let q = &self.q[i]; - let c = q.inner(&a); + let c = q.inner(a); azip!((a in &mut *a, &q in q) *a -= c * q); coef[i] = c; } @@ -77,12 +77,12 @@ impl Orthogonalizer for MGS { self.div_append(&mut a) } - fn div_append(&mut self, mut a: &mut ArrayBase) -> AppendResult + fn div_append(&mut self, a: &mut ArrayBase) -> AppendResult where A: Lapack, S: DataMut, { - let coef = self.decompose(&mut a); + let coef = self.decompose(a); let nrm = coef[coef.len() - 1].re(); if nrm < self.tol { // Linearly dependent diff --git a/ndarray-linalg/src/layout.rs b/ndarray-linalg/src/layout.rs index 9ca772bb..d0d13585 100644 --- a/ndarray-linalg/src/layout.rs +++ b/ndarray-linalg/src/layout.rs @@ -67,9 +67,8 @@ where } fn as_allocated(&self) -> Result<&[A]> { - Ok(self - .as_slice_memory_order() - .ok_or_else(|| LinalgError::MemoryNotCont)?) + self.as_slice_memory_order() + .ok_or(LinalgError::MemoryNotCont) } } @@ -78,8 +77,7 @@ where S: DataMut, { fn as_allocated_mut(&mut self) -> Result<&mut [A]> { - Ok(self - .as_slice_memory_order_mut() - .ok_or_else(|| LinalgError::MemoryNotCont)?) + self.as_slice_memory_order_mut() + .ok_or(LinalgError::MemoryNotCont) } } diff --git a/ndarray-linalg/src/least_squares.rs b/ndarray-linalg/src/least_squares.rs index 25e700e4..86ca17f3 100644 --- a/ndarray-linalg/src/least_squares.rs +++ b/ndarray-linalg/src/least_squares.rs @@ -304,12 +304,12 @@ where a.layout()?, a.as_allocated_mut()?, rhs.as_slice_memory_order_mut() - .ok_or_else(|| LinalgError::MemoryNotCont)?, + .ok_or(LinalgError::MemoryNotCont)?, )?; let (m, n) = (a.shape()[0], a.shape()[1]); let solution = rhs.slice(s![0..n]).to_owned(); - let residual_sum_of_squares = compute_residual_scalar(m, n, rank, &rhs); + let residual_sum_of_squares = compute_residual_scalar(m, n, rank, rhs); Ok(LeastSquaresResult { solution, singular_values: Array::from_shape_vec((singular_values.len(),), singular_values)?, @@ -399,7 +399,7 @@ where let solution: Array2 = rhs.slice(s![..a.shape()[1], ..]).to_owned(); let singular_values = Array::from_shape_vec((singular_values.len(),), singular_values)?; let (m, n) = (a.shape()[0], a.shape()[1]); - let residual_sum_of_squares = compute_residual_array1(m, n, rank, &rhs); + let residual_sum_of_squares = compute_residual_array1(m, n, rank, rhs); Ok(LeastSquaresResult { solution, singular_values, diff --git a/ndarray-linalg/src/lobpcg/lobpcg.rs b/ndarray-linalg/src/lobpcg/lobpcg.rs index 1bc4f2ad..8b7ab616 100644 --- a/ndarray-linalg/src/lobpcg/lobpcg.rs +++ b/ndarray-linalg/src/lobpcg/lobpcg.rs @@ -83,12 +83,11 @@ fn apply_constraints( let u = gram_yv .columns() .into_iter() - .map(|x| { + .flat_map(|x| { let res = cholesky_yy.solvec(&x).unwrap(); res.to_vec() }) - .flatten() .collect::>(); let rows = gram_yv.len_of(Axis(0)); diff --git a/ndarray-linalg/src/lobpcg/svd.rs b/ndarray-linalg/src/lobpcg/svd.rs index 7a326293..f46857cc 100644 --- a/ndarray-linalg/src/lobpcg/svd.rs +++ b/ndarray-linalg/src/lobpcg/svd.rs @@ -30,7 +30,7 @@ impl + 'static + MagnitudeCorrection> Trunc let mut a = self.eigvals.iter().enumerate().collect::>(); // sort by magnitude - a.sort_by(|(_, x), (_, y)| x.partial_cmp(&y).unwrap().reverse()); + a.sort_by(|(_, x), (_, y)| x.partial_cmp(y).unwrap().reverse()); // calculate cut-off magnitude (borrowed from scipy) let cutoff = A::epsilon() * // float precision diff --git a/ndarray-linalg/src/tridiagonal.rs b/ndarray-linalg/src/tridiagonal.rs index b603a5ee..b5aebbf2 100644 --- a/ndarray-linalg/src/tridiagonal.rs +++ b/ndarray-linalg/src/tridiagonal.rs @@ -272,7 +272,7 @@ where Sb: DataMut, { A::solve_tridiagonal( - &self, + self, rhs.layout()?, Transpose::No, rhs.as_slice_mut().unwrap(), @@ -287,7 +287,7 @@ where Sb: DataMut, { A::solve_tridiagonal( - &self, + self, rhs.layout()?, Transpose::Transpose, rhs.as_slice_mut().unwrap(), @@ -302,7 +302,7 @@ where Sb: DataMut, { A::solve_tridiagonal( - &self, + self, rhs.layout()?, Transpose::Hermite, rhs.as_slice_mut().unwrap(), @@ -622,7 +622,7 @@ where { fn det_tridiagonal(&self) -> Result { let n = self.d.len(); - Ok(rec_rel(&self)[n]) + Ok(rec_rel(self)[n]) } } @@ -671,7 +671,7 @@ where A: Scalar + Lapack, { fn rcond_tridiagonal(&self) -> Result { - Ok(A::rcond_tridiagonal(&self)?) + Ok(A::rcond_tridiagonal(self)?) } } From ab92fec8930b784edb19d68efdc891ab3a3f87f7 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 19:05:33 +0900 Subject: [PATCH 007/165] Init GitHub Release generator setting --- .github/release.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..5c93e33b --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,20 @@ +changelog: + categories: + - title: "Bug fixes" + labels: + - bug + - title: "Breaking changes" + labels: + - "breaking change" + - title: "New features" + labels: + - "new feature" + - title: Deprecated + labels: + - deprecate + - title: Removed + labels: + - remove + - title: Others + labels: + - "*" From 0e6be18e33729d8bfc20eafaaefc6b0e91098710 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 19:42:18 +0900 Subject: [PATCH 008/165] Remove out-dated cargo-release option from Cargo.toml --- lax/Cargo.toml | 3 --- ndarray-linalg/Cargo.toml | 3 --- 2 files changed, 6 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 77b2d955..fa5a54e4 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -53,6 +53,3 @@ features = ["cblas"] [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", "katex-header.html"] - -[package.metadata.release] -no-dev-version = true diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 8d3aebff..a61212e7 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -81,6 +81,3 @@ harness = false [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", "katex-header.html"] - -[package.metadata.release] -no-dev-version = true From 6a4d48062b76ecb2c045419a085023a82da6fb84 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:04:46 +0900 Subject: [PATCH 009/165] (cargo-release) version 0.15.0-rc.0 --- lax/Cargo.toml | 2 +- ndarray-linalg/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index fa5a54e4..09a6415b 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.2.0" +version = "0.15.0-rc.0" authors = ["Toshiki Teramura "] edition = "2018" diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index a61212e7..5b148188 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -41,7 +41,7 @@ features = ["blas", "approx", "std"] default-features = false [dependencies.lax] -version = "0.2.0" +version = "0.15.0-rc.0" path = "../lax" default-features = false From 2f1dbf28c9c46ece6e2f7dcc5c97073c88284abb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:04:49 +0900 Subject: [PATCH 010/165] (cargo-release) version 0.15.0-rc.0 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 5b148188..3945f262 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.14.1" +version = "0.15.0-rc.0" authors = ["Toshiki Teramura "] edition = "2018" From 99d859348e63152ae8b9263df0ba6763f1b0befe Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:08:19 +0900 Subject: [PATCH 011/165] Add "Change" and "Update Dependencies" sections --- .github/release.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 5c93e33b..570c4fc0 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,20 +1,26 @@ changelog: categories: - - title: "Bug fixes" - labels: - - bug - title: "Breaking changes" labels: - "breaking change" - title: "New features" labels: - "new feature" + - title: "Bug fixes" + labels: + - bug + - title: Changes + labels: + - change - title: Deprecated labels: - deprecate - title: Removed labels: - remove + - title: "Update Dependencies" + labels: + - dependencies - title: Others labels: - "*" From e46ade587c34afa89ef91bcc6f7783ee0cc3cc3e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:12:12 +0900 Subject: [PATCH 012/165] (cargo-release) version 0.15.0-rc.1 --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 09a6415b..43019132 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.15.0-rc.0" +version = "0.15.0-rc.1" authors = ["Toshiki Teramura "] edition = "2018" From 770811937266692f489924d1ef49521ba59b3fa4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:12:12 +0900 Subject: [PATCH 013/165] (cargo-release) version 0.15.0-rc.1 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 3945f262..48fcc5d5 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.15.0-rc.0" +version = "0.15.0-rc.1" authors = ["Toshiki Teramura "] edition = "2018" From 656854a899beef85df67a79cd72bae702b70d649 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:13:58 +0900 Subject: [PATCH 014/165] (cargo-release) version 0.15.0 --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 43019132..1ed31f73 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.15.0-rc.1" +version = "0.15.0" authors = ["Toshiki Teramura "] edition = "2018" From a839bc1ec117ec3676e649e22f1132c9371fff35 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:13:58 +0900 Subject: [PATCH 015/165] (cargo-release) version 0.15.0 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 48fcc5d5..6147a2eb 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.15.0-rc.1" +version = "0.15.0" authors = ["Toshiki Teramura "] edition = "2018" From 7b2261edab75b6363c1b352b30c0fc044bef1466 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 30 Aug 2022 23:18:39 +0900 Subject: [PATCH 016/165] Remove CHANGELOG.md --- CHANGELOG.md | 180 --------------------------------------------------- 1 file changed, 180 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 3f01111f..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,180 +0,0 @@ -Unreleased ------------ - -0.14.1 - 14 August 2021 -======================== - -Fixed ------- -- Add crate:: prefix to top-level wildcard imports https://github.com/rust-ndarray/ndarray-linalg/pull/302 - -0.14.0 - 17 July 2021 -===================== - -Updated dependencies ---------------------- -- ndarray 0.15 https://github.com/rust-ndarray/ndarray-linalg/pull/273 -- cauchy 0.4 (num-complex 0.4, rand 0.8), lapack 0.18 https://github.com/rust-ndarray/ndarray-linalg/pull/276 - -Fixed ------ -- Fix memory layout of the output of inverse of LUFactorized https://github.com/rust-ndarray/ndarray-linalg/pull/297 -- Fix Eig for column-major arrays with real elements https://github.com/rust-ndarray/ndarray-linalg/pull/298 -- Fix Solve::solve_h_* for complex inputs with standard layout https://github.com/rust-ndarray/ndarray-linalg/pull/296 -- Add checks for matching shapes in Solve, SolveH, and EighInplace https://github.com/rust-ndarray/ndarray-linalg/pull/290 - -Changed --------- -- Avoid unnecessary calculation of eigenvectors in EigVals::eigvals and relax the DataMut bound https://github.com/rust-ndarray/ndarray-linalg/pull/286 -- Add basic documentation for eigh module https://github.com/rust-ndarray/ndarray-linalg/pull/283 -- Port usage of uninitialized to build_uninit https://github.com/rust-ndarray/ndarray-linalg/pull/287 -- Relax type bounds for LeastSquaresSvd family https://github.com/rust-ndarray/ndarray-linalg/pull/272 - -0.13.1 - 13 March 2021 -===================== - -Changed --------- -- Not require continious layout for trace https://github.com/rust-ndarray/ndarray-linalg/pull/263 - -Maintenance ------------ -- Fix doc.rs for KaTeX integration https://github.com/rust-ndarray/ndarray-linalg/issues/268 - -0.13.0 - 20 Feb 2021 -===================== - -https://github.com/rust-ndarray/ndarray-linalg/milestone/5 - -Updated dependencies ---------------------- -- ndarray 0.14 https://github.com/rust-ndarray/ndarray-linalg/pull/258 -- cauchy 0.3.0 (num-complex 0.3.1, rand 0.7.3), lapack 0.17.0 https://github.com/rust-ndarray/ndarray-linalg/pull/260 - -### optional dependencies - -- openblas-src 0.10.2 https://github.com/rust-ndarray/ndarray-linalg/pull/253 -- intel-mkl-src 0.6.0 https://github.com/rust-ndarray/ndarray-linalg/pull/204 - -Added ------- -- Split out `ndarray_linalg::lapack` as "lax" crate https://github.com/rust-ndarray/ndarray-linalg/pull/207 - - cargo-workspace https://github.com/rust-ndarray/ndarray-linalg/pull/209 - -Changed --------- -- Dual license, MIT or Apache-2.0 License https://github.com/rust-ndarray/ndarray-linalg/pull/262 -- Revise tests for least-square problem https://github.com/rust-ndarray/ndarray-linalg/pull/227 -- Support static link to LAPACK backend https://github.com/rust-ndarray/ndarray-linalg/pull/204 -- Drop LAPACKE dependence, and rewrite them in Rust (see below) https://github.com/rust-ndarray/ndarray-linalg/pull/206 -- Named record like `C { row: i32, lda: i32 }` instead of enum for `MatrixLayout` https://github.com/rust-ndarray/ndarray-linalg/pull/211 -- Split LAPACK error into computational failure and invalid values https://github.com/rust-ndarray/ndarray-linalg/pull/210 -- Use thiserror crate https://github.com/rust-ndarray/ndarray-linalg/pull/208 - -### LAPACKE rewrite - -- Cholesky https://github.com/rust-ndarray/ndarray-linalg/pull/225 -- Eigenvalue for general matrix https://github.com/rust-ndarray/ndarray-linalg/pull/212 -- Eigenvalue for symmetric/Hermitian matrix https://github.com/rust-ndarray/ndarray-linalg/pull/217 -- least squares problem https://github.com/rust-ndarray/ndarray-linalg/pull/220 -- QR decomposition https://github.com/rust-ndarray/ndarray-linalg/pull/224 -- LU decomposition https://github.com/rust-ndarray/ndarray-linalg/pull/213 -- LDL decomposition https://github.com/rust-ndarray/ndarray-linalg/pull/216 -- SVD https://github.com/rust-ndarray/ndarray-linalg/pull/218 -- SVD divid-and-conquer https://github.com/rust-ndarray/ndarray-linalg/pull/219 -- Tridiagonal https://github.com/rust-ndarray/ndarray-linalg/pull/235 - -Maintenance ------------ -- Coverage report using codecov https://github.com/rust-ndarray/ndarray-linalg/pull/215 -- Fix for clippy, and add CI check https://github.com/rust-ndarray/ndarray-linalg/pull/205 - -0.12.1 - 28 June 2020 -====================== - -Added ------- -- Tridiagonal matrix support https://github.com/rust-ndarray/ndarray-linalg/pull/196 -- KaTeX support in rustdoc https://github.com/rust-ndarray/ndarray-linalg/pull/202 -- Least square problems https://github.com/rust-ndarray/ndarray-linalg/pull/197 -- LOBPCG solver https://github.com/rust-ndarray/ndarray-linalg/pull/184 - -Changed -------- -- Grouping and Plot in benchmark https://github.com/rust-ndarray/ndarray-linalg/pull/200 -- `Clone` trait for `LUFactorized` https://github.com/rust-ndarray/ndarray-linalg/pull/192 - -Maintenance ------------ -- Fix repository URL https://github.com/rust-ndarray/ndarray-linalg/pull/198 -- Use GitHub Actions instead of Azure Pipeline https://github.com/rust-ndarray/ndarray-linalg/pull/193 -- Test cargo-fmt on CI https://github.com/rust-ndarray/ndarray-linalg/pull/194 - -0.12.0 - 14 Oct 2019 -==================== - -Added ------ -- SVD by divide-and-conquer https://github.com/rust-ndarray/ndarray-linalg/pull/164 -- Householder reflection https://github.com/rust-ndarray/ndarray-linalg/pull/154 -- Arnoldi iteration https://github.com/rust-ndarray/ndarray-linalg/pull/155 - -Changed ----------- -- Replace `operator::Operator*` traits by new `LinearOperator trait` https://github.com/rust-ndarray/ndarray-linalg/pull/159 -- ndarray 0.13.0 https://github.com/rust-ndarray/ndarray-linalg/pull/172 -- blas-src 0.4.0, lapack-src 0.4.0, openblas-src 0.7.0 https://github.com/rust-ndarray/ndarray-linalg/pull/174 -- restore `static` feature flag - -0.11.1 - 12 June 2019 -====================== - -- Hotfix for document generation https://github.com/rust-ndarray/ndarray-linalg/pull/153 - -0.11.0 - 12 June 2019 -==================== - -Added --------- -- Dependency to cauchy 0.2 https://github.com/rust-ndarray/ndarray-linalg/pull/139 -- `generate::random_{unitary,regular}` for debug use https://github.com/rust-ndarray/ndarray-linalg/pull/140 -- `krylov` submodule - - modified Gram-Schmit https://github.com/rust-ndarray/ndarray-linalg/pull/149 https://github.com/rust-ndarray/ndarray-linalg/pull/150 - - Krylov subspace methods are not implemented yet. - -Removed ----------- -- `static` feature https://github.com/rust-ndarray/ndarray-linalg/pull/136 - - See README for detail -- `accelerate` feature https://github.com/rust-ndarray/ndarray-linalg/pull/141 -- Dependencies to derive-new, procedurals - -Changed ---------- -- Switch CI service: Circle CI -> Azure Pipeline https://github.com/rust-ndarray/ndarray-linalg/pull/141 -- submodule `lapack_traits` is renamed to https://github.com/rust-ndarray/ndarray-linalg/pull/139 -- `ndarray_linalg::Scalar` trait is split into two parts https://github.com/rust-ndarray/ndarray-linalg/pull/139 - - [cauchy::Scalar](https://docs.rs/cauchy/0.2.0/cauchy/trait.Scalar.html) is a refined real/complex common trait - - `lapack::Lapack` is a trait for primitive types which LAPACK supports -- Error type becomes simple https://github.com/rust-ndarray/ndarray-linalg/pull/118 https://github.com/rust-ndarray/ndarray-linalg/pull/127 -- Assertions becomes more verbose https://github.com/rust-ndarray/ndarray-linalg/pull/147 -- blas-src 0.3, lapack-src 0.3 - - intel-mkl-src becomes 0.4, which supports Windows! https://github.com/rust-ndarray/ndarray-linalg/pull/146 - -0.10.0 - 2 Sep 2018 -=================== - -Update Dependencies --------------------- - -- ndarray 0.12 -- rand 0.5 -- num-complex 0.2 -- openblas-src 0.6 -- lapacke 0.2 - -See also https://github.com/rust-ndarray/ndarray-linalg/pull/110 - -Added ------- -- serde-1 feature gate https://github.com/rust-ndarray/ndarray-linalg/pull/99, https://github.com/rust-ndarray/ndarray-linalg/pull/116 From 3c184eb28f5bbb7909a635b3aac502bc1ec44e72 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 00:15:27 +0900 Subject: [PATCH 017/165] Use intel-mkl-system feature for calc coverage --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4f80c808..a557c8a1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,6 +33,6 @@ jobs: - uses: actions/checkout@v2 - name: Generate code coverage run: | - cargo tarpaulin --verbose --features=intel-mkl --out Xml --manifest-path=ndarray-linalg/Cargo.toml + cargo tarpaulin --features=intel-mkl-system --out Xml - name: Upload to codecov.io uses: codecov/codecov-action@v1 From ce962f14fb53aa2f92741f4f2ff8ed27fd413b6e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 15:08:56 +0900 Subject: [PATCH 018/165] Add lapack-sys --- lax/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 1ed31f73..d62f2064 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -33,6 +33,7 @@ thiserror = "1.0.24" cauchy = "0.4.0" num-traits = "0.2.14" lapack = "0.18.0" +lapack-sys = "0.14.0" [dependencies.intel-mkl-src] version = "0.7.0" From 328abd3524d97517a75a336c01bb70c46942872e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 15:46:49 +0900 Subject: [PATCH 019/165] Add `as_ptr` for primitive enums --- lax/src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 41c15237..c04efe60 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -141,6 +141,11 @@ impl UPLO { UPLO::Lower => UPLO::Upper, } } + + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const UPLO as *const i8 + } } #[derive(Debug, Clone, Copy)] @@ -151,6 +156,13 @@ pub enum Transpose { Hermite = b'C', } +impl Transpose { + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const Transpose as *const i8 + } +} + #[derive(Debug, Clone, Copy)] #[repr(u8)] pub enum NormType { @@ -167,6 +179,11 @@ impl NormType { NormType::Frobenius => NormType::Frobenius, } } + + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const NormType as *const i8 + } } /// Create a vector without initialization From 6d3924b97b0139884406da2d2bc5268eba91dc3e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 16:14:55 +0900 Subject: [PATCH 020/165] `AsPtr` helper trait to get pointer of slice --- lax/src/lib.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index c04efe60..00aacecd 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -126,6 +126,31 @@ impl Lapack for f64 {} impl Lapack for c32 {} impl Lapack for c64 {} +/// Helper for getting pointer of slice +pub(crate) trait AsPtr: Sized { + type Elem; + fn as_ptr(vec: &[Self]) -> *const Self::Elem; + fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem; +} + +macro_rules! impl_as_ptr { + ($target:ty, $elem:ty) => { + impl AsPtr for $target { + type Elem = $elem; + fn as_ptr(vec: &[Self]) -> *const Self::Elem { + vec.as_ptr() as *const _ + } + fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem { + vec.as_mut_ptr() as *mut _ + } + } + }; +} +impl_as_ptr!(f32, f32); +impl_as_ptr!(f64, f64); +impl_as_ptr!(c32, lapack_sys::__BindgenComplex); +impl_as_ptr!(c64, lapack_sys::__BindgenComplex); + /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy)] #[repr(u8)] From 7dd8e258435620ce559997d9536a79e68962b4a6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 16:15:41 +0900 Subject: [PATCH 021/165] Use lapack_sys in OperatorNorm_ trait impl --- lax/src/opnorm.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index dd84f441..ddcc2c85 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -1,6 +1,6 @@ //! Operator norms of matrices -use super::NormType; +use super::{AsPtr, NormType}; use crate::{layout::MatrixLayout, *}; use cauchy::*; @@ -18,18 +18,27 @@ macro_rules! impl_opnorm { MatrixLayout::F { .. } => t, MatrixLayout::C { .. } => t.transpose(), }; - let mut work = if matches!(t, NormType::Infinity) { + let mut work: Vec = if matches!(t, NormType::Infinity) { unsafe { vec_uninit(m as usize) } } else { Vec::new() }; - unsafe { $lange(t as u8, m, n, a, m, &mut work) } + unsafe { + $lange( + t.as_ptr(), + &m, + &n, + AsPtr::as_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut work), + ) + } } } }; } // impl_opnorm! -impl_opnorm!(f64, lapack::dlange); -impl_opnorm!(f32, lapack::slange); -impl_opnorm!(c64, lapack::zlange); -impl_opnorm!(c32, lapack::clange); +impl_opnorm!(f64, lapack_sys::dlange_); +impl_opnorm!(f32, lapack_sys::slange_); +impl_opnorm!(c64, lapack_sys::zlange_); +impl_opnorm!(c32, lapack_sys::clange_); From b96462591a3fb3039fd6667cc34eae05a94cf341 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 17:23:36 +0900 Subject: [PATCH 022/165] Use lapack_sys in Cholesky_ --- lax/src/cholesky.rs | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 8305efe5..94e683ff 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -29,7 +29,7 @@ macro_rules! impl_cholesky { } let mut info = 0; unsafe { - $trf(uplo as u8, n, a, n, &mut info); + $trf(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &n, &mut info); } info.as_lapack_result()?; if matches!(l, MatrixLayout::C { .. }) { @@ -45,7 +45,7 @@ macro_rules! impl_cholesky { } let mut info = 0; unsafe { - $tri(uplo as u8, n, a, l.lda(), &mut info); + $tri(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &l.lda(), &mut info); } info.as_lapack_result()?; if matches!(l, MatrixLayout::C { .. }) { @@ -70,7 +70,16 @@ macro_rules! impl_cholesky { } } unsafe { - $trs(uplo as u8, n, nrhs, a, l.lda(), b, n, &mut info); + $trs( + uplo.as_ptr(), + &n, + &nrhs, + AsPtr::as_ptr(a), + &l.lda(), + AsPtr::as_mut_ptr(b), + &n, + &mut info, + ); } info.as_lapack_result()?; if matches!(l, MatrixLayout::C { .. }) { @@ -84,7 +93,27 @@ macro_rules! impl_cholesky { }; } // end macro_rules -impl_cholesky!(f64, lapack::dpotrf, lapack::dpotri, lapack::dpotrs); -impl_cholesky!(f32, lapack::spotrf, lapack::spotri, lapack::spotrs); -impl_cholesky!(c64, lapack::zpotrf, lapack::zpotri, lapack::zpotrs); -impl_cholesky!(c32, lapack::cpotrf, lapack::cpotri, lapack::cpotrs); +impl_cholesky!( + f64, + lapack_sys::dpotrf_, + lapack_sys::dpotri_, + lapack_sys::dpotrs_ +); +impl_cholesky!( + f32, + lapack_sys::spotrf_, + lapack_sys::spotri_, + lapack_sys::spotrs_ +); +impl_cholesky!( + c64, + lapack_sys::zpotrf_, + lapack_sys::zpotri_, + lapack_sys::zpotrs_ +); +impl_cholesky!( + c32, + lapack_sys::cpotrf_, + lapack_sys::cpotri_, + lapack_sys::cpotrs_ +); From c0c3a374c05c614f2894949b0f99275a864116d4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 17:37:32 +0900 Subject: [PATCH 023/165] Introduce EigenVectorFlag --- lax/src/eig.rs | 40 ++++++++++++++++++++-------------------- lax/src/eigh.rs | 12 ++++++------ lax/src/lib.rs | 15 +++++++++++++++ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index a6d6a16e..1f15ddfe 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -35,21 +35,21 @@ macro_rules! impl_eig_complex { // eigenvalues are the eigenvalues computed with `A`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (b'V', b'N'), - MatrixLayout::F { .. } => (b'N', b'V'), + MatrixLayout::C { .. } => (EigenVectorFlag::Calc, EigenVectorFlag::Not), + MatrixLayout::F { .. } => (EigenVectorFlag::Not, EigenVectorFlag::Calc), } } else { - (b'N', b'N') + (EigenVectorFlag::Not, EigenVectorFlag::Not) }; let mut eigs = unsafe { vec_uninit(n as usize) }; let mut rwork = unsafe { vec_uninit(2 * n as usize) }; - let mut vl = if jobvl == b'V' { + let mut vl = if jobvl == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None }; - let mut vr = if jobvr == b'V' { + let mut vr = if jobvr == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None @@ -60,8 +60,8 @@ macro_rules! impl_eig_complex { let mut work_size = [Self::zero()]; unsafe { $ev( - jobvl, - jobvr, + jobvl as u8, + jobvr as u8, n, &mut a, n, @@ -83,8 +83,8 @@ macro_rules! impl_eig_complex { let mut work = unsafe { vec_uninit(lwork) }; unsafe { $ev( - jobvl, - jobvr, + jobvl as u8, + jobvr as u8, n, &mut a, n, @@ -102,7 +102,7 @@ macro_rules! impl_eig_complex { info.as_lapack_result()?; // Hermite conjugate - if jobvl == b'V' { + if jobvl == EigenVectorFlag::Calc { for c in vl.as_mut().unwrap().iter_mut() { c.im = -c.im } @@ -144,21 +144,21 @@ macro_rules! impl_eig_real { // `sgeev`/`dgeev`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (b'V', b'N'), - MatrixLayout::F { .. } => (b'N', b'V'), + MatrixLayout::C { .. } => (EigenVectorFlag::Calc, EigenVectorFlag::Not), + MatrixLayout::F { .. } => (EigenVectorFlag::Not, EigenVectorFlag::Calc), } } else { - (b'N', b'N') + (EigenVectorFlag::Not, EigenVectorFlag::Not) }; let mut eig_re = unsafe { vec_uninit(n as usize) }; let mut eig_im = unsafe { vec_uninit(n as usize) }; - let mut vl = if jobvl == b'V' { + let mut vl = if jobvl == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None }; - let mut vr = if jobvr == b'V' { + let mut vr = if jobvr == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None @@ -169,8 +169,8 @@ macro_rules! impl_eig_real { let mut work_size = [0.0]; unsafe { $ev( - jobvl, - jobvr, + jobvl as u8, + jobvr as u8, n, &mut a, n, @@ -192,8 +192,8 @@ macro_rules! impl_eig_real { let mut work = unsafe { vec_uninit(lwork) }; unsafe { $ev( - jobvl, - jobvr, + jobvl as u8, + jobvr as u8, n, &mut a, n, @@ -254,7 +254,7 @@ macro_rules! impl_eig_real { for row in 0..n { let re = v[row + col * n]; let mut im = v[row + (col + 1) * n]; - if jobvl == b'V' { + if jobvl == EigenVectorFlag::Calc { im = -im; } eigvecs[row + col * n] = Self::complex(re, im); diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index ad8963dc..238c95c2 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -41,7 +41,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { b'V' } else { b'N' }; + let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; let mut eigs = unsafe { vec_uninit(n as usize) }; $( @@ -53,7 +53,7 @@ macro_rules! impl_eigh { let mut work_size = [Self::zero()]; unsafe { $ev( - jobz, + jobz as u8, uplo as u8, n, &mut a, @@ -72,7 +72,7 @@ macro_rules! impl_eigh { let mut work = unsafe { vec_uninit(lwork) }; unsafe { $ev( - jobz, + jobz as u8, uplo as u8, n, &mut a, @@ -97,7 +97,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { b'V' } else { b'N' }; + let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; let mut eigs = unsafe { vec_uninit(n as usize) }; $( @@ -110,7 +110,7 @@ macro_rules! impl_eigh { unsafe { $evg( &[1], - jobz, + jobz as u8, uplo as u8, n, &mut a, @@ -132,7 +132,7 @@ macro_rules! impl_eigh { unsafe { $evg( &[1], - jobz, + jobz as u8, uplo as u8, n, &mut a, diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 00aacecd..a6d838b7 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -211,6 +211,21 @@ impl NormType { } } +/// Flag for calculating eigenvectors or not +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum EigenVectorFlag { + Calc = b'V', + Not = b'N', +} + +impl EigenVectorFlag { + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const EigenVectorFlag as *const i8 + } +} + /// Create a vector without initialization /// /// Safety From 25dc071c7efa751e26510eeec44febf667331b97 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 17:57:52 +0900 Subject: [PATCH 024/165] Use lapack_sys in Eig_ impl for real --- lax/src/eig.rs | 71 +++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 1f15ddfe..dae63240 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -123,7 +123,7 @@ macro_rules! impl_eig_real { fn eig( calc_v: bool, l: MatrixLayout, - mut a: &mut [Self], + a: &mut [Self], ) -> Result<(Vec, Vec)> { let (n, _) = l.size(); // LAPACK assumes a column-major input. A row-major input can @@ -150,15 +150,15 @@ macro_rules! impl_eig_real { } else { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; - let mut eig_re = unsafe { vec_uninit(n as usize) }; - let mut eig_im = unsafe { vec_uninit(n as usize) }; + let mut eig_re: Vec = unsafe { vec_uninit(n as usize) }; + let mut eig_im: Vec = unsafe { vec_uninit(n as usize) }; - let mut vl = if jobvl == EigenVectorFlag::Calc { + let mut vl: Option> = if jobvl == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None }; - let mut vr = if jobvr == EigenVectorFlag::Calc { + let mut vr: Option> = if jobvr == EigenVectorFlag::Calc { Some(unsafe { vec_uninit((n * n) as usize) }) } else { None @@ -166,22 +166,22 @@ macro_rules! impl_eig_real { // calc work size let mut info = 0; - let mut work_size = [0.0]; + let mut work_size: [Self; 1] = [0.0]; unsafe { $ev( - jobvl as u8, - jobvr as u8, - n, - &mut a, - n, - &mut eig_re, - &mut eig_im, - vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work_size, - -1, + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eig_re), + AsPtr::as_mut_ptr(&mut eig_im), + AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), &mut info, ) }; @@ -189,22 +189,23 @@ macro_rules! impl_eig_real { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; + let lwork = lwork as i32; unsafe { $ev( - jobvl as u8, - jobvr as u8, - n, - &mut a, - n, - &mut eig_re, - &mut eig_im, - vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work, - lwork as i32, + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eig_re), + AsPtr::as_mut_ptr(&mut eig_im), + AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work), + &lwork, &mut info, ) }; @@ -270,5 +271,5 @@ macro_rules! impl_eig_real { }; } -impl_eig_real!(f64, lapack::dgeev); -impl_eig_real!(f32, lapack::sgeev); +impl_eig_real!(f64, lapack_sys::dgeev_); +impl_eig_real!(f32, lapack_sys::sgeev_); From e21e932298240205c991e74eb537d40f2139cbcb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 18:10:24 +0900 Subject: [PATCH 025/165] Introduce EigenVectorFlag::then --- lax/src/eig.rs | 30 ++++++++---------------------- lax/src/lib.rs | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index dae63240..eff4591a 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -44,16 +44,8 @@ macro_rules! impl_eig_complex { let mut eigs = unsafe { vec_uninit(n as usize) }; let mut rwork = unsafe { vec_uninit(2 * n as usize) }; - let mut vl = if jobvl == EigenVectorFlag::Calc { - Some(unsafe { vec_uninit((n * n) as usize) }) - } else { - None - }; - let mut vr = if jobvr == EigenVectorFlag::Calc { - Some(unsafe { vec_uninit((n * n) as usize) }) - } else { - None - }; + let mut vl = jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vr = jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); // calc work size let mut info = 0; @@ -102,7 +94,7 @@ macro_rules! impl_eig_complex { info.as_lapack_result()?; // Hermite conjugate - if jobvl == EigenVectorFlag::Calc { + if jobvl.is_calc() { for c in vl.as_mut().unwrap().iter_mut() { c.im = -c.im } @@ -153,16 +145,10 @@ macro_rules! impl_eig_real { let mut eig_re: Vec = unsafe { vec_uninit(n as usize) }; let mut eig_im: Vec = unsafe { vec_uninit(n as usize) }; - let mut vl: Option> = if jobvl == EigenVectorFlag::Calc { - Some(unsafe { vec_uninit((n * n) as usize) }) - } else { - None - }; - let mut vr: Option> = if jobvr == EigenVectorFlag::Calc { - Some(unsafe { vec_uninit((n * n) as usize) }) - } else { - None - }; + let mut vl: Option> = + jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vr: Option> = + jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); // calc work size let mut info = 0; @@ -255,7 +241,7 @@ macro_rules! impl_eig_real { for row in 0..n { let re = v[row + col * n]; let mut im = v[row + (col + 1) * n]; - if jobvl == EigenVectorFlag::Calc { + if jobvl.is_calc() { im = -im; } eigvecs[row + col * n] = Self::complex(re, im); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index a6d838b7..26b740bb 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -220,6 +220,21 @@ pub enum EigenVectorFlag { } impl EigenVectorFlag { + pub fn is_calc(&self) -> bool { + match self { + EigenVectorFlag::Calc => true, + EigenVectorFlag::Not => false, + } + } + + pub fn then T>(&self, f: F) -> Option { + if self.is_calc() { + Some(f()) + } else { + None + } + } + /// To use Fortran LAPACK API in lapack-sys crate pub fn as_ptr(&self) -> *const i8 { self as *const EigenVectorFlag as *const i8 From 3c4dc5ea2d2b586c0560443ad8d5f94a795c1f6f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 18:18:43 +0900 Subject: [PATCH 026/165] Use lapack_sys in Eig_ impl for complex --- lax/src/eig.rs | 69 ++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index eff4591a..bf3b9f8c 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -20,7 +20,7 @@ macro_rules! impl_eig_complex { fn eig( calc_v: bool, l: MatrixLayout, - mut a: &mut [Self], + a: &mut [Self], ) -> Result<(Vec, Vec)> { let (n, _) = l.size(); // LAPACK assumes a column-major input. A row-major input can @@ -42,29 +42,31 @@ macro_rules! impl_eig_complex { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; let mut eigs = unsafe { vec_uninit(n as usize) }; - let mut rwork = unsafe { vec_uninit(2 * n as usize) }; + let mut rwork: Vec = unsafe { vec_uninit(2 * n as usize) }; - let mut vl = jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); - let mut vr = jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vl: Option> = + jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vr: Option> = + jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); // calc work size let mut info = 0; let mut work_size = [Self::zero()]; unsafe { $ev( - jobvl as u8, - jobvr as u8, - n, - &mut a, - n, - &mut eigs, - &mut vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work_size, - -1, - &mut rwork, + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), &mut info, ) }; @@ -72,22 +74,23 @@ macro_rules! impl_eig_complex { // actal ev let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; + let lwork = lwork as i32; unsafe { $ev( - jobvl as u8, - jobvr as u8, - n, - &mut a, - n, - &mut eigs, - &mut vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work, - lwork as i32, - &mut rwork, + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work), + &lwork, + AsPtr::as_mut_ptr(&mut rwork), &mut info, ) }; @@ -106,8 +109,8 @@ macro_rules! impl_eig_complex { }; } -impl_eig_complex!(c64, lapack::zgeev); -impl_eig_complex!(c32, lapack::cgeev); +impl_eig_complex!(c64, lapack_sys::zgeev_); +impl_eig_complex!(c32, lapack_sys::cgeev_); macro_rules! impl_eig_real { ($scalar:ty, $ev:path) => { From 4bf441472401f777b74394ab72fa7bbf0f95458f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 18:46:16 +0900 Subject: [PATCH 027/165] Use lapack_sys in Eigh_ --- lax/src/eigh.rs | 108 ++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 238c95c2..a8403e90 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -37,7 +37,7 @@ macro_rules! impl_eigh { calc_v: bool, layout: MatrixLayout, uplo: UPLO, - mut a: &mut [Self], + a: &mut [Self], ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); @@ -45,7 +45,7 @@ macro_rules! impl_eigh { let mut eigs = unsafe { vec_uninit(n as usize) }; $( - let mut $rwork_ident = unsafe { vec_uninit(3 * n as usize - 2 as usize) }; + let mut $rwork_ident: Vec = unsafe { vec_uninit(3 * n as usize - 2 as usize) }; )* // calc work size @@ -53,15 +53,15 @@ macro_rules! impl_eigh { let mut work_size = [Self::zero()]; unsafe { $ev( - jobz as u8, - uplo as u8, - n, - &mut a, - n, - &mut eigs, - &mut work_size, - -1, - $(&mut $rwork_ident,)* + jobz.as_ptr() , + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -69,18 +69,19 @@ macro_rules! impl_eigh { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; + let lwork = lwork as i32; unsafe { $ev( - jobz as u8, - uplo as u8, - n, - &mut a, - n, - &mut eigs, - &mut work, - lwork as i32, - $(&mut $rwork_ident,)* + jobz.as_ptr(), + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work), + &lwork, + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -92,8 +93,8 @@ macro_rules! impl_eigh { calc_v: bool, layout: MatrixLayout, uplo: UPLO, - mut a: &mut [Self], - mut b: &mut [Self], + a: &mut [Self], + b: &mut [Self], ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); @@ -101,7 +102,7 @@ macro_rules! impl_eigh { let mut eigs = unsafe { vec_uninit(n as usize) }; $( - let mut $rwork_ident = unsafe { vec_uninit(3 * n as usize - 2) }; + let mut $rwork_ident: Vec = unsafe { vec_uninit(3 * n as usize - 2) }; )* // calc work size @@ -109,18 +110,18 @@ macro_rules! impl_eigh { let mut work_size = [Self::zero()]; unsafe { $evg( - &[1], - jobz as u8, - uplo as u8, - n, - &mut a, - n, - &mut b, - n, - &mut eigs, - &mut work_size, - -1, - $(&mut $rwork_ident,)* + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(b), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -128,21 +129,22 @@ macro_rules! impl_eigh { // actual evg let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; + let lwork = lwork as i32; unsafe { $evg( - &[1], - jobz as u8, - uplo as u8, - n, - &mut a, - n, - &mut b, - n, - &mut eigs, - &mut work, - lwork as i32, - $(&mut $rwork_ident,)* + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(b), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work), + &lwork, + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -153,7 +155,7 @@ macro_rules! impl_eigh { }; } // impl_eigh! -impl_eigh!(@real, f64, lapack::dsyev, lapack::dsygv); -impl_eigh!(@real, f32, lapack::ssyev, lapack::ssygv); -impl_eigh!(@complex, c64, lapack::zheev, lapack::zhegv); -impl_eigh!(@complex, c32, lapack::cheev, lapack::chegv); +impl_eigh!(@real, f64, lapack_sys::dsyev_, lapack_sys::dsygv_); +impl_eigh!(@real, f32, lapack_sys::ssyev_, lapack_sys::ssygv_); +impl_eigh!(@complex, c64, lapack_sys::zheev_, lapack_sys::zhegv_); +impl_eigh!(@complex, c32, lapack_sys::cheev_, lapack_sys::chegv_); From 6be0ad1b2459dd9c4f6ed423857de271677986fb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 19:01:01 +0900 Subject: [PATCH 028/165] Use lapack_sys in least_squares.rs --- lax/src/least_squares.rs | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index fc378aa6..97f9a839 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -97,20 +97,20 @@ macro_rules! impl_least_squares { )* unsafe { $gelsd( - m, - n, - nrhs, - a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a), - a_layout.lda(), - b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b), - b_layout.lda(), - &mut singular_values, - rcond, + &m, + &n, + &nrhs, + AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), + &a_layout.lda(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut singular_values), + &rcond, &mut rank, - &mut work_size, - -1, - $(&mut $rwork,)* - &mut iwork_size, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork),)* + iwork_size.as_mut_ptr(), &mut info, ) }; @@ -118,29 +118,29 @@ macro_rules! impl_least_squares { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit( lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; let liwork = iwork_size[0].to_usize().unwrap(); - let mut iwork = unsafe { vec_uninit( liwork) }; + let mut iwork = unsafe { vec_uninit(liwork) }; $( let lrwork = $rwork[0].to_usize().unwrap(); - let mut $rwork = unsafe { vec_uninit( lrwork) }; + let mut $rwork: Vec = unsafe { vec_uninit(lrwork) }; )* unsafe { $gelsd( - m, - n, - nrhs, - a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a), - a_layout.lda(), - b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b), - b_layout.lda(), - &mut singular_values, - rcond, + &m, + &n, + &nrhs, + AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), + &a_layout.lda(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut singular_values), + &rcond, &mut rank, - &mut work, - lwork as i32, - $(&mut $rwork,)* - &mut iwork, + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), + $(AsPtr::as_mut_ptr(&mut $rwork),)* + iwork.as_mut_ptr(), &mut info, ); } @@ -161,7 +161,7 @@ macro_rules! impl_least_squares { }; } -impl_least_squares!(@real, f64, lapack::dgelsd); -impl_least_squares!(@real, f32, lapack::sgelsd); -impl_least_squares!(@complex, c64, lapack::zgelsd); -impl_least_squares!(@complex, c32, lapack::cgelsd); +impl_least_squares!(@real, f64, lapack_sys::dgelsd_); +impl_least_squares!(@real, f32, lapack_sys::sgelsd_); +impl_least_squares!(@complex, c64, lapack_sys::zgelsd_); +impl_least_squares!(@complex, c32, lapack_sys::cgelsd_); From b470f5f6d66685d02f2bff13faa397a8a447325a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 19:05:28 +0900 Subject: [PATCH 029/165] Use lapack_sys in rcond.rs --- lax/src/rcond.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 91d7458c..75a93a9f 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -54,22 +54,22 @@ macro_rules! impl_rcond_complex { let (n, _) = l.size(); let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work = unsafe { vec_uninit(2 * n as usize) }; - let mut rwork = unsafe { vec_uninit(2 * n as usize) }; + let mut work: Vec = unsafe { vec_uninit(2 * n as usize) }; + let mut rwork: Vec = unsafe { vec_uninit(2 * n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, - } as u8; + }; unsafe { $gecon( - norm_type, - n, - a, - l.lda(), - anorm, + norm_type.as_ptr(), + &n, + AsPtr::as_ptr(a), + &l.lda(), + &anorm, &mut rcond, - &mut work, - &mut rwork, + AsPtr::as_mut_ptr(&mut work), + AsPtr::as_mut_ptr(&mut rwork), &mut info, ) }; @@ -81,5 +81,5 @@ macro_rules! impl_rcond_complex { }; } -impl_rcond_complex!(c32, lapack::cgecon); -impl_rcond_complex!(c64, lapack::zgecon); +impl_rcond_complex!(c32, lapack_sys::cgecon_); +impl_rcond_complex!(c64, lapack_sys::zgecon_); From 68b8ae54c44704ba13db455dce1c0e0f1a1a6357 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 19:09:34 +0900 Subject: [PATCH 030/165] Use lapack_sys in solve.rs --- lax/src/solve.rs | 79 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index e09a7e8d..9c19c874 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -35,7 +35,16 @@ macro_rules! impl_solve { let k = ::std::cmp::min(row, col); let mut ipiv = unsafe { vec_uninit(k as usize) }; let mut info = 0; - unsafe { $getrf(l.lda(), l.len(), a, l.lda(), &mut ipiv, &mut info) }; + unsafe { + $getrf( + &l.lda(), + &l.len(), + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_mut_ptr(), + &mut info, + ) + }; info.as_lapack_result()?; Ok(ipiv) } @@ -50,20 +59,30 @@ macro_rules! impl_solve { // calc work size let mut info = 0; let mut work_size = [Self::zero()]; - unsafe { $getri(n, a, l.lda(), ipiv, &mut work_size, -1, &mut info) }; + unsafe { + $getri( + &n, + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; info.as_lapack_result()?; // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; unsafe { $getri( - l.len(), - a, - l.lda(), - ipiv, - &mut work, - lwork as i32, + &l.len(), + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), &mut info, ) }; @@ -116,7 +135,19 @@ macro_rules! impl_solve { *b_elem = b_elem.conj(); } } - unsafe { $getrs(t as u8, n, nrhs, a, l.lda(), ipiv, b, ldb, &mut info) }; + unsafe { + $getrs( + t.as_ptr(), + &n, + &nrhs, + AsPtr::as_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(b), + &ldb, + &mut info, + ) + }; if conj { for b_elem in &mut *b { *b_elem = b_elem.conj(); @@ -129,7 +160,27 @@ macro_rules! impl_solve { }; } // impl_solve! -impl_solve!(f64, lapack::dgetrf, lapack::dgetri, lapack::dgetrs); -impl_solve!(f32, lapack::sgetrf, lapack::sgetri, lapack::sgetrs); -impl_solve!(c64, lapack::zgetrf, lapack::zgetri, lapack::zgetrs); -impl_solve!(c32, lapack::cgetrf, lapack::cgetri, lapack::cgetrs); +impl_solve!( + f64, + lapack_sys::dgetrf_, + lapack_sys::dgetri_, + lapack_sys::dgetrs_ +); +impl_solve!( + f32, + lapack_sys::sgetrf_, + lapack_sys::sgetri_, + lapack_sys::sgetrs_ +); +impl_solve!( + c64, + lapack_sys::zgetrf_, + lapack_sys::zgetri_, + lapack_sys::zgetrs_ +); +impl_solve!( + c32, + lapack_sys::cgetrf_, + lapack_sys::cgetri_, + lapack_sys::cgetrs_ +); From f155620c58646edda38aadbffb6b18a8e6a62fe2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 19:25:06 +0900 Subject: [PATCH 031/165] Use lapack_sys in solveh.rs --- lax/src/solveh.rs | 86 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 1a4d6e3e..c5259dda 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -30,13 +30,13 @@ macro_rules! impl_solveh { let mut work_size = [Self::zero()]; unsafe { $trf( - uplo as u8, - n, - a, - l.lda(), - &mut ipiv, - &mut work_size, - -1, + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), &mut info, ) }; @@ -44,16 +44,16 @@ macro_rules! impl_solveh { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; unsafe { $trf( - uplo as u8, - n, - a, - l.lda(), - &mut ipiv, - &mut work, - lwork as i32, + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), &mut info, ) }; @@ -64,8 +64,18 @@ macro_rules! impl_solveh { fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { let (n, _) = l.size(); let mut info = 0; - let mut work = unsafe { vec_uninit(n as usize) }; - unsafe { $tri(uplo as u8, n, a, l.lda(), ipiv, &mut work, &mut info) }; + let mut work: Vec = unsafe { vec_uninit(n as usize) }; + unsafe { + $tri( + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut work), + &mut info, + ) + }; info.as_lapack_result()?; Ok(()) } @@ -79,7 +89,19 @@ macro_rules! impl_solveh { ) -> Result<()> { let (n, _) = l.size(); let mut info = 0; - unsafe { $trs(uplo as u8, n, 1, a, l.lda(), ipiv, b, n, &mut info) }; + unsafe { + $trs( + uplo.as_ptr(), + &n, + &1, + AsPtr::as_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(b), + &n, + &mut info, + ) + }; info.as_lapack_result()?; Ok(()) } @@ -87,7 +109,27 @@ macro_rules! impl_solveh { }; } // impl_solveh! -impl_solveh!(f64, lapack::dsytrf, lapack::dsytri, lapack::dsytrs); -impl_solveh!(f32, lapack::ssytrf, lapack::ssytri, lapack::ssytrs); -impl_solveh!(c64, lapack::zhetrf, lapack::zhetri, lapack::zhetrs); -impl_solveh!(c32, lapack::chetrf, lapack::chetri, lapack::chetrs); +impl_solveh!( + f64, + lapack_sys::dsytrf_, + lapack_sys::dsytri_, + lapack_sys::dsytrs_ +); +impl_solveh!( + f32, + lapack_sys::ssytrf_, + lapack_sys::ssytri_, + lapack_sys::ssytrs_ +); +impl_solveh!( + c64, + lapack_sys::zhetrf_, + lapack_sys::zhetri_, + lapack_sys::zhetrs_ +); +impl_solveh!( + c32, + lapack_sys::chetrf_, + lapack_sys::chetri_, + lapack_sys::chetrs_ +); From 0ab9c599da20004e7a1b5e92a5f7b5bbb36a8bf8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 20:00:22 +0900 Subject: [PATCH 032/165] Use lapack_sys in svd.rs --- lax/src/svd.rs | 74 ++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/lax/src/svd.rs b/lax/src/svd.rs index c990cd27..0ee56428 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -21,6 +21,10 @@ impl FlagSVD { FlagSVD::No } } + + fn as_ptr(&self) -> *const i8 { + self as *const FlagSVD as *const i8 + } } /// Result of SVD @@ -49,7 +53,7 @@ macro_rules! impl_svd { }; (@body, $scalar:ty, $gesvd:path, $($rwork_ident:ident),*) => { impl SVD_ for $scalar { - fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, mut a: &mut [Self],) -> Result> { + fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self],) -> Result> { let ju = match l { MatrixLayout::F { .. } => FlagSVD::from_bool(calc_u), MatrixLayout::C { .. } => FlagSVD::from_bool(calc_vt), @@ -75,7 +79,7 @@ macro_rules! impl_svd { let mut s = unsafe { vec_uninit( k as usize) }; $( - let mut $rwork_ident = unsafe { vec_uninit( 5 * k as usize) }; + let mut $rwork_ident: Vec = unsafe { vec_uninit( 5 * k as usize) }; )* // eval work size @@ -83,20 +87,20 @@ macro_rules! impl_svd { let mut work_size = [Self::zero()]; unsafe { $gesvd( - ju as u8, - jvt as u8, - m, - n, - &mut a, - m, - &mut s, - u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - m, - vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work_size, - -1, - $(&mut $rwork_ident,)* + ju.as_ptr(), + jvt.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -104,23 +108,23 @@ macro_rules! impl_svd { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit( lwork) }; + let mut work: Vec = unsafe { vec_uninit( lwork) }; unsafe { $gesvd( - ju as u8, - jvt as u8, - m, - n, - &mut a, - m, - &mut s, - u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - m, - vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - n, - &mut work, - lwork as i32, - $(&mut $rwork_ident,)* + ju.as_ptr(), + jvt.as_ptr() , + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* &mut info, ); } @@ -134,7 +138,7 @@ macro_rules! impl_svd { }; } // impl_svd! -impl_svd!(@real, f64, lapack::dgesvd); -impl_svd!(@real, f32, lapack::sgesvd); -impl_svd!(@complex, c64, lapack::zgesvd); -impl_svd!(@complex, c32, lapack::cgesvd); +impl_svd!(@real, f64, lapack_sys::dgesvd_); +impl_svd!(@real, f32, lapack_sys::sgesvd_); +impl_svd!(@complex, c64, lapack_sys::zgesvd_); +impl_svd!(@complex, c32, lapack_sys::cgesvd_); From aff89b7e4254791fc1d28d1af79359374f0a8447 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 20:16:50 +0900 Subject: [PATCH 033/165] Use lapack_sys in svddc.rs --- lax/src/svddc.rs | 76 ++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index a94bdace..c1198286 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -16,6 +16,12 @@ pub enum UVTFlag { None = b'N', } +impl UVTFlag { + fn as_ptr(&self) -> *const i8 { + self as *const UVTFlag as *const i8 + } +} + pub trait SVDDC_: Scalar { fn svddc(l: MatrixLayout, jobz: UVTFlag, a: &mut [Self]) -> Result>; } @@ -29,7 +35,7 @@ macro_rules! impl_svddc { }; (@body, $scalar:ty, $gesdd:path, $($rwork_ident:ident),*) => { impl SVDDC_ for $scalar { - fn svddc(l: MatrixLayout, jobz: UVTFlag, mut a: &mut [Self],) -> Result> { + fn svddc(l: MatrixLayout, jobz: UVTFlag, a: &mut [Self],) -> Result> { let m = l.lda(); let n = l.len(); let k = m.min(n); @@ -58,7 +64,7 @@ macro_rules! impl_svddc { UVTFlag::None => 7 * mn, _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), }; - let mut $rwork_ident = unsafe { vec_uninit( lrwork) }; + let mut $rwork_ident: Vec = unsafe { vec_uninit( lrwork) }; )* // eval work size @@ -67,20 +73,20 @@ macro_rules! impl_svddc { let mut work_size = [Self::zero()]; unsafe { $gesdd( - jobz as u8, - m, - n, - &mut a, - m, - &mut s, - u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - m, - vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - vt_row, - &mut work_size, - -1, - $(&mut $rwork_ident,)* - &mut iwork, + jobz.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &vt_row, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* + iwork.as_mut_ptr(), &mut info, ); } @@ -88,23 +94,23 @@ macro_rules! impl_svddc { // do svd let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit( lwork) }; + let mut work: Vec = unsafe { vec_uninit( lwork) }; unsafe { $gesdd( - jobz as u8, - m, - n, - &mut a, - m, - &mut s, - u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - m, - vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), - vt_row, - &mut work, - lwork as i32, - $(&mut $rwork_ident,)* - &mut iwork, + jobz.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &vt_row, + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* + iwork.as_mut_ptr(), &mut info, ); } @@ -119,7 +125,7 @@ macro_rules! impl_svddc { }; } -impl_svddc!(@real, f32, lapack::sgesdd); -impl_svddc!(@real, f64, lapack::dgesdd); -impl_svddc!(@complex, c32, lapack::cgesdd); -impl_svddc!(@complex, c64, lapack::zgesdd); +impl_svddc!(@real, f32, lapack_sys::sgesdd_); +impl_svddc!(@real, f64, lapack_sys::dgesdd_); +impl_svddc!(@complex, c32, lapack_sys::cgesdd_); +impl_svddc!(@complex, c64, lapack_sys::zgesdd_); From e15c93c1628ac4741ab4be0905826976e5ae64f5 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 20:24:35 +0900 Subject: [PATCH 034/165] Use lapack_sys in triangular.rs --- lax/src/triangular.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lax/src/triangular.rs b/lax/src/triangular.rs index a48b12b3..0288d6ba 100644 --- a/lax/src/triangular.rs +++ b/lax/src/triangular.rs @@ -10,6 +10,12 @@ pub enum Diag { NonUnit = b'N', } +impl Diag { + fn as_ptr(&self) -> *const i8 { + self as *const Diag as *const i8 + } +} + /// Wraps `*trtri` and `*trtrs` pub trait Triangular_: Scalar { fn solve_triangular( @@ -60,15 +66,15 @@ macro_rules! impl_triangular { let mut info = 0; unsafe { $trtrs( - uplo as u8, - Transpose::No as u8, - diag as u8, - m, - nrhs, - a_t.as_ref().map(|v| v.as_slice()).unwrap_or(a), - a_layout.lda(), - b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b), - b_layout.lda(), + uplo.as_ptr(), + Transpose::No.as_ptr(), + diag.as_ptr(), + &m, + &nrhs, + AsPtr::as_ptr(a_t.as_ref().map(|v| v.as_slice()).unwrap_or(a)), + &a_layout.lda(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &b_layout.lda(), &mut info, ); } @@ -84,7 +90,7 @@ macro_rules! impl_triangular { }; } // impl_triangular! -impl_triangular!(f64, lapack::dtrtri, lapack::dtrtrs); -impl_triangular!(f32, lapack::strtri, lapack::strtrs); -impl_triangular!(c64, lapack::ztrtri, lapack::ztrtrs); -impl_triangular!(c32, lapack::ctrtri, lapack::ctrtrs); +impl_triangular!(f64, lapack_sys::dtrtri_, lapack_sys::dtrtrs_); +impl_triangular!(f32, lapack_sys::strtri_, lapack_sys::strtrs_); +impl_triangular!(c64, lapack_sys::ztrtri_, lapack_sys::ztrtrs_); +impl_triangular!(c32, lapack_sys::ctrtri_, lapack_sys::ctrtrs_); From bd6f3feab1a01742020f055cc2e8807ce1c5c0d9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:04:16 +0900 Subject: [PATCH 035/165] Use lapack_sys in tridiagonal.rs --- lax/src/tridiagonal.rs | 62 ++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index 2b1d98e2..c80ad4b5 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -157,7 +157,17 @@ macro_rules! impl_tridiagonal { // We have to calc one-norm before LU factorization let a_opnorm_one = a.opnorm_one(); let mut info = 0; - unsafe { $gttrf(n, &mut a.dl, &mut a.d, &mut a.du, &mut du2, &mut ipiv, &mut info,) }; + unsafe { + $gttrf( + &n, + AsPtr::as_mut_ptr(&mut a.dl), + AsPtr::as_mut_ptr(&mut a.d), + AsPtr::as_mut_ptr(&mut a.du), + AsPtr::as_mut_ptr(&mut du2), + ipiv.as_mut_ptr(), + &mut info, + ) + }; info.as_lapack_result()?; Ok(LUFactorizedTridiagonal { a, @@ -170,7 +180,7 @@ macro_rules! impl_tridiagonal { fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { let (n, _) = lu.a.l.size(); let ipiv = &lu.ipiv; - let mut work = unsafe { vec_uninit( 2 * n as usize) }; + let mut work: Vec = unsafe { vec_uninit( 2 * n as usize) }; $( let mut $iwork = unsafe { vec_uninit( n as usize) }; )* @@ -178,17 +188,17 @@ macro_rules! impl_tridiagonal { let mut info = 0; unsafe { $gtcon( - NormType::One as u8, - n, - &lu.a.dl, - &lu.a.d, - &lu.a.du, - &lu.du2, - ipiv, - lu.a_opnorm_one, + NormType::One.as_ptr(), + &n, + AsPtr::as_ptr(&lu.a.dl), + AsPtr::as_ptr(&lu.a.d), + AsPtr::as_ptr(&lu.a.du), + AsPtr::as_ptr(&lu.du2), + ipiv.as_ptr(), + &lu.a_opnorm_one, &mut rcond, - &mut work, - $(&mut $iwork,)* + AsPtr::as_mut_ptr(&mut work), + $($iwork.as_mut_ptr(),)* &mut info, ); } @@ -217,16 +227,16 @@ macro_rules! impl_tridiagonal { let mut info = 0; unsafe { $gttrs( - t as u8, - n, - nrhs, - &lu.a.dl, - &lu.a.d, - &lu.a.du, - &lu.du2, - ipiv, - b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b), - ldb, + t.as_ptr(), + &n, + &nrhs, + AsPtr::as_ptr(&lu.a.dl), + AsPtr::as_ptr(&lu.a.d), + AsPtr::as_ptr(&lu.a.du), + AsPtr::as_ptr(&lu.du2), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &ldb, &mut info, ); } @@ -240,7 +250,7 @@ macro_rules! impl_tridiagonal { }; } // impl_tridiagonal! -impl_tridiagonal!(@real, f64, lapack::dgttrf, lapack::dgtcon, lapack::dgttrs); -impl_tridiagonal!(@real, f32, lapack::sgttrf, lapack::sgtcon, lapack::sgttrs); -impl_tridiagonal!(@complex, c64, lapack::zgttrf, lapack::zgtcon, lapack::zgttrs); -impl_tridiagonal!(@complex, c32, lapack::cgttrf, lapack::cgtcon, lapack::cgttrs); +impl_tridiagonal!(@real, f64, lapack_sys::dgttrf_, lapack_sys::dgtcon_, lapack_sys::dgttrs_); +impl_tridiagonal!(@real, f32, lapack_sys::sgttrf_, lapack_sys::sgtcon_, lapack_sys::sgttrs_); +impl_tridiagonal!(@complex, c64, lapack_sys::zgttrf_, lapack_sys::zgtcon_, lapack_sys::zgttrs_); +impl_tridiagonal!(@complex, c32, lapack_sys::cgttrf_, lapack_sys::cgtcon_, lapack_sys::cgttrs_); From efc877d06d4335fe7c1d08a6b0d39e762e5c2db6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:12:18 +0900 Subject: [PATCH 036/165] Use lapack_sys in qr.rs --- lax/src/qr.rs | 146 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 48 deletions(-) diff --git a/lax/src/qr.rs b/lax/src/qr.rs index 6460b8b9..33de0372 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -21,7 +21,7 @@ pub trait QR_: Sized { macro_rules! impl_qr { ($scalar:ty, $qrf:path, $lqf:path, $gqr:path, $glq:path) => { impl QR_ for $scalar { - fn householder(l: MatrixLayout, mut a: &mut [Self]) -> Result> { + fn householder(l: MatrixLayout, a: &mut [Self]) -> Result> { let m = l.lda(); let n = l.len(); let k = m.min(n); @@ -33,10 +33,28 @@ macro_rules! impl_qr { unsafe { match l { MatrixLayout::F { .. } => { - $qrf(m, n, &mut a, m, &mut tau, &mut work_size, -1, &mut info); + $qrf( + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ); } MatrixLayout::C { .. } => { - $lqf(m, n, &mut a, m, &mut tau, &mut work_size, -1, &mut info); + $lqf( + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ); } } } @@ -44,30 +62,30 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; unsafe { match l { MatrixLayout::F { .. } => { $qrf( - m, - n, - &mut a, - m, - &mut tau, - &mut work, - lwork as i32, + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), &mut info, ); } MatrixLayout::C { .. } => { $lqf( - m, - n, - &mut a, - m, - &mut tau, - &mut work, - lwork as i32, + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), &mut info, ); } @@ -78,7 +96,7 @@ macro_rules! impl_qr { Ok(tau) } - fn q(l: MatrixLayout, mut a: &mut [Self], tau: &[Self]) -> Result<()> { + fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()> { let m = l.lda(); let n = l.len(); let k = m.min(n); @@ -89,26 +107,58 @@ macro_rules! impl_qr { let mut work_size = [Self::zero()]; unsafe { match l { - MatrixLayout::F { .. } => { - $gqr(m, k, k, &mut a, m, &tau, &mut work_size, -1, &mut info) - } - MatrixLayout::C { .. } => { - $glq(k, n, k, &mut a, m, &tau, &mut work_size, -1, &mut info) - } + MatrixLayout::F { .. } => $gqr( + &m, + &k, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ), + MatrixLayout::C { .. } => $glq( + &k, + &n, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ), } }; // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work = unsafe { vec_uninit(lwork) }; + let mut work: Vec = unsafe { vec_uninit(lwork) }; unsafe { match l { - MatrixLayout::F { .. } => { - $gqr(m, k, k, &mut a, m, &tau, &mut work, lwork as i32, &mut info) - } - MatrixLayout::C { .. } => { - $glq(k, n, k, &mut a, m, &tau, &mut work, lwork as i32, &mut info) - } + MatrixLayout::F { .. } => $gqr( + &m, + &k, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), + &mut info, + ), + MatrixLayout::C { .. } => $glq( + &k, + &n, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut work), + &(lwork as i32), + &mut info, + ), } } info.as_lapack_result()?; @@ -127,29 +177,29 @@ macro_rules! impl_qr { impl_qr!( f64, - lapack::dgeqrf, - lapack::dgelqf, - lapack::dorgqr, - lapack::dorglq + lapack_sys::dgeqrf_, + lapack_sys::dgelqf_, + lapack_sys::dorgqr_, + lapack_sys::dorglq_ ); impl_qr!( f32, - lapack::sgeqrf, - lapack::sgelqf, - lapack::sorgqr, - lapack::sorglq + lapack_sys::sgeqrf_, + lapack_sys::sgelqf_, + lapack_sys::sorgqr_, + lapack_sys::sorglq_ ); impl_qr!( c64, - lapack::zgeqrf, - lapack::zgelqf, - lapack::zungqr, - lapack::zunglq + lapack_sys::zgeqrf_, + lapack_sys::zgelqf_, + lapack_sys::zungqr_, + lapack_sys::zunglq_ ); impl_qr!( c32, - lapack::cgeqrf, - lapack::cgelqf, - lapack::cungqr, - lapack::cunglq + lapack_sys::cgeqrf_, + lapack_sys::cgelqf_, + lapack_sys::cungqr_, + lapack_sys::cunglq_ ); From 5fc0f2d73cbdb1b1706ffc75f91e070b645818a8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:14:26 +0900 Subject: [PATCH 037/165] Fix rcond not to use lapack --- lax/src/rcond.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 75a93a9f..fcd4211f 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -17,22 +17,22 @@ macro_rules! impl_rcond_real { let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work = unsafe { vec_uninit(4 * n as usize) }; + let mut work: Vec = unsafe { vec_uninit(4 * n as usize) }; let mut iwork = unsafe { vec_uninit(n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, - } as u8; + }; unsafe { $gecon( - norm_type, - n, - a, - l.lda(), - anorm, + norm_type.as_ptr(), + &n, + AsPtr::as_ptr(a), + &l.lda(), + &anorm, &mut rcond, - &mut work, - &mut iwork, + AsPtr::as_mut_ptr(&mut work), + iwork.as_mut_ptr(), &mut info, ) }; @@ -44,8 +44,8 @@ macro_rules! impl_rcond_real { }; } -impl_rcond_real!(f32, lapack::sgecon); -impl_rcond_real!(f64, lapack::dgecon); +impl_rcond_real!(f32, lapack_sys::sgecon_); +impl_rcond_real!(f64, lapack_sys::dgecon_); macro_rules! impl_rcond_complex { ($scalar:ty, $gecon:path) => { From 2fc0ac815a7cc92b67883998b79da8fe785ee509 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:14:36 +0900 Subject: [PATCH 038/165] Drop lapack dependency --- lax/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index d62f2064..a36c34ab 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -32,7 +32,6 @@ intel-mkl-system = ["intel-mkl-src/mkl-dynamic-lp64-seq"] thiserror = "1.0.24" cauchy = "0.4.0" num-traits = "0.2.14" -lapack = "0.18.0" lapack-sys = "0.14.0" [dependencies.intel-mkl-src] From 6e5b4b0d53192f69e52d37d364be4c66f232bb6f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:36:02 +0900 Subject: [PATCH 039/165] impl AsPtr for Vec> --- lax/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 26b740bb..3fbe7643 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -100,6 +100,7 @@ pub use self::triangular::*; pub use self::tridiagonal::*; use cauchy::*; +use std::mem::MaybeUninit; pub type Pivot = Vec; @@ -150,6 +151,10 @@ impl_as_ptr!(f32, f32); impl_as_ptr!(f64, f64); impl_as_ptr!(c32, lapack_sys::__BindgenComplex); impl_as_ptr!(c64, lapack_sys::__BindgenComplex); +impl_as_ptr!(MaybeUninit, f32); +impl_as_ptr!(MaybeUninit, f64); +impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); +impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy)] From e35bdbb5a421746508465d7ac849c7ee9d12deb2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 21:40:40 +0900 Subject: [PATCH 040/165] Introduce VecAssumeInit to convert Vec> to Vec Use ManuallyDrop to implement VecAssumeInit --- lax/src/lib.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 3fbe7643..9b1db0a3 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -156,6 +156,30 @@ impl_as_ptr!(MaybeUninit, f64); impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); +pub(crate) trait VecAssumeInit { + type Target; + unsafe fn assume_init(self) -> Self::Target; +} + +macro_rules! impl_vec_assume_init { + ($e:ty) => { + impl VecAssumeInit for Vec> { + type Target = Vec<$e>; + unsafe fn assume_init(self) -> Self::Target { + // FIXME use Vec::into_raw_parts instead after stablized + // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts + let mut me = std::mem::ManuallyDrop::new(self); + Vec::from_raw_parts(me.as_mut_ptr() as *mut $e, me.len(), me.capacity()) + } + } + }; +} + +impl_vec_assume_init!(f32); +impl_vec_assume_init!(f64); +impl_vec_assume_init!(c32); +impl_vec_assume_init!(c64); + /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy)] #[repr(u8)] From e6d03a542b22796196f6347346f0183822043e1c Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 22:50:44 +0900 Subject: [PATCH 041/165] Introduce alternative `vec_uninit2`, and replace `vec_uninit` in eig.rs --- lax/src/eig.rs | 49 ++++++++++++++++++++++++++++++------------------- lax/src/lib.rs | 12 ++++++++++++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index bf3b9f8c..ce84cd43 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -41,13 +41,14 @@ macro_rules! impl_eig_complex { } else { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; - let mut eigs = unsafe { vec_uninit(n as usize) }; - let mut rwork: Vec = unsafe { vec_uninit(2 * n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut rwork: Vec> = + unsafe { vec_uninit2(2 * n as usize) }; - let mut vl: Option> = - jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); - let mut vr: Option> = - jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vl: Option>> = + jobvl.then(|| unsafe { vec_uninit2((n * n) as usize) }); + let mut vr: Option>> = + jobvr.then(|| unsafe { vec_uninit2((n * n) as usize) }); // calc work size let mut info = 0; @@ -74,7 +75,7 @@ macro_rules! impl_eig_complex { // actal ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -96,10 +97,14 @@ macro_rules! impl_eig_complex { }; info.as_lapack_result()?; + let eigs = unsafe { eigs.assume_init() }; + let vr = unsafe { vr.map(|v| v.assume_init()) }; + let mut vl = unsafe { vl.map(|v| v.assume_init()) }; + // Hermite conjugate if jobvl.is_calc() { for c in vl.as_mut().unwrap().iter_mut() { - c.im = -c.im + c.im = -c.im; } } @@ -145,13 +150,13 @@ macro_rules! impl_eig_real { } else { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; - let mut eig_re: Vec = unsafe { vec_uninit(n as usize) }; - let mut eig_im: Vec = unsafe { vec_uninit(n as usize) }; + let mut eig_re: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut eig_im: Vec> = unsafe { vec_uninit2(n as usize) }; - let mut vl: Option> = - jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); - let mut vr: Option> = - jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); + let mut vl: Option>> = + jobvl.then(|| unsafe { vec_uninit2((n * n) as usize) }); + let mut vr: Option>> = + jobvr.then(|| unsafe { vec_uninit2((n * n) as usize) }); // calc work size let mut info = 0; @@ -178,7 +183,7 @@ macro_rules! impl_eig_real { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -200,6 +205,11 @@ macro_rules! impl_eig_real { }; info.as_lapack_result()?; + let eig_re = unsafe { eig_re.assume_init() }; + let eig_im = unsafe { eig_im.assume_init() }; + let vl = unsafe { vl.map(|v| v.assume_init()) }; + let vr = unsafe { vr.map(|v| v.assume_init()) }; + // reconstruct eigenvalues let eigs: Vec = eig_re .iter() @@ -228,14 +238,14 @@ macro_rules! impl_eig_real { let n = n as usize; let v = vr.or(vl).unwrap(); - let mut eigvecs = unsafe { vec_uninit(n * n) }; + let mut eigvecs: Vec> = unsafe { vec_uninit2(n * n) }; let mut col = 0; while col < n { if eig_im[col] == 0. { // The corresponding eigenvalue is real. for row in 0..n { let re = v[row + col * n]; - eigvecs[row + col * n] = Self::complex(re, 0.); + eigvecs[row + col * n].write(Self::complex(re, 0.)); } col += 1; } else { @@ -247,12 +257,13 @@ macro_rules! impl_eig_real { if jobvl.is_calc() { im = -im; } - eigvecs[row + col * n] = Self::complex(re, im); - eigvecs[row + (col + 1) * n] = Self::complex(re, -im); + eigvecs[row + col * n].write(Self::complex(re, im)); + eigvecs[row + (col + 1) * n].write(Self::complex(re, -im)); } col += 2; } } + let eigvecs = unsafe { eigvecs.assume_init() }; Ok((eigs, eigvecs)) } diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 9b1db0a3..e4fe85a0 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -281,3 +281,15 @@ unsafe fn vec_uninit(n: usize) -> Vec { v.set_len(n); v } + +/// Create a vector without initialization +/// +/// Safety +/// ------ +/// - Memory is not initialized. Do not read the memory before write. +/// +unsafe fn vec_uninit2(n: usize) -> Vec> { + let mut v = Vec::with_capacity(n); + v.set_len(n); + v +} From 8c63bc7d7bcabaa70a1dc53d70026e071161d647 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 31 Aug 2022 23:30:47 +0900 Subject: [PATCH 042/165] Use vec_uninit2 in eigh.rs --- lax/src/eigh.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index a8403e90..2a112217 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -42,10 +42,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; - let mut eigs = unsafe { vec_uninit(n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; $( - let mut $rwork_ident: Vec = unsafe { vec_uninit(3 * n as usize - 2 as usize) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit2(3 * n as usize - 2 as usize) }; )* // calc work size @@ -69,7 +69,7 @@ macro_rules! impl_eigh { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -86,6 +86,8 @@ macro_rules! impl_eigh { ); } info.as_lapack_result()?; + + let eigs = unsafe { eigs.assume_init() }; Ok(eigs) } @@ -99,10 +101,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; - let mut eigs = unsafe { vec_uninit(n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; $( - let mut $rwork_ident: Vec = unsafe { vec_uninit(3 * n as usize - 2) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit2(3 * n as usize - 2) }; )* // calc work size @@ -129,7 +131,7 @@ macro_rules! impl_eigh { // actual evg let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; let lwork = lwork as i32; unsafe { $evg( @@ -149,6 +151,7 @@ macro_rules! impl_eigh { ); } info.as_lapack_result()?; + let eigs = unsafe { eigs.assume_init() }; Ok(eigs) } } From ee00a0dadf0db5cb65596cf1edfbbec81d5293ce Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 11:07:26 +0900 Subject: [PATCH 043/165] Impl VecAssumeInit for any Vec --- lax/src/lib.rs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index e4fe85a0..6ae21af2 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -161,25 +161,16 @@ pub(crate) trait VecAssumeInit { unsafe fn assume_init(self) -> Self::Target; } -macro_rules! impl_vec_assume_init { - ($e:ty) => { - impl VecAssumeInit for Vec> { - type Target = Vec<$e>; - unsafe fn assume_init(self) -> Self::Target { - // FIXME use Vec::into_raw_parts instead after stablized - // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts - let mut me = std::mem::ManuallyDrop::new(self); - Vec::from_raw_parts(me.as_mut_ptr() as *mut $e, me.len(), me.capacity()) - } - } - }; +impl VecAssumeInit for Vec> { + type Target = Vec; + unsafe fn assume_init(self) -> Self::Target { + // FIXME use Vec::into_raw_parts instead after stablized + // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts + let mut me = std::mem::ManuallyDrop::new(self); + Vec::from_raw_parts(me.as_mut_ptr() as *mut T, me.len(), me.capacity()) + } } -impl_vec_assume_init!(f32); -impl_vec_assume_init!(f64); -impl_vec_assume_init!(c32); -impl_vec_assume_init!(c64); - /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy)] #[repr(u8)] From 152e3d5ac3edd7f841e4be5b26db295fdeff8cfe Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 11:20:31 +0900 Subject: [PATCH 044/165] Rename `transpose` to `transpose_over`, create new `transpose` to write uninitilized buffer --- lax/src/layout.rs | 75 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/lax/src/layout.rs b/lax/src/layout.rs index e7ab1da4..e83923b3 100644 --- a/lax/src/layout.rs +++ b/lax/src/layout.rs @@ -37,7 +37,7 @@ //! This `S` for a matrix `A` is called "leading dimension of the array A" in LAPACK document, and denoted by `lda`. //! -use cauchy::Scalar; +use super::*; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MatrixLayout { @@ -153,7 +153,7 @@ impl MatrixLayout { /// ------ /// - If size of `a` and `layout` size mismatch /// -pub fn square_transpose(layout: MatrixLayout, a: &mut [T]) { +pub fn square_transpose(layout: MatrixLayout, a: &mut [T]) { let (m, n) = layout.size(); let n = n as usize; let m = m as usize; @@ -162,23 +162,78 @@ pub fn square_transpose(layout: MatrixLayout, a: &mut [T]) { for j in (i + 1)..n { let a_ij = a[i * n + j]; let a_ji = a[j * m + i]; - a[i * n + j] = a_ji.conj(); - a[j * m + i] = a_ij.conj(); + a[i * n + j] = a_ji; + a[j * m + i] = a_ij; } } } /// Out-place transpose for general matrix /// -/// Inplace transpose of non-square matrices is hard. -/// See also: https://en.wikipedia.org/wiki/In-place_matrix_transposition +/// Examples +/// --------- +/// +/// ```rust +/// # use lax::layout::*; +/// let layout = MatrixLayout::C { row: 2, lda: 3 }; +/// let a = vec![1., 2., 3., 4., 5., 6.]; +/// let (l, b) = transpose(layout, &a); +/// assert_eq!(l, MatrixLayout::F { col: 3, lda: 2 }); +/// assert_eq!(b, &[1., 4., 2., 5., 3., 6.]); +/// ``` +/// +/// ```rust +/// # use lax::layout::*; +/// let layout = MatrixLayout::F { col: 2, lda: 3 }; +/// let a = vec![1., 2., 3., 4., 5., 6.]; +/// let (l, b) = transpose(layout, &a); +/// assert_eq!(l, MatrixLayout::C { row: 3, lda: 2 }); +/// assert_eq!(b, &[1., 4., 2., 5., 3., 6.]); +/// ``` +/// +/// Panics +/// ------ +/// - If input array size and `layout` size mismatch +/// +pub fn transpose(layout: MatrixLayout, input: &[T]) -> (MatrixLayout, Vec) { + let (m, n) = layout.size(); + let transposed = layout.resized(n, m).t(); + let m = m as usize; + let n = n as usize; + assert_eq!(input.len(), m * n); + + let mut out: Vec> = unsafe { vec_uninit2(m * n) }; + + match layout { + MatrixLayout::C { .. } => { + for i in 0..m { + for j in 0..n { + out[j * m + i].write(input[i * n + j]); + } + } + } + MatrixLayout::F { .. } => { + for i in 0..m { + for j in 0..n { + out[i * n + j].write(input[j * m + i]); + } + } + } + } + (transposed, unsafe { out.assume_init() }) +} + +/// Out-place transpose for general matrix +/// +/// Examples +/// --------- /// /// ```rust /// # use lax::layout::*; /// let layout = MatrixLayout::C { row: 2, lda: 3 }; /// let a = vec![1., 2., 3., 4., 5., 6.]; /// let mut b = vec![0.0; a.len()]; -/// let l = transpose(layout, &a, &mut b); +/// let l = transpose_over(layout, &a, &mut b); /// assert_eq!(l, MatrixLayout::F { col: 3, lda: 2 }); /// assert_eq!(b, &[1., 4., 2., 5., 3., 6.]); /// ``` @@ -188,16 +243,16 @@ pub fn square_transpose(layout: MatrixLayout, a: &mut [T]) { /// let layout = MatrixLayout::F { col: 2, lda: 3 }; /// let a = vec![1., 2., 3., 4., 5., 6.]; /// let mut b = vec![0.0; a.len()]; -/// let l = transpose(layout, &a, &mut b); +/// let l = transpose_over(layout, &a, &mut b); /// assert_eq!(l, MatrixLayout::C { row: 3, lda: 2 }); /// assert_eq!(b, &[1., 4., 2., 5., 3., 6.]); /// ``` /// /// Panics /// ------ -/// - If size of `a` and `layout` size mismatch +/// - If input array sizes and `layout` size mismatch /// -pub fn transpose(layout: MatrixLayout, from: &[T], to: &mut [T]) -> MatrixLayout { +pub fn transpose_over(layout: MatrixLayout, from: &[T], to: &mut [T]) -> MatrixLayout { let (m, n) = layout.size(); let transposed = layout.resized(n, m).t(); let m = m as usize; From 066939192e0cf9727abfd34d69bf47d9e83b9adc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 12:30:29 +0900 Subject: [PATCH 045/165] Use new `transpose` and `transpose_over` --- lax/src/least_squares.rs | 12 +++++++----- lax/src/triangular.rs | 12 +++++++----- lax/src/tridiagonal.rs | 7 ++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 97f9a839..527fc77d 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -68,8 +68,9 @@ macro_rules! impl_least_squares { let mut a_t = None; let a_layout = match a_layout { MatrixLayout::C { .. } => { - a_t = Some(unsafe { vec_uninit( a.len()) }); - transpose(a_layout, a, a_t.as_mut().unwrap()) + let (layout, t) = transpose(a_layout, a); + a_t = Some(t); + layout } MatrixLayout::F { .. } => a_layout, }; @@ -78,8 +79,9 @@ macro_rules! impl_least_squares { let mut b_t = None; let b_layout = match b_layout { MatrixLayout::C { .. } => { - b_t = Some(unsafe { vec_uninit( b.len()) }); - transpose(b_layout, b, b_t.as_mut().unwrap()) + let (layout, t) = transpose(b_layout, b); + b_t = Some(t); + layout } MatrixLayout::F { .. } => b_layout, }; @@ -149,7 +151,7 @@ macro_rules! impl_least_squares { // Skip a_t -> a transpose because A has been destroyed // Re-transpose b if let Some(b_t) = b_t { - transpose(b_layout, &b_t, b); + transpose_over(b_layout, &b_t, b); } Ok(LeastSquaresOutput { diff --git a/lax/src/triangular.rs b/lax/src/triangular.rs index 0288d6ba..e8825758 100644 --- a/lax/src/triangular.rs +++ b/lax/src/triangular.rs @@ -43,8 +43,9 @@ macro_rules! impl_triangular { let mut a_t = None; let a_layout = match a_layout { MatrixLayout::C { .. } => { - a_t = Some(unsafe { vec_uninit(a.len()) }); - transpose(a_layout, a, a_t.as_mut().unwrap()) + let (layout, t) = transpose(a_layout, a); + a_t = Some(t); + layout } MatrixLayout::F { .. } => a_layout, }; @@ -53,8 +54,9 @@ macro_rules! impl_triangular { let mut b_t = None; let b_layout = match b_layout { MatrixLayout::C { .. } => { - b_t = Some(unsafe { vec_uninit(b.len()) }); - transpose(b_layout, b, b_t.as_mut().unwrap()) + let (layout, t) = transpose(b_layout, b); + b_t = Some(t); + layout } MatrixLayout::F { .. } => b_layout, }; @@ -82,7 +84,7 @@ macro_rules! impl_triangular { // Re-transpose b if let Some(b_t) = b_t { - transpose(b_layout, &b_t, b); + transpose_over(b_layout, &b_t, b); } Ok(()) } diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index c80ad4b5..5d243632 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -218,8 +218,9 @@ macro_rules! impl_tridiagonal { let mut b_t = None; let b_layout = match b_layout { MatrixLayout::C { .. } => { - b_t = Some(unsafe { vec_uninit( b.len()) }); - transpose(b_layout, b, b_t.as_mut().unwrap()) + let (layout, t) = transpose(b_layout, b); + b_t = Some(t); + layout } MatrixLayout::F { .. } => b_layout, }; @@ -242,7 +243,7 @@ macro_rules! impl_tridiagonal { } info.as_lapack_result()?; if let Some(b_t) = b_t { - transpose(b_layout, &b_t, b); + transpose_over(b_layout, &b_t, b); } Ok(()) } From 41a3247af155a15f740b9b27c38cb240f05491df Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 15:33:58 +0900 Subject: [PATCH 046/165] Use vec_uninit2 in qr.rs, opnorm.rs --- lax/src/opnorm.rs | 4 ++-- lax/src/qr.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index ddcc2c85..ba831456 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -18,8 +18,8 @@ macro_rules! impl_opnorm { MatrixLayout::F { .. } => t, MatrixLayout::C { .. } => t.transpose(), }; - let mut work: Vec = if matches!(t, NormType::Infinity) { - unsafe { vec_uninit(m as usize) } + let mut work: Vec> = if matches!(t, NormType::Infinity) { + unsafe { vec_uninit2(m as usize) } } else { Vec::new() }; diff --git a/lax/src/qr.rs b/lax/src/qr.rs index 33de0372..4dc1d5a7 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -25,7 +25,7 @@ macro_rules! impl_qr { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut tau = unsafe { vec_uninit(k as usize) }; + let mut tau = unsafe { vec_uninit2(k as usize) }; // eval work size let mut info = 0; @@ -62,7 +62,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; unsafe { match l { MatrixLayout::F { .. } => { @@ -93,6 +93,8 @@ macro_rules! impl_qr { } info.as_lapack_result()?; + let tau = unsafe { tau.assume_init() }; + Ok(tau) } @@ -134,7 +136,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; unsafe { match l { MatrixLayout::F { .. } => $gqr( From dd425f42626375a84b17e265759c0cf1cdecc642 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 15:58:10 +0900 Subject: [PATCH 047/165] Use vec_uninit2 in all --- lax/src/least_squares.rs | 12 +++++++----- lax/src/lib.rs | 2 ++ lax/src/rcond.rs | 11 ++++++----- lax/src/solve.rs | 7 ++++--- lax/src/solveh.rs | 11 ++++++----- lax/src/svd.rs | 15 ++++++++++----- lax/src/svddc.rs | 24 ++++++++++++++---------- lax/src/tridiagonal.rs | 14 ++++++++------ 8 files changed, 57 insertions(+), 39 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 527fc77d..71e0dbf5 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -87,7 +87,7 @@ macro_rules! impl_least_squares { }; let rcond: Self::Real = -1.; - let mut singular_values: Vec = unsafe { vec_uninit( k as usize) }; + let mut singular_values: Vec> = unsafe { vec_uninit2( k as usize) }; let mut rank: i32 = 0; // eval work size @@ -120,12 +120,12 @@ macro_rules! impl_least_squares { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; let liwork = iwork_size[0].to_usize().unwrap(); - let mut iwork = unsafe { vec_uninit(liwork) }; + let mut iwork: Vec> = unsafe { vec_uninit2(liwork) }; $( let lrwork = $rwork[0].to_usize().unwrap(); - let mut $rwork: Vec = unsafe { vec_uninit(lrwork) }; + let mut $rwork: Vec> = unsafe { vec_uninit2(lrwork) }; )* unsafe { $gelsd( @@ -142,12 +142,14 @@ macro_rules! impl_least_squares { AsPtr::as_mut_ptr(&mut work), &(lwork as i32), $(AsPtr::as_mut_ptr(&mut $rwork),)* - iwork.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut iwork), &mut info, ); } info.as_lapack_result()?; + let singular_values = unsafe { singular_values.assume_init() }; + // Skip a_t -> a transpose because A has been destroyed // Re-transpose b if let Some(b_t) = b_t { diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 6ae21af2..bc908da6 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -147,10 +147,12 @@ macro_rules! impl_as_ptr { } }; } +impl_as_ptr!(i32, i32); impl_as_ptr!(f32, f32); impl_as_ptr!(f64, f64); impl_as_ptr!(c32, lapack_sys::__BindgenComplex); impl_as_ptr!(c64, lapack_sys::__BindgenComplex); +impl_as_ptr!(MaybeUninit, i32); impl_as_ptr!(MaybeUninit, f32); impl_as_ptr!(MaybeUninit, f64); impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index fcd4211f..39dc4799 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -17,8 +17,8 @@ macro_rules! impl_rcond_real { let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec = unsafe { vec_uninit(4 * n as usize) }; - let mut iwork = unsafe { vec_uninit(n as usize) }; + let mut work: Vec> = unsafe { vec_uninit2(4 * n as usize) }; + let mut iwork: Vec> = unsafe { vec_uninit2(n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, @@ -32,7 +32,7 @@ macro_rules! impl_rcond_real { &anorm, &mut rcond, AsPtr::as_mut_ptr(&mut work), - iwork.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut iwork), &mut info, ) }; @@ -54,8 +54,9 @@ macro_rules! impl_rcond_complex { let (n, _) = l.size(); let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec = unsafe { vec_uninit(2 * n as usize) }; - let mut rwork: Vec = unsafe { vec_uninit(2 * n as usize) }; + let mut work: Vec> = unsafe { vec_uninit2(2 * n as usize) }; + let mut rwork: Vec> = + unsafe { vec_uninit2(2 * n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 9c19c874..84affe39 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -33,7 +33,7 @@ macro_rules! impl_solve { return Ok(Vec::new()); } let k = ::std::cmp::min(row, col); - let mut ipiv = unsafe { vec_uninit(k as usize) }; + let mut ipiv = unsafe { vec_uninit2(k as usize) }; let mut info = 0; unsafe { $getrf( @@ -41,11 +41,12 @@ macro_rules! impl_solve { &l.len(), AsPtr::as_mut_ptr(a), &l.lda(), - ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut ipiv), &mut info, ) }; info.as_lapack_result()?; + let ipiv = unsafe { ipiv.assume_init() }; Ok(ipiv) } @@ -74,7 +75,7 @@ macro_rules! impl_solve { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; unsafe { $getri( &l.len(), diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index c5259dda..09221f03 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -20,7 +20,7 @@ macro_rules! impl_solveh { impl Solveh_ for $scalar { fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result { let (n, _) = l.size(); - let mut ipiv = unsafe { vec_uninit(n as usize) }; + let mut ipiv = unsafe { vec_uninit2(n as usize) }; if n == 0 { return Ok(Vec::new()); } @@ -34,7 +34,7 @@ macro_rules! impl_solveh { &n, AsPtr::as_mut_ptr(a), &l.lda(), - ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut ipiv), AsPtr::as_mut_ptr(&mut work_size), &(-1), &mut info, @@ -44,27 +44,28 @@ macro_rules! impl_solveh { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = unsafe { vec_uninit2(lwork) }; unsafe { $trf( uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &l.lda(), - ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut ipiv), AsPtr::as_mut_ptr(&mut work), &(lwork as i32), &mut info, ) }; info.as_lapack_result()?; + let ipiv = unsafe { ipiv.assume_init() }; Ok(ipiv) } fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { let (n, _) = l.size(); let mut info = 0; - let mut work: Vec = unsafe { vec_uninit(n as usize) }; + let mut work: Vec> = unsafe { vec_uninit2(n as usize) }; unsafe { $tri( uplo.as_ptr(), diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 0ee56428..2968cff6 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -65,21 +65,21 @@ macro_rules! impl_svd { let m = l.lda(); let mut u = match ju { - FlagSVD::All => Some(unsafe { vec_uninit( (m * m) as usize) }), + FlagSVD::All => Some(unsafe { vec_uninit2( (m * m) as usize) }), FlagSVD::No => None, }; let n = l.len(); let mut vt = match jvt { - FlagSVD::All => Some(unsafe { vec_uninit( (n * n) as usize) }), + FlagSVD::All => Some(unsafe { vec_uninit2( (n * n) as usize) }), FlagSVD::No => None, }; let k = std::cmp::min(m, n); - let mut s = unsafe { vec_uninit( k as usize) }; + let mut s = unsafe { vec_uninit2( k as usize) }; $( - let mut $rwork_ident: Vec = unsafe { vec_uninit( 5 * k as usize) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit2( 5 * k as usize) }; )* // eval work size @@ -108,7 +108,7 @@ macro_rules! impl_svd { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit( lwork) }; + let mut work: Vec> = unsafe { vec_uninit2( lwork) }; unsafe { $gesvd( ju.as_ptr(), @@ -129,6 +129,11 @@ macro_rules! impl_svd { ); } info.as_lapack_result()?; + + let s = unsafe { s.assume_init() }; + let u = u.map(|v| unsafe { v.assume_init() }); + let vt = vt.map(|v| unsafe { v.assume_init() }); + match l { MatrixLayout::F { .. } => Ok(SVDOutput { s, u, vt }), MatrixLayout::C { .. } => Ok(SVDOutput { s, u: vt, vt: u }), diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index c1198286..6a703bd6 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -39,7 +39,7 @@ macro_rules! impl_svddc { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut s = unsafe { vec_uninit( k as usize) }; + let mut s = unsafe { vec_uninit2( k as usize) }; let (u_col, vt_row) = match jobz { UVTFlag::Full | UVTFlag::None => (m, n), @@ -47,12 +47,12 @@ macro_rules! impl_svddc { }; let (mut u, mut vt) = match jobz { UVTFlag::Full => ( - Some(unsafe { vec_uninit( (m * m) as usize) }), - Some(unsafe { vec_uninit( (n * n) as usize) }), + Some(unsafe { vec_uninit2( (m * m) as usize) }), + Some(unsafe { vec_uninit2( (n * n) as usize) }), ), UVTFlag::Some => ( - Some(unsafe { vec_uninit( (m * u_col) as usize) }), - Some(unsafe { vec_uninit( (n * vt_row) as usize) }), + Some(unsafe { vec_uninit2( (m * u_col) as usize) }), + Some(unsafe { vec_uninit2( (n * vt_row) as usize) }), ), UVTFlag::None => (None, None), }; @@ -64,12 +64,12 @@ macro_rules! impl_svddc { UVTFlag::None => 7 * mn, _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), }; - let mut $rwork_ident: Vec = unsafe { vec_uninit( lrwork) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit2( lrwork) }; )* // eval work size let mut info = 0; - let mut iwork = unsafe { vec_uninit( 8 * k as usize) }; + let mut iwork: Vec> = unsafe { vec_uninit2( 8 * k as usize) }; let mut work_size = [Self::zero()]; unsafe { $gesdd( @@ -86,7 +86,7 @@ macro_rules! impl_svddc { AsPtr::as_mut_ptr(&mut work_size), &(-1), $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - iwork.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut iwork), &mut info, ); } @@ -94,7 +94,7 @@ macro_rules! impl_svddc { // do svd let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec = unsafe { vec_uninit( lwork) }; + let mut work: Vec> = unsafe { vec_uninit2( lwork) }; unsafe { $gesdd( jobz.as_ptr(), @@ -110,12 +110,16 @@ macro_rules! impl_svddc { AsPtr::as_mut_ptr(&mut work), &(lwork as i32), $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - iwork.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut iwork), &mut info, ); } info.as_lapack_result()?; + let s = unsafe { s.assume_init() }; + let u = u.map(|v| unsafe { v.assume_init() }); + let vt = vt.map(|v| unsafe { v.assume_init() }); + match l { MatrixLayout::F { .. } => Ok(SVDOutput { s, u, vt }), MatrixLayout::C { .. } => Ok(SVDOutput { s, u: vt, vt: u }), diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index 5d243632..3f86bd0e 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -152,8 +152,8 @@ macro_rules! impl_tridiagonal { impl Tridiagonal_ for $scalar { fn lu_tridiagonal(mut a: Tridiagonal) -> Result> { let (n, _) = a.l.size(); - let mut du2 = unsafe { vec_uninit( (n - 2) as usize) }; - let mut ipiv = unsafe { vec_uninit( n as usize) }; + let mut du2 = unsafe { vec_uninit2( (n - 2) as usize) }; + let mut ipiv = unsafe { vec_uninit2( n as usize) }; // We have to calc one-norm before LU factorization let a_opnorm_one = a.opnorm_one(); let mut info = 0; @@ -164,11 +164,13 @@ macro_rules! impl_tridiagonal { AsPtr::as_mut_ptr(&mut a.d), AsPtr::as_mut_ptr(&mut a.du), AsPtr::as_mut_ptr(&mut du2), - ipiv.as_mut_ptr(), + AsPtr::as_mut_ptr(&mut ipiv), &mut info, ) }; info.as_lapack_result()?; + let du2 = unsafe { du2.assume_init() }; + let ipiv = unsafe { ipiv.assume_init() }; Ok(LUFactorizedTridiagonal { a, du2, @@ -180,9 +182,9 @@ macro_rules! impl_tridiagonal { fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { let (n, _) = lu.a.l.size(); let ipiv = &lu.ipiv; - let mut work: Vec = unsafe { vec_uninit( 2 * n as usize) }; + let mut work: Vec> = unsafe { vec_uninit2( 2 * n as usize) }; $( - let mut $iwork = unsafe { vec_uninit( n as usize) }; + let mut $iwork: Vec> = unsafe { vec_uninit2( n as usize) }; )* let mut rcond = Self::Real::zero(); let mut info = 0; @@ -198,7 +200,7 @@ macro_rules! impl_tridiagonal { &lu.a_opnorm_one, &mut rcond, AsPtr::as_mut_ptr(&mut work), - $($iwork.as_mut_ptr(),)* + $(AsPtr::as_mut_ptr(&mut $iwork),)* &mut info, ); } From b5b92e3b59754745aae57991344f2e61b4ceb832 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 16:00:11 +0900 Subject: [PATCH 048/165] Replace vec_uninit by vec_uninit2 --- lax/src/eig.rs | 23 +++++++++++------------ lax/src/eigh.rs | 12 ++++++------ lax/src/layout.rs | 2 +- lax/src/least_squares.rs | 8 ++++---- lax/src/lib.rs | 14 +------------- lax/src/opnorm.rs | 2 +- lax/src/qr.rs | 6 +++--- lax/src/rcond.rs | 9 ++++----- lax/src/solve.rs | 4 ++-- lax/src/solveh.rs | 6 +++--- lax/src/svd.rs | 10 +++++----- lax/src/svddc.rs | 16 ++++++++-------- lax/src/tridiagonal.rs | 8 ++++---- 13 files changed, 53 insertions(+), 67 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index ce84cd43..f11f5287 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -41,14 +41,13 @@ macro_rules! impl_eig_complex { } else { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; - let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; - let mut rwork: Vec> = - unsafe { vec_uninit2(2 * n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; + let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; let mut vl: Option>> = - jobvl.then(|| unsafe { vec_uninit2((n * n) as usize) }); + jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); let mut vr: Option>> = - jobvr.then(|| unsafe { vec_uninit2((n * n) as usize) }); + jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); // calc work size let mut info = 0; @@ -75,7 +74,7 @@ macro_rules! impl_eig_complex { // actal ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -150,13 +149,13 @@ macro_rules! impl_eig_real { } else { (EigenVectorFlag::Not, EigenVectorFlag::Not) }; - let mut eig_re: Vec> = unsafe { vec_uninit2(n as usize) }; - let mut eig_im: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut eig_re: Vec> = unsafe { vec_uninit(n as usize) }; + let mut eig_im: Vec> = unsafe { vec_uninit(n as usize) }; let mut vl: Option>> = - jobvl.then(|| unsafe { vec_uninit2((n * n) as usize) }); + jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); let mut vr: Option>> = - jobvr.then(|| unsafe { vec_uninit2((n * n) as usize) }); + jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); // calc work size let mut info = 0; @@ -183,7 +182,7 @@ macro_rules! impl_eig_real { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -238,7 +237,7 @@ macro_rules! impl_eig_real { let n = n as usize; let v = vr.or(vl).unwrap(); - let mut eigvecs: Vec> = unsafe { vec_uninit2(n * n) }; + let mut eigvecs: Vec> = unsafe { vec_uninit(n * n) }; let mut col = 0; while col < n { if eig_im[col] == 0. { diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 2a112217..0692f921 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -42,10 +42,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; - let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit2(3 * n as usize - 2 as usize) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit(3 * n as usize - 2 as usize) }; )* // calc work size @@ -69,7 +69,7 @@ macro_rules! impl_eigh { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; let lwork = lwork as i32; unsafe { $ev( @@ -101,10 +101,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; - let mut eigs: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit2(3 * n as usize - 2) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit(3 * n as usize - 2) }; )* // calc work size @@ -131,7 +131,7 @@ macro_rules! impl_eigh { // actual evg let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; let lwork = lwork as i32; unsafe { $evg( diff --git a/lax/src/layout.rs b/lax/src/layout.rs index e83923b3..e695d8e7 100644 --- a/lax/src/layout.rs +++ b/lax/src/layout.rs @@ -202,7 +202,7 @@ pub fn transpose(layout: MatrixLayout, input: &[T]) -> (MatrixLayout, V let n = n as usize; assert_eq!(input.len(), m * n); - let mut out: Vec> = unsafe { vec_uninit2(m * n) }; + let mut out: Vec> = unsafe { vec_uninit(m * n) }; match layout { MatrixLayout::C { .. } => { diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 71e0dbf5..6be44f33 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -87,7 +87,7 @@ macro_rules! impl_least_squares { }; let rcond: Self::Real = -1.; - let mut singular_values: Vec> = unsafe { vec_uninit2( k as usize) }; + let mut singular_values: Vec> = unsafe { vec_uninit( k as usize) }; let mut rank: i32 = 0; // eval work size @@ -120,12 +120,12 @@ macro_rules! impl_least_squares { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; let liwork = iwork_size[0].to_usize().unwrap(); - let mut iwork: Vec> = unsafe { vec_uninit2(liwork) }; + let mut iwork: Vec> = unsafe { vec_uninit(liwork) }; $( let lrwork = $rwork[0].to_usize().unwrap(); - let mut $rwork: Vec> = unsafe { vec_uninit2(lrwork) }; + let mut $rwork: Vec> = unsafe { vec_uninit(lrwork) }; )* unsafe { $gelsd( diff --git a/lax/src/lib.rs b/lax/src/lib.rs index bc908da6..c8d2264d 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -269,19 +269,7 @@ impl EigenVectorFlag { /// ------ /// - Memory is not initialized. Do not read the memory before write. /// -unsafe fn vec_uninit(n: usize) -> Vec { - let mut v = Vec::with_capacity(n); - v.set_len(n); - v -} - -/// Create a vector without initialization -/// -/// Safety -/// ------ -/// - Memory is not initialized. Do not read the memory before write. -/// -unsafe fn vec_uninit2(n: usize) -> Vec> { +unsafe fn vec_uninit(n: usize) -> Vec> { let mut v = Vec::with_capacity(n); v.set_len(n); v diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index ba831456..fca7704c 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -19,7 +19,7 @@ macro_rules! impl_opnorm { MatrixLayout::C { .. } => t.transpose(), }; let mut work: Vec> = if matches!(t, NormType::Infinity) { - unsafe { vec_uninit2(m as usize) } + unsafe { vec_uninit(m as usize) } } else { Vec::new() }; diff --git a/lax/src/qr.rs b/lax/src/qr.rs index 4dc1d5a7..553bb606 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -25,7 +25,7 @@ macro_rules! impl_qr { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut tau = unsafe { vec_uninit2(k as usize) }; + let mut tau = unsafe { vec_uninit(k as usize) }; // eval work size let mut info = 0; @@ -62,7 +62,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; unsafe { match l { MatrixLayout::F { .. } => { @@ -136,7 +136,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; unsafe { match l { MatrixLayout::F { .. } => $gqr( diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 39dc4799..dfc8a941 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -17,8 +17,8 @@ macro_rules! impl_rcond_real { let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit2(4 * n as usize) }; - let mut iwork: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut work: Vec> = unsafe { vec_uninit(4 * n as usize) }; + let mut iwork: Vec> = unsafe { vec_uninit(n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, @@ -54,9 +54,8 @@ macro_rules! impl_rcond_complex { let (n, _) = l.size(); let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit2(2 * n as usize) }; - let mut rwork: Vec> = - unsafe { vec_uninit2(2 * n as usize) }; + let mut work: Vec> = unsafe { vec_uninit(2 * n as usize) }; + let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 84affe39..ae76f190 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -33,7 +33,7 @@ macro_rules! impl_solve { return Ok(Vec::new()); } let k = ::std::cmp::min(row, col); - let mut ipiv = unsafe { vec_uninit2(k as usize) }; + let mut ipiv = unsafe { vec_uninit(k as usize) }; let mut info = 0; unsafe { $getrf( @@ -75,7 +75,7 @@ macro_rules! impl_solve { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; unsafe { $getri( &l.len(), diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 09221f03..9f65978d 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -20,7 +20,7 @@ macro_rules! impl_solveh { impl Solveh_ for $scalar { fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result { let (n, _) = l.size(); - let mut ipiv = unsafe { vec_uninit2(n as usize) }; + let mut ipiv = unsafe { vec_uninit(n as usize) }; if n == 0 { return Ok(Vec::new()); } @@ -44,7 +44,7 @@ macro_rules! impl_solveh { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2(lwork) }; + let mut work: Vec> = unsafe { vec_uninit(lwork) }; unsafe { $trf( uplo.as_ptr(), @@ -65,7 +65,7 @@ macro_rules! impl_solveh { fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { let (n, _) = l.size(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit2(n as usize) }; + let mut work: Vec> = unsafe { vec_uninit(n as usize) }; unsafe { $tri( uplo.as_ptr(), diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 2968cff6..8c731c7a 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -65,21 +65,21 @@ macro_rules! impl_svd { let m = l.lda(); let mut u = match ju { - FlagSVD::All => Some(unsafe { vec_uninit2( (m * m) as usize) }), + FlagSVD::All => Some(unsafe { vec_uninit( (m * m) as usize) }), FlagSVD::No => None, }; let n = l.len(); let mut vt = match jvt { - FlagSVD::All => Some(unsafe { vec_uninit2( (n * n) as usize) }), + FlagSVD::All => Some(unsafe { vec_uninit( (n * n) as usize) }), FlagSVD::No => None, }; let k = std::cmp::min(m, n); - let mut s = unsafe { vec_uninit2( k as usize) }; + let mut s = unsafe { vec_uninit( k as usize) }; $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit2( 5 * k as usize) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit( 5 * k as usize) }; )* // eval work size @@ -108,7 +108,7 @@ macro_rules! impl_svd { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2( lwork) }; + let mut work: Vec> = unsafe { vec_uninit( lwork) }; unsafe { $gesvd( ju.as_ptr(), diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index 6a703bd6..f956d848 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -39,7 +39,7 @@ macro_rules! impl_svddc { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut s = unsafe { vec_uninit2( k as usize) }; + let mut s = unsafe { vec_uninit( k as usize) }; let (u_col, vt_row) = match jobz { UVTFlag::Full | UVTFlag::None => (m, n), @@ -47,12 +47,12 @@ macro_rules! impl_svddc { }; let (mut u, mut vt) = match jobz { UVTFlag::Full => ( - Some(unsafe { vec_uninit2( (m * m) as usize) }), - Some(unsafe { vec_uninit2( (n * n) as usize) }), + Some(unsafe { vec_uninit( (m * m) as usize) }), + Some(unsafe { vec_uninit( (n * n) as usize) }), ), UVTFlag::Some => ( - Some(unsafe { vec_uninit2( (m * u_col) as usize) }), - Some(unsafe { vec_uninit2( (n * vt_row) as usize) }), + Some(unsafe { vec_uninit( (m * u_col) as usize) }), + Some(unsafe { vec_uninit( (n * vt_row) as usize) }), ), UVTFlag::None => (None, None), }; @@ -64,12 +64,12 @@ macro_rules! impl_svddc { UVTFlag::None => 7 * mn, _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), }; - let mut $rwork_ident: Vec> = unsafe { vec_uninit2( lrwork) }; + let mut $rwork_ident: Vec> = unsafe { vec_uninit( lrwork) }; )* // eval work size let mut info = 0; - let mut iwork: Vec> = unsafe { vec_uninit2( 8 * k as usize) }; + let mut iwork: Vec> = unsafe { vec_uninit( 8 * k as usize) }; let mut work_size = [Self::zero()]; unsafe { $gesdd( @@ -94,7 +94,7 @@ macro_rules! impl_svddc { // do svd let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit2( lwork) }; + let mut work: Vec> = unsafe { vec_uninit( lwork) }; unsafe { $gesdd( jobz.as_ptr(), diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index 3f86bd0e..ef8dfdf6 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -152,8 +152,8 @@ macro_rules! impl_tridiagonal { impl Tridiagonal_ for $scalar { fn lu_tridiagonal(mut a: Tridiagonal) -> Result> { let (n, _) = a.l.size(); - let mut du2 = unsafe { vec_uninit2( (n - 2) as usize) }; - let mut ipiv = unsafe { vec_uninit2( n as usize) }; + let mut du2 = unsafe { vec_uninit( (n - 2) as usize) }; + let mut ipiv = unsafe { vec_uninit( n as usize) }; // We have to calc one-norm before LU factorization let a_opnorm_one = a.opnorm_one(); let mut info = 0; @@ -182,9 +182,9 @@ macro_rules! impl_tridiagonal { fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { let (n, _) = lu.a.l.size(); let ipiv = &lu.ipiv; - let mut work: Vec> = unsafe { vec_uninit2( 2 * n as usize) }; + let mut work: Vec> = unsafe { vec_uninit( 2 * n as usize) }; $( - let mut $iwork: Vec> = unsafe { vec_uninit2( n as usize) }; + let mut $iwork: Vec> = unsafe { vec_uninit( n as usize) }; )* let mut rcond = Self::Real::zero(); let mut info = 0; From 7eabc4027309c32022bdcccae27d643f05b9a298 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 21:00:28 +0900 Subject: [PATCH 049/165] Deploy GitHub Pages using GitHub Action --- .github/workflows/gh-pages.yml | 56 +++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 1c910568..f8509459 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -1,21 +1,55 @@ +# Based on starter workflow +# https://github.com/actions/starter-workflows/blob/8217436fdee2338da2d6fd02b7c9fcff634c40e7/pages/static.yml +# +# Simple workflow for deploying static content to GitHub Pages name: "GitHub Pages" on: + # Runs on pushes targeting the default branch push: branches: - master + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + jobs: - pages: - runs-on: ubuntu-22.04 + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Generate code coverage - run: | - RUSTDOCFLAGS="--html-in-header ndarray-linalg/katex-header.html" cargo doc --no-deps - mv target/doc public - - name: Deploy GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + - name: Checkout + uses: actions/checkout@v3 + + # Generate cargo-doc + - uses: actions-rs/cargo@v1 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./public + command: doc + args: --no-deps + + - name: Setup Pages + uses: actions/configure-pages@v2 + + # Upload target/doc directory + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: 'target/doc' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 From 9e4e3c6df566c8ba6709018cf221f5c42711ae39 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 21:15:16 +0900 Subject: [PATCH 050/165] Update badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 58cc22e2..ce78e31c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ ndarray-linalg =============== -[![crate](https://img.shields.io/badge/crates.io-ndarray--linalg-blue)](https://crates.io/crates/ndarray-linalg) +[![crate](https://img.shields.io/crates/v/ndarray-linalg.svg)](https://crates.io/crates/ndarray-linalg) [![docs.rs](https://docs.rs/ndarray-linalg/badge.svg)](https://docs.rs/ndarray-linalg) +[![master](https://img.shields.io/badge/docs-master-blue)](https://rust-ndarray.github.io/ndarray-linalg/ndarray_linalg/index.html) Linear algebra package for Rust with [ndarray](https://github.com/rust-ndarray/ndarray) based on external LAPACK implementations. From 08aae3bede7cc26801aff5d9804a78763a2a5977 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 1 Sep 2022 21:24:05 +0900 Subject: [PATCH 051/165] Add RUSTDOCFLAGS when generating GitHub Pages --- .github/workflows/gh-pages.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index f8509459..90ad7b90 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -40,6 +40,8 @@ jobs: with: command: doc args: --no-deps + env: + RUSTDOCFLAGS: "--html-in-header ndarray-linalg/katex-header.html" - name: Setup Pages uses: actions/configure-pages@v2 From 9109a2f6370e4e7e3f79df673cdf6e59ffbd6dfa Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 2 Sep 2022 17:17:04 +0900 Subject: [PATCH 052/165] Gather enum definitions --- lax/src/flags.rs | 145 ++++++++++++++++++++++++++++++++++++++++++ lax/src/lib.rs | 92 +-------------------------- lax/src/svd.rs | 25 +------- lax/src/svddc.rs | 20 ------ lax/src/triangular.rs | 13 ---- 5 files changed, 148 insertions(+), 147 deletions(-) create mode 100644 lax/src/flags.rs diff --git a/lax/src/flags.rs b/lax/src/flags.rs new file mode 100644 index 00000000..a0e146db --- /dev/null +++ b/lax/src/flags.rs @@ -0,0 +1,145 @@ +/// Upper/Lower specification for seveal usages +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum UPLO { + Upper = b'U', + Lower = b'L', +} + +impl UPLO { + pub fn t(self) -> Self { + match self { + UPLO::Upper => UPLO::Lower, + UPLO::Lower => UPLO::Upper, + } + } + + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const UPLO as *const i8 + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum Transpose { + No = b'N', + Transpose = b'T', + Hermite = b'C', +} + +impl Transpose { + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const Transpose as *const i8 + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum NormType { + One = b'O', + Infinity = b'I', + Frobenius = b'F', +} + +impl NormType { + pub fn transpose(self) -> Self { + match self { + NormType::One => NormType::Infinity, + NormType::Infinity => NormType::One, + NormType::Frobenius => NormType::Frobenius, + } + } + + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const NormType as *const i8 + } +} + +/// Flag for calculating eigenvectors or not +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum EigenVectorFlag { + Calc = b'V', + Not = b'N', +} + +impl EigenVectorFlag { + pub fn is_calc(&self) -> bool { + match self { + EigenVectorFlag::Calc => true, + EigenVectorFlag::Not => false, + } + } + + pub fn then T>(&self, f: F) -> Option { + if self.is_calc() { + Some(f()) + } else { + None + } + } + + /// To use Fortran LAPACK API in lapack-sys crate + pub fn as_ptr(&self) -> *const i8 { + self as *const EigenVectorFlag as *const i8 + } +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone)] +pub enum FlagSVD { + All = b'A', + // OverWrite = b'O', + // Separately = b'S', + No = b'N', +} + +impl FlagSVD { + pub fn from_bool(calc_uv: bool) -> Self { + if calc_uv { + FlagSVD::All + } else { + FlagSVD::No + } + } + + pub fn as_ptr(&self) -> *const i8 { + self as *const FlagSVD as *const i8 + } +} + +/// Specifies how many of the columns of *U* and rows of *V*ᵀ are computed and returned. +/// +/// For an input array of shape *m*×*n*, the following are computed: +#[derive(Clone, Copy, Eq, PartialEq)] +#[repr(u8)] +pub enum UVTFlag { + /// All *m* columns of *U* and all *n* rows of *V*ᵀ. + Full = b'A', + /// The first min(*m*,*n*) columns of *U* and the first min(*m*,*n*) rows of *V*ᵀ. + Some = b'S', + /// No columns of *U* or rows of *V*ᵀ. + None = b'N', +} + +impl UVTFlag { + pub fn as_ptr(&self) -> *const i8 { + self as *const UVTFlag as *const i8 + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum Diag { + Unit = b'U', + NonUnit = b'N', +} + +impl Diag { + pub fn as_ptr(&self) -> *const i8 { + self as *const Diag as *const i8 + } +} diff --git a/lax/src/lib.rs b/lax/src/lib.rs index c8d2264d..0a6249ec 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -74,6 +74,7 @@ pub mod layout; mod cholesky; mod eig; mod eigh; +mod flags; mod least_squares; mod opnorm; mod qr; @@ -88,6 +89,7 @@ mod tridiagonal; pub use self::cholesky::*; pub use self::eig::*; pub use self::eigh::*; +pub use self::flags::*; pub use self::least_squares::*; pub use self::opnorm::*; pub use self::qr::*; @@ -173,96 +175,6 @@ impl VecAssumeInit for Vec> { } } -/// Upper/Lower specification for seveal usages -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum UPLO { - Upper = b'U', - Lower = b'L', -} - -impl UPLO { - pub fn t(self) -> Self { - match self { - UPLO::Upper => UPLO::Lower, - UPLO::Lower => UPLO::Upper, - } - } - - /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const UPLO as *const i8 - } -} - -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum Transpose { - No = b'N', - Transpose = b'T', - Hermite = b'C', -} - -impl Transpose { - /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const Transpose as *const i8 - } -} - -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum NormType { - One = b'O', - Infinity = b'I', - Frobenius = b'F', -} - -impl NormType { - pub fn transpose(self) -> Self { - match self { - NormType::One => NormType::Infinity, - NormType::Infinity => NormType::One, - NormType::Frobenius => NormType::Frobenius, - } - } - - /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const NormType as *const i8 - } -} - -/// Flag for calculating eigenvectors or not -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum EigenVectorFlag { - Calc = b'V', - Not = b'N', -} - -impl EigenVectorFlag { - pub fn is_calc(&self) -> bool { - match self { - EigenVectorFlag::Calc => true, - EigenVectorFlag::Not => false, - } - } - - pub fn then T>(&self, f: F) -> Option { - if self.is_calc() { - Some(f()) - } else { - None - } - } - - /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const EigenVectorFlag as *const i8 - } -} - /// Create a vector without initialization /// /// Safety diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 8c731c7a..c9e73f89 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -1,32 +1,9 @@ //! Singular-value decomposition -use crate::{error::*, layout::MatrixLayout, *}; +use super::{error::*, layout::*, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -#[repr(u8)] -#[derive(Debug, Copy, Clone)] -enum FlagSVD { - All = b'A', - // OverWrite = b'O', - // Separately = b'S', - No = b'N', -} - -impl FlagSVD { - fn from_bool(calc_uv: bool) -> Self { - if calc_uv { - FlagSVD::All - } else { - FlagSVD::No - } - } - - fn as_ptr(&self) -> *const i8 { - self as *const FlagSVD as *const i8 - } -} - /// Result of SVD pub struct SVDOutput { /// diagonal values diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index f956d848..e31f126f 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -2,26 +2,6 @@ use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -/// Specifies how many of the columns of *U* and rows of *V*ᵀ are computed and returned. -/// -/// For an input array of shape *m*×*n*, the following are computed: -#[derive(Clone, Copy, Eq, PartialEq)] -#[repr(u8)] -pub enum UVTFlag { - /// All *m* columns of *U* and all *n* rows of *V*ᵀ. - Full = b'A', - /// The first min(*m*,*n*) columns of *U* and the first min(*m*,*n*) rows of *V*ᵀ. - Some = b'S', - /// No columns of *U* or rows of *V*ᵀ. - None = b'N', -} - -impl UVTFlag { - fn as_ptr(&self) -> *const i8 { - self as *const UVTFlag as *const i8 - } -} - pub trait SVDDC_: Scalar { fn svddc(l: MatrixLayout, jobz: UVTFlag, a: &mut [Self]) -> Result>; } diff --git a/lax/src/triangular.rs b/lax/src/triangular.rs index e8825758..14f29807 100644 --- a/lax/src/triangular.rs +++ b/lax/src/triangular.rs @@ -3,19 +3,6 @@ use crate::{error::*, layout::*, *}; use cauchy::*; -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum Diag { - Unit = b'U', - NonUnit = b'N', -} - -impl Diag { - fn as_ptr(&self) -> *const i8 { - self as *const Diag as *const i8 - } -} - /// Wraps `*trtri` and `*trtrs` pub trait Triangular_: Scalar { fn solve_triangular( From 262a2c966e97abcdde957b5e5daa33babc66d6b9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 2 Sep 2022 17:28:30 +0900 Subject: [PATCH 053/165] Make `lax::flags` module public --- lax/src/flags.rs | 2 ++ lax/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index a0e146db..f3b3ecd0 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -1,3 +1,5 @@ +//! Charactor flags, e.g. `'T'`, used in LAPACK API + /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy)] #[repr(u8)] diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 0a6249ec..83cf3658 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -69,12 +69,12 @@ extern crate openblas_src as _src; extern crate netlib_src as _src; pub mod error; +pub mod flags; pub mod layout; mod cholesky; mod eig; mod eigh; -mod flags; mod least_squares; mod opnorm; mod qr; From 4912a115a156d6fd25c786cc81260a49975f546b Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 00:20:14 +0900 Subject: [PATCH 054/165] Rename `EigenVectorFlag` to `JobEv` --- lax/src/eig.rs | 12 ++++++------ lax/src/eigh.rs | 4 ++-- lax/src/flags.rs | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index f11f5287..184172a0 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -35,11 +35,11 @@ macro_rules! impl_eig_complex { // eigenvalues are the eigenvalues computed with `A`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (EigenVectorFlag::Calc, EigenVectorFlag::Not), - MatrixLayout::F { .. } => (EigenVectorFlag::Not, EigenVectorFlag::Calc), + MatrixLayout::C { .. } => (JobEv::Calc, JobEv::Not), + MatrixLayout::F { .. } => (JobEv::Not, JobEv::Calc), } } else { - (EigenVectorFlag::Not, EigenVectorFlag::Not) + (JobEv::Not, JobEv::Not) }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; @@ -143,11 +143,11 @@ macro_rules! impl_eig_real { // `sgeev`/`dgeev`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (EigenVectorFlag::Calc, EigenVectorFlag::Not), - MatrixLayout::F { .. } => (EigenVectorFlag::Not, EigenVectorFlag::Calc), + MatrixLayout::C { .. } => (JobEv::Calc, JobEv::Not), + MatrixLayout::F { .. } => (JobEv::Not, JobEv::Calc), } } else { - (EigenVectorFlag::Not, EigenVectorFlag::Not) + (JobEv::Not, JobEv::Not) }; let mut eig_re: Vec> = unsafe { vec_uninit(n as usize) }; let mut eig_im: Vec> = unsafe { vec_uninit(n as usize) }; diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 0692f921..08e5f689 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -41,7 +41,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; + let jobz = if calc_v { JobEv::Calc } else { JobEv::Not }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( @@ -100,7 +100,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { EigenVectorFlag::Calc } else { EigenVectorFlag::Not }; + let jobz = if calc_v { JobEv::Calc } else { JobEv::Not }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( diff --git a/lax/src/flags.rs b/lax/src/flags.rs index f3b3ecd0..86d1d985 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -63,16 +63,16 @@ impl NormType { /// Flag for calculating eigenvectors or not #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] -pub enum EigenVectorFlag { +pub enum JobEv { Calc = b'V', Not = b'N', } -impl EigenVectorFlag { +impl JobEv { pub fn is_calc(&self) -> bool { match self { - EigenVectorFlag::Calc => true, - EigenVectorFlag::Not => false, + JobEv::Calc => true, + JobEv::Not => false, } } @@ -86,7 +86,7 @@ impl EigenVectorFlag { /// To use Fortran LAPACK API in lapack-sys crate pub fn as_ptr(&self) -> *const i8 { - self as *const EigenVectorFlag as *const i8 + self as *const JobEv as *const i8 } } From 62e40d134cf6f3eb07a277a4f43e48078d6ffeee Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 14:40:14 +0900 Subject: [PATCH 055/165] Replace FlagSVD by UVTFlag --- lax/src/flags.rs | 31 ++++++++----------------------- lax/src/svd.rs | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 86d1d985..23625ee3 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -90,29 +90,6 @@ impl JobEv { } } -#[repr(u8)] -#[derive(Debug, Copy, Clone)] -pub enum FlagSVD { - All = b'A', - // OverWrite = b'O', - // Separately = b'S', - No = b'N', -} - -impl FlagSVD { - pub fn from_bool(calc_uv: bool) -> Self { - if calc_uv { - FlagSVD::All - } else { - FlagSVD::No - } - } - - pub fn as_ptr(&self) -> *const i8 { - self as *const FlagSVD as *const i8 - } -} - /// Specifies how many of the columns of *U* and rows of *V*ᵀ are computed and returned. /// /// For an input array of shape *m*×*n*, the following are computed: @@ -128,6 +105,14 @@ pub enum UVTFlag { } impl UVTFlag { + pub fn from_bool(calc_uv: bool) -> Self { + if calc_uv { + UVTFlag::Full + } else { + UVTFlag::None + } + } + pub fn as_ptr(&self) -> *const i8 { self as *const UVTFlag as *const i8 } diff --git a/lax/src/svd.rs b/lax/src/svd.rs index c9e73f89..7807acc9 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -32,24 +32,26 @@ macro_rules! impl_svd { impl SVD_ for $scalar { fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self],) -> Result> { let ju = match l { - MatrixLayout::F { .. } => FlagSVD::from_bool(calc_u), - MatrixLayout::C { .. } => FlagSVD::from_bool(calc_vt), + MatrixLayout::F { .. } => UVTFlag::from_bool(calc_u), + MatrixLayout::C { .. } => UVTFlag::from_bool(calc_vt), }; let jvt = match l { - MatrixLayout::F { .. } => FlagSVD::from_bool(calc_vt), - MatrixLayout::C { .. } => FlagSVD::from_bool(calc_u), + MatrixLayout::F { .. } => UVTFlag::from_bool(calc_vt), + MatrixLayout::C { .. } => UVTFlag::from_bool(calc_u), }; let m = l.lda(); let mut u = match ju { - FlagSVD::All => Some(unsafe { vec_uninit( (m * m) as usize) }), - FlagSVD::No => None, + UVTFlag::Full => Some(unsafe { vec_uninit( (m * m) as usize) }), + UVTFlag::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet") }; let n = l.len(); let mut vt = match jvt { - FlagSVD::All => Some(unsafe { vec_uninit( (n * n) as usize) }), - FlagSVD::No => None, + UVTFlag::Full => Some(unsafe { vec_uninit( (n * n) as usize) }), + UVTFlag::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet") }; let k = std::cmp::min(m, n); From 950b7dabdba51c53ac0cba8f4868636e4a2d7b13 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 14:51:27 +0900 Subject: [PATCH 056/165] Rename UVTFlag to JobSvd, and `Full` to `All` --- lax/src/flags.rs | 12 ++++++------ lax/src/svd.rs | 16 ++++++++-------- lax/src/svddc.rs | 16 ++++++++-------- ndarray-linalg/src/svddc.rs | 20 ++++++++++---------- ndarray-linalg/tests/svddc.rs | 20 ++++++++++---------- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 23625ee3..e8f5b9a3 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -95,26 +95,26 @@ impl JobEv { /// For an input array of shape *m*×*n*, the following are computed: #[derive(Clone, Copy, Eq, PartialEq)] #[repr(u8)] -pub enum UVTFlag { +pub enum JobSvd { /// All *m* columns of *U* and all *n* rows of *V*ᵀ. - Full = b'A', + All = b'A', /// The first min(*m*,*n*) columns of *U* and the first min(*m*,*n*) rows of *V*ᵀ. Some = b'S', /// No columns of *U* or rows of *V*ᵀ. None = b'N', } -impl UVTFlag { +impl JobSvd { pub fn from_bool(calc_uv: bool) -> Self { if calc_uv { - UVTFlag::Full + JobSvd::All } else { - UVTFlag::None + JobSvd::None } } pub fn as_ptr(&self) -> *const i8 { - self as *const UVTFlag as *const i8 + self as *const JobSvd as *const i8 } } diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 7807acc9..0a509a0e 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -32,25 +32,25 @@ macro_rules! impl_svd { impl SVD_ for $scalar { fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self],) -> Result> { let ju = match l { - MatrixLayout::F { .. } => UVTFlag::from_bool(calc_u), - MatrixLayout::C { .. } => UVTFlag::from_bool(calc_vt), + MatrixLayout::F { .. } => JobSvd::from_bool(calc_u), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_vt), }; let jvt = match l { - MatrixLayout::F { .. } => UVTFlag::from_bool(calc_vt), - MatrixLayout::C { .. } => UVTFlag::from_bool(calc_u), + MatrixLayout::F { .. } => JobSvd::from_bool(calc_vt), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_u), }; let m = l.lda(); let mut u = match ju { - UVTFlag::Full => Some(unsafe { vec_uninit( (m * m) as usize) }), - UVTFlag::None => None, + JobSvd::All => Some(unsafe { vec_uninit( (m * m) as usize) }), + JobSvd::None => None, _ => unimplemented!("SVD with partial vector output is not supported yet") }; let n = l.len(); let mut vt = match jvt { - UVTFlag::Full => Some(unsafe { vec_uninit( (n * n) as usize) }), - UVTFlag::None => None, + JobSvd::All => Some(unsafe { vec_uninit( (n * n) as usize) }), + JobSvd::None => None, _ => unimplemented!("SVD with partial vector output is not supported yet") }; diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index e31f126f..bb59348f 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -3,7 +3,7 @@ use cauchy::*; use num_traits::{ToPrimitive, Zero}; pub trait SVDDC_: Scalar { - fn svddc(l: MatrixLayout, jobz: UVTFlag, a: &mut [Self]) -> Result>; + fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; } macro_rules! impl_svddc { @@ -15,33 +15,33 @@ macro_rules! impl_svddc { }; (@body, $scalar:ty, $gesdd:path, $($rwork_ident:ident),*) => { impl SVDDC_ for $scalar { - fn svddc(l: MatrixLayout, jobz: UVTFlag, a: &mut [Self],) -> Result> { + fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self],) -> Result> { let m = l.lda(); let n = l.len(); let k = m.min(n); let mut s = unsafe { vec_uninit( k as usize) }; let (u_col, vt_row) = match jobz { - UVTFlag::Full | UVTFlag::None => (m, n), - UVTFlag::Some => (k, k), + JobSvd::All | JobSvd::None => (m, n), + JobSvd::Some => (k, k), }; let (mut u, mut vt) = match jobz { - UVTFlag::Full => ( + JobSvd::All => ( Some(unsafe { vec_uninit( (m * m) as usize) }), Some(unsafe { vec_uninit( (n * n) as usize) }), ), - UVTFlag::Some => ( + JobSvd::Some => ( Some(unsafe { vec_uninit( (m * u_col) as usize) }), Some(unsafe { vec_uninit( (n * vt_row) as usize) }), ), - UVTFlag::None => (None, None), + JobSvd::None => (None, None), }; $( // for complex only let mx = n.max(m) as usize; let mn = n.min(m) as usize; let lrwork = match jobz { - UVTFlag::None => 7 * mn, + JobSvd::None => 7 * mn, _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), }; let mut $rwork_ident: Vec> = unsafe { vec_uninit( lrwork) }; diff --git a/ndarray-linalg/src/svddc.rs b/ndarray-linalg/src/svddc.rs index ff73407f..0b0ae237 100644 --- a/ndarray-linalg/src/svddc.rs +++ b/ndarray-linalg/src/svddc.rs @@ -3,14 +3,14 @@ use super::{convert::*, error::*, layout::*, types::*}; use ndarray::*; -pub use lax::UVTFlag; +pub use lax::JobSvd; /// Singular-value decomposition of matrix (copying) by divide-and-conquer pub trait SVDDC { type U; type VT; type Sigma; - fn svddc(&self, uvt_flag: UVTFlag) -> Result<(Option, Self::Sigma, Option)>; + fn svddc(&self, uvt_flag: JobSvd) -> Result<(Option, Self::Sigma, Option)>; } /// Singular-value decomposition of matrix by divide-and-conquer @@ -20,7 +20,7 @@ pub trait SVDDCInto { type Sigma; fn svddc_into( self, - uvt_flag: UVTFlag, + uvt_flag: JobSvd, ) -> Result<(Option, Self::Sigma, Option)>; } @@ -31,7 +31,7 @@ pub trait SVDDCInplace { type Sigma; fn svddc_inplace( &mut self, - uvt_flag: UVTFlag, + uvt_flag: JobSvd, ) -> Result<(Option, Self::Sigma, Option)>; } @@ -44,7 +44,7 @@ where type VT = Array2; type Sigma = Array1; - fn svddc(&self, uvt_flag: UVTFlag) -> Result<(Option, Self::Sigma, Option)> { + fn svddc(&self, uvt_flag: JobSvd) -> Result<(Option, Self::Sigma, Option)> { self.to_owned().svddc_into(uvt_flag) } } @@ -60,7 +60,7 @@ where fn svddc_into( mut self, - uvt_flag: UVTFlag, + uvt_flag: JobSvd, ) -> Result<(Option, Self::Sigma, Option)> { self.svddc_inplace(uvt_flag) } @@ -77,7 +77,7 @@ where fn svddc_inplace( &mut self, - uvt_flag: UVTFlag, + uvt_flag: JobSvd, ) -> Result<(Option, Self::Sigma, Option)> { let l = self.layout()?; let svd_res = A::svddc(l, uvt_flag, self.as_allocated_mut()?)?; @@ -85,9 +85,9 @@ where let k = m.min(n); let (u_col, vt_row) = match uvt_flag { - UVTFlag::Full => (m, n), - UVTFlag::Some => (k, k), - UVTFlag::None => (0, 0), + JobSvd::All => (m, n), + JobSvd::Some => (k, k), + JobSvd::None => (0, 0), }; let u = svd_res diff --git a/ndarray-linalg/tests/svddc.rs b/ndarray-linalg/tests/svddc.rs index fb26c8d5..ed28fc30 100644 --- a/ndarray-linalg/tests/svddc.rs +++ b/ndarray-linalg/tests/svddc.rs @@ -1,16 +1,16 @@ use ndarray::*; use ndarray_linalg::*; -fn test(a: &Array2, flag: UVTFlag) { +fn test(a: &Array2, flag: JobSvd) { let (n, m) = a.dim(); let k = n.min(m); let answer = a.clone(); println!("a = \n{:?}", a); let (u, s, vt): (_, Array1<_>, _) = a.svddc(flag).unwrap(); let mut sm: Array2 = match flag { - UVTFlag::Full => Array::zeros((n, m)), - UVTFlag::Some => Array::zeros((k, k)), - UVTFlag::None => { + JobSvd::All => Array::zeros((n, m)), + JobSvd::Some => Array::zeros((k, k)), + JobSvd::None => { assert!(u.is_none()); assert!(vt.is_none()); return; @@ -33,37 +33,37 @@ macro_rules! test_svd_impl { #[test] fn []() { let a = random(($n, $m)); - test::<$scalar>(&a, UVTFlag::Full); + test::<$scalar>(&a, JobSvd::All); } #[test] fn []() { let a = random(($n, $m)); - test::<$scalar>(&a, UVTFlag::Some); + test::<$scalar>(&a, JobSvd::Some); } #[test] fn []() { let a = random(($n, $m)); - test::<$scalar>(&a, UVTFlag::None); + test::<$scalar>(&a, JobSvd::None); } #[test] fn []() { let a = random(($n, $m).f()); - test::<$scalar>(&a, UVTFlag::Full); + test::<$scalar>(&a, JobSvd::All); } #[test] fn []() { let a = random(($n, $m).f()); - test::<$scalar>(&a, UVTFlag::Some); + test::<$scalar>(&a, JobSvd::Some); } #[test] fn []() { let a = random(($n, $m).f()); - test::<$scalar>(&a, UVTFlag::None); + test::<$scalar>(&a, JobSvd::None); } } }; From b029bfbf1745e9ffcdf98bae7fee7d91cd106326 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 15:10:38 +0900 Subject: [PATCH 057/165] Rename JobEv::{Calc, Not} to {All, None} to match JobSvd --- lax/src/eig.rs | 12 ++++++------ lax/src/eigh.rs | 4 ++-- lax/src/flags.rs | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 184172a0..d3c07222 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -35,11 +35,11 @@ macro_rules! impl_eig_complex { // eigenvalues are the eigenvalues computed with `A`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (JobEv::Calc, JobEv::Not), - MatrixLayout::F { .. } => (JobEv::Not, JobEv::Calc), + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), } } else { - (JobEv::Not, JobEv::Not) + (JobEv::None, JobEv::None) }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; @@ -143,11 +143,11 @@ macro_rules! impl_eig_real { // `sgeev`/`dgeev`. let (jobvl, jobvr) = if calc_v { match l { - MatrixLayout::C { .. } => (JobEv::Calc, JobEv::Not), - MatrixLayout::F { .. } => (JobEv::Not, JobEv::Calc), + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), } } else { - (JobEv::Not, JobEv::Not) + (JobEv::None, JobEv::None) }; let mut eig_re: Vec> = unsafe { vec_uninit(n as usize) }; let mut eig_im: Vec> = unsafe { vec_uninit(n as usize) }; diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 08e5f689..a9406ee6 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -41,7 +41,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { JobEv::Calc } else { JobEv::Not }; + let jobz = if calc_v { JobEv::All } else { JobEv::None }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( @@ -100,7 +100,7 @@ macro_rules! impl_eigh { ) -> Result> { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); - let jobz = if calc_v { JobEv::Calc } else { JobEv::Not }; + let jobz = if calc_v { JobEv::All } else { JobEv::None }; let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; $( diff --git a/lax/src/flags.rs b/lax/src/flags.rs index e8f5b9a3..cdf7faf5 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -64,15 +64,17 @@ impl NormType { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum JobEv { - Calc = b'V', - Not = b'N', + /// Calculate eigenvectors in addition to eigenvalues + All = b'V', + /// Do not calculate eigenvectors. Only calculate eigenvalues. + None = b'N', } impl JobEv { pub fn is_calc(&self) -> bool { match self { - JobEv::Calc => true, - JobEv::Not => false, + JobEv::All => true, + JobEv::None => false, } } From ef80e1a66a2da56b124bb8b83964a5bcda48f8df Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 15:17:36 +0900 Subject: [PATCH 058/165] Document for Diag --- lax/src/flags.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index cdf7faf5..4d4f9bd1 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -120,10 +120,13 @@ impl JobSvd { } } +/// Specify whether input triangular matrix is unit or not #[derive(Debug, Clone, Copy)] #[repr(u8)] pub enum Diag { + /// Unit triangular matrix, i.e. all diagonal elements of the matrix are `1` Unit = b'U', + /// Non-unit triangular matrix. Its diagonal elements may be different from `1` NonUnit = b'N', } From 86c61c39556949ff19fd297296a4105608fc1449 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 15:19:22 +0900 Subject: [PATCH 059/165] More auto-derives --- lax/src/flags.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 4d4f9bd1..37a11b3c 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -1,7 +1,7 @@ //! Charactor flags, e.g. `'T'`, used in LAPACK API /// Upper/Lower specification for seveal usages -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum UPLO { Upper = b'U', @@ -22,7 +22,7 @@ impl UPLO { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum Transpose { No = b'N', @@ -37,7 +37,7 @@ impl Transpose { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum NormType { One = b'O', @@ -61,7 +61,7 @@ impl NormType { } /// Flag for calculating eigenvectors or not -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum JobEv { /// Calculate eigenvectors in addition to eigenvalues @@ -95,7 +95,7 @@ impl JobEv { /// Specifies how many of the columns of *U* and rows of *V*ᵀ are computed and returned. /// /// For an input array of shape *m*×*n*, the following are computed: -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum JobSvd { /// All *m* columns of *U* and all *n* rows of *V*ᵀ. @@ -121,7 +121,7 @@ impl JobSvd { } /// Specify whether input triangular matrix is unit or not -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum Diag { /// Unit triangular matrix, i.e. all diagonal elements of the matrix are `1` From 7aab3ca738b80f12a92c220ba1b2f8cfed23a7e0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 2 Sep 2022 14:48:22 +0900 Subject: [PATCH 060/165] Add katexit in dependencies --- lax/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index a36c34ab..26a6f35e 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -33,6 +33,7 @@ thiserror = "1.0.24" cauchy = "0.4.0" num-traits = "0.2.14" lapack-sys = "0.14.0" +katexit = "0.1.2" [dependencies.intel-mkl-src] version = "0.7.0" From 014b5eee3df309e8e40f0857aead02524ca0a8b8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 16:09:43 +0900 Subject: [PATCH 061/165] Use katex in JobSvd document --- lax/src/flags.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 37a11b3c..dd9dde3d 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -92,17 +92,19 @@ impl JobEv { } } -/// Specifies how many of the columns of *U* and rows of *V*ᵀ are computed and returned. +/// Specifies how many singular vectors are computed /// -/// For an input array of shape *m*×*n*, the following are computed: +/// For an input matrix $A$ of shape $m \times n$, +/// the following are computed on the singular value decomposition $A = U\Sigma V^T$: +#[cfg_attr(doc, katexit::katexit)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum JobSvd { - /// All *m* columns of *U* and all *n* rows of *V*ᵀ. + /// All $m$ columns of $U$, and/or all $n$ rows of $V^T$. All = b'A', - /// The first min(*m*,*n*) columns of *U* and the first min(*m*,*n*) rows of *V*ᵀ. + /// The first $\min(m, n)$ columns of $U$ and/or the first $\min(m, n)$ rows of $V^T$. Some = b'S', - /// No columns of *U* or rows of *V*ᵀ. + /// No columns of $U$ and/or rows of $V^T$. None = b'N', } From fe5deea72524c09b9b20667fdea31202376e7109 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 17:28:31 +0900 Subject: [PATCH 062/165] Update document in solve.rs Drop module-level doc in solve.rs --- lax/src/solve.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index ae76f190..ab601031 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -1,24 +1,43 @@ -//! Solve linear problem using LU decomposition - use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +#[cfg_attr(doc, katexit::katexit)] +/// Solve linear equations using LU-decomposition +/// +/// For a given matrix $A$, LU decomposition is described as $PA = LU$ where +/// +/// - $L$ is lower matrix +/// - $U$ is upper matrix +/// - $P$ is permutation matrix represented by [Pivot] +/// +/// This is designed as two step computation according to LAPACK API: +/// +/// 1. Factorize input matrix $A$ into $L$, $U$, and $P$. +/// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ +/// using the output of LU factorization. +/// pub trait Solve_: Scalar + Sized { - /// Computes the LU factorization of a general `m x n` matrix `a` using - /// partial pivoting with row interchanges. + /// Computes the LU factorization of a general $m \times n$ matrix + /// with partial pivoting with row interchanges. /// - /// $ PA = LU $ + /// Output + /// ------- + /// - $U$ and $L$ are stored in `a` after LU factorization has succeeded. + /// - $P$ is returned as [Pivot] /// /// Error /// ------ - /// - `LapackComputationalFailure { return_code }` when the matrix is singular - /// - Division by zero will occur if it is used to solve a system of equations - /// because `U[(return_code-1, return_code-1)]` is exactly zero. + /// - if the matrix is singular + /// - On this case, `return_code` in [Error::LapackComputationalFailure] means + /// `return_code`-th diagonal element of $U$ becomes zero. + /// fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; + /// Compute inverse matrix $A^{-1}$ from the output of LU-factorization fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()>; + /// Solve linear equations $Ax = b$ using the output of LU-factorization fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } From 1ea3a767d322ef46e09ac30f87036fc0a5ea5639 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 3 Sep 2022 17:39:59 +0900 Subject: [PATCH 063/165] Update document of solveh.rs --- lax/src/solveh.rs | 67 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 9f65978d..76829982 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -1,17 +1,70 @@ -//! Solve symmetric linear problem using the Bunch-Kaufman diagonal pivoting method. -//! -//! See also [the manual of dsytrf](http://www.netlib.org/lapack/lapack-3.1.1/html/dsytrf.f.html) - use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +#[cfg_attr(doc, katexit::katexit)] +/// Solve symmetric/hermite indefinite linear problem using the [Bunch-Kaufman diagonal pivoting method][BK]. +/// +/// For a given symmetric matrix $A$, +/// this method factorizes $A = U^T D U$ or $A = L D L^T$ where +/// +/// - $U$ (or $L$) are is a product of permutation and unit upper (lower) triangular matrices +/// - $D$ is symmetric and block diagonal with 1-by-1 and 2-by-2 diagonal blocks. +/// +/// This takes two-step approach based in LAPACK: +/// +/// 1. Factorize given matrix $A$ into upper ($U$) or lower ($L$) form with diagonal matrix $D$ +/// 2. Then solve linear equation $Ax = b$, and/or calculate inverse matrix $A^{-1}$ +/// +/// [BK]: https://doi.org/10.2307/2005787 +/// pub trait Solveh_: Sized { - /// Bunch-Kaufman: wrapper of `*sytrf` and `*hetrf` + /// Factorize input matrix using Bunch-Kaufman diagonal pivoting method + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [ssytrf] | [dsytrf] | [chetrf] | [zhetrf] | + /// + /// [ssytrf]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_ga12d2e56511cf7df066712c61d9acec45.html + /// [dsytrf]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_gad91bde1212277b3e909eb6af7f64858a.html + /// [chetrf]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_ga081dd1908e46d064c2bf0a1f6b664b86.html + /// [zhetrf]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_gadc84a5c9818ee12ea19944623131bd52.html + /// fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; - /// Wrapper of `*sytri` and `*hetri` + + /// Compute inverse matrix $A^{-1}$ from factroized result + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [ssytri] | [dsytri] | [chetri] | [zhetri] | + /// + /// [ssytri]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_gaef378ec0761234aac417f487b43b7a8b.html + /// [dsytri]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_ga75e09b4299b7955044a3bbf84c46b593.html + /// [chetri]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_gad87a6a1ac131c5635d47ac440e672bcf.html + /// [zhetri]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_ga4d9cfa0653de400029b8051996ce1e96.html + /// fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()>; - /// Wrapper of `*sytrs` and `*hetrs` + + /// Solve linear equation $Ax = b$ using factroized result + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [ssytrs] | [dsytrs] | [chetrs] | [zhetrs] | + /// + /// [ssytrs]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_gae20133a1119b69a7319783ff982c8c62.html + /// [dsytrs]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_ga6a223e61effac7232e98b422f147f032.html + /// [chetrs]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_ga6f9d8da222ffaa7b7535efc922faa1dc.html + /// [zhetrs]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_gacf697e3bb72c5fd88cd90972999401dd.html + /// fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; } From 26251681026bd09719a55fffd1bb605f83007a39 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 7 Sep 2022 14:05:47 +0900 Subject: [PATCH 064/165] About trait design in crate level document --- lax/src/lib.rs | 67 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 83cf3658..2c78cf82 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -1,32 +1,55 @@ -//! Linear Algebra eXtension (LAX) -//! =============================== -//! //! ndarray-free safe Rust wrapper for LAPACK FFI //! -//! Linear equation, Inverse matrix, Condition number -//! -------------------------------------------------- +//! `Lapack` trait and sub-traits +//! ------------------------------- +//! +//! This crates provides LAPACK wrapper as `impl` of traits to base scalar types. +//! For example, LU factorization to double-precision matrix is provided like: +//! +//! ```ignore +//! impl Solve_ for f64 { +//! fn lu(l: MatrixLayout, a: &mut [Self]) -> Result { ... } +//! } +//! ``` +//! +//! see [Solve_] for detail. You can use it like `f64::lu`: //! -//! As the property of $A$, several types of triangular factorization are used: +//! ``` +//! use lax::{Solve_, layout::MatrixLayout, Transpose}; //! -//! - LU-decomposition for general matrix -//! - $PA = LU$, where $L$ is lower matrix, $U$ is upper matrix, and $P$ is permutation matrix -//! - Bunch-Kaufman diagonal pivoting method for nonpositive-definite Hermitian matrix -//! - $A = U D U^\dagger$, where $U$ is upper matrix, -//! $D$ is Hermitian and block diagonal with 1-by-1 and 2-by-2 diagonal blocks. +//! let mut a = vec![ +//! 1.0, 2.0, +//! 3.0, 4.0 +//! ]; +//! let mut b = vec![1.0, 2.0]; +//! let layout = MatrixLayout::C { row: 2, lda: 2 }; +//! let pivot = f64::lu(layout, &mut a).unwrap(); +//! f64::solve(layout, Transpose::No, &a, &pivot, &mut b).unwrap(); +//! ``` //! -//! | matrix type | Triangler factorization (TRF) | Solve (TRS) | Inverse matrix (TRI) | Reciprocal condition number (CON) | -//! |:--------------------------------|:------------------------------|:------------|:---------------------|:----------------------------------| -//! | General (GE) | [lu] | [solve] | [inv] | [rcond] | -//! | Symmetric (SY) / Hermitian (HE) | [bk] | [solveh] | [invh] | - | +//! When you want to write generic algorithm for real and complex matrices, +//! this trait can be used as a trait bound: +//! +//! ``` +//! use lax::{Solve_, layout::MatrixLayout, Transpose}; +//! +//! fn solve_at_once(layout: MatrixLayout, a: &mut [T], b: &mut [T]) -> Result<(), lax::error::Error> { +//! let pivot = T::lu(layout, a)?; +//! T::solve(layout, Transpose::No, a, &pivot, b)?; +//! Ok(()) +//! } +//! ``` +//! +//! There are several similar traits as described below to keep development easy. +//! They are merged into a single trait, [Lapack]. +//! +//! Linear equation, Inverse matrix, Condition number +//! -------------------------------------------------- //! -//! [lu]: solve/trait.Solve_.html#tymethod.lu -//! [solve]: solve/trait.Solve_.html#tymethod.solve -//! [inv]: solve/trait.Solve_.html#tymethod.inv -//! [rcond]: solve/trait.Solve_.html#tymethod.rcond +//! According to the property input metrix, several types of triangular factorization are used: //! -//! [bk]: solveh/trait.Solveh_.html#tymethod.bk -//! [solveh]: solveh/trait.Solveh_.html#tymethod.solveh -//! [invh]: solveh/trait.Solveh_.html#tymethod.invh +//! - [Solve_] trait provides methods for LU-decomposition for general matrix. +//! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix //! //! Eigenvalue Problem //! ------------------- From e94e03d7467ccb3431804318b9bc546a8d61e1c6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 12:21:13 +0900 Subject: [PATCH 065/165] Document for Cholesky decomposition --- lax/src/cholesky.rs | 61 +++++++++++++++++++++++++++++++++++++++------ lax/src/lib.rs | 3 ++- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 94e683ff..04875b64 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -1,21 +1,68 @@ -//! Cholesky decomposition - use super::*; use crate::{error::*, layout::*}; use cauchy::*; +#[cfg_attr(doc, katexit::katexit)] +/// Solve symmetric/hermite positive-definite linear equations using Cholesky decomposition +/// +/// For a given positive definite matrix $A$, +/// Cholesky decomposition is described as $A = U^T U$ or $A = LL^T$ where +/// +/// - $L$ is lower matrix +/// - $U$ is upper matrix +/// +/// This is designed as two step computation according to LAPACK API +/// +/// 1. Factorize input matrix $A$ into $L$ or $U$ +/// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ +/// using $U$ or $L$. pub trait Cholesky_: Sized { - /// Cholesky: wrapper of `*potrf` + /// Compute Cholesky decomposition $A = U^T U$ or $A = L L^T$ according to [UPLO] + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [spotrf] | [dpotrf] | [cpotrf] | [zpotrf] | + /// + /// [spotrf]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_gaaf31db7ab15b4f4ba527a3d31a15a58e.html + /// [dpotrf]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga2f55f604a6003d03b5cd4a0adcfb74d6.html + /// [cpotrf]: https://netlib.org/lapack/explore-html/d8/d6c/group__variants_p_ocomputational_ga4e85f48dbd837ccbbf76aa077f33de19.html + /// [zpotrf]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_ga93e22b682170873efb50df5a79c5e4eb.html /// - /// **Warning: Only the portion of `a` corresponding to `UPLO` is written.** fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - /// Wrapper of `*potri` + /// Compute inverse matrix $A^{-1}$ using $U$ or $L$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [spotri] | [dpotri] | [cpotri] | [zpotri] | + /// + /// [spotri]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_ga4c381894bb34b1583fcc0dceafc5bea1.html + /// [dpotri]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga9dfc04beae56a3b1c1f75eebc838c14c.html + /// [cpotri]: https://netlib.org/lapack/explore-html/d6/df6/group__complex_p_ocomputational_ga52b8da4d314abefaee93dd5c1ed7739e.html + /// [zpotri]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_gaf37e3b8bbacd3332e83ffb3f1018bcf1.html /// - /// **Warning: Only the portion of `a` corresponding to `UPLO` is written.** fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - /// Wrapper of `*potrs` + /// Solve linear equation $Ax = b$ using $U$ or $L$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:---------|:---------|:---------|:---------| + /// | [spotrs] | [dpotrs] | [cpotrs] | [zpotrs] | + /// + /// [spotrs]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_gaf5cc1531aa5ffe706533fbca343d55dd.html + /// [dpotrs]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga167aa0166c4ce726385f65e4ab05e7c1.html + /// [cpotrs]: https://netlib.org/lapack/explore-html/d6/df6/group__complex_p_ocomputational_gad9052b4b70569dfd6e8943971c9b38b2.html + /// [zpotrs]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_gaa2116ea574b01efda584dff0b74c9fcd.html + /// fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; } diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 2c78cf82..d8d2d831 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -49,7 +49,8 @@ //! According to the property input metrix, several types of triangular factorization are used: //! //! - [Solve_] trait provides methods for LU-decomposition for general matrix. -//! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix +//! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. +//! - [Cholesky_] triat provides methods for Cholesky decomposition for symmetric/hermite positive dinite matrix. //! //! Eigenvalue Problem //! ------------------- From 7432f0237569327cec5561a463d2cfcb6b7ad099 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 14:31:13 +0900 Subject: [PATCH 066/165] Unify LAPACK correspondance table --- lax/src/cholesky.rs | 33 +++++++++------------------------ lax/src/solve.rs | 27 +++++++++++++++++++++++++-- lax/src/solveh.rs | 33 +++++++++------------------------ 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 04875b64..9b213246 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -22,14 +22,9 @@ pub trait Cholesky_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [spotrf] | [dpotrf] | [cpotrf] | [zpotrf] | - /// - /// [spotrf]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_gaaf31db7ab15b4f4ba527a3d31a15a58e.html - /// [dpotrf]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga2f55f604a6003d03b5cd4a0adcfb74d6.html - /// [cpotrf]: https://netlib.org/lapack/explore-html/d8/d6c/group__variants_p_ocomputational_ga4e85f48dbd837ccbbf76aa077f33de19.html - /// [zpotrf]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_ga93e22b682170873efb50df5a79c5e4eb.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | spotrf | dpotrf | cpotrf | zpotrf | /// fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; @@ -38,14 +33,9 @@ pub trait Cholesky_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [spotri] | [dpotri] | [cpotri] | [zpotri] | - /// - /// [spotri]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_ga4c381894bb34b1583fcc0dceafc5bea1.html - /// [dpotri]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga9dfc04beae56a3b1c1f75eebc838c14c.html - /// [cpotri]: https://netlib.org/lapack/explore-html/d6/df6/group__complex_p_ocomputational_ga52b8da4d314abefaee93dd5c1ed7739e.html - /// [zpotri]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_gaf37e3b8bbacd3332e83ffb3f1018bcf1.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | spotri | dpotri | cpotri | zpotri | /// fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; @@ -54,14 +44,9 @@ pub trait Cholesky_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [spotrs] | [dpotrs] | [cpotrs] | [zpotrs] | - /// - /// [spotrs]: https://netlib.org/lapack/explore-html/d8/db2/group__real_p_ocomputational_gaf5cc1531aa5ffe706533fbca343d55dd.html - /// [dpotrs]: https://netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga167aa0166c4ce726385f65e4ab05e7c1.html - /// [cpotrs]: https://netlib.org/lapack/explore-html/d6/df6/group__complex_p_ocomputational_gad9052b4b70569dfd6e8943971c9b38b2.html - /// [zpotrs]: https://netlib.org/lapack/explore-html/d3/d8d/group__complex16_p_ocomputational_gaa2116ea574b01efda584dff0b74c9fcd.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | spotrs | dpotrs | cpotrs | zpotrs | /// fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; } diff --git a/lax/src/solve.rs b/lax/src/solve.rs index ab601031..565aab59 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -32,12 +32,35 @@ pub trait Solve_: Scalar + Sized { /// - On this case, `return_code` in [Error::LapackComputationalFailure] means /// `return_code`-th diagonal element of $U$ becomes zero. /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetrf | dgetrf | cgetrf | zgetrf | + /// fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; - /// Compute inverse matrix $A^{-1}$ from the output of LU-factorization + /// Compute inverse matrix $A^{-1}$ from the output of LU-decomposition + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetri | dgetri | cgetri | zgetri | + /// fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()>; - /// Solve linear equations $Ax = b$ using the output of LU-factorization + /// Solve linear equations $Ax = b$ using the output of LU-decomposition + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetrs | dgetrs | cgetrs | zgetrs | + /// fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 76829982..5d1a3d02 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -24,14 +24,9 @@ pub trait Solveh_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [ssytrf] | [dsytrf] | [chetrf] | [zhetrf] | - /// - /// [ssytrf]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_ga12d2e56511cf7df066712c61d9acec45.html - /// [dsytrf]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_gad91bde1212277b3e909eb6af7f64858a.html - /// [chetrf]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_ga081dd1908e46d064c2bf0a1f6b664b86.html - /// [zhetrf]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_gadc84a5c9818ee12ea19944623131bd52.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | ssytrf | dsytrf | chetrf | zhetrf | /// fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; @@ -40,14 +35,9 @@ pub trait Solveh_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [ssytri] | [dsytri] | [chetri] | [zhetri] | - /// - /// [ssytri]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_gaef378ec0761234aac417f487b43b7a8b.html - /// [dsytri]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_ga75e09b4299b7955044a3bbf84c46b593.html - /// [chetri]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_gad87a6a1ac131c5635d47ac440e672bcf.html - /// [zhetri]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_ga4d9cfa0653de400029b8051996ce1e96.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | ssytri | dsytri | chetri | zhetri | /// fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()>; @@ -56,14 +46,9 @@ pub trait Solveh_: Sized { /// LAPACK correspondance /// ---------------------- /// - /// | f32 | f64 | c32 | c64 | - /// |:---------|:---------|:---------|:---------| - /// | [ssytrs] | [dsytrs] | [chetrs] | [zhetrs] | - /// - /// [ssytrs]: https://netlib.org/lapack/explore-html/d0/d14/group__real_s_ycomputational_gae20133a1119b69a7319783ff982c8c62.html - /// [dsytrs]: https://netlib.org/lapack/explore-html/d3/db6/group__double_s_ycomputational_ga6a223e61effac7232e98b422f147f032.html - /// [chetrs]: https://netlib.org/lapack/explore-html/d4/d74/group__complex_h_ecomputational_ga6f9d8da222ffaa7b7535efc922faa1dc.html - /// [zhetrs]: https://netlib.org/lapack/explore-html/d3/d80/group__complex16_h_ecomputational_gacf697e3bb72c5fd88cd90972999401dd.html + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | ssytrs | dsytrs | chetrs | zhetrs | /// fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; } From 4fe45f991ff83e107bb95307c735279d49dfd678 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 14:31:28 +0900 Subject: [PATCH 067/165] s/factorization/decomposition/g --- lax/src/lib.rs | 4 ++-- lax/src/solve.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index d8d2d831..d7b1be4a 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -4,7 +4,7 @@ //! ------------------------------- //! //! This crates provides LAPACK wrapper as `impl` of traits to base scalar types. -//! For example, LU factorization to double-precision matrix is provided like: +//! For example, LU decomposition to double-precision matrix is provided like: //! //! ```ignore //! impl Solve_ for f64 { @@ -46,7 +46,7 @@ //! Linear equation, Inverse matrix, Condition number //! -------------------------------------------------- //! -//! According to the property input metrix, several types of triangular factorization are used: +//! According to the property input metrix, several types of triangular decomposition are used: //! //! - [Solve_] trait provides methods for LU-decomposition for general matrix. //! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 565aab59..9f25c9bf 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -5,7 +5,7 @@ use num_traits::{ToPrimitive, Zero}; #[cfg_attr(doc, katexit::katexit)] /// Solve linear equations using LU-decomposition /// -/// For a given matrix $A$, LU decomposition is described as $PA = LU$ where +/// For a given matrix $A$, LU decomposition is described as $A = PLU$ where: /// /// - $L$ is lower matrix /// - $U$ is upper matrix @@ -15,15 +15,15 @@ use num_traits::{ToPrimitive, Zero}; /// /// 1. Factorize input matrix $A$ into $L$, $U$, and $P$. /// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ -/// using the output of LU factorization. +/// using the output of LU decomposition. /// pub trait Solve_: Scalar + Sized { - /// Computes the LU factorization of a general $m \times n$ matrix + /// Computes the LU decomposition of a general $m \times n$ matrix /// with partial pivoting with row interchanges. /// /// Output /// ------- - /// - $U$ and $L$ are stored in `a` after LU factorization has succeeded. + /// - $U$ and $L$ are stored in `a` after LU decomposition has succeeded. /// - $P$ is returned as [Pivot] /// /// Error From 0d89696569e247e9fc2e5e11445bafb2f4563d7f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 14:43:50 +0900 Subject: [PATCH 068/165] Update document of `Eig_` and `Eigh_` traits --- lax/src/eig.rs | 15 +++++++++++---- lax/src/eigh.rs | 24 ++++++++++++++++++++---- lax/src/lib.rs | 19 ++++--------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index d3c07222..deafba2f 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -1,12 +1,19 @@ -//! Eigenvalue decomposition for general matrices - use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -/// Wraps `*geev` for general matrices +#[cfg_attr(doc, katexit::katexit)] +/// Eigenvalue problem for general matrix pub trait Eig_: Scalar { - /// Calculate Right eigenvalue + /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:------|:------|:------|:------| + /// | sgeev | dgeev | cgeev | zgeev | + /// fn eig( calc_v: bool, l: MatrixLayout, diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index a9406ee6..055b2f47 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -1,12 +1,20 @@ -//! Eigenvalue decomposition for Symmetric/Hermite matrices - use super::*; use crate::{error::*, layout::MatrixLayout}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +#[cfg_attr(doc, katexit::katexit)] +/// Eigenvalue problem for symmetric/hermite matrix pub trait Eigh_: Scalar { - /// Wraps `*syev` for real and `*heev` for complex + /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:------|:------|:------|:------| + /// | ssyev | dsyev | cheev | zheev | + /// fn eigh( calc_eigenvec: bool, layout: MatrixLayout, @@ -14,7 +22,15 @@ pub trait Eigh_: Scalar { a: &mut [Self], ) -> Result>; - /// Wraps `*sygv` for real and `*hegv` for complex + /// Compute generalized right eigenvalue and eigenvectors $Ax = \lambda B x$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:------|:------|:------|:------| + /// | ssygv | dsygv | chegv | zhegv | + /// fn eigh_generalized( calc_eigenvec: bool, layout: MatrixLayout, diff --git a/lax/src/lib.rs b/lax/src/lib.rs index d7b1be4a..9a8bf1cf 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -55,22 +55,11 @@ //! Eigenvalue Problem //! ------------------- //! -//! Solve eigenvalue problem for a matrix $A$ +//! According to the property input metrix, +//! there are several types of eigenvalue problem API //! -//! $$ Av_i = \lambda_i v_i $$ -//! -//! or generalized eigenvalue problem -//! -//! $$ Av_i = \lambda_i B v_i $$ -//! -//! | matrix type | Eigenvalue (EV) | Generalized Eigenvalue Problem (EG) | -//! |:--------------------------------|:----------------|:------------------------------------| -//! | General (GE) |[eig] | - | -//! | Symmetric (SY) / Hermitian (HE) |[eigh] |[eigh_generalized] | -//! -//! [eig]: eig/trait.Eig_.html#tymethod.eig -//! [eigh]: eigh/trait.Eigh_.html#tymethod.eigh -//! [eigh_generalized]: eigh/trait.Eigh_.html#tymethod.eigh_generalized +//! - [Eig_] trait provides methods for eigenvalue problem for general matrix. +//! - [Eigh_] trait provides methods for eigenvalue problem for symmetric/hermite matrix. //! //! Singular Value Decomposition (SVD), Least square problem //! ---------------------------------------------------------- From 94d884346cbca4baed01f5800adbd61d43b33581 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 15:58:11 +0900 Subject: [PATCH 069/165] Update document about SVD --- lax/src/least_squares.rs | 6 +++++- lax/src/lib.rs | 15 +++++++-------- lax/src/svd.rs | 13 +++++++++++-- lax/src/svddc.rs | 11 +++++++++++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 6be44f33..5699257d 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -12,14 +12,18 @@ pub struct LeastSquaresOutput { pub rank: i32, } -/// Wraps `*gelsd` +#[cfg_attr(doc, katexit::katexit)] +/// Solve least square problem pub trait LeastSquaresSvdDivideConquer_: Scalar { + /// Compute a vector $x$ which minimizes Euclidian norm $\| Ax - b\|$ + /// for a given matrix $A$ and a vector $b$. fn least_squares( a_layout: MatrixLayout, a: &mut [Self], b: &mut [Self], ) -> Result>; + /// Solve least square problems $\argmin_X \| AX - B\|$ fn least_squares_nrhs( a_layout: MatrixLayout, a: &mut [Self], diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 9a8bf1cf..47f3eedc 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -61,16 +61,15 @@ //! - [Eig_] trait provides methods for eigenvalue problem for general matrix. //! - [Eigh_] trait provides methods for eigenvalue problem for symmetric/hermite matrix. //! -//! Singular Value Decomposition (SVD), Least square problem -//! ---------------------------------------------------------- +//! Singular Value Decomposition +//! ----------------------------- //! -//! | matrix type | Singular Value Decomposition (SVD) | SVD with divided-and-conquer (SDD) | Least square problem (LSD) | -//! |:-------------|:-----------------------------------|:-----------------------------------|:---------------------------| -//! | General (GE) | [svd] | [svddc] | [least_squares] | +//! - [SVD_] trait provides methods for singular value decomposition for general matrix +//! - [SVDDC_] trait provides methods for singular value decomposition for general matrix +//! with divided-and-conquer algorithm +//! - [LeastSquaresSvdDivideConquer_] trait provides methods +//! for solving least square problem by SVD //! -//! [svd]: svd/trait.SVD_.html#tymethod.svd -//! [svddc]: svddck/trait.SVDDC_.html#tymethod.svddc -//! [least_squares]: least_squares/trait.LeastSquaresSvdDivideConquer_.html#tymethod.least_squares #[cfg(any(feature = "intel-mkl-system", feature = "intel-mkl-static"))] extern crate intel_mkl_src as _src; diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 0a509a0e..0f0f81ab 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -14,9 +14,18 @@ pub struct SVDOutput { pub vt: Option>, } -/// Wraps `*gesvd` +#[cfg_attr(doc, katexit::katexit)] +/// Singular value decomposition pub trait SVD_: Scalar { - /// Calculate singular value decomposition $ A = U \Sigma V^T $ + /// Compute singular value decomposition $A = U \Sigma V^T$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgesvd | dgesvd | cgesvd | zgesvd | + /// fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self]) -> Result>; } diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index bb59348f..55ddfd46 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -2,7 +2,18 @@ use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +#[cfg_attr(doc, katexit::katexit)] +/// Singular value decomposition with divide-and-conquer method pub trait SVDDC_: Scalar { + /// Compute singular value decomposition $A = U \Sigma V^T$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgesdd | dgesdd | cgesdd | zgesdd | + /// fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; } From eaec8e9dd02af794fefa8a90960b7682846365e8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 17:09:55 +0900 Subject: [PATCH 070/165] Remove KaTeX auto render header for lax --- lax/Cargo.toml | 3 --- lax/katex-header.html | 16 ---------------- ndarray-linalg/Cargo.toml | 3 --- ndarray-linalg/katex-header.html | 16 ---------------- 4 files changed, 38 deletions(-) delete mode 100644 lax/katex-header.html delete mode 100644 ndarray-linalg/katex-header.html diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 26a6f35e..f46bdbb8 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -51,6 +51,3 @@ version = "0.10.4" optional = true default-features = false features = ["cblas"] - -[package.metadata.docs.rs] -rustdoc-args = ["--html-in-header", "katex-header.html"] diff --git a/lax/katex-header.html b/lax/katex-header.html deleted file mode 100644 index 6e10c052..00000000 --- a/lax/katex-header.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 6147a2eb..4ef8c17d 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -78,6 +78,3 @@ harness = false [[bench]] name = "solveh" harness = false - -[package.metadata.docs.rs] -rustdoc-args = ["--html-in-header", "katex-header.html"] diff --git a/ndarray-linalg/katex-header.html b/ndarray-linalg/katex-header.html deleted file mode 100644 index 6e10c052..00000000 --- a/ndarray-linalg/katex-header.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - From 25703b83a1ebb6a831a4e727658550a4881e0caf Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 17:20:11 +0900 Subject: [PATCH 071/165] Add katexit for ndarray-linalg --- ndarray-linalg/Cargo.toml | 1 + ndarray-linalg/src/eig.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 4ef8c17d..bfab605f 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -30,6 +30,7 @@ intel-mkl-system = ["lax/intel-mkl-system"] [dependencies] cauchy = "0.4.0" +katexit = "0.1.2" num-complex = "0.4.0" num-traits = "0.2.14" rand = "0.8.3" diff --git a/ndarray-linalg/src/eig.rs b/ndarray-linalg/src/eig.rs index 833f6d49..bde5bd7d 100644 --- a/ndarray-linalg/src/eig.rs +++ b/ndarray-linalg/src/eig.rs @@ -5,6 +5,7 @@ use crate::layout::*; use crate::types::*; use ndarray::*; +#[cfg_attr(doc, katexit::katexit)] /// Eigenvalue decomposition of general matrix reference pub trait Eig { /// EigVec is the right eivenvector From c25cc23da52b146e8579eed6fee3f3866b3a1ca4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 17:20:54 +0900 Subject: [PATCH 072/165] Drop RUSTDOCFLAGS from GitHub Pages --- .github/workflows/gh-pages.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 90ad7b90..f8509459 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -40,8 +40,6 @@ jobs: with: command: doc args: --no-deps - env: - RUSTDOCFLAGS: "--html-in-header ndarray-linalg/katex-header.html" - name: Setup Pages uses: actions/configure-pages@v2 From f7356c79b0b6666c867e80839d08d3dacdbe042a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 17:38:33 +0900 Subject: [PATCH 073/165] Remove out-dated KaTeX part in README --- README.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/README.md b/README.md index ce78e31c..530cc589 100644 --- a/README.md +++ b/README.md @@ -86,25 +86,6 @@ Only x86_64 system is supported currently. |Netlib |✔️ |- |- | |Intel MKL|✔️ |✔️ |✔️ | -Generate document with KaTeX ------------------------------- - -You need to set `RUSTDOCFLAGS` explicitly: - -```shell -RUSTDOCFLAGS="--html-in-header katex-header.html" cargo doc --no-deps -``` - -This **only** works for `--no-deps` build because `katex-header.html` does not exists for dependent crates. -If you wish to set `RUSTDOCFLAGS` automatically in this crate, you can put [.cargo/config](https://doc.rust-lang.org/cargo/reference/config.html): - -```toml -[build] -rustdocflags = ["--html-in-header", "katex-header.html"] -``` - -But, be sure that this works only for `--no-deps`. `cargo doc` will fail with this `.cargo/config`. - License -------- From ecd09eb2cd82b5168dc2fd791d13f8249a878895 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 8 Sep 2022 23:48:46 +0900 Subject: [PATCH 074/165] Split lax::alloc submodule --- lax/src/alloc.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ lax/src/lib.rs | 60 ++---------------------------------------------- 2 files changed, 62 insertions(+), 58 deletions(-) create mode 100644 lax/src/alloc.rs diff --git a/lax/src/alloc.rs b/lax/src/alloc.rs new file mode 100644 index 00000000..62116f85 --- /dev/null +++ b/lax/src/alloc.rs @@ -0,0 +1,60 @@ +use cauchy::*; +use std::mem::MaybeUninit; + +/// Helper for getting pointer of slice +pub(crate) trait AsPtr: Sized { + type Elem; + fn as_ptr(vec: &[Self]) -> *const Self::Elem; + fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem; +} + +macro_rules! impl_as_ptr { + ($target:ty, $elem:ty) => { + impl AsPtr for $target { + type Elem = $elem; + fn as_ptr(vec: &[Self]) -> *const Self::Elem { + vec.as_ptr() as *const _ + } + fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem { + vec.as_mut_ptr() as *mut _ + } + } + }; +} +impl_as_ptr!(i32, i32); +impl_as_ptr!(f32, f32); +impl_as_ptr!(f64, f64); +impl_as_ptr!(c32, lapack_sys::__BindgenComplex); +impl_as_ptr!(c64, lapack_sys::__BindgenComplex); +impl_as_ptr!(MaybeUninit, i32); +impl_as_ptr!(MaybeUninit, f32); +impl_as_ptr!(MaybeUninit, f64); +impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); +impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); + +pub(crate) trait VecAssumeInit { + type Target; + unsafe fn assume_init(self) -> Self::Target; +} + +impl VecAssumeInit for Vec> { + type Target = Vec; + unsafe fn assume_init(self) -> Self::Target { + // FIXME use Vec::into_raw_parts instead after stablized + // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts + let mut me = std::mem::ManuallyDrop::new(self); + Vec::from_raw_parts(me.as_mut_ptr() as *mut T, me.len(), me.capacity()) + } +} + +/// Create a vector without initialization +/// +/// Safety +/// ------ +/// - Memory is not initialized. Do not read the memory before write. +/// +pub(crate) unsafe fn vec_uninit(n: usize) -> Vec> { + let mut v = Vec::with_capacity(n); + v.set_len(n); + v +} diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 47f3eedc..8f697fbc 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -84,6 +84,7 @@ pub mod error; pub mod flags; pub mod layout; +mod alloc; mod cholesky; mod eig; mod eigh; @@ -113,6 +114,7 @@ pub use self::svddc::*; pub use self::triangular::*; pub use self::tridiagonal::*; +use self::alloc::*; use cauchy::*; use std::mem::MaybeUninit; @@ -140,61 +142,3 @@ impl Lapack for f32 {} impl Lapack for f64 {} impl Lapack for c32 {} impl Lapack for c64 {} - -/// Helper for getting pointer of slice -pub(crate) trait AsPtr: Sized { - type Elem; - fn as_ptr(vec: &[Self]) -> *const Self::Elem; - fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem; -} - -macro_rules! impl_as_ptr { - ($target:ty, $elem:ty) => { - impl AsPtr for $target { - type Elem = $elem; - fn as_ptr(vec: &[Self]) -> *const Self::Elem { - vec.as_ptr() as *const _ - } - fn as_mut_ptr(vec: &mut [Self]) -> *mut Self::Elem { - vec.as_mut_ptr() as *mut _ - } - } - }; -} -impl_as_ptr!(i32, i32); -impl_as_ptr!(f32, f32); -impl_as_ptr!(f64, f64); -impl_as_ptr!(c32, lapack_sys::__BindgenComplex); -impl_as_ptr!(c64, lapack_sys::__BindgenComplex); -impl_as_ptr!(MaybeUninit, i32); -impl_as_ptr!(MaybeUninit, f32); -impl_as_ptr!(MaybeUninit, f64); -impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); -impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); - -pub(crate) trait VecAssumeInit { - type Target; - unsafe fn assume_init(self) -> Self::Target; -} - -impl VecAssumeInit for Vec> { - type Target = Vec; - unsafe fn assume_init(self) -> Self::Target { - // FIXME use Vec::into_raw_parts instead after stablized - // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts - let mut me = std::mem::ManuallyDrop::new(self); - Vec::from_raw_parts(me.as_mut_ptr() as *mut T, me.len(), me.capacity()) - } -} - -/// Create a vector without initialization -/// -/// Safety -/// ------ -/// - Memory is not initialized. Do not read the memory before write. -/// -unsafe fn vec_uninit(n: usize) -> Vec> { - let mut v = Vec::with_capacity(n); - v.set_len(n); - v -} From 4737b9c328863b8e9e1a286716c6cfa755aadbff Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 9 Sep 2022 00:06:36 +0900 Subject: [PATCH 075/165] Drop unsafe from vec_uninit definition --- lax/src/alloc.rs | 6 ++++-- lax/src/eig.rs | 22 +++++++++++----------- lax/src/eigh.rs | 12 ++++++------ lax/src/layout.rs | 2 +- lax/src/least_squares.rs | 8 ++++---- lax/src/opnorm.rs | 2 +- lax/src/qr.rs | 6 +++--- lax/src/rcond.rs | 8 ++++---- lax/src/solve.rs | 4 ++-- lax/src/solveh.rs | 6 +++--- lax/src/svd.rs | 10 +++++----- lax/src/svddc.rs | 16 ++++++++-------- lax/src/tridiagonal.rs | 8 ++++---- 13 files changed, 56 insertions(+), 54 deletions(-) diff --git a/lax/src/alloc.rs b/lax/src/alloc.rs index 62116f85..5872bab4 100644 --- a/lax/src/alloc.rs +++ b/lax/src/alloc.rs @@ -53,8 +53,10 @@ impl VecAssumeInit for Vec> { /// ------ /// - Memory is not initialized. Do not read the memory before write. /// -pub(crate) unsafe fn vec_uninit(n: usize) -> Vec> { +pub(crate) fn vec_uninit(n: usize) -> Vec> { let mut v = Vec::with_capacity(n); - v.set_len(n); + unsafe { + v.set_len(n); + } v } diff --git a/lax/src/eig.rs b/lax/src/eig.rs index deafba2f..6fcbc26a 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -48,13 +48,13 @@ macro_rules! impl_eig_complex { } else { (JobEv::None, JobEv::None) }; - let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; - let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; + let mut eigs: Vec> = vec_uninit(n as usize); + let mut rwork: Vec> = vec_uninit(2 * n as usize); let mut vl: Option>> = - jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); + jobvl.then(|| vec_uninit((n * n) as usize)); let mut vr: Option>> = - jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); + jobvr.then(|| vec_uninit((n * n) as usize)); // calc work size let mut info = 0; @@ -81,7 +81,7 @@ macro_rules! impl_eig_complex { // actal ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); let lwork = lwork as i32; unsafe { $ev( @@ -156,13 +156,13 @@ macro_rules! impl_eig_real { } else { (JobEv::None, JobEv::None) }; - let mut eig_re: Vec> = unsafe { vec_uninit(n as usize) }; - let mut eig_im: Vec> = unsafe { vec_uninit(n as usize) }; + let mut eig_re: Vec> = vec_uninit(n as usize); + let mut eig_im: Vec> = vec_uninit(n as usize); let mut vl: Option>> = - jobvl.then(|| unsafe { vec_uninit((n * n) as usize) }); + jobvl.then(|| vec_uninit((n * n) as usize)); let mut vr: Option>> = - jobvr.then(|| unsafe { vec_uninit((n * n) as usize) }); + jobvr.then(|| vec_uninit((n * n) as usize)); // calc work size let mut info = 0; @@ -189,7 +189,7 @@ macro_rules! impl_eig_real { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); let lwork = lwork as i32; unsafe { $ev( @@ -244,7 +244,7 @@ macro_rules! impl_eig_real { let n = n as usize; let v = vr.or(vl).unwrap(); - let mut eigvecs: Vec> = unsafe { vec_uninit(n * n) }; + let mut eigvecs: Vec> = vec_uninit(n * n); let mut col = 0; while col < n { if eig_im[col] == 0. { diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 055b2f47..54af63f7 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -58,10 +58,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { JobEv::All } else { JobEv::None }; - let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; + let mut eigs: Vec> = vec_uninit(n as usize); $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit(3 * n as usize - 2 as usize) }; + let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2 as usize); )* // calc work size @@ -85,7 +85,7 @@ macro_rules! impl_eigh { // actual ev let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); let lwork = lwork as i32; unsafe { $ev( @@ -117,10 +117,10 @@ macro_rules! impl_eigh { assert_eq!(layout.len(), layout.lda()); let n = layout.len(); let jobz = if calc_v { JobEv::All } else { JobEv::None }; - let mut eigs: Vec> = unsafe { vec_uninit(n as usize) }; + let mut eigs: Vec> = vec_uninit(n as usize); $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit(3 * n as usize - 2) }; + let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2); )* // calc work size @@ -147,7 +147,7 @@ macro_rules! impl_eigh { // actual evg let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); let lwork = lwork as i32; unsafe { $evg( diff --git a/lax/src/layout.rs b/lax/src/layout.rs index e695d8e7..28b35122 100644 --- a/lax/src/layout.rs +++ b/lax/src/layout.rs @@ -202,7 +202,7 @@ pub fn transpose(layout: MatrixLayout, input: &[T]) -> (MatrixLayout, V let n = n as usize; assert_eq!(input.len(), m * n); - let mut out: Vec> = unsafe { vec_uninit(m * n) }; + let mut out: Vec> = vec_uninit(m * n); match layout { MatrixLayout::C { .. } => { diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 5699257d..1bfd4d37 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -91,7 +91,7 @@ macro_rules! impl_least_squares { }; let rcond: Self::Real = -1.; - let mut singular_values: Vec> = unsafe { vec_uninit( k as usize) }; + let mut singular_values: Vec> = vec_uninit( k as usize); let mut rank: i32 = 0; // eval work size @@ -124,12 +124,12 @@ macro_rules! impl_least_squares { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); let liwork = iwork_size[0].to_usize().unwrap(); - let mut iwork: Vec> = unsafe { vec_uninit(liwork) }; + let mut iwork: Vec> = vec_uninit(liwork); $( let lrwork = $rwork[0].to_usize().unwrap(); - let mut $rwork: Vec> = unsafe { vec_uninit(lrwork) }; + let mut $rwork: Vec> = vec_uninit(lrwork); )* unsafe { $gelsd( diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index fca7704c..60933489 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -19,7 +19,7 @@ macro_rules! impl_opnorm { MatrixLayout::C { .. } => t.transpose(), }; let mut work: Vec> = if matches!(t, NormType::Infinity) { - unsafe { vec_uninit(m as usize) } + vec_uninit(m as usize) } else { Vec::new() }; diff --git a/lax/src/qr.rs b/lax/src/qr.rs index 553bb606..bdfb7571 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -25,7 +25,7 @@ macro_rules! impl_qr { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut tau = unsafe { vec_uninit(k as usize) }; + let mut tau = vec_uninit(k as usize); // eval work size let mut info = 0; @@ -62,7 +62,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { match l { MatrixLayout::F { .. } => { @@ -136,7 +136,7 @@ macro_rules! impl_qr { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { match l { MatrixLayout::F { .. } => $gqr( diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index dfc8a941..6cc72749 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -17,8 +17,8 @@ macro_rules! impl_rcond_real { let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit(4 * n as usize) }; - let mut iwork: Vec> = unsafe { vec_uninit(n as usize) }; + let mut work: Vec> = vec_uninit(4 * n as usize); + let mut iwork: Vec> = vec_uninit(n as usize); let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, @@ -54,8 +54,8 @@ macro_rules! impl_rcond_complex { let (n, _) = l.size(); let mut rcond = Self::Real::zero(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit(2 * n as usize) }; - let mut rwork: Vec> = unsafe { vec_uninit(2 * n as usize) }; + let mut work: Vec> = vec_uninit(2 * n as usize); + let mut rwork: Vec> = vec_uninit(2 * n as usize); let norm_type = match l { MatrixLayout::C { .. } => NormType::Infinity, MatrixLayout::F { .. } => NormType::One, diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 9f25c9bf..d0f764fd 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -75,7 +75,7 @@ macro_rules! impl_solve { return Ok(Vec::new()); } let k = ::std::cmp::min(row, col); - let mut ipiv = unsafe { vec_uninit(k as usize) }; + let mut ipiv = vec_uninit(k as usize); let mut info = 0; unsafe { $getrf( @@ -117,7 +117,7 @@ macro_rules! impl_solve { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { $getri( &l.len(), diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 5d1a3d02..bbc6f363 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -58,7 +58,7 @@ macro_rules! impl_solveh { impl Solveh_ for $scalar { fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result { let (n, _) = l.size(); - let mut ipiv = unsafe { vec_uninit(n as usize) }; + let mut ipiv = vec_uninit(n as usize); if n == 0 { return Ok(Vec::new()); } @@ -82,7 +82,7 @@ macro_rules! impl_solveh { // actual let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit(lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { $trf( uplo.as_ptr(), @@ -103,7 +103,7 @@ macro_rules! impl_solveh { fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { let (n, _) = l.size(); let mut info = 0; - let mut work: Vec> = unsafe { vec_uninit(n as usize) }; + let mut work: Vec> = vec_uninit(n as usize); unsafe { $tri( uplo.as_ptr(), diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 0f0f81ab..0d9bd353 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -51,23 +51,23 @@ macro_rules! impl_svd { let m = l.lda(); let mut u = match ju { - JobSvd::All => Some(unsafe { vec_uninit( (m * m) as usize) }), + JobSvd::All => Some(vec_uninit( (m * m) as usize)), JobSvd::None => None, _ => unimplemented!("SVD with partial vector output is not supported yet") }; let n = l.len(); let mut vt = match jvt { - JobSvd::All => Some(unsafe { vec_uninit( (n * n) as usize) }), + JobSvd::All => Some(vec_uninit( (n * n) as usize)), JobSvd::None => None, _ => unimplemented!("SVD with partial vector output is not supported yet") }; let k = std::cmp::min(m, n); - let mut s = unsafe { vec_uninit( k as usize) }; + let mut s = vec_uninit( k as usize); $( - let mut $rwork_ident: Vec> = unsafe { vec_uninit( 5 * k as usize) }; + let mut $rwork_ident: Vec> = vec_uninit(5 * k as usize); )* // eval work size @@ -96,7 +96,7 @@ macro_rules! impl_svd { // calc let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit( lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { $gesvd( ju.as_ptr(), diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index 55ddfd46..928fd44c 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -30,7 +30,7 @@ macro_rules! impl_svddc { let m = l.lda(); let n = l.len(); let k = m.min(n); - let mut s = unsafe { vec_uninit( k as usize) }; + let mut s = vec_uninit(k as usize); let (u_col, vt_row) = match jobz { JobSvd::All | JobSvd::None => (m, n), @@ -38,12 +38,12 @@ macro_rules! impl_svddc { }; let (mut u, mut vt) = match jobz { JobSvd::All => ( - Some(unsafe { vec_uninit( (m * m) as usize) }), - Some(unsafe { vec_uninit( (n * n) as usize) }), + Some(vec_uninit((m * m) as usize)), + Some(vec_uninit((n * n) as usize)), ), JobSvd::Some => ( - Some(unsafe { vec_uninit( (m * u_col) as usize) }), - Some(unsafe { vec_uninit( (n * vt_row) as usize) }), + Some(vec_uninit((m * u_col) as usize)), + Some(vec_uninit((n * vt_row) as usize)), ), JobSvd::None => (None, None), }; @@ -55,12 +55,12 @@ macro_rules! impl_svddc { JobSvd::None => 7 * mn, _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), }; - let mut $rwork_ident: Vec> = unsafe { vec_uninit( lrwork) }; + let mut $rwork_ident: Vec> = vec_uninit(lrwork); )* // eval work size let mut info = 0; - let mut iwork: Vec> = unsafe { vec_uninit( 8 * k as usize) }; + let mut iwork: Vec> = vec_uninit(8 * k as usize); let mut work_size = [Self::zero()]; unsafe { $gesdd( @@ -85,7 +85,7 @@ macro_rules! impl_svddc { // do svd let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = unsafe { vec_uninit( lwork) }; + let mut work: Vec> = vec_uninit(lwork); unsafe { $gesdd( jobz.as_ptr(), diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal.rs index ef8dfdf6..3d28c63a 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal.rs @@ -152,8 +152,8 @@ macro_rules! impl_tridiagonal { impl Tridiagonal_ for $scalar { fn lu_tridiagonal(mut a: Tridiagonal) -> Result> { let (n, _) = a.l.size(); - let mut du2 = unsafe { vec_uninit( (n - 2) as usize) }; - let mut ipiv = unsafe { vec_uninit( n as usize) }; + let mut du2 = vec_uninit( (n - 2) as usize); + let mut ipiv = vec_uninit( n as usize); // We have to calc one-norm before LU factorization let a_opnorm_one = a.opnorm_one(); let mut info = 0; @@ -182,9 +182,9 @@ macro_rules! impl_tridiagonal { fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { let (n, _) = lu.a.l.size(); let ipiv = &lu.ipiv; - let mut work: Vec> = unsafe { vec_uninit( 2 * n as usize) }; + let mut work: Vec> = vec_uninit(2 * n as usize); $( - let mut $iwork: Vec> = unsafe { vec_uninit( n as usize) }; + let mut $iwork: Vec> = vec_uninit(n as usize); )* let mut rcond = Self::Real::zero(); let mut info = 0; From ccf9dfd1e0aab8cdb5489e692d4458ccf03adcce Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Sep 2022 15:20:00 +0900 Subject: [PATCH 076/165] Upgrade intel-mkl-src to 0.8.1 --- lax/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index f46bdbb8..d4e7773c 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -25,7 +25,7 @@ netlib-system = ["netlib-src/system"] openblas-static = ["openblas-src/static"] openblas-system = ["openblas-src/system"] -intel-mkl-static = ["intel-mkl-src/mkl-static-lp64-seq", "intel-mkl-src/download"] +intel-mkl-static = ["intel-mkl-src/mkl-static-lp64-seq"] intel-mkl-system = ["intel-mkl-src/mkl-dynamic-lp64-seq"] [dependencies] @@ -36,7 +36,7 @@ lapack-sys = "0.14.0" katexit = "0.1.2" [dependencies.intel-mkl-src] -version = "0.7.0" +version = "0.8.1" default-features = false optional = true From 0d2fc06a227f29f354cb2b92db7ff111cd6a8bf9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 16 Sep 2022 22:23:04 +0900 Subject: [PATCH 077/165] ndarray_linalg::generate::random_*_using API for using given RNG --- ndarray-linalg/src/generate.rs | 100 ++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/ndarray-linalg/src/generate.rs b/ndarray-linalg/src/generate.rs index 67def14a..5646c808 100644 --- a/ndarray-linalg/src/generate.rs +++ b/ndarray-linalg/src/generate.rs @@ -22,7 +22,10 @@ where a } -/// Generate random array +/// Generate random array with given shape +/// +/// - This function uses [rand::thread_rng]. +/// See [random_using] for using another RNG pub fn random(sh: Sh) -> ArrayBase where A: Scalar, @@ -31,29 +34,77 @@ where Sh: ShapeBuilder, { let mut rng = thread_rng(); - ArrayBase::from_shape_fn(sh, |_| A::rand(&mut rng)) + random_using(sh, &mut rng) +} + +/// Generate random array with given RNG +/// +/// - See [random] for using default RNG +pub fn random_using(sh: Sh, rng: &mut R) -> ArrayBase +where + A: Scalar, + S: DataOwned, + D: Dimension, + Sh: ShapeBuilder, + R: Rng, +{ + ArrayBase::from_shape_fn(sh, |_| A::rand(rng)) } /// Generate random unitary matrix using QR decomposition /// -/// Be sure that this it **NOT** a uniform distribution. Use it only for test purpose. +/// - Be sure that this it **NOT** a uniform distribution. +/// Use it only for test purpose. +/// - This function uses [rand::thread_rng]. +/// See [random_unitary_using] for using another RNG. pub fn random_unitary(n: usize) -> Array2 where A: Scalar + Lapack, { - let a: Array2 = random((n, n)); + let mut rng = thread_rng(); + random_unitary_using(n, &mut rng) +} + +/// Generate random unitary matrix using QR decomposition with given RNG +/// +/// - Be sure that this it **NOT** a uniform distribution. +/// Use it only for test purpose. +/// - See [random_unitary] for using default RNG. +pub fn random_unitary_using(n: usize, rng: &mut R) -> Array2 +where + A: Scalar + Lapack, + R: Rng, +{ + let a: Array2 = random_using((n, n), rng); let (q, _r) = a.qr_into().unwrap(); q } /// Generate random regular matrix /// -/// Be sure that this it **NOT** a uniform distribution. Use it only for test purpose. +/// - Be sure that this it **NOT** a uniform distribution. +/// Use it only for test purpose. +/// - This function uses [rand::thread_rng]. +/// See [random_regular_using] for using another RNG. pub fn random_regular(n: usize) -> Array2 where A: Scalar + Lapack, { - let a: Array2 = random((n, n)); + let mut rng = rand::thread_rng(); + random_regular_using(n, &mut rng) +} + +/// Generate random regular matrix with given RNG +/// +/// - Be sure that this it **NOT** a uniform distribution. +/// Use it only for test purpose. +/// - See [random_regular] for using default RNG. +pub fn random_regular_using(n: usize, rng: &mut R) -> Array2 +where + A: Scalar + Lapack, + R: Rng, +{ + let a: Array2 = random_using((n, n), rng); let (q, mut r) = a.qr_into().unwrap(); for i in 0..n { r[(i, i)] = A::one() + A::from_real(r[(i, i)].abs()); @@ -62,12 +113,28 @@ where } /// Random Hermite matrix +/// +/// - This function uses [rand::thread_rng]. +/// See [random_hermite_using] for using another RNG. pub fn random_hermite(n: usize) -> ArrayBase where A: Scalar, S: DataOwned + DataMut, { - let mut a: ArrayBase = random((n, n)); + let mut rng = rand::thread_rng(); + random_hermite_using(n, &mut rng) +} + +/// Random Hermite matrix with given RNG +/// +/// - See [random_hermite] for using default RNG. +pub fn random_hermite_using(n: usize, rng: &mut R) -> ArrayBase +where + A: Scalar, + S: DataOwned + DataMut, + R: Rng, +{ + let mut a: ArrayBase = random_using((n, n), rng); for i in 0..n { a[(i, i)] = a[(i, i)] + a[(i, i)].conj(); for j in (i + 1)..n { @@ -80,13 +147,30 @@ where /// Random Hermite Positive-definite matrix /// /// - Eigenvalue of matrix must be larger than 1 (thus non-singular) +/// - This function uses [rand::thread_rng]. +/// See [random_hpd_using] for using another RNG. /// pub fn random_hpd(n: usize) -> ArrayBase where A: Scalar, S: DataOwned + DataMut, { - let a: Array2 = random((n, n)); + let mut rng = rand::thread_rng(); + random_hpd_using(n, &mut rng) +} + +/// Random Hermite Positive-definite matrix with given RNG +/// +/// - Eigenvalue of matrix must be larger than 1 (thus non-singular) +/// - See [random_hpd] for using default RNG. +/// +pub fn random_hpd_using(n: usize, rng: &mut R) -> ArrayBase +where + A: Scalar, + S: DataOwned + DataMut, + R: Rng, +{ + let a: Array2 = random_using((n, n), rng); let ah: Array2 = conjugate(&a); ArrayBase::eye(n) + &ah.dot(&a) } From d10cc6a316db3ee5825479760b31bc901dcd6441 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 01:27:35 +0900 Subject: [PATCH 078/165] Fix renamed enum in #331 --- ndarray-linalg/benches/svd.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ndarray-linalg/benches/svd.rs b/ndarray-linalg/benches/svd.rs index a1870a8f..02ea5806 100644 --- a/ndarray-linalg/benches/svd.rs +++ b/ndarray-linalg/benches/svd.rs @@ -62,37 +62,37 @@ fn svddc_small(c: &mut Criterion) { group.bench_with_input(BenchmarkId::new("C", n), &n, |b, n| { let a: Array2 = random((*n, *n)); b.iter(|| { - let _ = a.svddc(UVTFlag::None).unwrap(); + let _ = a.svddc(JobSvd::None).unwrap(); }) }); group.bench_with_input(BenchmarkId::new("F", n), &n, |b, n| { let a: Array2 = random((*n, *n).f()); b.iter(|| { - let _ = a.svddc(UVTFlag::None).unwrap(); + let _ = a.svddc(JobSvd::None).unwrap(); }) }); group.bench_with_input(BenchmarkId::new("some/C", n), &n, |b, n| { let a: Array2 = random((*n, *n)); b.iter(|| { - let _ = a.svddc(UVTFlag::Some).unwrap(); + let _ = a.svddc(JobSvd::Some).unwrap(); }) }); group.bench_with_input(BenchmarkId::new("some/F", n), &n, |b, n| { let a: Array2 = random((*n, *n).f()); b.iter(|| { - let _ = a.svddc(UVTFlag::Some).unwrap(); + let _ = a.svddc(JobSvd::Some).unwrap(); }) }); group.bench_with_input(BenchmarkId::new("full/C", n), &n, |b, n| { let a: Array2 = random((*n, *n)); b.iter(|| { - let _ = a.svddc(UVTFlag::Full).unwrap(); + let _ = a.svddc(JobSvd::All).unwrap(); }) }); group.bench_with_input(BenchmarkId::new("full/F", n), &n, |b, n| { let a: Array2 = random((*n, *n).f()); b.iter(|| { - let _ = a.svddc(UVTFlag::Full).unwrap(); + let _ = a.svddc(JobSvd::All).unwrap(); }) }); } From f5c067dc3850379857a7d8b6077de30fd2447a84 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 01:29:07 +0900 Subject: [PATCH 079/165] Add cargo-check on CI --- .github/workflows/rust.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a557c8a1..199b9cad 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,6 +16,15 @@ jobs: command: fmt args: -- --check + check: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: check + args: --all-targets + clippy: runs-on: ubuntu-22.04 steps: From b1fe48f9be91cea1520f5b4605958640cf88f1b8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 00:50:34 +0900 Subject: [PATCH 080/165] Add rand_pcg as dev-dependency --- ndarray-linalg/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index bfab605f..c7006cb2 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -51,6 +51,7 @@ paste = "1.0.5" criterion = "0.3.4" # Keep the same version as ndarray's dependency! approx = { version = "0.4.0", features = ["num-complex"] } +rand_pcg = "0.3.1" [[bench]] name = "truncated_eig" From 0470a82fc8dc85b0d5a4e088ac8079d2b9fe4502 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 00:56:58 +0900 Subject: [PATCH 081/165] Use random_*_using API in ndarray_linalg/tests --- ndarray-linalg/tests/arnoldi.rs | 20 +++++--- ndarray-linalg/tests/cholesky.rs | 17 +++--- ndarray-linalg/tests/convert.rs | 3 +- ndarray-linalg/tests/det.rs | 40 +++++++++++---- ndarray-linalg/tests/deth.rs | 3 +- ndarray-linalg/tests/eigh.rs | 12 +++-- ndarray-linalg/tests/householder.rs | 9 ++-- ndarray-linalg/tests/inner.rs | 3 +- ndarray-linalg/tests/inv.rs | 6 ++- ndarray-linalg/tests/least_squares.rs | 27 ++++++---- ndarray-linalg/tests/least_squares_nrhs.rs | 60 +++++++++++++--------- ndarray-linalg/tests/mgs.rs | 9 ++-- ndarray-linalg/tests/normalize.rs | 6 ++- ndarray-linalg/tests/qr.rs | 24 ++++++--- ndarray-linalg/tests/solve.rs | 56 ++++++++++++-------- ndarray-linalg/tests/solveh.rs | 30 ++++++----- ndarray-linalg/tests/svd.rs | 6 ++- ndarray-linalg/tests/svddc.rs | 18 ++++--- ndarray-linalg/tests/trace.rs | 3 +- ndarray-linalg/tests/triangular.rs | 60 +++++++++++++--------- ndarray-linalg/tests/tridiagonal.rs | 21 +++++--- 21 files changed, 277 insertions(+), 156 deletions(-) diff --git a/ndarray-linalg/tests/arnoldi.rs b/ndarray-linalg/tests/arnoldi.rs index bbc15553..dd56e0a0 100644 --- a/ndarray-linalg/tests/arnoldi.rs +++ b/ndarray-linalg/tests/arnoldi.rs @@ -3,8 +3,9 @@ use ndarray_linalg::{krylov::*, *}; #[test] fn aq_qh_mgs() { - let a: Array2 = random((5, 5)); - let v: Array1 = random(5); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((5, 5), &mut rng); + let v: Array1 = random_using(5, &mut rng); let (q, h) = arnoldi_mgs(a.clone(), v, 1e-9); println!("A = \n{:?}", &a); println!("Q = \n{:?}", &q); @@ -18,8 +19,9 @@ fn aq_qh_mgs() { #[test] fn aq_qh_householder() { - let a: Array2 = random((5, 5)); - let v: Array1 = random(5); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((5, 5), &mut rng); + let v: Array1 = random_using(5, &mut rng); let (q, h) = arnoldi_mgs(a.clone(), v, 1e-9); println!("A = \n{:?}", &a); println!("Q = \n{:?}", &q); @@ -33,8 +35,9 @@ fn aq_qh_householder() { #[test] fn aq_qh_mgs_complex() { - let a: Array2 = random((5, 5)); - let v: Array1 = random(5); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((5, 5), &mut rng); + let v: Array1 = random_using(5, &mut rng); let (q, h) = arnoldi_mgs(a.clone(), v, 1e-9); println!("A = \n{:?}", &a); println!("Q = \n{:?}", &q); @@ -48,8 +51,9 @@ fn aq_qh_mgs_complex() { #[test] fn aq_qh_householder_complex() { - let a: Array2 = random((5, 5)); - let v: Array1 = random(5); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((5, 5), &mut rng); + let v: Array1 = random_using(5, &mut rng); let (q, h) = arnoldi_mgs(a.clone(), v, 1e-9); println!("A = \n{:?}", &a); println!("Q = \n{:?}", &q); diff --git a/ndarray-linalg/tests/cholesky.rs b/ndarray-linalg/tests/cholesky.rs index a498afc3..d3e9942b 100644 --- a/ndarray-linalg/tests/cholesky.rs +++ b/ndarray-linalg/tests/cholesky.rs @@ -6,7 +6,8 @@ macro_rules! cholesky { paste::item! { #[test] fn []() { - let a_orig: Array2<$elem> = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a_orig: Array2<$elem> = random_hpd_using(3, &mut rng); println!("a = \n{:?}", a_orig); let upper = a_orig.cholesky(UPLO::Upper).unwrap(); @@ -79,7 +80,8 @@ macro_rules! cholesky_into_lower_upper { paste::item! { #[test] fn []() { - let a: Array2<$elem> = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hpd_using(3, &mut rng); println!("a = \n{:?}", a); let upper = a.cholesky(UPLO::Upper).unwrap(); let fac_upper = a.factorizec(UPLO::Upper).unwrap(); @@ -106,7 +108,8 @@ macro_rules! cholesky_into_inverse { paste::item! { #[test] fn []() { - let a: Array2<$elem> = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hpd_using(3, &mut rng); println!("a = \n{:?}", a); let inv = a.invc().unwrap(); assert_close_l2!(&a.dot(&inv), &Array2::eye(3), $rtol); @@ -134,7 +137,8 @@ macro_rules! cholesky_det { paste::item! { #[test] fn []() { - let a: Array2<$elem> = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hpd_using(3, &mut rng); println!("a = \n{:?}", a); let ln_det = a .eigvalsh(UPLO::Upper) @@ -168,8 +172,9 @@ macro_rules! cholesky_solve { paste::item! { #[test] fn []() { - let a: Array2<$elem> = random_hpd(3); - let x: Array1<$elem> = random(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hpd_using(3, &mut rng); + let x: Array1<$elem> = random_using(3, &mut rng); let b = a.dot(&x); println!("a = \n{:?}", a); println!("x = \n{:?}", x); diff --git a/ndarray-linalg/tests/convert.rs b/ndarray-linalg/tests/convert.rs index 3a6155d8..1e20d916 100644 --- a/ndarray-linalg/tests/convert.rs +++ b/ndarray-linalg/tests/convert.rs @@ -3,7 +3,8 @@ use ndarray_linalg::*; #[test] fn generalize() { - let a: Array3 = random((3, 2, 4).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array3 = random_using((3, 2, 4).f(), &mut rng); let ans = a.clone(); let a: Array3 = convert::generalize(a); assert_eq!(a, ans); diff --git a/ndarray-linalg/tests/det.rs b/ndarray-linalg/tests/det.rs index c3986528..d14fc8d0 100644 --- a/ndarray-linalg/tests/det.rs +++ b/ndarray-linalg/tests/det.rs @@ -136,15 +136,36 @@ fn det() { assert_rclose!(result.1, ln_det, rtol); } } + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for rows in 1..5 { - det_impl(random_regular::(rows), 1e-9); - det_impl(random_regular::(rows), 1e-4); - det_impl(random_regular::(rows), 1e-9); - det_impl(random_regular::(rows), 1e-4); - det_impl(random_regular::(rows).t().to_owned(), 1e-9); - det_impl(random_regular::(rows).t().to_owned(), 1e-4); - det_impl(random_regular::(rows).t().to_owned(), 1e-9); - det_impl(random_regular::(rows).t().to_owned(), 1e-4); + det_impl(random_regular_using::(rows, &mut rng), 1e-9); + det_impl(random_regular_using::(rows, &mut rng), 1e-4); + det_impl(random_regular_using::(rows, &mut rng), 1e-9); + det_impl(random_regular_using::(rows, &mut rng), 1e-4); + det_impl( + random_regular_using::(rows, &mut rng) + .t() + .to_owned(), + 1e-9, + ); + det_impl( + random_regular_using::(rows, &mut rng) + .t() + .to_owned(), + 1e-4, + ); + det_impl( + random_regular_using::(rows, &mut rng) + .t() + .to_owned(), + 1e-9, + ); + det_impl( + random_regular_using::(rows, &mut rng) + .t() + .to_owned(), + 1e-4, + ); } } @@ -152,7 +173,8 @@ fn det() { fn det_nonsquare() { macro_rules! det_nonsquare { ($elem:ty, $shape:expr) => { - let a: Array2<$elem> = random($shape); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_using($shape, &mut rng); assert!(a.factorize().unwrap().det().is_err()); assert!(a.factorize().unwrap().sln_det().is_err()); assert!(a.factorize().unwrap().det_into().is_err()); diff --git a/ndarray-linalg/tests/deth.rs b/ndarray-linalg/tests/deth.rs index abd54105..4785aadc 100644 --- a/ndarray-linalg/tests/deth.rs +++ b/ndarray-linalg/tests/deth.rs @@ -72,7 +72,8 @@ fn deth_zero_nonsquare() { fn deth() { macro_rules! deth { ($elem:ty, $rows:expr, $atol:expr) => { - let a: Array2<$elem> = random_hermite($rows); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hermite_using($rows, &mut rng); println!("a = \n{:?}", a); // Compute determinant from eigenvalues. diff --git a/ndarray-linalg/tests/eigh.rs b/ndarray-linalg/tests/eigh.rs index dd1445be..8d8ce385 100644 --- a/ndarray-linalg/tests/eigh.rs +++ b/ndarray-linalg/tests/eigh.rs @@ -79,7 +79,8 @@ fn fixed_t_lower() { #[test] fn ssqrt() { - let a: Array2 = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng); let ans = a.clone(); let s = a.ssqrt(UPLO::Upper).unwrap(); println!("a = {:?}", &ans); @@ -92,7 +93,8 @@ fn ssqrt() { #[test] fn ssqrt_t() { - let a: Array2 = random_hpd(3).reversed_axes(); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng).reversed_axes(); let ans = a.clone(); let s = a.ssqrt(UPLO::Upper).unwrap(); println!("a = {:?}", &ans); @@ -105,7 +107,8 @@ fn ssqrt_t() { #[test] fn ssqrt_lower() { - let a: Array2 = random_hpd(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng); let ans = a.clone(); let s = a.ssqrt(UPLO::Lower).unwrap(); println!("a = {:?}", &ans); @@ -118,7 +121,8 @@ fn ssqrt_lower() { #[test] fn ssqrt_t_lower() { - let a: Array2 = random_hpd(3).reversed_axes(); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng).reversed_axes(); let ans = a.clone(); let s = a.ssqrt(UPLO::Lower).unwrap(); println!("a = {:?}", &ans); diff --git a/ndarray-linalg/tests/householder.rs b/ndarray-linalg/tests/householder.rs index adc4d9b2..83b500f7 100644 --- a/ndarray-linalg/tests/householder.rs +++ b/ndarray-linalg/tests/householder.rs @@ -3,7 +3,8 @@ use ndarray_linalg::{krylov::*, *}; fn over(rtol: A::Real) { const N: usize = 4; - let a: Array2 = random((N, N * 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N * 2), &mut rng); // Terminate let (q, r) = householder(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); @@ -45,7 +46,8 @@ fn over_c64() { fn full(rtol: A::Real) { const N: usize = 5; - let a: Array2 = random((N, N)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N), &mut rng); let (q, r) = householder(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); let qc: Array2 = conjugate(&q); assert_close_l2!(&qc.dot(&q), &Array::eye(N), rtol; "Check Q^H Q = I"); @@ -71,7 +73,8 @@ fn full_c64() { fn half(rtol: A::Real) { const N: usize = 4; - let a: Array2 = random((N, N / 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N / 2), &mut rng); let (q, r) = householder(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); let qc: Array2 = conjugate(&q); assert_close_l2!(&qc.dot(&q), &Array::eye(N / 2), rtol; "Check Q^H Q = I"); diff --git a/ndarray-linalg/tests/inner.rs b/ndarray-linalg/tests/inner.rs index 7fc42c83..076b2791 100644 --- a/ndarray-linalg/tests/inner.rs +++ b/ndarray-linalg/tests/inner.rs @@ -19,7 +19,8 @@ fn size_longer() { #[test] fn abs() { - let a: Array1 = random(1); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array1 = random_using(1, &mut rng); let aa = a.inner(&a); assert_aclose!(aa.re(), a.norm().powi(2), 1e-5); assert_aclose!(aa.im(), 0.0, 1e-5); diff --git a/ndarray-linalg/tests/inv.rs b/ndarray-linalg/tests/inv.rs index 71e8973a..030773c1 100644 --- a/ndarray-linalg/tests/inv.rs +++ b/ndarray-linalg/tests/inv.rs @@ -5,7 +5,8 @@ fn test_inv_random(n: usize, set_f: bool, rtol: A::Real) where A: Scalar + Lapack, { - let a: Array2 = random([n; 2].set_f(set_f)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using([n; 2].set_f(set_f), &mut rng); let identity = Array2::eye(n); assert_close_l2!(&a.inv().unwrap().dot(&a), &identity, rtol); assert_close_l2!( @@ -24,7 +25,8 @@ fn test_inv_into_random(n: usize, set_f: bool, rtol: A::Real) where A: Scalar + Lapack, { - let a: Array2 = random([n; 2].set_f(set_f)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using([n; 2].set_f(set_f), &mut rng); let identity = Array2::eye(n); assert_close_l2!(&a.clone().inv_into().unwrap().dot(&a), &identity, rtol); assert_close_l2!( diff --git a/ndarray-linalg/tests/least_squares.rs b/ndarray-linalg/tests/least_squares.rs index 33e20ca7..17f62b64 100644 --- a/ndarray-linalg/tests/least_squares.rs +++ b/ndarray-linalg/tests/least_squares.rs @@ -5,7 +5,8 @@ use ndarray_linalg::*; /// A is square. `x = A^{-1} b`, `|b - Ax| = 0` fn test_exact(a: Array2) { - let b: Array1 = random(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(3, &mut rng); let result = a.least_squares(&b).unwrap(); // unpack result let x = result.solution; @@ -27,13 +28,15 @@ macro_rules! impl_exact { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3), &mut rng); test_exact(a) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 3).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3).f(), &mut rng); test_exact(a) } } @@ -51,7 +54,8 @@ fn test_overdetermined(a: Array2) where T::Real: AbsDiffEq, { - let b: Array1 = random(4); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(4, &mut rng); let result = a.least_squares(&b).unwrap(); // unpack result let x = result.solution; @@ -73,13 +77,15 @@ macro_rules! impl_overdetermined { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((4, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3), &mut rng); test_overdetermined(a) } #[test] fn []() { - let a: Array2<$scalar> = random((4, 3).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3).f(), &mut rng); test_overdetermined(a) } } @@ -94,7 +100,8 @@ impl_overdetermined!(c64); /// #column > #row case. /// Linear problem is underdetermined, `|b - Ax| = 0` and `x` is not unique fn test_underdetermined(a: Array2) { - let b: Array1 = random(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(3, &mut rng); let result = a.least_squares(&b).unwrap(); assert_eq!(result.rank, 3); assert!(result.residual_sum_of_squares.is_none()); @@ -110,13 +117,15 @@ macro_rules! impl_underdetermined { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((3, 4)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4), &mut rng); test_underdetermined(a) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 4).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4).f(), &mut rng); test_underdetermined(a) } } diff --git a/ndarray-linalg/tests/least_squares_nrhs.rs b/ndarray-linalg/tests/least_squares_nrhs.rs index dd7d283c..bcf6d013 100644 --- a/ndarray-linalg/tests/least_squares_nrhs.rs +++ b/ndarray-linalg/tests/least_squares_nrhs.rs @@ -32,29 +32,33 @@ macro_rules! impl_exact { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((3, 3)); - let b: Array2<$scalar> = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3), &mut rng); + let b: Array2<$scalar> = random_using((3, 2), &mut rng); test_exact(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 3)); - let b: Array2<$scalar> = random((3, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3), &mut rng); + let b: Array2<$scalar> = random_using((3, 2).f(), &mut rng); test_exact(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 3).f()); - let b: Array2<$scalar> = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3).f(), &mut rng); + let b: Array2<$scalar> = random_using((3, 2), &mut rng); test_exact(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 3).f()); - let b: Array2<$scalar> = random((3, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 3).f(), &mut rng); + let b: Array2<$scalar> = random_using((3, 2).f(), &mut rng); test_exact(a, b) } } @@ -100,29 +104,33 @@ macro_rules! impl_overdetermined { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((4, 3)); - let b: Array2<$scalar> = random((4, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3), &mut rng); + let b: Array2<$scalar> = random_using((4, 2), &mut rng); test_overdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((4, 3).f()); - let b: Array2<$scalar> = random((4, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3).f(), &mut rng); + let b: Array2<$scalar> = random_using((4, 2), &mut rng); test_overdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((4, 3)); - let b: Array2<$scalar> = random((4, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3), &mut rng); + let b: Array2<$scalar> = random_using((4, 2).f(), &mut rng); test_overdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((4, 3).f()); - let b: Array2<$scalar> = random((4, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((4, 3).f(), &mut rng); + let b: Array2<$scalar> = random_using((4, 2).f(), &mut rng); test_overdetermined(a, b) } } @@ -155,29 +163,33 @@ macro_rules! impl_underdetermined { paste::item! { #[test] fn []() { - let a: Array2<$scalar> = random((3, 4)); - let b: Array2<$scalar> = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4), &mut rng); + let b: Array2<$scalar> = random_using((3, 2), &mut rng); test_underdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 4).f()); - let b: Array2<$scalar> = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4).f(), &mut rng); + let b: Array2<$scalar> = random_using((3, 2), &mut rng); test_underdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 4)); - let b: Array2<$scalar> = random((3, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4), &mut rng); + let b: Array2<$scalar> = random_using((3, 2).f(), &mut rng); test_underdetermined(a, b) } #[test] fn []() { - let a: Array2<$scalar> = random((3, 4).f()); - let b: Array2<$scalar> = random((3, 2).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$scalar> = random_using((3, 4).f(), &mut rng); + let b: Array2<$scalar> = random_using((3, 2).f(), &mut rng); test_underdetermined(a, b) } } diff --git a/ndarray-linalg/tests/mgs.rs b/ndarray-linalg/tests/mgs.rs index 35c860de..9e9aa29e 100644 --- a/ndarray-linalg/tests/mgs.rs +++ b/ndarray-linalg/tests/mgs.rs @@ -5,7 +5,8 @@ fn qr_full() { const N: usize = 5; let rtol: A::Real = A::real(1e-9); - let a: Array2 = random((N, N)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N), &mut rng); let (q, r) = mgs(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); assert_close_l2!(&q.dot(&r), &a, rtol); @@ -27,7 +28,8 @@ fn qr() { const N: usize = 4; let rtol: A::Real = A::real(1e-9); - let a: Array2 = random((N, N / 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N / 2), &mut rng); let (q, r) = mgs(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); assert_close_l2!(&q.dot(&r), &a, rtol); @@ -49,7 +51,8 @@ fn qr_over() { const N: usize = 4; let rtol: A::Real = A::real(1e-9); - let a: Array2 = random((N, N * 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((N, N * 2), &mut rng); // Terminate let (q, r) = mgs(a.axis_iter(Axis(1)), N, rtol, Strategy::Terminate); diff --git a/ndarray-linalg/tests/normalize.rs b/ndarray-linalg/tests/normalize.rs index ca50912e..8d71a009 100644 --- a/ndarray-linalg/tests/normalize.rs +++ b/ndarray-linalg/tests/normalize.rs @@ -3,14 +3,16 @@ use ndarray_linalg::*; #[test] fn n_columns() { - let a: Array2 = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 2), &mut rng); let (n, v) = normalize(a.clone(), NormalizeAxis::Column); assert_close_l2!(&n.dot(&from_diag(&v)), &a, 1e-7); } #[test] fn n_rows() { - let a: Array2 = random((3, 2)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 2), &mut rng); let (n, v) = normalize(a.clone(), NormalizeAxis::Row); assert_close_l2!(&from_diag(&v).dot(&n), &a, 1e-7); } diff --git a/ndarray-linalg/tests/qr.rs b/ndarray-linalg/tests/qr.rs index a69d89e2..702ed060 100644 --- a/ndarray-linalg/tests/qr.rs +++ b/ndarray-linalg/tests/qr.rs @@ -26,48 +26,56 @@ fn test_square(a: &Array2, n: usize, m: usize) { #[test] fn qr_sq() { - let a = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 3), &mut rng); test_square(&a, 3, 3); } #[test] fn qr_sq_t() { - let a = random((3, 3).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 3).f(), &mut rng); test_square(&a, 3, 3); } #[test] fn qr_3x3() { - let a = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 3), &mut rng); test(&a, 3, 3); } #[test] fn qr_3x3_t() { - let a = random((3, 3).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 3).f(), &mut rng); test(&a, 3, 3); } #[test] fn qr_3x4() { - let a = random((3, 4)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 4), &mut rng); test(&a, 3, 4); } #[test] fn qr_3x4_t() { - let a = random((3, 4).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((3, 4).f(), &mut rng); test(&a, 3, 4); } #[test] fn qr_4x3() { - let a = random((4, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((4, 3), &mut rng); test(&a, 4, 3); } #[test] fn qr_4x3_t() { - let a = random((4, 3).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using((4, 3).f(), &mut rng); test(&a, 4, 3); } diff --git a/ndarray-linalg/tests/solve.rs b/ndarray-linalg/tests/solve.rs index b3d0cb53..57c29b67 100644 --- a/ndarray-linalg/tests/solve.rs +++ b/ndarray-linalg/tests/solve.rs @@ -1,6 +1,7 @@ use ndarray::prelude::*; use ndarray_linalg::{ - assert_aclose, assert_close_l2, c32, c64, random, random_hpd, solve::*, OperationNorm, Scalar, + assert_aclose, assert_close_l2, c32, c64, random_hpd_using, random_using, solve::*, + OperationNorm, Scalar, }; macro_rules! test_solve { @@ -97,12 +98,13 @@ macro_rules! test_solve_all { #[test] fn solve_random_float() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [f32 => 1e-3, f64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.dot(&x), [solve, solve_into, solve_inplace], ); @@ -112,12 +114,13 @@ fn solve_random_float() { #[test] fn solve_random_complex() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [c32 => 1e-3, c64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.dot(&x), [solve, solve_into, solve_inplace], ); @@ -128,19 +131,21 @@ fn solve_random_complex() { #[should_panic] #[test] fn solve_shape_mismatch() { - let a: Array2 = random((3, 3)); - let b: Array1 = random(2); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 3), &mut rng); + let b: Array1 = random_using(2, &mut rng); let _ = a.solve_into(b); } #[test] fn solve_t_random_float() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [f32 => 1e-3, f64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.t().dot(&x), [solve_t, solve_t_into, solve_t_inplace], ); @@ -151,19 +156,21 @@ fn solve_t_random_float() { #[should_panic] #[test] fn solve_t_shape_mismatch() { - let a: Array2 = random((3, 3).f()); - let b: Array1 = random(4); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 3).f(), &mut rng); + let b: Array1 = random_using(4, &mut rng); let _ = a.solve_into(b); } #[test] fn solve_t_random_complex() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [c32 => 1e-3, c64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.t().dot(&x), [solve_t, solve_t_into, solve_t_inplace], ); @@ -174,20 +181,22 @@ fn solve_t_random_complex() { #[should_panic] #[test] fn solve_factorized_shape_mismatch() { - let a: Array2 = random((3, 3)); - let b: Array1 = random(4); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 3), &mut rng); + let b: Array1 = random_using(4, &mut rng); let f = a.factorize_into().unwrap(); let _ = f.solve_into(b); } #[test] fn solve_h_random_float() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [f32 => 1e-3, f64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.t().mapv(|x| x.conj()).dot(&x), [solve_h, solve_h_into, solve_h_inplace], ); @@ -198,20 +207,22 @@ fn solve_h_random_float() { #[should_panic] #[test] fn solve_factorized_t_shape_mismatch() { - let a: Array2 = random((3, 3).f()); - let b: Array1 = random(4); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 3).f(), &mut rng); + let b: Array1 = random_using(4, &mut rng); let f = a.factorize_into().unwrap(); let _ = f.solve_into(b); } #[test] fn solve_h_random_complex() { + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); for n in 0..=8 { for &set_f in &[false, true] { test_solve_all!( [c32 => 1e-3, c64 => 1e-9], - a = random([n; 2].set_f(set_f)), - x = random(n), + a = random_using([n; 2].set_f(set_f), &mut rng), + x = random_using(n, &mut rng), b = a.t().mapv(|x| x.conj()).dot(&x), [solve_h, solve_h_into, solve_h_inplace], ); @@ -223,7 +234,8 @@ fn solve_h_random_complex() { fn rcond() { macro_rules! rcond { ($elem:ty, $rows:expr, $atol:expr) => { - let a: Array2<$elem> = random_hpd($rows); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2<$elem> = random_hpd_using($rows, &mut rng); let rcond = 1. / (a.opnorm_one().unwrap() * a.inv().unwrap().opnorm_one().unwrap()); assert_aclose!(a.rcond().unwrap(), rcond, $atol); assert_aclose!(a.rcond_into().unwrap(), rcond, $atol); diff --git a/ndarray-linalg/tests/solveh.rs b/ndarray-linalg/tests/solveh.rs index 56d016c4..25513551 100644 --- a/ndarray-linalg/tests/solveh.rs +++ b/ndarray-linalg/tests/solveh.rs @@ -4,24 +4,27 @@ use ndarray_linalg::*; #[should_panic] #[test] fn solveh_shape_mismatch() { - let a: Array2 = random_hpd(3); - let b: Array1 = random(2); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng); + let b: Array1 = random_using(2, &mut rng); let _ = a.solveh_into(b); } #[should_panic] #[test] fn factorizeh_solveh_shape_mismatch() { - let a: Array2 = random_hpd(3); - let b: Array1 = random(2); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng); + let b: Array1 = random_using(2, &mut rng); let f = a.factorizeh_into().unwrap(); let _ = f.solveh_into(b); } #[test] fn solveh_random() { - let a: Array2 = random_hpd(3); - let x: Array1 = random(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng); + let x: Array1 = random_using(3, &mut rng); let b = a.dot(&x); let y = a.solveh_into(b).unwrap(); assert_close_l2!(&x, &y, 1e-7); @@ -35,24 +38,27 @@ fn solveh_random() { #[should_panic] #[test] fn solveh_t_shape_mismatch() { - let a: Array2 = random_hpd(3).reversed_axes(); - let b: Array1 = random(2); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng).reversed_axes(); + let b: Array1 = random_using(2, &mut rng); let _ = a.solveh_into(b); } #[should_panic] #[test] fn factorizeh_solveh_t_shape_mismatch() { - let a: Array2 = random_hpd(3).reversed_axes(); - let b: Array1 = random(2); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng).reversed_axes(); + let b: Array1 = random_using(2, &mut rng); let f = a.factorizeh_into().unwrap(); let _ = f.solveh_into(b); } #[test] fn solveh_random_t() { - let a: Array2 = random_hpd(3).reversed_axes(); - let x: Array1 = random(3); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_hpd_using(3, &mut rng).reversed_axes(); + let x: Array1 = random_using(3, &mut rng); let b = a.dot(&x); let y = a.solveh_into(b).unwrap(); assert_close_l2!(&x, &y, 1e-7); diff --git a/ndarray-linalg/tests/svd.rs b/ndarray-linalg/tests/svd.rs index c83885e1..0eac35ea 100644 --- a/ndarray-linalg/tests/svd.rs +++ b/ndarray-linalg/tests/svd.rs @@ -53,13 +53,15 @@ macro_rules! test_svd_impl { paste::item! { #[test] fn []() { - let a = random(($n, $m)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m), &mut rng); $test::<$type>(&a); } #[test] fn []() { - let a = random(($n, $m).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m).f(), &mut rng); $test::<$type>(&a); } } diff --git a/ndarray-linalg/tests/svddc.rs b/ndarray-linalg/tests/svddc.rs index ed28fc30..60c6bd66 100644 --- a/ndarray-linalg/tests/svddc.rs +++ b/ndarray-linalg/tests/svddc.rs @@ -32,37 +32,43 @@ macro_rules! test_svd_impl { paste::item! { #[test] fn []() { - let a = random(($n, $m)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m), &mut rng); test::<$scalar>(&a, JobSvd::All); } #[test] fn []() { - let a = random(($n, $m)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m), &mut rng); test::<$scalar>(&a, JobSvd::Some); } #[test] fn []() { - let a = random(($n, $m)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m), &mut rng); test::<$scalar>(&a, JobSvd::None); } #[test] fn []() { - let a = random(($n, $m).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m).f(), &mut rng); test::<$scalar>(&a, JobSvd::All); } #[test] fn []() { - let a = random(($n, $m).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m).f(), &mut rng); test::<$scalar>(&a, JobSvd::Some); } #[test] fn []() { - let a = random(($n, $m).f()); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a = random_using(($n, $m).f(), &mut rng); test::<$scalar>(&a, JobSvd::None); } } diff --git a/ndarray-linalg/tests/trace.rs b/ndarray-linalg/tests/trace.rs index 9127be39..f93bb52b 100644 --- a/ndarray-linalg/tests/trace.rs +++ b/ndarray-linalg/tests/trace.rs @@ -3,6 +3,7 @@ use ndarray_linalg::*; #[test] fn trace() { - let a: Array2 = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = random_using((3, 3), &mut rng); assert_rclose!(a.trace().unwrap(), a[(0, 0)] + a[(1, 1)] + a[(2, 2)], 1e-7); } diff --git a/ndarray-linalg/tests/triangular.rs b/ndarray-linalg/tests/triangular.rs index ca609c5c..4ddf0a8b 100644 --- a/ndarray-linalg/tests/triangular.rs +++ b/ndarray-linalg/tests/triangular.rs @@ -34,87 +34,99 @@ where #[test] fn triangular_1d_upper() { let n = 3; - let b: Array1 = random(n); - let a: Array2 = random((n, n)).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(n, &mut rng); + let a: Array2 = random_using((n, n), &mut rng).into_triangular(UPLO::Upper); test1d(UPLO::Upper, &a, &b, 1e-7); } #[test] fn triangular_1d_lower() { let n = 3; - let b: Array1 = random(n); - let a: Array2 = random((n, n)).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(n, &mut rng); + let a: Array2 = random_using((n, n), &mut rng).into_triangular(UPLO::Lower); test1d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_1d_upper_t() { let n = 3; - let b: Array1 = random(n); - let a: Array2 = random((n, n).f()).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(n, &mut rng); + let a: Array2 = random_using((n, n).f(), &mut rng).into_triangular(UPLO::Upper); test1d(UPLO::Upper, &a, &b, 1e-7); } #[test] fn triangular_1d_lower_t() { let n = 3; - let b: Array1 = random(n); - let a: Array2 = random((n, n).f()).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array1 = random_using(n, &mut rng); + let a: Array2 = random_using((n, n).f(), &mut rng).into_triangular(UPLO::Lower); test1d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_2d_upper() { - let b: Array2 = random((3, 4)); - let a: Array2 = random((3, 3)).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4), &mut rng); + let a: Array2 = random_using((3, 3), &mut rng).into_triangular(UPLO::Upper); test2d(UPLO::Upper, &a, &b, 1e-7); } #[test] fn triangular_2d_lower() { - let b: Array2 = random((3, 4)); - let a: Array2 = random((3, 3)).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4), &mut rng); + let a: Array2 = random_using((3, 3), &mut rng).into_triangular(UPLO::Lower); test2d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_2d_lower_t() { - let b: Array2 = random((3, 4)); - let a: Array2 = random((3, 3).f()).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4), &mut rng); + let a: Array2 = random_using((3, 3).f(), &mut rng).into_triangular(UPLO::Lower); test2d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_2d_upper_t() { - let b: Array2 = random((3, 4)); - let a: Array2 = random((3, 3).f()).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4), &mut rng); + let a: Array2 = random_using((3, 3).f(), &mut rng).into_triangular(UPLO::Upper); test2d(UPLO::Upper, &a, &b, 1e-7); } #[test] fn triangular_2d_upper_bt() { - let b: Array2 = random((3, 4).f()); - let a: Array2 = random((3, 3)).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4).f(), &mut rng); + let a: Array2 = random_using((3, 3), &mut rng).into_triangular(UPLO::Upper); test2d(UPLO::Upper, &a, &b, 1e-7); } #[test] fn triangular_2d_lower_bt() { - let b: Array2 = random((3, 4).f()); - let a: Array2 = random((3, 3)).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4).f(), &mut rng); + let a: Array2 = random_using((3, 3), &mut rng).into_triangular(UPLO::Lower); test2d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_2d_lower_t_bt() { - let b: Array2 = random((3, 4).f()); - let a: Array2 = random((3, 3).f()).into_triangular(UPLO::Lower); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4).f(), &mut rng); + let a: Array2 = random_using((3, 3).f(), &mut rng).into_triangular(UPLO::Lower); test2d(UPLO::Lower, &a, &b, 1e-7); } #[test] fn triangular_2d_upper_t_bt() { - let b: Array2 = random((3, 4).f()); - let a: Array2 = random((3, 3).f()).into_triangular(UPLO::Upper); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let b: Array2 = random_using((3, 4).f(), &mut rng); + let a: Array2 = random_using((3, 3).f(), &mut rng).into_triangular(UPLO::Upper); test2d(UPLO::Upper, &a, &b, 1e-7); } diff --git a/ndarray-linalg/tests/tridiagonal.rs b/ndarray-linalg/tests/tridiagonal.rs index 38278951..513d625b 100644 --- a/ndarray-linalg/tests/tridiagonal.rs +++ b/ndarray-linalg/tests/tridiagonal.rs @@ -28,7 +28,8 @@ fn tridiagonal_index() { #[test] fn opnorm_tridiagonal() { - let mut a: Array2 = random((4, 4)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let mut a: Array2 = random_using((4, 4), &mut rng); a[[0, 2]] = 0.0; a[[0, 3]] = 0.0; a[[1, 3]] = 0.0; @@ -129,10 +130,11 @@ fn solve_tridiagonal_c64() { #[test] fn solve_tridiagonal_random() { - let mut a: Array2 = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let mut a: Array2 = random_using((3, 3), &mut rng); a[[0, 2]] = 0.0; a[[2, 0]] = 0.0; - let x: Array1 = random(3); + let x: Array1 = random_using(3, &mut rng); let b1 = a.dot(&x); let b2 = b1.clone(); let y1 = a.solve_tridiagonal_into(b1).unwrap(); @@ -143,10 +145,11 @@ fn solve_tridiagonal_random() { #[test] fn solve_tridiagonal_random_t() { - let mut a: Array2 = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let mut a: Array2 = random_using((3, 3), &mut rng); a[[0, 2]] = 0.0; a[[2, 0]] = 0.0; - let x: Array1 = random(3); + let x: Array1 = random_using(3, &mut rng); let at = a.t(); let b1 = at.dot(&x); let b2 = b1.clone(); @@ -158,11 +161,12 @@ fn solve_tridiagonal_random_t() { #[test] fn extract_tridiagonal_solve_random() { - let mut a: Array2 = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let mut a: Array2 = random_using((3, 3), &mut rng); a[[0, 2]] = 0.0; a[[2, 0]] = 0.0; let tridiag = a.extract_tridiagonal().unwrap(); - let x: Array1 = random(3); + let x: Array1 = random_using(3, &mut rng); let b1 = a.dot(&x); let b2 = b1.clone(); let y1 = tridiag.solve_tridiagonal_into(b1).unwrap(); @@ -180,7 +184,8 @@ fn det_tridiagonal_f64() { #[test] fn det_tridiagonal_random() { - let mut a: Array2 = random((3, 3)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let mut a: Array2 = random_using((3, 3), &mut rng); a[[0, 2]] = 0.0; a[[2, 0]] = 0.0; assert_aclose!(a.det_tridiagonal().unwrap(), a.det().unwrap(), 1e-7); From 36a0276957d28fba2b6f13c2ff9c65bf19c82ff8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 15:33:52 +0900 Subject: [PATCH 082/165] Update doctests to use random_*_using --- ndarray-linalg/src/solve.rs | 20 +++++--------------- ndarray-linalg/src/solveh.rs | 19 +++++-------------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index 2e4b568b..79df1f77 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -5,20 +5,13 @@ //! Solve `A * x = b`: //! //! ``` -//! #[macro_use] -//! extern crate ndarray; -//! extern crate ndarray_linalg; -//! //! use ndarray::prelude::*; //! use ndarray_linalg::Solve; -//! # fn main() { //! //! let a: Array2 = array![[3., 2., -1.], [2., -2., 4.], [-2., 1., -2.]]; //! let b: Array1 = array![1., -2., 0.]; //! let x = a.solve_into(b).unwrap(); //! assert!(x.abs_diff_eq(&array![1., -2., -2.], 1e-9)); -//! -//! # } //! ``` //! //! There are also special functions for solving `A^T * x = b` and @@ -29,21 +22,18 @@ //! the beginning than solving directly using `A`: //! //! ``` -//! # extern crate ndarray; -//! # extern crate ndarray_linalg; -//! //! use ndarray::prelude::*; //! use ndarray_linalg::*; -//! # fn main() { //! -//! let a: Array2 = random((3, 3)); +//! /// Use fixed algorithm and seed of PRNG for reproducible test +//! let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); +//! +//! let a: Array2 = random_using((3, 3), &mut rng); //! let f = a.factorize_into().unwrap(); // LU factorize A (A is consumed) //! for _ in 0..10 { -//! let b: Array1 = random(3); +//! let b: Array1 = random_using(3, &mut rng); //! let x = f.solve_into(b).unwrap(); // Solve A * x = b using factorized L, U //! } -//! -//! # } //! ``` use ndarray::*; diff --git a/ndarray-linalg/src/solveh.rs b/ndarray-linalg/src/solveh.rs index c6dccba4..df0b3a6f 100644 --- a/ndarray-linalg/src/solveh.rs +++ b/ndarray-linalg/src/solveh.rs @@ -8,13 +8,8 @@ //! Solve `A * x = b`, where `A` is a Hermitian (or real symmetric) matrix: //! //! ``` -//! #[macro_use] -//! extern crate ndarray; -//! extern crate ndarray_linalg; -//! //! use ndarray::prelude::*; //! use ndarray_linalg::SolveH; -//! # fn main() { //! //! let a: Array2 = array![ //! [3., 2., -1.], @@ -24,8 +19,6 @@ //! let b: Array1 = array![11., -12., 1.]; //! let x = a.solveh_into(b).unwrap(); //! assert!(x.abs_diff_eq(&array![1., 3., -2.], 1e-9)); -//! -//! # } //! ``` //! //! If you are solving multiple systems of linear equations with the same @@ -33,20 +26,18 @@ //! the factorization once at the beginning than solving directly using `A`: //! //! ``` -//! # extern crate ndarray; -//! # extern crate ndarray_linalg; //! use ndarray::prelude::*; //! use ndarray_linalg::*; -//! # fn main() { //! -//! let a: Array2 = random((3, 3)); +//! /// Use fixed algorithm and seed of PRNG for reproducible test +//! let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); +//! +//! let a: Array2 = random_using((3, 3), &mut rng); //! let f = a.factorizeh_into().unwrap(); // Factorize A (A is consumed) //! for _ in 0..10 { -//! let b: Array1 = random(3); +//! let b: Array1 = random_using(3, &mut rng); //! let x = f.solveh_into(b).unwrap(); // Solve A * x = b using the factorization //! } -//! -//! # } //! ``` use ndarray::*; From 4ea4f369d5485e5c7af44046eb3ab5da8c1413c6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Sep 2022 15:48:24 +0900 Subject: [PATCH 083/165] Fix RNG for testing LOBPCG --- ndarray-linalg/src/lobpcg/lobpcg.rs | 18 ++++++++++++------ ndarray-linalg/src/lobpcg/svd.rs | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ndarray-linalg/src/lobpcg/lobpcg.rs b/ndarray-linalg/src/lobpcg/lobpcg.rs index 8b7ab616..10d12da2 100644 --- a/ndarray-linalg/src/lobpcg/lobpcg.rs +++ b/ndarray-linalg/src/lobpcg/lobpcg.rs @@ -460,7 +460,8 @@ mod tests { /// Test the `sorted_eigen` function #[test] fn test_sorted_eigen() { - let matrix: Array2 = generate::random((10, 10)) * 10.0; + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let matrix: Array2 = generate::random_using((10, 10), &mut rng) * 10.0; let matrix = matrix.t().dot(&matrix); // return all eigenvectors with largest first @@ -476,7 +477,8 @@ mod tests { /// Test the masking function #[test] fn test_masking() { - let matrix: Array2 = generate::random((10, 5)) * 10.0; + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let matrix: Array2 = generate::random_using((10, 5), &mut rng) * 10.0; let masked_matrix = ndarray_mask(matrix.view(), &[true, true, false, true, false]); close_l2( &masked_matrix.slice(s![.., 2]), @@ -488,7 +490,8 @@ mod tests { /// Test orthonormalization of a random matrix #[test] fn test_orthonormalize() { - let matrix: Array2 = generate::random((10, 10)) * 10.0; + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let matrix: Array2 = generate::random_using((10, 10), &mut rng) * 10.0; let (n, l) = orthonormalize(matrix.clone()).unwrap(); @@ -509,7 +512,8 @@ mod tests { assert_symmetric(a); let n = a.len_of(Axis(0)); - let x: Array2 = generate::random((n, num)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let x: Array2 = generate::random_using((n, num), &mut rng); let result = lobpcg(|y| a.dot(&y), x, |_| {}, None, 1e-5, n * 2, order); match result { @@ -553,7 +557,8 @@ mod tests { #[test] fn test_eigsolver_constructed() { let n = 50; - let tmp = generate::random((n, n)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let tmp = generate::random_using((n, n), &mut rng); //let (v, _) = tmp.qr_square().unwrap(); let (v, _) = orthonormalize(tmp).unwrap(); @@ -570,7 +575,8 @@ mod tests { fn test_eigsolver_constrained() { let diag = arr1(&[1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]); let a = Array2::from_diag(&diag); - let x: Array2 = generate::random((10, 1)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let x: Array2 = generate::random_using((10, 1), &mut rng); let y: Array2 = arr2(&[ [1.0, 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 1.0, 0., 0., 0., 0., 0., 0., 0., 0.], diff --git a/ndarray-linalg/src/lobpcg/svd.rs b/ndarray-linalg/src/lobpcg/svd.rs index f46857cc..62d18b49 100644 --- a/ndarray-linalg/src/lobpcg/svd.rs +++ b/ndarray-linalg/src/lobpcg/svd.rs @@ -214,7 +214,8 @@ mod tests { #[test] fn test_truncated_svd_random() { - let a: Array2 = generate::random((50, 10)); + let mut rng = rand_pcg::Mcg128Xsl64::new(0xcafef00dd15ea5e5); + let a: Array2 = generate::random_using((50, 10), &mut rng); let res = TruncatedSvd::new(a.clone(), Order::Largest) .precision(1e-5) From 25800c9e17a611cad60f869646fdebe50e15f359 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 18 Sep 2022 18:14:17 +0900 Subject: [PATCH 084/165] slice_assume_init_{ref,mut} for VecAssumeInit --- lax/src/alloc.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lax/src/alloc.rs b/lax/src/alloc.rs index 5872bab4..63458818 100644 --- a/lax/src/alloc.rs +++ b/lax/src/alloc.rs @@ -33,18 +33,34 @@ impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); impl_as_ptr!(MaybeUninit, lapack_sys::__BindgenComplex); pub(crate) trait VecAssumeInit { - type Target; - unsafe fn assume_init(self) -> Self::Target; + type Elem; + unsafe fn assume_init(self) -> Vec; + + /// An replacement of unstable API + /// https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref + unsafe fn slice_assume_init_ref(&self) -> &[Self::Elem]; + + /// An replacement of unstable API + /// https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.slice_assume_init_mut + unsafe fn slice_assume_init_mut(&mut self) -> &mut [Self::Elem]; } impl VecAssumeInit for Vec> { - type Target = Vec; - unsafe fn assume_init(self) -> Self::Target { + type Elem = T; + unsafe fn assume_init(self) -> Vec { // FIXME use Vec::into_raw_parts instead after stablized // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts let mut me = std::mem::ManuallyDrop::new(self); Vec::from_raw_parts(me.as_mut_ptr() as *mut T, me.len(), me.capacity()) } + + unsafe fn slice_assume_init_ref(&self) -> &[T] { + std::slice::from_raw_parts(self.as_ptr() as *const T, self.len()) + } + + unsafe fn slice_assume_init_mut(&mut self) -> &mut [T] { + std::slice::from_raw_parts_mut(self.as_mut_ptr() as *mut T, self.len()) + } } /// Create a vector without initialization From ee38384e2386f97a80eefd2ee6678470fae9be54 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 21 Sep 2022 22:30:04 +0900 Subject: [PATCH 085/165] Move implement comment to Eig_ trait document --- lax/src/eig.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 6fcbc26a..0ca3c3bf 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -4,6 +4,17 @@ use num_traits::{ToPrimitive, Zero}; #[cfg_attr(doc, katexit::katexit)] /// Eigenvalue problem for general matrix +/// +/// LAPACK assumes a column-major input. A row-major input can +/// be interpreted as the transpose of a column-major input. So, +/// for row-major inputs, we we want to solve the following, +/// given the column-major input `A`: +/// +/// A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H +/// +/// So, in this case, the right eigenvectors are the conjugates +/// of the left eigenvectors computed with `A`, and the +/// eigenvalues are the eigenvalues computed with `A`. pub trait Eig_: Scalar { /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ /// From 8d2b5e71912d59f626c04b7ade787ff9af4f42fc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 21 Sep 2022 22:31:44 +0900 Subject: [PATCH 086/165] EigWork as a working memory for Eig_ trait --- lax/src/eig.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 0ca3c3bf..569e1337 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -32,6 +32,33 @@ pub trait Eig_: Scalar { ) -> Result<(Vec, Vec)>; } +/// Working memory for [Eig_] +#[derive(Debug, Clone)] +pub struct EigWork { + pub n: i32, + pub jobvr: JobEv, + pub jobvl: JobEv, + + /// Eigenvalues used in complex routines + pub eigs: Vec>, + /// Real part of eigenvalues used in real routines + pub eigs_re: Option>>, + /// Imaginary part of eigenvalues used in real routines + pub eigs_im: Option>>, + + /// Left eigenvectors + pub vc_l: Option>>, + pub vr_l: Option>>, + /// Right eigenvectors + pub vc_r: Option>>, + pub vr_r: Option>>, + + /// Working memory + pub work: Vec>, + /// Working memory with `T::Real` + pub rwork: Option>>, +} + macro_rules! impl_eig_complex { ($scalar:ty, $ev:path) => { impl Eig_ for $scalar { From ea9d44371235324e6d8afb020c5472a39f67ddf8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 23 Sep 2022 18:08:36 +0900 Subject: [PATCH 087/165] EigWorkImpl for c64 --- lax/src/eig.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 569e1337..f06a5874 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -59,6 +59,174 @@ pub struct EigWork { pub rwork: Option>>, } +#[derive(Debug, Clone, PartialEq)] +pub struct Eig { + pub eigs: Vec, + pub vr: Option>, + pub vl: Option>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct EigRef<'work, T: Scalar> { + pub eigs: &'work [T::Complex], + pub vr: Option<&'work [T::Complex]>, + pub vl: Option<&'work [T::Complex]>, +} + +pub trait EigWorkImpl: Sized { + type Elem: Scalar; + /// Create new working memory for eigenvalues compution. + fn new(calc_v: bool, l: MatrixLayout) -> Result; + /// Compute eigenvalues and vectors on this working memory. + fn calc<'work>(&'work mut self, a: &mut [Self::Elem]) -> Result>; + /// Compute eigenvalues and vectors by consuming this working memory. + fn eval(self, a: &mut [Self::Elem]) -> Result>; +} + +impl EigWorkImpl for EigWork { + type Elem = c64; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut eigs: Vec> = vec_uninit(n as usize); + let mut rwork: Vec> = vec_uninit(2 * n as usize); + + let mut vc_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vc_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size = [c64::zero()]; + unsafe { + lapack_sys::zgeev_( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(vc_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vc_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ) + }; + info.as_lapack_result()?; + + let lwork = work_size[0].to_usize().unwrap(); + let work: Vec> = vec_uninit(lwork); + Ok(Self { + n, + jobvl, + jobvr, + eigs, + eigs_re: None, + eigs_im: None, + rwork: Some(rwork), + vc_l, + vc_r, + vr_l: None, + vr_r: None, + work, + }) + } + + fn calc<'work>(&'work mut self, a: &mut [c64]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + lapack_sys::zgeev_( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + + let eigs = unsafe { self.eigs.slice_assume_init_ref() }; + + // Hermite conjugate + if let Some(vl) = self.vc_l.as_mut() { + for value in vl { + let value = unsafe { value.assume_init_mut() }; + value.im = -value.im; + } + } + Ok(EigRef { + eigs, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) + } + + fn eval(mut self, a: &mut [c64]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + lapack_sys::zgeev_( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + let eigs = unsafe { self.eigs.assume_init() }; + + // Hermite conjugate + if let Some(vl) = self.vc_l.as_mut() { + for value in vl { + let value = unsafe { value.assume_init_mut() }; + value.im = -value.im; + } + } + Ok(Eig { + eigs, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) + } +} + macro_rules! impl_eig_complex { ($scalar:ty, $ev:path) => { impl Eig_ for $scalar { From 60946d141b44a52ab67dc4ca9a3c4f2aa31260e9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 00:32:13 +0900 Subject: [PATCH 088/165] EigWorkImpl for f64 --- lax/src/eig.rs | 291 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 243 insertions(+), 48 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index f06a5874..30ff5885 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -227,6 +227,179 @@ impl EigWorkImpl for EigWork { } } +impl EigWorkImpl for EigWork { + type Elem = f64; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut eigs_re: Vec> = vec_uninit(n as usize); + let mut eigs_im: Vec> = vec_uninit(n as usize); + + let mut vr_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vr_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); + let vc_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); + let vc_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size: [f64; 1] = [0.0]; + unsafe { + lapack_sys::dgeev_( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs_re), + AsPtr::as_mut_ptr(&mut eigs_im), + AsPtr::as_mut_ptr(vr_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; + + // actual ev + let lwork = work_size[0].to_usize().unwrap(); + let work: Vec> = vec_uninit(lwork); + + Ok(Self { + n, + jobvr, + jobvl, + eigs: vec_uninit(n as usize), + eigs_re: Some(eigs_re), + eigs_im: Some(eigs_im), + rwork: None, + vr_l, + vr_r, + vc_l, + vc_r, + work, + }) + } + + fn calc<'work>(&'work mut self, a: &mut [f64]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + lapack_sys::dgeev_( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + + let eigs_re: &[f64] = self + .eigs_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let eigs_im: &[f64] = self + .eigs_im + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); + + if let Some(v) = self.vr_l.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + if let Some(v) = self.vr_r.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + + Ok(EigRef { + eigs: unsafe { self.eigs.slice_assume_init_ref() }, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) + } + + fn eval(mut self, a: &mut [f64]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + lapack_sys::dgeev_( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + + let eigs_re: &[f64] = self + .eigs_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let eigs_im: &[f64] = self + .eigs_im + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); + + if let Some(v) = self.vr_l.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + if let Some(v) = self.vr_r.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + + Ok(Eig { + eigs: unsafe { self.eigs.assume_init() }, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) + } +} + macro_rules! impl_eig_complex { ($scalar:ty, $ev:path) => { impl Eig_ for $scalar { @@ -429,55 +602,18 @@ macro_rules! impl_eig_real { .map(|(&re, &im)| Self::complex(re, im)) .collect(); - if !calc_v { - return Ok((eigs, Vec::new())); - } - - // Reconstruct eigenvectors into complex-array - // -------------------------------------------- - // - // From LAPACK API https://software.intel.com/en-us/node/469230 - // - // - If the j-th eigenvalue is real, - // - v(j) = VR(:,j), the j-th column of VR. - // - // - If the j-th and (j+1)-st eigenvalues form a complex conjugate pair, - // - v(j) = VR(:,j) + i*VR(:,j+1) - // - v(j+1) = VR(:,j) - i*VR(:,j+1). - // - // In the C-layout case, we need the conjugates of the left - // eigenvectors, so the signs should be reversed. - - let n = n as usize; - let v = vr.or(vl).unwrap(); - let mut eigvecs: Vec> = vec_uninit(n * n); - let mut col = 0; - while col < n { - if eig_im[col] == 0. { - // The corresponding eigenvalue is real. - for row in 0..n { - let re = v[row + col * n]; - eigvecs[row + col * n].write(Self::complex(re, 0.)); - } - col += 1; - } else { - // This is a complex conjugate pair. - assert!(col + 1 < n); - for row in 0..n { - let re = v[row + col * n]; - let mut im = v[row + (col + 1) * n]; - if jobvl.is_calc() { - im = -im; - } - eigvecs[row + col * n].write(Self::complex(re, im)); - eigvecs[row + (col + 1) * n].write(Self::complex(re, -im)); - } - col += 2; - } + if calc_v { + let mut eigvecs = vec_uninit((n * n) as usize); + reconstruct_eigenvectors( + jobvl.is_calc(), + &eig_im, + &vr.or(vl).unwrap(), + &mut eigvecs, + ); + Ok((eigs, unsafe { eigvecs.assume_init() })) + } else { + Ok((eigs, Vec::new())) } - let eigvecs = unsafe { eigvecs.assume_init() }; - - Ok((eigs, eigvecs)) } } }; @@ -485,3 +621,62 @@ macro_rules! impl_eig_real { impl_eig_real!(f64, lapack_sys::dgeev_); impl_eig_real!(f32, lapack_sys::sgeev_); + +/// Reconstruct eigenvectors into complex-array +/// +/// From LAPACK API https://software.intel.com/en-us/node/469230 +/// +/// - If the j-th eigenvalue is real, +/// - v(j) = VR(:,j), the j-th column of VR. +/// +/// - If the j-th and (j+1)-st eigenvalues form a complex conjugate pair, +/// - v(j) = VR(:,j) + i*VR(:,j+1) +/// - v(j+1) = VR(:,j) - i*VR(:,j+1). +/// +/// In the C-layout case, we need the conjugates of the left +/// eigenvectors, so the signs should be reversed. +fn reconstruct_eigenvectors( + take_hermite_conjugate: bool, + eig_im: &[T], + vr: &[T], + vc: &mut [MaybeUninit], +) { + let n = eig_im.len(); + assert_eq!(vr.len(), n * n); + assert_eq!(vc.len(), n * n); + + let mut col = 0; + while col < n { + if eig_im[col].is_zero() { + // The corresponding eigenvalue is real. + for row in 0..n { + let re = vr[row + col * n]; + vc[row + col * n].write(T::complex(re, T::zero())); + } + col += 1; + } else { + // This is a complex conjugate pair. + assert!(col + 1 < n); + for row in 0..n { + let re = vr[row + col * n]; + let mut im = vr[row + (col + 1) * n]; + if take_hermite_conjugate { + im = -im; + } + vc[row + col * n].write(T::complex(re, im)); + vc[row + (col + 1) * n].write(T::complex(re, -im)); + } + col += 2; + } + } +} + +/// Create complex eigenvalues from real and imaginary parts. +fn reconstruct_eigs(re: &[T], im: &[T], eigs: &mut [MaybeUninit]) { + let n = eigs.len(); + assert_eq!(re.len(), n); + assert_eq!(im.len(), n); + for i in 0..n { + eigs[i].write(T::complex(re[i], im[i])); + } +} From 1c8b639f97c284b8b536b0770728b06995445755 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 00:37:46 +0900 Subject: [PATCH 089/165] EigWorkImpl for c32 and f32 --- lax/src/eig.rs | 615 +++++++++++++++++++++++++------------------------ 1 file changed, 314 insertions(+), 301 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 30ff5885..e4462be3 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -83,322 +83,335 @@ pub trait EigWorkImpl: Sized { fn eval(self, a: &mut [Self::Elem]) -> Result>; } -impl EigWorkImpl for EigWork { - type Elem = c64; - - fn new(calc_v: bool, l: MatrixLayout) -> Result { - let (n, _) = l.size(); - let (jobvl, jobvr) = if calc_v { - match l { - MatrixLayout::C { .. } => (JobEv::All, JobEv::None), - MatrixLayout::F { .. } => (JobEv::None, JobEv::All), +macro_rules! impl_eig_work_c { + ($c:ty, $ev:path) => { + impl EigWorkImpl for EigWork<$c> { + type Elem = $c; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut eigs = vec_uninit(n as usize); + let mut rwork = vec_uninit(2 * n as usize); + + let mut vc_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vc_r = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size = [<$c>::zero()]; + unsafe { + $ev( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(vc_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vc_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ) + }; + info.as_lapack_result()?; + + let lwork = work_size[0].to_usize().unwrap(); + let work: Vec> = vec_uninit(lwork); + Ok(Self { + n, + jobvl, + jobvr, + eigs, + eigs_re: None, + eigs_im: None, + rwork: Some(rwork), + vc_l, + vc_r, + vr_l: None, + vr_r: None, + work, + }) } - } else { - (JobEv::None, JobEv::None) - }; - let mut eigs: Vec> = vec_uninit(n as usize); - let mut rwork: Vec> = vec_uninit(2 * n as usize); - - let mut vc_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); - let mut vc_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); - - // calc work size - let mut info = 0; - let mut work_size = [c64::zero()]; - unsafe { - lapack_sys::zgeev_( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - std::ptr::null_mut(), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(vc_l.as_deref_mut().unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vc_r.as_deref_mut().unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - AsPtr::as_mut_ptr(&mut rwork), - &mut info, - ) - }; - info.as_lapack_result()?; - - let lwork = work_size[0].to_usize().unwrap(); - let work: Vec> = vec_uninit(lwork); - Ok(Self { - n, - jobvl, - jobvr, - eigs, - eigs_re: None, - eigs_im: None, - rwork: Some(rwork), - vc_l, - vc_r, - vr_l: None, - vr_r: None, - work, - }) - } - fn calc<'work>(&'work mut self, a: &mut [c64]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - lapack_sys::zgeev_( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(&mut self.eigs), - AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), - &mut info, - ) - }; - info.as_lapack_result()?; - - let eigs = unsafe { self.eigs.slice_assume_init_ref() }; - - // Hermite conjugate - if let Some(vl) = self.vc_l.as_mut() { - for value in vl { - let value = unsafe { value.assume_init_mut() }; - value.im = -value.im; + fn calc<'work>( + &'work mut self, + a: &mut [Self::Elem], + ) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + // Hermite conjugate + if let Some(vl) = self.vc_l.as_mut() { + for value in vl { + let value = unsafe { value.assume_init_mut() }; + value.im = -value.im; + } + } + Ok(EigRef { + eigs: unsafe { self.eigs.slice_assume_init_ref() }, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) } - } - Ok(EigRef { - eigs, - vl: self - .vc_l - .as_ref() - .map(|v| unsafe { v.slice_assume_init_ref() }), - vr: self - .vc_r - .as_ref() - .map(|v| unsafe { v.slice_assume_init_ref() }), - }) - } - fn eval(mut self, a: &mut [c64]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - lapack_sys::zgeev_( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(&mut self.eigs), - AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), - &mut info, - ) - }; - info.as_lapack_result()?; - let eigs = unsafe { self.eigs.assume_init() }; - - // Hermite conjugate - if let Some(vl) = self.vc_l.as_mut() { - for value in vl { - let value = unsafe { value.assume_init_mut() }; - value.im = -value.im; + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + // Hermite conjugate + if let Some(vl) = self.vc_l.as_mut() { + for value in vl { + let value = unsafe { value.assume_init_mut() }; + value.im = -value.im; + } + } + Ok(Eig { + eigs: unsafe { self.eigs.assume_init() }, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) } } - Ok(Eig { - eigs, - vl: self.vc_l.map(|v| unsafe { v.assume_init() }), - vr: self.vc_r.map(|v| unsafe { v.assume_init() }), - }) - } + }; } -impl EigWorkImpl for EigWork { - type Elem = f64; +impl_eig_work_c!(c32, lapack_sys::cgeev_); +impl_eig_work_c!(c64, lapack_sys::zgeev_); + +macro_rules! impl_eig_work_r { + ($f:ty, $ev:path) => { + impl EigWorkImpl for EigWork<$f> { + type Elem = $f; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut eigs_re = vec_uninit(n as usize); + let mut eigs_im = vec_uninit(n as usize); + let mut vr_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vr_r = jobvr.then(|| vec_uninit((n * n) as usize)); + let vc_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let vc_r = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size: [$f; 1] = [0.0]; + unsafe { + $ev( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs_re), + AsPtr::as_mut_ptr(&mut eigs_im), + AsPtr::as_mut_ptr(vr_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; - fn new(calc_v: bool, l: MatrixLayout) -> Result { - let (n, _) = l.size(); - let (jobvl, jobvr) = if calc_v { - match l { - MatrixLayout::C { .. } => (JobEv::All, JobEv::None), - MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + // actual ev + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + + Ok(Self { + n, + jobvr, + jobvl, + eigs: vec_uninit(n as usize), + eigs_re: Some(eigs_re), + eigs_im: Some(eigs_im), + rwork: None, + vr_l, + vr_r, + vc_l, + vc_r, + work, + }) } - } else { - (JobEv::None, JobEv::None) - }; - let mut eigs_re: Vec> = vec_uninit(n as usize); - let mut eigs_im: Vec> = vec_uninit(n as usize); - - let mut vr_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); - let mut vr_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); - let vc_l: Option>> = jobvl.then(|| vec_uninit((n * n) as usize)); - let vc_r: Option>> = jobvr.then(|| vec_uninit((n * n) as usize)); - - // calc work size - let mut info = 0; - let mut work_size: [f64; 1] = [0.0]; - unsafe { - lapack_sys::dgeev_( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - std::ptr::null_mut(), - &n, - AsPtr::as_mut_ptr(&mut eigs_re), - AsPtr::as_mut_ptr(&mut eigs_im), - AsPtr::as_mut_ptr(vr_l.as_deref_mut().unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vr_r.as_deref_mut().unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ) - }; - info.as_lapack_result()?; - - // actual ev - let lwork = work_size[0].to_usize().unwrap(); - let work: Vec> = vec_uninit(lwork); - - Ok(Self { - n, - jobvr, - jobvl, - eigs: vec_uninit(n as usize), - eigs_re: Some(eigs_re), - eigs_im: Some(eigs_im), - rwork: None, - vr_l, - vr_r, - vc_l, - vc_r, - work, - }) - } - fn calc<'work>(&'work mut self, a: &mut [f64]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - lapack_sys::dgeev_( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - &mut info, - ) - }; - info.as_lapack_result()?; - - let eigs_re: &[f64] = self - .eigs_re - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - let eigs_im: &[f64] = self - .eigs_im - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); - - if let Some(v) = self.vr_l.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); - } - if let Some(v) = self.vr_r.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); - } + fn calc<'work>( + &'work mut self, + a: &mut [Self::Elem], + ) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; - Ok(EigRef { - eigs: unsafe { self.eigs.slice_assume_init_ref() }, - vl: self - .vc_l - .as_ref() - .map(|v| unsafe { v.slice_assume_init_ref() }), - vr: self - .vc_r - .as_ref() - .map(|v| unsafe { v.slice_assume_init_ref() }), - }) - } + let eigs_re = self + .eigs_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let eigs_im = self + .eigs_im + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); + + if let Some(v) = self.vr_l.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + if let Some(v) = self.vr_r.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } - fn eval(mut self, a: &mut [f64]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - lapack_sys::dgeev_( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - &mut info, - ) - }; - info.as_lapack_result()?; - - let eigs_re: &[f64] = self - .eigs_re - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - let eigs_im: &[f64] = self - .eigs_im - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); - - if let Some(v) = self.vr_l.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); - } - if let Some(v) = self.vr_r.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); - } + Ok(EigRef { + eigs: unsafe { self.eigs.slice_assume_init_ref() }, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) + } - Ok(Eig { - eigs: unsafe { self.eigs.assume_init() }, - vl: self.vc_l.map(|v| unsafe { v.assume_init() }), - vr: self.vc_r.map(|v| unsafe { v.assume_init() }), - }) - } + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + + let eigs_re = self + .eigs_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let eigs_im = self + .eigs_im + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); + + if let Some(v) = self.vr_l.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + if let Some(v) = self.vr_r.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + } + + Ok(Eig { + eigs: unsafe { self.eigs.assume_init() }, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) + } + } + }; } +impl_eig_work_r!(f32, lapack_sys::sgeev_); +impl_eig_work_r!(f64, lapack_sys::dgeev_); macro_rules! impl_eig_complex { ($scalar:ty, $ev:path) => { From 0d7ca20dd7012f94c43c9ffdec826ebc22f5810e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 14:50:23 +0900 Subject: [PATCH 090/165] Use EigWork in Eig_ impl --- lax/src/eig.rs | 242 ++++--------------------------------------------- 1 file changed, 20 insertions(+), 222 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index e4462be3..ebb9e38c 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -32,6 +32,26 @@ pub trait Eig_: Scalar { ) -> Result<(Vec, Vec)>; } +macro_rules! impl_eig { + ($s:ty) => { + impl Eig_ for $s { + fn eig( + calc_v: bool, + l: MatrixLayout, + a: &mut [Self], + ) -> Result<(Vec, Vec)> { + let work = EigWork::<$s>::new(calc_v, l)?; + let Eig { eigs, vr, vl } = work.eval(a)?; + Ok((eigs, vr.or(vl).unwrap_or_default())) + } + } + }; +} +impl_eig!(c64); +impl_eig!(c32); +impl_eig!(f64); +impl_eig!(f32); + /// Working memory for [Eig_] #[derive(Debug, Clone)] pub struct EigWork { @@ -413,228 +433,6 @@ macro_rules! impl_eig_work_r { impl_eig_work_r!(f32, lapack_sys::sgeev_); impl_eig_work_r!(f64, lapack_sys::dgeev_); -macro_rules! impl_eig_complex { - ($scalar:ty, $ev:path) => { - impl Eig_ for $scalar { - fn eig( - calc_v: bool, - l: MatrixLayout, - a: &mut [Self], - ) -> Result<(Vec, Vec)> { - let (n, _) = l.size(); - // LAPACK assumes a column-major input. A row-major input can - // be interpreted as the transpose of a column-major input. So, - // for row-major inputs, we we want to solve the following, - // given the column-major input `A`: - // - // A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H - // - // So, in this case, the right eigenvectors are the conjugates - // of the left eigenvectors computed with `A`, and the - // eigenvalues are the eigenvalues computed with `A`. - let (jobvl, jobvr) = if calc_v { - match l { - MatrixLayout::C { .. } => (JobEv::All, JobEv::None), - MatrixLayout::F { .. } => (JobEv::None, JobEv::All), - } - } else { - (JobEv::None, JobEv::None) - }; - let mut eigs: Vec> = vec_uninit(n as usize); - let mut rwork: Vec> = vec_uninit(2 * n as usize); - - let mut vl: Option>> = - jobvl.then(|| vec_uninit((n * n) as usize)); - let mut vr: Option>> = - jobvr.then(|| vec_uninit((n * n) as usize)); - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $ev( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - AsPtr::as_mut_ptr(&mut rwork), - &mut info, - ) - }; - info.as_lapack_result()?; - - // actal ev - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let lwork = lwork as i32; - unsafe { - $ev( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work), - &lwork, - AsPtr::as_mut_ptr(&mut rwork), - &mut info, - ) - }; - info.as_lapack_result()?; - - let eigs = unsafe { eigs.assume_init() }; - let vr = unsafe { vr.map(|v| v.assume_init()) }; - let mut vl = unsafe { vl.map(|v| v.assume_init()) }; - - // Hermite conjugate - if jobvl.is_calc() { - for c in vl.as_mut().unwrap().iter_mut() { - c.im = -c.im; - } - } - - Ok((eigs, vr.or(vl).unwrap_or(Vec::new()))) - } - } - }; -} - -impl_eig_complex!(c64, lapack_sys::zgeev_); -impl_eig_complex!(c32, lapack_sys::cgeev_); - -macro_rules! impl_eig_real { - ($scalar:ty, $ev:path) => { - impl Eig_ for $scalar { - fn eig( - calc_v: bool, - l: MatrixLayout, - a: &mut [Self], - ) -> Result<(Vec, Vec)> { - let (n, _) = l.size(); - // LAPACK assumes a column-major input. A row-major input can - // be interpreted as the transpose of a column-major input. So, - // for row-major inputs, we we want to solve the following, - // given the column-major input `A`: - // - // A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H - // - // So, in this case, the right eigenvectors are the conjugates - // of the left eigenvectors computed with `A`, and the - // eigenvalues are the eigenvalues computed with `A`. - // - // We could conjugate the eigenvalues instead of the - // eigenvectors, but we have to reconstruct the eigenvectors - // into new matrices anyway, and by not modifying the - // eigenvalues, we preserve the nice ordering specified by - // `sgeev`/`dgeev`. - let (jobvl, jobvr) = if calc_v { - match l { - MatrixLayout::C { .. } => (JobEv::All, JobEv::None), - MatrixLayout::F { .. } => (JobEv::None, JobEv::All), - } - } else { - (JobEv::None, JobEv::None) - }; - let mut eig_re: Vec> = vec_uninit(n as usize); - let mut eig_im: Vec> = vec_uninit(n as usize); - - let mut vl: Option>> = - jobvl.then(|| vec_uninit((n * n) as usize)); - let mut vr: Option>> = - jobvr.then(|| vec_uninit((n * n) as usize)); - - // calc work size - let mut info = 0; - let mut work_size: [Self; 1] = [0.0]; - unsafe { - $ev( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eig_re), - AsPtr::as_mut_ptr(&mut eig_im), - AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ) - }; - info.as_lapack_result()?; - - // actual ev - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let lwork = lwork as i32; - unsafe { - $ev( - jobvl.as_ptr(), - jobvr.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eig_re), - AsPtr::as_mut_ptr(&mut eig_im), - AsPtr::as_mut_ptr(vl.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(vr.as_mut().map(|v| v.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work), - &lwork, - &mut info, - ) - }; - info.as_lapack_result()?; - - let eig_re = unsafe { eig_re.assume_init() }; - let eig_im = unsafe { eig_im.assume_init() }; - let vl = unsafe { vl.map(|v| v.assume_init()) }; - let vr = unsafe { vr.map(|v| v.assume_init()) }; - - // reconstruct eigenvalues - let eigs: Vec = eig_re - .iter() - .zip(eig_im.iter()) - .map(|(&re, &im)| Self::complex(re, im)) - .collect(); - - if calc_v { - let mut eigvecs = vec_uninit((n * n) as usize); - reconstruct_eigenvectors( - jobvl.is_calc(), - &eig_im, - &vr.or(vl).unwrap(), - &mut eigvecs, - ); - Ok((eigs, unsafe { eigvecs.assume_init() })) - } else { - Ok((eigs, Vec::new())) - } - } - } - }; -} - -impl_eig_real!(f64, lapack_sys::dgeev_); -impl_eig_real!(f32, lapack_sys::sgeev_); - /// Reconstruct eigenvectors into complex-array /// /// From LAPACK API https://software.intel.com/en-us/node/469230 From a94bd45e0e33a6b3e9b43190d7b44c8861733eda Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 15:57:16 +0900 Subject: [PATCH 091/165] Bug fix in using reconstruct_eigenvectors --- lax/src/eig.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index ebb9e38c..5e581cd9 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -357,11 +357,11 @@ macro_rules! impl_eig_work_r { if let Some(v) = self.vr_l.as_ref() { let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + reconstruct_eigenvectors(true, eigs_im, v, self.vc_l.as_mut().unwrap()); } if let Some(v) = self.vr_r.as_ref() { let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + reconstruct_eigenvectors(false, eigs_im, v, self.vc_r.as_mut().unwrap()); } Ok(EigRef { @@ -414,11 +414,11 @@ macro_rules! impl_eig_work_r { if let Some(v) = self.vr_l.as_ref() { let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + reconstruct_eigenvectors(true, eigs_im, v, self.vc_l.as_mut().unwrap()); } if let Some(v) = self.vr_r.as_ref() { let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_l.as_mut().unwrap()); + reconstruct_eigenvectors(false, eigs_im, v, self.vc_r.as_mut().unwrap()); } Ok(Eig { From 2bae6257fe47e18bf7978aefde0f0edd3aa040e8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 17:29:44 +0900 Subject: [PATCH 092/165] Expose lax::eig, update documents --- lax/src/eig.rs | 97 +++++++++++++++++++++++++++++++++++++++----------- lax/src/lib.rs | 5 +-- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 5e581cd9..35cee15e 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -1,3 +1,5 @@ +//! Eigenvalue problem for general matricies + use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; @@ -5,16 +7,39 @@ use num_traits::{ToPrimitive, Zero}; #[cfg_attr(doc, katexit::katexit)] /// Eigenvalue problem for general matrix /// -/// LAPACK assumes a column-major input. A row-major input can -/// be interpreted as the transpose of a column-major input. So, -/// for row-major inputs, we we want to solve the following, -/// given the column-major input `A`: +/// To manage memory more strictly, use [EigWork]. +/// +/// Right and Left eigenvalue problem +/// ---------------------------------- +/// LAPACK can solve both right eigenvalue problem +/// $$ +/// AV_R = V_R \Lambda +/// $$ +/// where $V_R = \left( v_R^1, \cdots, v_R^n \right)$ are right eigenvectors +/// and left eigenvalue problem +/// $$ +/// V_L^\dagger A = V_L^\dagger \Lambda +/// $$ +/// where $V_L = \left( v_L^1, \cdots, v_L^n \right)$ are left eigenvectors +/// and eigenvalues +/// $$ +/// \Lambda = \begin{pmatrix} +/// \lambda_1 & & 0 \\\\ +/// & \ddots & \\\\ +/// 0 & & \lambda_n +/// \end{pmatrix} +/// $$ +/// which satisfies $A v_R^i = \lambda_i v_R^i$ and +/// $\left(v_L^i\right)^\dagger A = \lambda_i \left(v_L^i\right)^\dagger$ +/// for column-major matrices, although row-major matrices are not supported. +/// Since a row-major matrix can be interpreted +/// as a transpose of a column-major matrix, +/// this transforms right eigenvalue problem to left one: /// -/// A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H +/// $$ +/// A^\dagger V = V Λ ⟺ V^\dagger A = Λ V^\dagger +/// $$ /// -/// So, in this case, the right eigenvectors are the conjugates -/// of the left eigenvectors computed with `A`, and the -/// eigenvalues are the eigenvalues computed with `A`. pub trait Eig_: Scalar { /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ /// @@ -41,7 +66,7 @@ macro_rules! impl_eig { a: &mut [Self], ) -> Result<(Vec, Vec)> { let work = EigWork::<$s>::new(calc_v, l)?; - let Eig { eigs, vr, vl } = work.eval(a)?; + let EigOwned { eigs, vr, vl } = work.eval(a)?; Ok((eigs, vr.or(vl).unwrap_or_default())) } } @@ -53,13 +78,16 @@ impl_eig!(f64); impl_eig!(f32); /// Working memory for [Eig_] -#[derive(Debug, Clone)] +#[non_exhaustive] pub struct EigWork { + /// Problem size pub n: i32, + /// Compute right eigenvectors or not pub jobvr: JobEv, + /// Compute left eigenvectors or not pub jobvl: JobEv, - /// Eigenvalues used in complex routines + /// Eigenvalues pub eigs: Vec>, /// Real part of eigenvalues used in real routines pub eigs_re: Option>>, @@ -68,9 +96,11 @@ pub struct EigWork { /// Left eigenvectors pub vc_l: Option>>, + /// Left eigenvectors used in real routines pub vr_l: Option>>, /// Right eigenvectors pub vc_r: Option>>, + /// Right eigenvectors used in real routines pub vr_r: Option>>, /// Working memory @@ -79,28 +109,55 @@ pub struct EigWork { pub rwork: Option>>, } +impl EigWork +where + T: Scalar, + EigWork: EigWorkImpl, +{ + /// Create new working memory for eigenvalues compution. + pub fn new(calc_v: bool, l: MatrixLayout) -> Result { + EigWorkImpl::new(calc_v, l) + } + + /// Compute eigenvalues and vectors on this working memory. + pub fn calc(&mut self, a: &mut [T]) -> Result> { + EigWorkImpl::calc(self, a) + } + + /// Compute eigenvalues and vectors by consuming this working memory. + pub fn eval(self, a: &mut [T]) -> Result> { + EigWorkImpl::eval(self, a) + } +} + +/// Owned result of eigenvalue problem by [EigWork::eval] #[derive(Debug, Clone, PartialEq)] -pub struct Eig { +pub struct EigOwned { + /// Eigenvalues pub eigs: Vec, + /// Right eigenvectors pub vr: Option>, + /// Left eigenvectors pub vl: Option>, } +/// Reference result of eigenvalue problem by [EigWork::calc] #[derive(Debug, Clone, PartialEq)] pub struct EigRef<'work, T: Scalar> { + /// Eigenvalues pub eigs: &'work [T::Complex], + /// Right eigenvectors pub vr: Option<&'work [T::Complex]>, + /// Left eigenvectors pub vl: Option<&'work [T::Complex]>, } +/// Helper trait for implementing [EigWork] methods pub trait EigWorkImpl: Sized { type Elem: Scalar; - /// Create new working memory for eigenvalues compution. fn new(calc_v: bool, l: MatrixLayout) -> Result; - /// Compute eigenvalues and vectors on this working memory. fn calc<'work>(&'work mut self, a: &mut [Self::Elem]) -> Result>; - /// Compute eigenvalues and vectors by consuming this working memory. - fn eval(self, a: &mut [Self::Elem]) -> Result>; + fn eval(self, a: &mut [Self::Elem]) -> Result>; } macro_rules! impl_eig_work_c { @@ -210,7 +267,7 @@ macro_rules! impl_eig_work_c { }) } - fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { let lwork = self.work.len().to_i32().unwrap(); let mut info = 0; unsafe { @@ -239,7 +296,7 @@ macro_rules! impl_eig_work_c { value.im = -value.im; } } - Ok(Eig { + Ok(EigOwned { eigs: unsafe { self.eigs.assume_init() }, vl: self.vc_l.map(|v| unsafe { v.assume_init() }), vr: self.vc_r.map(|v| unsafe { v.assume_init() }), @@ -377,7 +434,7 @@ macro_rules! impl_eig_work_r { }) } - fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { let lwork = self.work.len().to_i32().unwrap(); let mut info = 0; unsafe { @@ -421,7 +478,7 @@ macro_rules! impl_eig_work_r { reconstruct_eigenvectors(false, eigs_im, v, self.vc_r.as_mut().unwrap()); } - Ok(Eig { + Ok(EigOwned { eigs: unsafe { self.eigs.assume_init() }, vl: self.vc_l.map(|v| unsafe { v.assume_init() }), vr: self.vc_r.map(|v| unsafe { v.assume_init() }), diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 8f697fbc..397970f7 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -84,9 +84,10 @@ pub mod error; pub mod flags; pub mod layout; +pub mod eig; + mod alloc; mod cholesky; -mod eig; mod eigh; mod least_squares; mod opnorm; @@ -100,7 +101,7 @@ mod triangular; mod tridiagonal; pub use self::cholesky::*; -pub use self::eig::*; +pub use self::eig::Eig_; pub use self::eigh::*; pub use self::flags::*; pub use self::least_squares::*; From 96a83e66790ed11ddcf8dc3caf7f5cfc8383eaa3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 17:40:40 +0900 Subject: [PATCH 093/165] Use calc from eval --- lax/src/eig.rs | 73 ++------------------------------------------------ 1 file changed, 2 insertions(+), 71 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 35cee15e..2c24a842 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -268,34 +268,7 @@ macro_rules! impl_eig_work_c { } fn eval(mut self, a: &mut [Self::Elem]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - $ev( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(&mut self.eigs), - AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), - &mut info, - ) - }; - info.as_lapack_result()?; - // Hermite conjugate - if let Some(vl) = self.vc_l.as_mut() { - for value in vl { - let value = unsafe { value.assume_init_mut() }; - value.im = -value.im; - } - } + let _eig_ref = self.calc(a)?; Ok(EigOwned { eigs: unsafe { self.eigs.assume_init() }, vl: self.vc_l.map(|v| unsafe { v.assume_init() }), @@ -435,49 +408,7 @@ macro_rules! impl_eig_work_r { } fn eval(mut self, a: &mut [Self::Elem]) -> Result> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - $ev( - self.jobvl.as_ptr(), - self.jobvr.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(self.eigs_re.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.eigs_im.as_mut().unwrap()), - AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), - &self.n, - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - &mut info, - ) - }; - info.as_lapack_result()?; - - let eigs_re = self - .eigs_re - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - let eigs_im = self - .eigs_im - .as_ref() - .map(|e| unsafe { e.slice_assume_init_ref() }) - .unwrap(); - reconstruct_eigs(eigs_re, eigs_im, &mut self.eigs); - - if let Some(v) = self.vr_l.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(true, eigs_im, v, self.vc_l.as_mut().unwrap()); - } - if let Some(v) = self.vr_r.as_ref() { - let v = unsafe { v.slice_assume_init_ref() }; - reconstruct_eigenvectors(false, eigs_im, v, self.vc_r.as_mut().unwrap()); - } - + let _eig_ref = self.calc(a)?; Ok(EigOwned { eigs: unsafe { self.eigs.assume_init() }, vl: self.vc_l.map(|v| unsafe { v.assume_init() }), From 1334d85fc93d8bd807b121d42161c6fc8f7f9b4a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 22:26:34 +0900 Subject: [PATCH 094/165] Merge Eig_ trait into Lapack trait --- lax/src/eig.rs | 38 -------------------------------------- lax/src/lib.rs | 34 +++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 2c24a842..e441c68d 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -40,44 +40,6 @@ use num_traits::{ToPrimitive, Zero}; /// A^\dagger V = V Λ ⟺ V^\dagger A = Λ V^\dagger /// $$ /// -pub trait Eig_: Scalar { - /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:------|:------|:------|:------| - /// | sgeev | dgeev | cgeev | zgeev | - /// - fn eig( - calc_v: bool, - l: MatrixLayout, - a: &mut [Self], - ) -> Result<(Vec, Vec)>; -} - -macro_rules! impl_eig { - ($s:ty) => { - impl Eig_ for $s { - fn eig( - calc_v: bool, - l: MatrixLayout, - a: &mut [Self], - ) -> Result<(Vec, Vec)> { - let work = EigWork::<$s>::new(calc_v, l)?; - let EigOwned { eigs, vr, vl } = work.eval(a)?; - Ok((eigs, vr.or(vl).unwrap_or_default())) - } - } - }; -} -impl_eig!(c64); -impl_eig!(c32); -impl_eig!(f64); -impl_eig!(f32); - -/// Working memory for [Eig_] #[non_exhaustive] pub struct EigWork { /// Problem size diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 397970f7..6036166f 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -101,7 +101,6 @@ mod triangular; mod tridiagonal; pub use self::cholesky::*; -pub use self::eig::Eig_; pub use self::eigh::*; pub use self::flags::*; pub use self::least_squares::*; @@ -115,7 +114,7 @@ pub use self::svddc::*; pub use self::triangular::*; pub use self::tridiagonal::*; -use self::alloc::*; +use self::{alloc::*, error::*, layout::*}; use cauchy::*; use std::mem::MaybeUninit; @@ -130,16 +129,37 @@ pub trait Lapack: + Solve_ + Solveh_ + Cholesky_ - + Eig_ + Eigh_ + Triangular_ + Tridiagonal_ + Rcond_ + LeastSquaresSvdDivideConquer_ { + /// Compute right eigenvalue and eigenvectors + fn eig( + calc_v: bool, + l: MatrixLayout, + a: &mut [Self], + ) -> Result<(Vec, Vec)>; } -impl Lapack for f32 {} -impl Lapack for f64 {} -impl Lapack for c32 {} -impl Lapack for c64 {} +macro_rules! impl_lapack { + ($s:ty) => { + impl Lapack for $s { + fn eig( + calc_v: bool, + l: MatrixLayout, + a: &mut [Self], + ) -> Result<(Vec, Vec)> { + use eig::*; + let work = EigWork::<$s>::new(calc_v, l)?; + let EigOwned { eigs, vr, vl } = work.eval(a)?; + Ok((eigs, vr.or(vl).unwrap_or_default())) + } + } + }; +} +impl_lapack!(c64); +impl_lapack!(c32); +impl_lapack!(f64); +impl_lapack!(f32); From 3535eee857a06300bb7c0c7c4f735c1889584f04 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 22:45:19 +0900 Subject: [PATCH 095/165] Update document --- lax/src/eig.rs | 8 ++++++++ lax/src/lib.rs | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index e441c68d..710beb9c 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -1,4 +1,12 @@ //! Eigenvalue problem for general matricies +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:------|:------|:------|:------| +//! | sgeev | dgeev | cgeev | zgeev | +//! use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 6036166f..00393916 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -58,7 +58,7 @@ //! According to the property input metrix, //! there are several types of eigenvalue problem API //! -//! - [Eig_] trait provides methods for eigenvalue problem for general matrix. +//! - [eig] module for eigenvalue problem for general matrix. //! - [Eigh_] trait provides methods for eigenvalue problem for symmetric/hermite matrix. //! //! Singular Value Decomposition @@ -146,6 +146,7 @@ pub trait Lapack: macro_rules! impl_lapack { ($s:ty) => { impl Lapack for $s { + /// Compute right eigenvalue and eigenvectors fn eig( calc_v: bool, l: MatrixLayout, From c953001c864a4bb752f64b0abc65c9258f9e16fc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 22:57:59 +0900 Subject: [PATCH 096/165] Split generalized eigenvalue routine --- lax/src/eigh.rs | 99 +++------------------------------ lax/src/eigh_generalized.rs | 106 ++++++++++++++++++++++++++++++++++++ lax/src/lib.rs | 3 + 3 files changed, 118 insertions(+), 90 deletions(-) create mode 100644 lax/src/eigh_generalized.rs diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 54af63f7..cd83f2b8 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -21,33 +21,16 @@ pub trait Eigh_: Scalar { uplo: UPLO, a: &mut [Self], ) -> Result>; - - /// Compute generalized right eigenvalue and eigenvectors $Ax = \lambda B x$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:------|:------|:------|:------| - /// | ssygv | dsygv | chegv | zhegv | - /// - fn eigh_generalized( - calc_eigenvec: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - b: &mut [Self], - ) -> Result>; } macro_rules! impl_eigh { - (@real, $scalar:ty, $ev:path, $evg:path) => { - impl_eigh!(@body, $scalar, $ev, $evg, ); + (@real, $scalar:ty, $ev:path) => { + impl_eigh!(@body, $scalar, $ev, ); }; - (@complex, $scalar:ty, $ev:path, $evg:path) => { - impl_eigh!(@body, $scalar, $ev, $evg, rwork); + (@complex, $scalar:ty, $ev:path) => { + impl_eigh!(@body, $scalar, $ev, rwork); }; - (@body, $scalar:ty, $ev:path, $evg:path, $($rwork_ident:ident),*) => { + (@body, $scalar:ty, $ev:path, $($rwork_ident:ident),*) => { impl Eigh_ for $scalar { fn eigh( calc_v: bool, @@ -106,75 +89,11 @@ macro_rules! impl_eigh { let eigs = unsafe { eigs.assume_init() }; Ok(eigs) } - - fn eigh_generalized( - calc_v: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - b: &mut [Self], - ) -> Result> { - assert_eq!(layout.len(), layout.lda()); - let n = layout.len(); - let jobz = if calc_v { JobEv::All } else { JobEv::None }; - let mut eigs: Vec> = vec_uninit(n as usize); - - $( - let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2); - )* - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $evg( - &1, // ITYPE A*x = (lambda)*B*x - jobz.as_ptr(), - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(b), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - // actual evg - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let lwork = lwork as i32; - unsafe { - $evg( - &1, // ITYPE A*x = (lambda)*B*x - jobz.as_ptr(), - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(b), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work), - &lwork, - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - let eigs = unsafe { eigs.assume_init() }; - Ok(eigs) - } } }; } // impl_eigh! -impl_eigh!(@real, f64, lapack_sys::dsyev_, lapack_sys::dsygv_); -impl_eigh!(@real, f32, lapack_sys::ssyev_, lapack_sys::ssygv_); -impl_eigh!(@complex, c64, lapack_sys::zheev_, lapack_sys::zhegv_); -impl_eigh!(@complex, c32, lapack_sys::cheev_, lapack_sys::chegv_); +impl_eigh!(@real, f64, lapack_sys::dsyev_); +impl_eigh!(@real, f32, lapack_sys::ssyev_); +impl_eigh!(@complex, c64, lapack_sys::zheev_); +impl_eigh!(@complex, c32, lapack_sys::cheev_); diff --git a/lax/src/eigh_generalized.rs b/lax/src/eigh_generalized.rs new file mode 100644 index 00000000..2a0c5302 --- /dev/null +++ b/lax/src/eigh_generalized.rs @@ -0,0 +1,106 @@ +use super::*; +use crate::{error::*, layout::MatrixLayout}; +use cauchy::*; +use num_traits::{ToPrimitive, Zero}; + +#[cfg_attr(doc, katexit::katexit)] +/// Eigenvalue problem for symmetric/hermite matrix +pub trait EighGeneralized_: Scalar { + /// Compute generalized right eigenvalue and eigenvectors $Ax = \lambda B x$ + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:------|:------|:------|:------| + /// | ssygv | dsygv | chegv | zhegv | + /// + fn eigh_generalized( + calc_eigenvec: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + b: &mut [Self], + ) -> Result>; +} + +macro_rules! impl_eigh { + (@real, $scalar:ty, $evg:path) => { + impl_eigh!(@body, $scalar, $evg, ); + }; + (@complex, $scalar:ty, $evg:path) => { + impl_eigh!(@body, $scalar, $evg, rwork); + }; + (@body, $scalar:ty, $evg:path, $($rwork_ident:ident),*) => { + impl EighGeneralized_ for $scalar { + fn eigh_generalized( + calc_v: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + b: &mut [Self], + ) -> Result> { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_v { JobEv::All } else { JobEv::None }; + let mut eigs: Vec> = vec_uninit(n as usize); + + $( + let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2); + )* + + // calc work size + let mut info = 0; + let mut work_size = [Self::zero()]; + unsafe { + $evg( + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(b), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* + &mut info, + ); + } + info.as_lapack_result()?; + + // actual evg + let lwork = work_size[0].to_usize().unwrap(); + let mut work: Vec> = vec_uninit(lwork); + let lwork = lwork as i32; + unsafe { + $evg( + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &n, + AsPtr::as_mut_ptr(b), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work), + &lwork, + $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* + &mut info, + ); + } + info.as_lapack_result()?; + let eigs = unsafe { eigs.assume_init() }; + Ok(eigs) + } + } + }; +} // impl_eigh! + +impl_eigh!(@real, f64, lapack_sys::dsygv_); +impl_eigh!(@real, f32, lapack_sys::ssygv_); +impl_eigh!(@complex, c64, lapack_sys::zhegv_); +impl_eigh!(@complex, c32, lapack_sys::chegv_); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 00393916..b246b665 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -89,6 +89,7 @@ pub mod eig; mod alloc; mod cholesky; mod eigh; +mod eigh_generalized; mod least_squares; mod opnorm; mod qr; @@ -102,6 +103,7 @@ mod tridiagonal; pub use self::cholesky::*; pub use self::eigh::*; +pub use self::eigh_generalized::*; pub use self::flags::*; pub use self::least_squares::*; pub use self::opnorm::*; @@ -130,6 +132,7 @@ pub trait Lapack: + Solveh_ + Cholesky_ + Eigh_ + + EighGeneralized_ + Triangular_ + Tridiagonal_ + Rcond_ From b864638d1a4382eac3336df3f0d3197079a157ac Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 24 Sep 2022 23:56:02 +0900 Subject: [PATCH 097/165] EighWork --- lax/src/eigh.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index cd83f2b8..b8e9e9f7 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -23,6 +23,98 @@ pub trait Eigh_: Scalar { ) -> Result>; } +pub struct EighWork { + pub n: i32, + pub jobz: JobEv, + pub eigs: Vec>, + pub work: Vec>, + pub rwork: Option>>, +} + +pub trait EighWorkImpl: Sized { + type Elem: Scalar; + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result; + fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem]) + -> Result<&[::Real]>; + fn eval(self, uplo: UPLO, a: &mut [Self::Elem]) -> Result::Real>>; +} + +impl EighWorkImpl for EighWork { + type Elem = c64; + + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_eigenvectors { + JobEv::All + } else { + JobEv::None + }; + let mut eigs = vec_uninit(n as usize); + let mut rwork = vec_uninit(3 * n as usize - 2 as usize); + let mut info = 0; + let mut work_size = [c64::zero()]; + unsafe { + lapack_sys::zheev_( + jobz.as_ptr(), + UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(EighWork { + n, + eigs, + jobz, + work, + rwork: Some(rwork), + }) + } + + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result<&[::Real]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + lapack_sys::zheev_( + self.jobz.as_ptr(), + uplo.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ); + } + info.as_lapack_result()?; + Ok(unsafe { self.eigs.slice_assume_init_ref() }) + } + + fn eval( + mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result::Real>> { + let _eig = self.calc(uplo, a)?; + Ok(unsafe { self.eigs.assume_init() }) + } +} + macro_rules! impl_eigh { (@real, $scalar:ty, $ev:path) => { impl_eigh!(@body, $scalar, $ev, ); From 5d0753879cd8817d259748d087f8f1f240387557 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 18:06:24 +0900 Subject: [PATCH 098/165] impl EighWorkImpl for EighWork in c32, f32, f64 --- lax/src/eigh.rs | 229 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 157 insertions(+), 72 deletions(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index b8e9e9f7..a8bbe14c 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -39,81 +39,166 @@ pub trait EighWorkImpl: Sized { fn eval(self, uplo: UPLO, a: &mut [Self::Elem]) -> Result::Real>>; } -impl EighWorkImpl for EighWork { - type Elem = c64; - - fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { - assert_eq!(layout.len(), layout.lda()); - let n = layout.len(); - let jobz = if calc_eigenvectors { - JobEv::All - } else { - JobEv::None - }; - let mut eigs = vec_uninit(n as usize); - let mut rwork = vec_uninit(3 * n as usize - 2 as usize); - let mut info = 0; - let mut work_size = [c64::zero()]; - unsafe { - lapack_sys::zheev_( - jobz.as_ptr(), - UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO - &n, - std::ptr::null_mut(), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - AsPtr::as_mut_ptr(&mut rwork), - &mut info, - ); - } - info.as_lapack_result()?; - let lwork = work_size[0].to_usize().unwrap(); - let work = vec_uninit(lwork); - Ok(EighWork { - n, - eigs, - jobz, - work, - rwork: Some(rwork), - }) - } - - fn calc( - &mut self, - uplo: UPLO, - a: &mut [Self::Elem], - ) -> Result<&[::Real]> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - lapack_sys::zheev_( - self.jobz.as_ptr(), - uplo.as_ptr(), - &self.n, - AsPtr::as_mut_ptr(a), - &self.n, - AsPtr::as_mut_ptr(&mut self.eigs), - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), - &mut info, - ); +macro_rules! impl_eigh_work_c { + ($c:ty, $ev:path) => { + impl EighWorkImpl for EighWork<$c> { + type Elem = $c; + + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_eigenvectors { + JobEv::All + } else { + JobEv::None + }; + let mut eigs = vec_uninit(n as usize); + let mut rwork = vec_uninit(3 * n as usize - 2 as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $ev( + jobz.as_ptr(), + UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(EighWork { + n, + eigs, + jobz, + work, + rwork: Some(rwork), + }) + } + + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result<&[::Real]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobz.as_ptr(), + uplo.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ); + } + info.as_lapack_result()?; + Ok(unsafe { self.eigs.slice_assume_init_ref() }) + } + + fn eval( + mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result::Real>> { + let _eig = self.calc(uplo, a)?; + Ok(unsafe { self.eigs.assume_init() }) + } } - info.as_lapack_result()?; - Ok(unsafe { self.eigs.slice_assume_init_ref() }) - } + }; +} +impl_eigh_work_c!(c64, lapack_sys::zheev_); +impl_eigh_work_c!(c32, lapack_sys::cheev_); - fn eval( - mut self, - uplo: UPLO, - a: &mut [Self::Elem], - ) -> Result::Real>> { - let _eig = self.calc(uplo, a)?; - Ok(unsafe { self.eigs.assume_init() }) - } +macro_rules! impl_eigh_work_r { + ($f:ty, $ev:path) => { + impl EighWorkImpl for EighWork<$f> { + type Elem = $f; + + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_eigenvectors { + JobEv::All + } else { + JobEv::None + }; + let mut eigs = vec_uninit(n as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $ev( + jobz.as_ptr(), + UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(EighWork { + n, + eigs, + jobz, + work, + rwork: None, + }) + } + + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result<&[::Real]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobz.as_ptr(), + uplo.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ); + } + info.as_lapack_result()?; + Ok(unsafe { self.eigs.slice_assume_init_ref() }) + } + + fn eval( + mut self, + uplo: UPLO, + a: &mut [Self::Elem], + ) -> Result::Real>> { + let _eig = self.calc(uplo, a)?; + Ok(unsafe { self.eigs.assume_init() }) + } + } + }; } +impl_eigh_work_r!(f64, lapack_sys::dsyev_); +impl_eigh_work_r!(f32, lapack_sys::ssyev_); macro_rules! impl_eigh { (@real, $scalar:ty, $ev:path) => { From 370c7d20742ff225cf8dc80e95ac6508c5b0e40b Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 18:16:28 +0900 Subject: [PATCH 099/165] Merge Eigh_ into Lapack trait --- lax/src/eigh.rs | 104 +++++------------------------------------------- lax/src/lib.rs | 23 +++++++++-- 2 files changed, 29 insertions(+), 98 deletions(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index a8bbe14c..429aaa75 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -1,28 +1,17 @@ +//! Eigenvalue problem for symmetric/Hermite matricies +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:------|:------|:------|:------| +//! | ssyev | dsyev | cheev | zheev | + use super::*; use crate::{error::*, layout::MatrixLayout}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -#[cfg_attr(doc, katexit::katexit)] -/// Eigenvalue problem for symmetric/hermite matrix -pub trait Eigh_: Scalar { - /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:------|:------|:------|:------| - /// | ssyev | dsyev | cheev | zheev | - /// - fn eigh( - calc_eigenvec: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - ) -> Result>; -} - pub struct EighWork { pub n: i32, pub jobz: JobEv, @@ -199,78 +188,3 @@ macro_rules! impl_eigh_work_r { } impl_eigh_work_r!(f64, lapack_sys::dsyev_); impl_eigh_work_r!(f32, lapack_sys::ssyev_); - -macro_rules! impl_eigh { - (@real, $scalar:ty, $ev:path) => { - impl_eigh!(@body, $scalar, $ev, ); - }; - (@complex, $scalar:ty, $ev:path) => { - impl_eigh!(@body, $scalar, $ev, rwork); - }; - (@body, $scalar:ty, $ev:path, $($rwork_ident:ident),*) => { - impl Eigh_ for $scalar { - fn eigh( - calc_v: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - ) -> Result> { - assert_eq!(layout.len(), layout.lda()); - let n = layout.len(); - let jobz = if calc_v { JobEv::All } else { JobEv::None }; - let mut eigs: Vec> = vec_uninit(n as usize); - - $( - let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2 as usize); - )* - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $ev( - jobz.as_ptr() , - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - // actual ev - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let lwork = lwork as i32; - unsafe { - $ev( - jobz.as_ptr(), - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work), - &lwork, - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - let eigs = unsafe { eigs.assume_init() }; - Ok(eigs) - } - } - }; -} // impl_eigh! - -impl_eigh!(@real, f64, lapack_sys::dsyev_); -impl_eigh!(@real, f32, lapack_sys::ssyev_); -impl_eigh!(@complex, c64, lapack_sys::zheev_); -impl_eigh!(@complex, c32, lapack_sys::cheev_); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index b246b665..f02532e5 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -131,25 +131,31 @@ pub trait Lapack: + Solve_ + Solveh_ + Cholesky_ - + Eigh_ + EighGeneralized_ + Triangular_ + Tridiagonal_ + Rcond_ + LeastSquaresSvdDivideConquer_ { - /// Compute right eigenvalue and eigenvectors + /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, l: MatrixLayout, a: &mut [Self], ) -> Result<(Vec, Vec)>; + + /// Compute right eigenvalue and eigenvectors for a symmetric or hermite matrix + fn eigh( + calc_eigenvec: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + ) -> Result>; } macro_rules! impl_lapack { ($s:ty) => { impl Lapack for $s { - /// Compute right eigenvalue and eigenvectors fn eig( calc_v: bool, l: MatrixLayout, @@ -160,6 +166,17 @@ macro_rules! impl_lapack { let EigOwned { eigs, vr, vl } = work.eval(a)?; Ok((eigs, vr.or(vl).unwrap_or_default())) } + + fn eigh( + calc_eigenvec: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + ) -> Result> { + use eigh::*; + let work = EighWork::<$s>::new(calc_eigenvec, layout)?; + work.eval(uplo, a) + } } }; } From c7a45862d184687da5ed73f1e2b0ef1fd5d60425 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 18:40:30 +0900 Subject: [PATCH 100/165] Add EighGeneralizedWork --- lax/src/eigh_generalized.rs | 212 ++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/lax/src/eigh_generalized.rs b/lax/src/eigh_generalized.rs index 2a0c5302..16ded9c6 100644 --- a/lax/src/eigh_generalized.rs +++ b/lax/src/eigh_generalized.rs @@ -1,8 +1,220 @@ +//! Compute generalized right eigenvalue and eigenvectors +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:------|:------|:------|:------| +//! | ssygv | dsygv | chegv | zhegv | +//! + use super::*; use crate::{error::*, layout::MatrixLayout}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +pub struct EighGeneralizedWork { + pub n: i32, + pub jobz: JobEv, + pub eigs: Vec>, + pub work: Vec>, + pub rwork: Option>>, +} + +pub trait EighGeneralizedWorkImpl: Sized { + type Elem: Scalar; + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result; + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result<&[::Real]>; + fn eval( + self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result::Real>>; +} + +macro_rules! impl_eigh_generalized_work_c { + ($c:ty, $gv:path) => { + impl EighGeneralizedWorkImpl for EighGeneralizedWork<$c> { + type Elem = $c; + + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_eigenvectors { + JobEv::All + } else { + JobEv::None + }; + let mut eigs = vec_uninit(n as usize); + let mut rwork = vec_uninit(3 * n as usize - 2 as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $gv( + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO + &n, + std::ptr::null_mut(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(EighGeneralizedWork { + n, + eigs, + jobz, + work, + rwork: Some(rwork), + }) + } + + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result<&[::Real]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $gv( + &1, // ITYPE A*x = (lambda)*B*x + self.jobz.as_ptr(), + uplo.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(b), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ); + } + info.as_lapack_result()?; + Ok(unsafe { self.eigs.slice_assume_init_ref() }) + } + + fn eval( + mut self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result::Real>> { + let _eig = self.calc(uplo, a, b)?; + Ok(unsafe { self.eigs.assume_init() }) + } + } + }; +} +impl_eigh_generalized_work_c!(c64, lapack_sys::zhegv_); +impl_eigh_generalized_work_c!(c32, lapack_sys::chegv_); + +macro_rules! impl_eigh_generalized_work_r { + ($f:ty, $gv:path) => { + impl EighGeneralizedWorkImpl for EighGeneralizedWork<$f> { + type Elem = $f; + + fn new(calc_eigenvectors: bool, layout: MatrixLayout) -> Result { + assert_eq!(layout.len(), layout.lda()); + let n = layout.len(); + let jobz = if calc_eigenvectors { + JobEv::All + } else { + JobEv::None + }; + let mut eigs = vec_uninit(n as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $gv( + &1, // ITYPE A*x = (lambda)*B*x + jobz.as_ptr(), + UPLO::Upper.as_ptr(), // dummy, working memory is not affected by UPLO + &n, + std::ptr::null_mut(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut eigs), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(EighGeneralizedWork { + n, + eigs, + jobz, + work, + rwork: None, + }) + } + + fn calc( + &mut self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result<&[::Real]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $gv( + &1, // ITYPE A*x = (lambda)*B*x + self.jobz.as_ptr(), + uplo.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(b), + &self.n, + AsPtr::as_mut_ptr(&mut self.eigs), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ); + } + info.as_lapack_result()?; + Ok(unsafe { self.eigs.slice_assume_init_ref() }) + } + + fn eval( + mut self, + uplo: UPLO, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result::Real>> { + let _eig = self.calc(uplo, a, b)?; + Ok(unsafe { self.eigs.assume_init() }) + } + } + }; +} +impl_eigh_generalized_work_r!(f64, lapack_sys::dsygv_); +impl_eigh_generalized_work_r!(f32, lapack_sys::ssygv_); + #[cfg_attr(doc, katexit::katexit)] /// Eigenvalue problem for symmetric/hermite matrix pub trait EighGeneralized_: Scalar { From bef10838ecd5266dfbbda3c04017b2f700cd546f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 18:51:01 +0900 Subject: [PATCH 101/165] Merge EighGeneralized_ to Lapack trait --- lax/src/eigh_generalized.rs | 102 ------------------------------------ lax/src/lib.rs | 22 +++++++- 2 files changed, 21 insertions(+), 103 deletions(-) diff --git a/lax/src/eigh_generalized.rs b/lax/src/eigh_generalized.rs index 16ded9c6..1771cee8 100644 --- a/lax/src/eigh_generalized.rs +++ b/lax/src/eigh_generalized.rs @@ -214,105 +214,3 @@ macro_rules! impl_eigh_generalized_work_r { } impl_eigh_generalized_work_r!(f64, lapack_sys::dsygv_); impl_eigh_generalized_work_r!(f32, lapack_sys::ssygv_); - -#[cfg_attr(doc, katexit::katexit)] -/// Eigenvalue problem for symmetric/hermite matrix -pub trait EighGeneralized_: Scalar { - /// Compute generalized right eigenvalue and eigenvectors $Ax = \lambda B x$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:------|:------|:------|:------| - /// | ssygv | dsygv | chegv | zhegv | - /// - fn eigh_generalized( - calc_eigenvec: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - b: &mut [Self], - ) -> Result>; -} - -macro_rules! impl_eigh { - (@real, $scalar:ty, $evg:path) => { - impl_eigh!(@body, $scalar, $evg, ); - }; - (@complex, $scalar:ty, $evg:path) => { - impl_eigh!(@body, $scalar, $evg, rwork); - }; - (@body, $scalar:ty, $evg:path, $($rwork_ident:ident),*) => { - impl EighGeneralized_ for $scalar { - fn eigh_generalized( - calc_v: bool, - layout: MatrixLayout, - uplo: UPLO, - a: &mut [Self], - b: &mut [Self], - ) -> Result> { - assert_eq!(layout.len(), layout.lda()); - let n = layout.len(); - let jobz = if calc_v { JobEv::All } else { JobEv::None }; - let mut eigs: Vec> = vec_uninit(n as usize); - - $( - let mut $rwork_ident: Vec> = vec_uninit(3 * n as usize - 2); - )* - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $evg( - &1, // ITYPE A*x = (lambda)*B*x - jobz.as_ptr(), - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(b), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - // actual evg - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let lwork = lwork as i32; - unsafe { - $evg( - &1, // ITYPE A*x = (lambda)*B*x - jobz.as_ptr(), - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &n, - AsPtr::as_mut_ptr(b), - &n, - AsPtr::as_mut_ptr(&mut eigs), - AsPtr::as_mut_ptr(&mut work), - &lwork, - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - let eigs = unsafe { eigs.assume_init() }; - Ok(eigs) - } - } - }; -} // impl_eigh! - -impl_eigh!(@real, f64, lapack_sys::dsygv_); -impl_eigh!(@real, f32, lapack_sys::ssygv_); -impl_eigh!(@complex, c64, lapack_sys::zhegv_); -impl_eigh!(@complex, c32, lapack_sys::chegv_); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index f02532e5..b5f9f16c 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -131,7 +131,6 @@ pub trait Lapack: + Solve_ + Solveh_ + Cholesky_ - + EighGeneralized_ + Triangular_ + Tridiagonal_ + Rcond_ @@ -151,6 +150,15 @@ pub trait Lapack: uplo: UPLO, a: &mut [Self], ) -> Result>; + + /// Compute right eigenvalue and eigenvectors for a symmetric or hermite matrix + fn eigh_generalized( + calc_eigenvec: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + b: &mut [Self], + ) -> Result>; } macro_rules! impl_lapack { @@ -177,6 +185,18 @@ macro_rules! impl_lapack { let work = EighWork::<$s>::new(calc_eigenvec, layout)?; work.eval(uplo, a) } + + fn eigh_generalized( + calc_eigenvec: bool, + layout: MatrixLayout, + uplo: UPLO, + a: &mut [Self], + b: &mut [Self], + ) -> Result> { + use eigh_generalized::*; + let work = EighGeneralizedWork::<$s>::new(calc_eigenvec, layout)?; + work.eval(uplo, a, b) + } } }; } From f45c83c851c2349374aa4e4dfaf3e6558e1afdd1 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 19:29:16 +0900 Subject: [PATCH 102/165] Do not allow rustdoc::broken_intra_doc_links --- lax/src/lib.rs | 2 ++ ndarray-linalg/src/lib.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index b5f9f16c..adefcfea 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -71,6 +71,8 @@ //! for solving least square problem by SVD //! +#![deny(rustdoc::broken_intra_doc_links)] + #[cfg(any(feature = "intel-mkl-system", feature = "intel-mkl-static"))] extern crate intel_mkl_src as _src; diff --git a/ndarray-linalg/src/lib.rs b/ndarray-linalg/src/lib.rs index 8a8088c8..a957e20b 100644 --- a/ndarray-linalg/src/lib.rs +++ b/ndarray-linalg/src/lib.rs @@ -44,6 +44,7 @@ clippy::type_complexity, clippy::ptr_arg )] +#![deny(rustdoc::broken_intra_doc_links)] #[macro_use] extern crate ndarray; From 6b984de5d37c86203188c3f22d8284e0a0cff5df Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 19:31:35 +0900 Subject: [PATCH 103/165] Add cargo-doc job --- .github/workflows/rust.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 199b9cad..9cd3919b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,6 +25,15 @@ jobs: command: check args: --all-targets + check-doc: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: doc + args: --no-deps + clippy: runs-on: ubuntu-22.04 steps: From 3f7349044549947c3c4f07226df834c98d74db66 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 25 Sep 2022 19:39:46 +0900 Subject: [PATCH 104/165] Update document --- lax/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index adefcfea..ede5b4d0 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -59,7 +59,8 @@ //! there are several types of eigenvalue problem API //! //! - [eig] module for eigenvalue problem for general matrix. -//! - [Eigh_] trait provides methods for eigenvalue problem for symmetric/hermite matrix. +//! - [eigh] module for eigenvalue problem for symmetric/hermite matrix. +//! - [eigh_generalized] module for generalized eigenvalue problem for symmetric/hermite matrix. //! //! Singular Value Decomposition //! ----------------------------- From 8c4aae3de88fd638697a6cdfa49d0ffd535a6c60 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 02:09:29 +0900 Subject: [PATCH 105/165] Deny rustdoc::private_intra_doc_links lint --- lax/src/lib.rs | 2 +- ndarray-linalg/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index ede5b4d0..0c236080 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -72,7 +72,7 @@ //! for solving least square problem by SVD //! -#![deny(rustdoc::broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)] #[cfg(any(feature = "intel-mkl-system", feature = "intel-mkl-static"))] extern crate intel_mkl_src as _src; diff --git a/ndarray-linalg/src/lib.rs b/ndarray-linalg/src/lib.rs index a957e20b..784e1dff 100644 --- a/ndarray-linalg/src/lib.rs +++ b/ndarray-linalg/src/lib.rs @@ -44,7 +44,7 @@ clippy::type_complexity, clippy::ptr_arg )] -#![deny(rustdoc::broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)] #[macro_use] extern crate ndarray; From 28be9bbf67acf1c83cd64ea6cd4a4b3b0ea08996 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 02:12:00 +0900 Subject: [PATCH 106/165] Make eigh and eigh_generalized sub-modules public, update documents --- lax/src/eigh.rs | 2 +- lax/src/eigh_generalized.rs | 2 +- lax/src/lib.rs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 429aaa75..bb3ca500 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -1,4 +1,4 @@ -//! Eigenvalue problem for symmetric/Hermite matricies +//! Eigenvalue problem for symmetric/Hermitian matricies //! //! LAPACK correspondance //! ---------------------- diff --git a/lax/src/eigh_generalized.rs b/lax/src/eigh_generalized.rs index 1771cee8..5d4d83ca 100644 --- a/lax/src/eigh_generalized.rs +++ b/lax/src/eigh_generalized.rs @@ -1,4 +1,4 @@ -//! Compute generalized right eigenvalue and eigenvectors +//! Generalized eigenvalue problem for symmetric/Hermitian matrices //! //! LAPACK correspondance //! ---------------------- diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 0c236080..624e1570 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -88,11 +88,11 @@ pub mod flags; pub mod layout; pub mod eig; +pub mod eigh; +pub mod eigh_generalized; mod alloc; mod cholesky; -mod eigh; -mod eigh_generalized; mod least_squares; mod opnorm; mod qr; @@ -105,8 +105,6 @@ mod triangular; mod tridiagonal; pub use self::cholesky::*; -pub use self::eigh::*; -pub use self::eigh_generalized::*; pub use self::flags::*; pub use self::least_squares::*; pub use self::opnorm::*; From cc369fbb41835a5999278fd164033b94b47e0aeb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 01:48:53 +0900 Subject: [PATCH 107/165] HouseholderWork and HouseholderWorkImpl --- lax/src/qr.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/lax/src/qr.rs b/lax/src/qr.rs index bdfb7571..bbaf9cd0 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -18,6 +18,116 @@ pub trait QR_: Sized { fn qr(l: MatrixLayout, a: &mut [Self]) -> Result>; } +pub struct HouseholderWork { + pub m: i32, + pub n: i32, + pub layout: MatrixLayout, + pub tau: Vec>, + pub work: Vec>, +} + +pub trait HouseholderWorkImpl: Sized { + type Elem: Scalar; + fn new(l: MatrixLayout) -> Result; + fn calc(&mut self, a: &mut [Self::Elem]) -> Result<&[Self::Elem]>; + fn eval(self, a: &mut [Self::Elem]) -> Result>; +} + +macro_rules! impl_householder_work { + ($s:ty, $qrf:path, $lqf: path) => { + impl HouseholderWorkImpl for HouseholderWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let m = layout.lda(); + let n = layout.len(); + let k = m.min(n); + let mut tau = vec_uninit(k as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + match layout { + MatrixLayout::F { .. } => unsafe { + $qrf( + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }, + MatrixLayout::C { .. } => unsafe { + $lqf( + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut tau), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }, + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(HouseholderWork { + n, + m, + layout, + tau, + work, + }) + } + + fn calc(&mut self, a: &mut [Self::Elem]) -> Result<&[Self::Elem]> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + match self.layout { + MatrixLayout::F { .. } => unsafe { + $qrf( + &self.m, + &self.n, + AsPtr::as_mut_ptr(a), + &self.m, + AsPtr::as_mut_ptr(&mut self.tau), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ); + }, + MatrixLayout::C { .. } => unsafe { + $lqf( + &self.m, + &self.n, + AsPtr::as_mut_ptr(a), + &self.m, + AsPtr::as_mut_ptr(&mut self.tau), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ); + }, + } + info.as_lapack_result()?; + Ok(unsafe { self.tau.slice_assume_init_ref() }) + } + + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let _eig = self.calc(a)?; + Ok(unsafe { self.tau.assume_init() }) + } + } + }; +} +impl_householder_work!(c64, lapack_sys::zgeqrf_, lapack_sys::zgelqf_); +impl_householder_work!(c32, lapack_sys::cgeqrf_, lapack_sys::cgelqf_); +impl_householder_work!(f64, lapack_sys::dgeqrf_, lapack_sys::dgelqf_); +impl_householder_work!(f32, lapack_sys::sgeqrf_, lapack_sys::sgelqf_); + macro_rules! impl_qr { ($scalar:ty, $qrf:path, $lqf:path, $gqr:path, $glq:path) => { impl QR_ for $scalar { From ae578577c0aa736f849b90634b419530e5bea03e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 18:13:07 +0900 Subject: [PATCH 108/165] QWork and QWorkImpl --- lax/src/qr.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/lax/src/qr.rs b/lax/src/qr.rs index bbaf9cd0..97abc00a 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -128,6 +128,107 @@ impl_householder_work!(c32, lapack_sys::cgeqrf_, lapack_sys::cgelqf_); impl_householder_work!(f64, lapack_sys::dgeqrf_, lapack_sys::dgelqf_); impl_householder_work!(f32, lapack_sys::sgeqrf_, lapack_sys::sgelqf_); +pub struct QWork { + pub layout: MatrixLayout, + pub work: Vec>, +} + +pub trait QWorkImpl: Sized { + type Elem: Scalar; + fn new(layout: MatrixLayout) -> Result; + fn calc(&mut self, a: &mut [Self::Elem], tau: &mut [Self::Elem]) -> Result<()>; +} + +macro_rules! impl_q_work { + ($s:ty, $gqr:path, $glq:path) => { + impl QWorkImpl for QWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let m = layout.lda(); + let n = layout.len(); + let k = m.min(n); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + match layout { + MatrixLayout::F { .. } => unsafe { + $gqr( + &m, + &k, + &k, + std::ptr::null_mut(), + &m, + std::ptr::null_mut(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }, + MatrixLayout::C { .. } => unsafe { + $glq( + &k, + &n, + &k, + std::ptr::null_mut(), + &m, + std::ptr::null_mut(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }, + } + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(QWork { layout, work }) + } + + fn calc(&mut self, a: &mut [Self::Elem], tau: &mut [Self::Elem]) -> Result<()> { + let m = self.layout.lda(); + let n = self.layout.len(); + let k = m.min(n); + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + match self.layout { + MatrixLayout::F { .. } => unsafe { + $gqr( + &m, + &k, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }, + MatrixLayout::C { .. } => unsafe { + $glq( + &k, + &n, + &k, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_ptr(&tau), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }, + } + info.as_lapack_result()?; + Ok(()) + } + } + }; +} + +impl_q_work!(c64, lapack_sys::zungqr_, lapack_sys::zunglq_); +impl_q_work!(c32, lapack_sys::cungqr_, lapack_sys::cunglq_); +impl_q_work!(f64, lapack_sys::dorgqr_, lapack_sys::dorglq_); +impl_q_work!(f32, lapack_sys::sorgqr_, lapack_sys::sorglq_); + macro_rules! impl_qr { ($scalar:ty, $qrf:path, $lqf:path, $gqr:path, $glq:path) => { impl QR_ for $scalar { From da97d382e66855bf106cb4e63fdcab2c356f0c38 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 19:07:00 +0900 Subject: [PATCH 109/165] Merge QR_ into Lapack trait --- lax/src/lib.rs | 36 ++++++++- lax/src/qr.rs | 206 +------------------------------------------------ 2 files changed, 35 insertions(+), 207 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 624e1570..6970eda6 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -90,12 +90,12 @@ pub mod layout; pub mod eig; pub mod eigh; pub mod eigh_generalized; +pub mod qr; mod alloc; mod cholesky; mod least_squares; mod opnorm; -mod qr; mod rcond; mod solve; mod solveh; @@ -108,7 +108,6 @@ pub use self::cholesky::*; pub use self::flags::*; pub use self::least_squares::*; pub use self::opnorm::*; -pub use self::qr::*; pub use self::rcond::*; pub use self::solve::*; pub use self::solveh::*; @@ -126,7 +125,6 @@ pub type Pivot = Vec; /// Trait for primitive types which implements LAPACK subroutines pub trait Lapack: OperatorNorm_ - + QR_ + SVD_ + SVDDC_ + Solve_ @@ -160,6 +158,18 @@ pub trait Lapack: a: &mut [Self], b: &mut [Self], ) -> Result>; + + /// Execute Householder reflection as the first step of QR-decomposition + /// + /// For C-continuous array, + /// this will call LQ-decomposition of the transposed matrix $ A^T = LQ^T $ + fn householder(l: MatrixLayout, a: &mut [Self]) -> Result>; + + /// Reconstruct Q-matrix from Householder-reflectors + fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()>; + + /// Execute QR-decomposition at once + fn qr(l: MatrixLayout, a: &mut [Self]) -> Result>; } macro_rules! impl_lapack { @@ -198,6 +208,26 @@ macro_rules! impl_lapack { let work = EighGeneralizedWork::<$s>::new(calc_eigenvec, layout)?; work.eval(uplo, a, b) } + + fn householder(l: MatrixLayout, a: &mut [Self]) -> Result> { + use qr::*; + let work = HouseholderWork::<$s>::new(l)?; + Ok(work.eval(a)?) + } + + fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()> { + use qr::*; + let mut work = QWork::<$s>::new(l)?; + work.calc(a, tau)?; + Ok(()) + } + + fn qr(l: MatrixLayout, a: &mut [Self]) -> Result> { + let tau = Self::householder(l, a)?; + let r = Vec::from(&*a); + Self::q(l, a, &tau)?; + Ok(r) + } } }; } diff --git a/lax/src/qr.rs b/lax/src/qr.rs index 97abc00a..f37bd579 100644 --- a/lax/src/qr.rs +++ b/lax/src/qr.rs @@ -4,20 +4,6 @@ use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -pub trait QR_: Sized { - /// Execute Householder reflection as the first step of QR-decomposition - /// - /// For C-continuous array, - /// this will call LQ-decomposition of the transposed matrix $ A^T = LQ^T $ - fn householder(l: MatrixLayout, a: &mut [Self]) -> Result>; - - /// Reconstruct Q-matrix from Householder-reflectors - fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()>; - - /// Execute QR-decomposition at once - fn qr(l: MatrixLayout, a: &mut [Self]) -> Result>; -} - pub struct HouseholderWork { pub m: i32, pub n: i32, @@ -136,7 +122,7 @@ pub struct QWork { pub trait QWorkImpl: Sized { type Elem: Scalar; fn new(layout: MatrixLayout) -> Result; - fn calc(&mut self, a: &mut [Self::Elem], tau: &mut [Self::Elem]) -> Result<()>; + fn calc(&mut self, a: &mut [Self::Elem], tau: &[Self::Elem]) -> Result<()>; } macro_rules! impl_q_work { @@ -183,7 +169,7 @@ macro_rules! impl_q_work { Ok(QWork { layout, work }) } - fn calc(&mut self, a: &mut [Self::Elem], tau: &mut [Self::Elem]) -> Result<()> { + fn calc(&mut self, a: &mut [Self::Elem], tau: &[Self::Elem]) -> Result<()> { let m = self.layout.lda(); let n = self.layout.len(); let k = m.min(n); @@ -228,191 +214,3 @@ impl_q_work!(c64, lapack_sys::zungqr_, lapack_sys::zunglq_); impl_q_work!(c32, lapack_sys::cungqr_, lapack_sys::cunglq_); impl_q_work!(f64, lapack_sys::dorgqr_, lapack_sys::dorglq_); impl_q_work!(f32, lapack_sys::sorgqr_, lapack_sys::sorglq_); - -macro_rules! impl_qr { - ($scalar:ty, $qrf:path, $lqf:path, $gqr:path, $glq:path) => { - impl QR_ for $scalar { - fn householder(l: MatrixLayout, a: &mut [Self]) -> Result> { - let m = l.lda(); - let n = l.len(); - let k = m.min(n); - let mut tau = vec_uninit(k as usize); - - // eval work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - match l { - MatrixLayout::F { .. } => { - $qrf( - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut tau), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ); - } - MatrixLayout::C { .. } => { - $lqf( - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut tau), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ); - } - } - } - info.as_lapack_result()?; - - // calc - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - match l { - MatrixLayout::F { .. } => { - $qrf( - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut tau), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ); - } - MatrixLayout::C { .. } => { - $lqf( - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut tau), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ); - } - } - } - info.as_lapack_result()?; - - let tau = unsafe { tau.assume_init() }; - - Ok(tau) - } - - fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()> { - let m = l.lda(); - let n = l.len(); - let k = m.min(n); - assert_eq!(tau.len(), k as usize); - - // eval work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - match l { - MatrixLayout::F { .. } => $gqr( - &m, - &k, - &k, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_ptr(&tau), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ), - MatrixLayout::C { .. } => $glq( - &k, - &n, - &k, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_ptr(&tau), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ), - } - }; - - // calc - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - match l { - MatrixLayout::F { .. } => $gqr( - &m, - &k, - &k, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_ptr(&tau), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ), - MatrixLayout::C { .. } => $glq( - &k, - &n, - &k, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_ptr(&tau), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ), - } - } - info.as_lapack_result()?; - Ok(()) - } - - fn qr(l: MatrixLayout, a: &mut [Self]) -> Result> { - let tau = Self::householder(l, a)?; - let r = Vec::from(&*a); - Self::q(l, a, &tau)?; - Ok(r) - } - } - }; -} // endmacro - -impl_qr!( - f64, - lapack_sys::dgeqrf_, - lapack_sys::dgelqf_, - lapack_sys::dorgqr_, - lapack_sys::dorglq_ -); -impl_qr!( - f32, - lapack_sys::sgeqrf_, - lapack_sys::sgelqf_, - lapack_sys::sorgqr_, - lapack_sys::sorglq_ -); -impl_qr!( - c64, - lapack_sys::zgeqrf_, - lapack_sys::zgelqf_, - lapack_sys::zungqr_, - lapack_sys::zunglq_ -); -impl_qr!( - c32, - lapack_sys::cgeqrf_, - lapack_sys::cgelqf_, - lapack_sys::cungqr_, - lapack_sys::cunglq_ -); From f0b2c869e1217301199a4bbc26defabaa85b0d96 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 26 Sep 2022 20:21:54 +0900 Subject: [PATCH 110/165] clippy fix --- lax/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 6970eda6..ffe1fb62 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -212,7 +212,7 @@ macro_rules! impl_lapack { fn householder(l: MatrixLayout, a: &mut [Self]) -> Result> { use qr::*; let work = HouseholderWork::<$s>::new(l)?; - Ok(work.eval(a)?) + work.eval(a) } fn q(l: MatrixLayout, a: &mut [Self], tau: &[Self]) -> Result<()> { From f9f16e23978d3570248b6ab5a6859a25912b4b6f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 27 Sep 2022 16:50:49 +0900 Subject: [PATCH 111/165] Add SvdWork --- lax/src/svd.rs | 301 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 0d9bd353..0c44241b 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -30,6 +30,307 @@ pub trait SVD_: Scalar { -> Result>; } +pub struct SvdWork { + pub ju: JobSvd, + pub jvt: JobSvd, + pub layout: MatrixLayout, + pub s: Vec>, + pub u: Option>>, + pub vt: Option>>, + pub work: Vec>, + pub rwork: Option>>, +} + +#[derive(Debug, Clone)] +pub struct SvdRef<'work, T: Scalar> { + pub s: &'work [T::Real], + pub u: Option<&'work [T]>, + pub vt: Option<&'work [T]>, +} + +#[derive(Debug, Clone)] +pub struct SvdOwned { + pub s: Vec, + pub u: Option>, + pub vt: Option>, +} + +pub trait SvdWorkImpl: Sized { + type Elem: Scalar; + fn new(layout: MatrixLayout, calc_u: bool, calc_vt: bool) -> Result; + fn calc(&mut self, a: &mut [Self::Elem]) -> Result>; + fn eval(self, a: &mut [Self::Elem]) -> Result>; +} + +macro_rules! impl_svd_work_c { + ($s:ty, $svd:path) => { + impl SvdWorkImpl for SvdWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout, calc_u: bool, calc_vt: bool) -> Result { + let ju = match layout { + MatrixLayout::F { .. } => JobSvd::from_bool(calc_u), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_vt), + }; + let jvt = match layout { + MatrixLayout::F { .. } => JobSvd::from_bool(calc_vt), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_u), + }; + + let m = layout.lda(); + let mut u = match ju { + JobSvd::All => Some(vec_uninit((m * m) as usize)), + JobSvd::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet"), + }; + + let n = layout.len(); + let mut vt = match jvt { + JobSvd::All => Some(vec_uninit((n * n) as usize)), + JobSvd::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet"), + }; + + let k = std::cmp::min(m, n); + let mut s = vec_uninit(k as usize); + let mut rwork = vec_uninit(5 * k as usize); + + // eval work size + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $svd( + ju.as_ptr(), + jvt.as_ptr(), + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(SvdWork { + layout, + ju, + jvt, + s, + u, + vt, + work, + rwork: Some(rwork), + }) + } + + fn calc(&mut self, a: &mut [Self::Elem]) -> Result> { + let m = self.layout.lda(); + let n = self.layout.len(); + let lwork = self.work.len().to_i32().unwrap(); + + let mut info = 0; + unsafe { + $svd( + self.ju.as_ptr(), + self.jvt.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut self.s), + AsPtr::as_mut_ptr( + self.u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), + ), + &m, + AsPtr::as_mut_ptr( + self.vt + .as_mut() + .map(|x| x.as_mut_slice()) + .unwrap_or(&mut []), + ), + &n, + AsPtr::as_mut_ptr(&mut self.work), + &(lwork as i32), + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ); + } + info.as_lapack_result()?; + + let s = unsafe { self.s.slice_assume_init_ref() }; + let u = self + .u + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + let vt = self + .vt + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + + match self.layout { + MatrixLayout::F { .. } => Ok(SvdRef { s, u, vt }), + MatrixLayout::C { .. } => Ok(SvdRef { s, u: vt, vt: u }), + } + } + + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let _ref = self.calc(a)?; + let s = unsafe { self.s.assume_init() }; + let u = self.u.map(|v| unsafe { v.assume_init() }); + let vt = self.vt.map(|v| unsafe { v.assume_init() }); + match self.layout { + MatrixLayout::F { .. } => Ok(SvdOwned { s, u, vt }), + MatrixLayout::C { .. } => Ok(SvdOwned { s, u: vt, vt: u }), + } + } + } + }; +} +impl_svd_work_c!(c64, lapack_sys::zgesvd_); +impl_svd_work_c!(c32, lapack_sys::cgesvd_); + +macro_rules! impl_svd_work_r { + ($s:ty, $svd:path) => { + impl SvdWorkImpl for SvdWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout, calc_u: bool, calc_vt: bool) -> Result { + let ju = match layout { + MatrixLayout::F { .. } => JobSvd::from_bool(calc_u), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_vt), + }; + let jvt = match layout { + MatrixLayout::F { .. } => JobSvd::from_bool(calc_vt), + MatrixLayout::C { .. } => JobSvd::from_bool(calc_u), + }; + + let m = layout.lda(); + let mut u = match ju { + JobSvd::All => Some(vec_uninit((m * m) as usize)), + JobSvd::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet"), + }; + + let n = layout.len(); + let mut vt = match jvt { + JobSvd::All => Some(vec_uninit((n * n) as usize)), + JobSvd::None => None, + _ => unimplemented!("SVD with partial vector output is not supported yet"), + }; + + let k = std::cmp::min(m, n); + let mut s = vec_uninit(k as usize); + + // eval work size + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $svd( + ju.as_ptr(), + jvt.as_ptr(), + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(SvdWork { + layout, + ju, + jvt, + s, + u, + vt, + work, + rwork: None, + }) + } + + fn calc(&mut self, a: &mut [Self::Elem]) -> Result> { + let m = self.layout.lda(); + let n = self.layout.len(); + let lwork = self.work.len().to_i32().unwrap(); + + let mut info = 0; + unsafe { + $svd( + self.ju.as_ptr(), + self.jvt.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut self.s), + AsPtr::as_mut_ptr( + self.u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), + ), + &m, + AsPtr::as_mut_ptr( + self.vt + .as_mut() + .map(|x| x.as_mut_slice()) + .unwrap_or(&mut []), + ), + &n, + AsPtr::as_mut_ptr(&mut self.work), + &(lwork as i32), + &mut info, + ); + } + info.as_lapack_result()?; + + let s = unsafe { self.s.slice_assume_init_ref() }; + let u = self + .u + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + let vt = self + .vt + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + + match self.layout { + MatrixLayout::F { .. } => Ok(SvdRef { s, u, vt }), + MatrixLayout::C { .. } => Ok(SvdRef { s, u: vt, vt: u }), + } + } + + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let _ref = self.calc(a)?; + let s = unsafe { self.s.assume_init() }; + let u = self.u.map(|v| unsafe { v.assume_init() }); + let vt = self.vt.map(|v| unsafe { v.assume_init() }); + match self.layout { + MatrixLayout::F { .. } => Ok(SvdOwned { s, u, vt }), + MatrixLayout::C { .. } => Ok(SvdOwned { s, u: vt, vt: u }), + } + } + } + }; +} +impl_svd_work_r!(f64, lapack_sys::dgesvd_); +impl_svd_work_r!(f32, lapack_sys::sgesvd_); + macro_rules! impl_svd { (@real, $scalar:ty, $gesvd:path) => { impl_svd!(@body, $scalar, $gesvd, ); From 3fd8c0b26bc98e5d12bd7d5b9fb59c57c60fb634 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 27 Sep 2022 16:57:43 +0900 Subject: [PATCH 112/165] Merge SVD_ to Lapack --- lax/src/lib.rs | 21 +++++-- lax/src/svd.rs | 140 +++-------------------------------------------- lax/src/svddc.rs | 8 +-- 3 files changed, 29 insertions(+), 140 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index ffe1fb62..582a980a 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -65,7 +65,7 @@ //! Singular Value Decomposition //! ----------------------------- //! -//! - [SVD_] trait provides methods for singular value decomposition for general matrix +//! - [svd] module for singular value decomposition (SVD) for general matrix //! - [SVDDC_] trait provides methods for singular value decomposition for general matrix //! with divided-and-conquer algorithm //! - [LeastSquaresSvdDivideConquer_] trait provides methods @@ -91,6 +91,7 @@ pub mod eig; pub mod eigh; pub mod eigh_generalized; pub mod qr; +pub mod svd; mod alloc; mod cholesky; @@ -99,7 +100,6 @@ mod opnorm; mod rcond; mod solve; mod solveh; -mod svd; mod svddc; mod triangular; mod tridiagonal; @@ -111,7 +111,7 @@ pub use self::opnorm::*; pub use self::rcond::*; pub use self::solve::*; pub use self::solveh::*; -pub use self::svd::*; +pub use self::svd::SvdOwned; pub use self::svddc::*; pub use self::triangular::*; pub use self::tridiagonal::*; @@ -125,7 +125,6 @@ pub type Pivot = Vec; /// Trait for primitive types which implements LAPACK subroutines pub trait Lapack: OperatorNorm_ - + SVD_ + SVDDC_ + Solve_ + Solveh_ @@ -170,6 +169,9 @@ pub trait Lapack: /// Execute QR-decomposition at once fn qr(l: MatrixLayout, a: &mut [Self]) -> Result>; + + /// Compute singular-value decomposition (SVD) + fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self]) -> Result>; } macro_rules! impl_lapack { @@ -228,6 +230,17 @@ macro_rules! impl_lapack { Self::q(l, a, &tau)?; Ok(r) } + + fn svd( + l: MatrixLayout, + calc_u: bool, + calc_vt: bool, + a: &mut [Self], + ) -> Result> { + use svd::*; + let work = SvdWork::<$s>::new(l, calc_u, calc_vt)?; + work.eval(a) + } } }; } diff --git a/lax/src/svd.rs b/lax/src/svd.rs index 0c44241b..fc695108 100644 --- a/lax/src/svd.rs +++ b/lax/src/svd.rs @@ -1,35 +1,17 @@ //! Singular-value decomposition +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:-------|:-------|:-------|:-------| +//! | sgesvd | dgesvd | cgesvd | zgesvd | +//! use super::{error::*, layout::*, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -/// Result of SVD -pub struct SVDOutput { - /// diagonal values - pub s: Vec, - /// Unitary matrix for destination space - pub u: Option>, - /// Unitary matrix for departure space - pub vt: Option>, -} - -#[cfg_attr(doc, katexit::katexit)] -/// Singular value decomposition -pub trait SVD_: Scalar { - /// Compute singular value decomposition $A = U \Sigma V^T$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgesvd | dgesvd | cgesvd | zgesvd | - /// - fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self]) - -> Result>; -} - pub struct SvdWork { pub ju: JobSvd, pub jvt: JobSvd, @@ -330,109 +312,3 @@ macro_rules! impl_svd_work_r { } impl_svd_work_r!(f64, lapack_sys::dgesvd_); impl_svd_work_r!(f32, lapack_sys::sgesvd_); - -macro_rules! impl_svd { - (@real, $scalar:ty, $gesvd:path) => { - impl_svd!(@body, $scalar, $gesvd, ); - }; - (@complex, $scalar:ty, $gesvd:path) => { - impl_svd!(@body, $scalar, $gesvd, rwork); - }; - (@body, $scalar:ty, $gesvd:path, $($rwork_ident:ident),*) => { - impl SVD_ for $scalar { - fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self],) -> Result> { - let ju = match l { - MatrixLayout::F { .. } => JobSvd::from_bool(calc_u), - MatrixLayout::C { .. } => JobSvd::from_bool(calc_vt), - }; - let jvt = match l { - MatrixLayout::F { .. } => JobSvd::from_bool(calc_vt), - MatrixLayout::C { .. } => JobSvd::from_bool(calc_u), - }; - - let m = l.lda(); - let mut u = match ju { - JobSvd::All => Some(vec_uninit( (m * m) as usize)), - JobSvd::None => None, - _ => unimplemented!("SVD with partial vector output is not supported yet") - }; - - let n = l.len(); - let mut vt = match jvt { - JobSvd::All => Some(vec_uninit( (n * n) as usize)), - JobSvd::None => None, - _ => unimplemented!("SVD with partial vector output is not supported yet") - }; - - let k = std::cmp::min(m, n); - let mut s = vec_uninit( k as usize); - - $( - let mut $rwork_ident: Vec> = vec_uninit(5 * k as usize); - )* - - // eval work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $gesvd( - ju.as_ptr(), - jvt.as_ptr(), - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut s), - AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &m, - AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - // calc - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - $gesvd( - ju.as_ptr(), - jvt.as_ptr() , - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut s), - AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &m, - AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &n, - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - &mut info, - ); - } - info.as_lapack_result()?; - - let s = unsafe { s.assume_init() }; - let u = u.map(|v| unsafe { v.assume_init() }); - let vt = vt.map(|v| unsafe { v.assume_init() }); - - match l { - MatrixLayout::F { .. } => Ok(SVDOutput { s, u, vt }), - MatrixLayout::C { .. } => Ok(SVDOutput { s, u: vt, vt: u }), - } - } - } - }; -} // impl_svd! - -impl_svd!(@real, f64, lapack_sys::dgesvd_); -impl_svd!(@real, f32, lapack_sys::sgesvd_); -impl_svd!(@complex, c64, lapack_sys::zgesvd_); -impl_svd!(@complex, c32, lapack_sys::cgesvd_); diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index 928fd44c..22612cee 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -14,7 +14,7 @@ pub trait SVDDC_: Scalar { /// |:-------|:-------|:-------|:-------| /// | sgesdd | dgesdd | cgesdd | zgesdd | /// - fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; + fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; } macro_rules! impl_svddc { @@ -26,7 +26,7 @@ macro_rules! impl_svddc { }; (@body, $scalar:ty, $gesdd:path, $($rwork_ident:ident),*) => { impl SVDDC_ for $scalar { - fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self],) -> Result> { + fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self],) -> Result> { let m = l.lda(); let n = l.len(); let k = m.min(n); @@ -112,8 +112,8 @@ macro_rules! impl_svddc { let vt = vt.map(|v| unsafe { v.assume_init() }); match l { - MatrixLayout::F { .. } => Ok(SVDOutput { s, u, vt }), - MatrixLayout::C { .. } => Ok(SVDOutput { s, u: vt, vt: u }), + MatrixLayout::F { .. } => Ok(SvdOwned { s, u, vt }), + MatrixLayout::C { .. } => Ok(SvdOwned { s, u: vt, vt: u }), } } } From f30931d61c241774ac069923a4b341fd0714f3e9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 27 Sep 2022 21:53:02 +0900 Subject: [PATCH 113/165] SvdDcWork and SvdDcWorkImpl --- lax/src/lib.rs | 2 +- lax/src/svddc.rs | 296 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 2 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 582a980a..9adc7fdb 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -111,7 +111,7 @@ pub use self::opnorm::*; pub use self::rcond::*; pub use self::solve::*; pub use self::solveh::*; -pub use self::svd::SvdOwned; +pub use self::svd::{SvdOwned, SvdRef}; pub use self::svddc::*; pub use self::triangular::*; pub use self::tridiagonal::*; diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index 22612cee..239e0d8c 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -14,9 +14,303 @@ pub trait SVDDC_: Scalar { /// |:-------|:-------|:-------|:-------| /// | sgesdd | dgesdd | cgesdd | zgesdd | /// - fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; + fn svddc(layout: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; } +pub struct SvdDcWork { + pub jobz: JobSvd, + pub layout: MatrixLayout, + pub s: Vec>, + pub u: Option>>, + pub vt: Option>>, + pub work: Vec>, + pub iwork: Vec>, + pub rwork: Option>>, +} + +pub trait SvdDcWorkImpl: Sized { + type Elem: Scalar; + fn new(layout: MatrixLayout, jobz: JobSvd) -> Result; + fn calc(&mut self, a: &mut [Self::Elem]) -> Result>; + fn eval(self, a: &mut [Self::Elem]) -> Result>; +} + +macro_rules! impl_svd_dc_work_c { + ($s:ty, $sdd:path) => { + impl SvdDcWorkImpl for SvdDcWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout, jobz: JobSvd) -> Result { + let m = layout.lda(); + let n = layout.len(); + let k = m.min(n); + let (u_col, vt_row) = match jobz { + JobSvd::All | JobSvd::None => (m, n), + JobSvd::Some => (k, k), + }; + + let mut s = vec_uninit(k as usize); + let (mut u, mut vt) = match jobz { + JobSvd::All => ( + Some(vec_uninit((m * m) as usize)), + Some(vec_uninit((n * n) as usize)), + ), + JobSvd::Some => ( + Some(vec_uninit((m * u_col) as usize)), + Some(vec_uninit((n * vt_row) as usize)), + ), + JobSvd::None => (None, None), + }; + let mut iwork = vec_uninit(8 * k as usize); + + let mx = n.max(m) as usize; + let mn = n.min(m) as usize; + let lrwork = match jobz { + JobSvd::None => 7 * mn, + _ => std::cmp::max(5 * mn * mn + 5 * mn, 2 * mx * mn + 2 * mn * mn + mn), + }; + let mut rwork = vec_uninit(lrwork); + + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $sdd( + jobz.as_ptr(), + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &vt_row, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + AsPtr::as_mut_ptr(&mut iwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(SvdDcWork { + layout, + jobz, + iwork, + work, + rwork: Some(rwork), + u, + vt, + s, + }) + } + + fn calc(&mut self, a: &mut [Self::Elem]) -> Result> { + let m = self.layout.lda(); + let n = self.layout.len(); + let k = m.min(n); + let (_, vt_row) = match self.jobz { + JobSvd::All | JobSvd::None => (m, n), + JobSvd::Some => (k, k), + }; + let lwork = self.work.len().to_i32().unwrap(); + + let mut info = 0; + unsafe { + $sdd( + self.jobz.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut self.s), + AsPtr::as_mut_ptr( + self.u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), + ), + &m, + AsPtr::as_mut_ptr( + self.vt + .as_mut() + .map(|x| x.as_mut_slice()) + .unwrap_or(&mut []), + ), + &vt_row, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + AsPtr::as_mut_ptr(&mut self.iwork), + &mut info, + ); + } + info.as_lapack_result()?; + + let s = unsafe { self.s.slice_assume_init_ref() }; + let u = self + .u + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + let vt = self + .vt + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + + Ok(match self.layout { + MatrixLayout::F { .. } => SvdRef { s, u, vt }, + MatrixLayout::C { .. } => SvdRef { s, u: vt, vt: u }, + }) + } + + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let _ref = self.calc(a)?; + let s = unsafe { self.s.assume_init() }; + let u = self.u.map(|v| unsafe { v.assume_init() }); + let vt = self.vt.map(|v| unsafe { v.assume_init() }); + Ok(match self.layout { + MatrixLayout::F { .. } => SvdOwned { s, u, vt }, + MatrixLayout::C { .. } => SvdOwned { s, u: vt, vt: u }, + }) + } + } + }; +} +impl_svd_dc_work_c!(c64, lapack_sys::zgesdd_); +impl_svd_dc_work_c!(c32, lapack_sys::cgesdd_); + +macro_rules! impl_svd_dc_work_r { + ($s:ty, $sdd:path) => { + impl SvdDcWorkImpl for SvdDcWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout, jobz: JobSvd) -> Result { + let m = layout.lda(); + let n = layout.len(); + let k = m.min(n); + let (u_col, vt_row) = match jobz { + JobSvd::All | JobSvd::None => (m, n), + JobSvd::Some => (k, k), + }; + + let mut s = vec_uninit(k as usize); + let (mut u, mut vt) = match jobz { + JobSvd::All => ( + Some(vec_uninit((m * m) as usize)), + Some(vec_uninit((n * n) as usize)), + ), + JobSvd::Some => ( + Some(vec_uninit((m * u_col) as usize)), + Some(vec_uninit((n * vt_row) as usize)), + ), + JobSvd::None => (None, None), + }; + let mut iwork = vec_uninit(8 * k as usize); + + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $sdd( + jobz.as_ptr(), + &m, + &n, + std::ptr::null_mut(), + &m, + AsPtr::as_mut_ptr(&mut s), + AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &m, + AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), + &vt_row, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut iwork), + &mut info, + ); + } + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(SvdDcWork { + layout, + jobz, + iwork, + work, + rwork: None, + u, + vt, + s, + }) + } + + fn calc(&mut self, a: &mut [Self::Elem]) -> Result> { + let m = self.layout.lda(); + let n = self.layout.len(); + let k = m.min(n); + let (_, vt_row) = match self.jobz { + JobSvd::All | JobSvd::None => (m, n), + JobSvd::Some => (k, k), + }; + let lwork = self.work.len().to_i32().unwrap(); + + let mut info = 0; + unsafe { + $sdd( + self.jobz.as_ptr(), + &m, + &n, + AsPtr::as_mut_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut self.s), + AsPtr::as_mut_ptr( + self.u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut []), + ), + &m, + AsPtr::as_mut_ptr( + self.vt + .as_mut() + .map(|x| x.as_mut_slice()) + .unwrap_or(&mut []), + ), + &vt_row, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(&mut self.iwork), + &mut info, + ); + } + info.as_lapack_result()?; + + let s = unsafe { self.s.slice_assume_init_ref() }; + let u = self + .u + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + let vt = self + .vt + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }); + + Ok(match self.layout { + MatrixLayout::F { .. } => SvdRef { s, u, vt }, + MatrixLayout::C { .. } => SvdRef { s, u: vt, vt: u }, + }) + } + + fn eval(mut self, a: &mut [Self::Elem]) -> Result> { + let _ref = self.calc(a)?; + let s = unsafe { self.s.assume_init() }; + let u = self.u.map(|v| unsafe { v.assume_init() }); + let vt = self.vt.map(|v| unsafe { v.assume_init() }); + Ok(match self.layout { + MatrixLayout::F { .. } => SvdOwned { s, u, vt }, + MatrixLayout::C { .. } => SvdOwned { s, u: vt, vt: u }, + }) + } + } + }; +} +impl_svd_dc_work_r!(f64, lapack_sys::dgesdd_); +impl_svd_dc_work_r!(f32, lapack_sys::sgesdd_); + macro_rules! impl_svddc { (@real, $scalar:ty, $gesdd:path) => { impl_svddc!(@body, $scalar, $gesdd, ); From 38c64c9ea4c2be2a93b166f6c04dd69434ec7ab8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 27 Sep 2022 22:20:11 +0900 Subject: [PATCH 114/165] Merge SVDDC_ to Lapack trait --- lax/src/lib.rs | 16 ++++-- lax/src/svddc.rs | 133 ++++------------------------------------------- 2 files changed, 21 insertions(+), 128 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 9adc7fdb..00ad27b3 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -66,8 +66,7 @@ //! ----------------------------- //! //! - [svd] module for singular value decomposition (SVD) for general matrix -//! - [SVDDC_] trait provides methods for singular value decomposition for general matrix -//! with divided-and-conquer algorithm +//! - [svddc] module for singular value decomposition (SVD) with divided-and-conquer algorithm for general matrix //! - [LeastSquaresSvdDivideConquer_] trait provides methods //! for solving least square problem by SVD //! @@ -92,6 +91,7 @@ pub mod eigh; pub mod eigh_generalized; pub mod qr; pub mod svd; +pub mod svddc; mod alloc; mod cholesky; @@ -100,7 +100,6 @@ mod opnorm; mod rcond; mod solve; mod solveh; -mod svddc; mod triangular; mod tridiagonal; @@ -112,7 +111,6 @@ pub use self::rcond::*; pub use self::solve::*; pub use self::solveh::*; pub use self::svd::{SvdOwned, SvdRef}; -pub use self::svddc::*; pub use self::triangular::*; pub use self::tridiagonal::*; @@ -125,7 +123,6 @@ pub type Pivot = Vec; /// Trait for primitive types which implements LAPACK subroutines pub trait Lapack: OperatorNorm_ - + SVDDC_ + Solve_ + Solveh_ + Cholesky_ @@ -172,6 +169,9 @@ pub trait Lapack: /// Compute singular-value decomposition (SVD) fn svd(l: MatrixLayout, calc_u: bool, calc_vt: bool, a: &mut [Self]) -> Result>; + + /// Compute singular value decomposition (SVD) with divide-and-conquer algorithm + fn svddc(layout: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; } macro_rules! impl_lapack { @@ -241,6 +241,12 @@ macro_rules! impl_lapack { let work = SvdWork::<$s>::new(l, calc_u, calc_vt)?; work.eval(a) } + + fn svddc(layout: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result> { + use svddc::*; + let work = SvdDcWork::<$s>::new(layout, jobz)?; + work.eval(a) + } } }; } diff --git a/lax/src/svddc.rs b/lax/src/svddc.rs index 239e0d8c..c16db4bb 100644 --- a/lax/src/svddc.rs +++ b/lax/src/svddc.rs @@ -1,22 +1,17 @@ +//! Compute singular value decomposition with divide-and-conquer algorithm +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:-------|:-------|:-------|:-------| +//! | sgesdd | dgesdd | cgesdd | zgesdd | +//! + use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -#[cfg_attr(doc, katexit::katexit)] -/// Singular value decomposition with divide-and-conquer method -pub trait SVDDC_: Scalar { - /// Compute singular value decomposition $A = U \Sigma V^T$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgesdd | dgesdd | cgesdd | zgesdd | - /// - fn svddc(layout: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; -} - pub struct SvdDcWork { pub jobz: JobSvd, pub layout: MatrixLayout, @@ -310,111 +305,3 @@ macro_rules! impl_svd_dc_work_r { } impl_svd_dc_work_r!(f64, lapack_sys::dgesdd_); impl_svd_dc_work_r!(f32, lapack_sys::sgesdd_); - -macro_rules! impl_svddc { - (@real, $scalar:ty, $gesdd:path) => { - impl_svddc!(@body, $scalar, $gesdd, ); - }; - (@complex, $scalar:ty, $gesdd:path) => { - impl_svddc!(@body, $scalar, $gesdd, rwork); - }; - (@body, $scalar:ty, $gesdd:path, $($rwork_ident:ident),*) => { - impl SVDDC_ for $scalar { - fn svddc(l: MatrixLayout, jobz: JobSvd, a: &mut [Self],) -> Result> { - let m = l.lda(); - let n = l.len(); - let k = m.min(n); - let mut s = vec_uninit(k as usize); - - let (u_col, vt_row) = match jobz { - JobSvd::All | JobSvd::None => (m, n), - JobSvd::Some => (k, k), - }; - let (mut u, mut vt) = match jobz { - JobSvd::All => ( - Some(vec_uninit((m * m) as usize)), - Some(vec_uninit((n * n) as usize)), - ), - JobSvd::Some => ( - Some(vec_uninit((m * u_col) as usize)), - Some(vec_uninit((n * vt_row) as usize)), - ), - JobSvd::None => (None, None), - }; - - $( // for complex only - let mx = n.max(m) as usize; - let mn = n.min(m) as usize; - let lrwork = match jobz { - JobSvd::None => 7 * mn, - _ => std::cmp::max(5*mn*mn + 5*mn, 2*mx*mn + 2*mn*mn + mn), - }; - let mut $rwork_ident: Vec> = vec_uninit(lrwork); - )* - - // eval work size - let mut info = 0; - let mut iwork: Vec> = vec_uninit(8 * k as usize); - let mut work_size = [Self::zero()]; - unsafe { - $gesdd( - jobz.as_ptr(), - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut s), - AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &m, - AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &vt_row, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - AsPtr::as_mut_ptr(&mut iwork), - &mut info, - ); - } - info.as_lapack_result()?; - - // do svd - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - $gesdd( - jobz.as_ptr(), - &m, - &n, - AsPtr::as_mut_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut s), - AsPtr::as_mut_ptr(u.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &m, - AsPtr::as_mut_ptr(vt.as_mut().map(|x| x.as_mut_slice()).unwrap_or(&mut [])), - &vt_row, - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - $(AsPtr::as_mut_ptr(&mut $rwork_ident),)* - AsPtr::as_mut_ptr(&mut iwork), - &mut info, - ); - } - info.as_lapack_result()?; - - let s = unsafe { s.assume_init() }; - let u = u.map(|v| unsafe { v.assume_init() }); - let vt = vt.map(|v| unsafe { v.assume_init() }); - - match l { - MatrixLayout::F { .. } => Ok(SvdOwned { s, u, vt }), - MatrixLayout::C { .. } => Ok(SvdOwned { s, u: vt, vt: u }), - } - } - } - }; -} - -impl_svddc!(@real, f32, lapack_sys::sgesdd_); -impl_svddc!(@real, f64, lapack_sys::dgesdd_); -impl_svddc!(@complex, c32, lapack_sys::cgesdd_); -impl_svddc!(@complex, c64, lapack_sys::zgesdd_); From ac2f7bcb18e817168367dd6ede85f8413e0a5fcf Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 28 Sep 2022 00:19:16 +0900 Subject: [PATCH 115/165] Rename LeastSquaresOutput to LeastSquaresOwned --- lax/src/least_squares.rs | 10 +++++----- ndarray-linalg/src/least_squares.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 1bfd4d37..532d1e6b 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -5,7 +5,7 @@ use cauchy::*; use num_traits::{ToPrimitive, Zero}; /// Result of LeastSquares -pub struct LeastSquaresOutput { +pub struct LeastSquaresOwned { /// singular values pub singular_values: Vec, /// The rank of the input matrix A @@ -21,7 +21,7 @@ pub trait LeastSquaresSvdDivideConquer_: Scalar { a_layout: MatrixLayout, a: &mut [Self], b: &mut [Self], - ) -> Result>; + ) -> Result>; /// Solve least square problems $\argmin_X \| AX - B\|$ fn least_squares_nrhs( @@ -46,7 +46,7 @@ macro_rules! impl_least_squares { l: MatrixLayout, a: &mut [Self], b: &mut [Self], - ) -> Result> { + ) -> Result> { let b_layout = l.resized(b.len() as i32, 1); Self::least_squares_nrhs(l, a, b_layout, b) } @@ -56,7 +56,7 @@ macro_rules! impl_least_squares { a: &mut [Self], b_layout: MatrixLayout, b: &mut [Self], - ) -> Result> { + ) -> Result> { // Minimize |b - Ax|_2 // // where @@ -160,7 +160,7 @@ macro_rules! impl_least_squares { transpose_over(b_layout, &b_t, b); } - Ok(LeastSquaresOutput { + Ok(LeastSquaresOwned { singular_values, rank, }) diff --git a/ndarray-linalg/src/least_squares.rs b/ndarray-linalg/src/least_squares.rs index 86ca17f3..0042ef74 100644 --- a/ndarray-linalg/src/least_squares.rs +++ b/ndarray-linalg/src/least_squares.rs @@ -297,7 +297,7 @@ where D1: DataMut, D2: DataMut, { - let LeastSquaresOutput:: { + let LeastSquaresOwned:: { singular_values, rank, } = E::least_squares( @@ -386,7 +386,7 @@ where { let a_layout = a.layout()?; let rhs_layout = rhs.layout()?; - let LeastSquaresOutput:: { + let LeastSquaresOwned:: { singular_values, rank, } = E::least_squares_nrhs( From da3221a74ea544b10afddca84ef75099f6670c5a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 28 Sep 2022 00:19:39 +0900 Subject: [PATCH 116/165] LeastSquaresWork --- lax/src/least_squares.rs | 327 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 326 insertions(+), 1 deletion(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 532d1e6b..44445316 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -12,6 +12,14 @@ pub struct LeastSquaresOwned { pub rank: i32, } +/// Result of LeastSquares +pub struct LeastSquaresRef<'work, A: Scalar> { + /// singular values + pub singular_values: &'work [A::Real], + /// The rank of the input matrix A + pub rank: i32, +} + #[cfg_attr(doc, katexit::katexit)] /// Solve least square problem pub trait LeastSquaresSvdDivideConquer_: Scalar { @@ -29,8 +37,325 @@ pub trait LeastSquaresSvdDivideConquer_: Scalar { a: &mut [Self], b_layout: MatrixLayout, b: &mut [Self], - ) -> Result>; + ) -> Result>; +} + +pub struct LeastSquaresWork { + pub a_layout: MatrixLayout, + pub b_layout: MatrixLayout, + pub singular_values: Vec>, + pub work: Vec>, + pub iwork: Vec>, + pub rwork: Option>>, +} + +pub trait LeastSquaresWorkImpl: Sized { + type Elem: Scalar; + fn new(a_layout: MatrixLayout, b_layout: MatrixLayout) -> Result; + fn calc( + &mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result>; + fn eval( + self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result>; +} + +macro_rules! impl_least_squares_work_c { + ($c:ty, $lsd:path) => { + impl LeastSquaresWorkImpl for LeastSquaresWork<$c> { + type Elem = $c; + + fn new(a_layout: MatrixLayout, b_layout: MatrixLayout) -> Result { + let (m, n) = a_layout.size(); + let (m_, nrhs) = b_layout.size(); + let k = m.min(n); + assert!(m_ >= m); + + let rcond = -1.; + let mut singular_values = vec_uninit(k as usize); + let mut rank: i32 = 0; + + // eval work size + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + let mut iwork_size = [0]; + let mut rwork = [::Real::zero()]; + unsafe { + $lsd( + &m, + &n, + &nrhs, + std::ptr::null_mut(), + &a_layout.lda(), + std::ptr::null_mut(), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut singular_values), + &rcond, + &mut rank, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + iwork_size.as_mut_ptr(), + &mut info, + ) + }; + info.as_lapack_result()?; + + let lwork = work_size[0].to_usize().unwrap(); + let liwork = iwork_size[0].to_usize().unwrap(); + let lrwork = rwork[0].to_usize().unwrap(); + + let work = vec_uninit(lwork); + let iwork = vec_uninit(liwork); + let rwork = vec_uninit(lrwork); + + Ok(LeastSquaresWork { + a_layout, + b_layout, + work, + iwork, + rwork: Some(rwork), + singular_values, + }) + } + + fn calc( + &mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let (m, n) = self.a_layout.size(); + let (m_, nrhs) = self.b_layout.size(); + assert!(m_ >= m); + + let lwork = self.work.len().to_i32().unwrap(); + + // Transpose if a is C-continuous + let mut a_t = None; + let a_layout = match self.a_layout { + MatrixLayout::C { .. } => { + let (layout, t) = transpose(self.a_layout, a); + a_t = Some(t); + layout + } + MatrixLayout::F { .. } => self.a_layout, + }; + + // Transpose if b is C-continuous + let mut b_t = None; + let b_layout = match self.b_layout { + MatrixLayout::C { .. } => { + let (layout, t) = transpose(self.b_layout, b); + b_t = Some(t); + layout + } + MatrixLayout::F { .. } => self.b_layout, + }; + + let rcond: ::Real = -1.; + let mut rank: i32 = 0; + + let mut info = 0; + unsafe { + $lsd( + &m, + &n, + &nrhs, + AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), + &a_layout.lda(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut self.singular_values), + &rcond, + &mut rank, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + AsPtr::as_mut_ptr(&mut self.iwork), + &mut info, + ); + } + info.as_lapack_result()?; + + let singular_values = unsafe { self.singular_values.slice_assume_init_ref() }; + + // Skip a_t -> a transpose because A has been destroyed + // Re-transpose b + if let Some(b_t) = b_t { + transpose_over(b_layout, &b_t, b); + } + + Ok(LeastSquaresRef { + singular_values, + rank, + }) + } + + fn eval( + mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let LeastSquaresRef { rank, .. } = self.calc(a, b)?; + let singular_values = unsafe { self.singular_values.assume_init() }; + Ok(LeastSquaresOwned { + singular_values, + rank, + }) + } + } + }; +} +impl_least_squares_work_c!(c64, lapack_sys::zgelsd_); +impl_least_squares_work_c!(c32, lapack_sys::cgelsd_); + +macro_rules! impl_least_squares_work_r { + ($c:ty, $lsd:path) => { + impl LeastSquaresWorkImpl for LeastSquaresWork<$c> { + type Elem = $c; + + fn new(a_layout: MatrixLayout, b_layout: MatrixLayout) -> Result { + let (m, n) = a_layout.size(); + let (m_, nrhs) = b_layout.size(); + let k = m.min(n); + assert!(m_ >= m); + + let rcond = -1.; + let mut singular_values = vec_uninit(k as usize); + let mut rank: i32 = 0; + + // eval work size + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + let mut iwork_size = [0]; + unsafe { + $lsd( + &m, + &n, + &nrhs, + std::ptr::null_mut(), + &a_layout.lda(), + std::ptr::null_mut(), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut singular_values), + &rcond, + &mut rank, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + iwork_size.as_mut_ptr(), + &mut info, + ) + }; + info.as_lapack_result()?; + + let lwork = work_size[0].to_usize().unwrap(); + let liwork = iwork_size[0].to_usize().unwrap(); + + let work = vec_uninit(lwork); + let iwork = vec_uninit(liwork); + + Ok(LeastSquaresWork { + a_layout, + b_layout, + work, + iwork, + rwork: None, + singular_values, + }) + } + + fn calc( + &mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let (m, n) = self.a_layout.size(); + let (m_, nrhs) = self.b_layout.size(); + assert!(m_ >= m); + + let lwork = self.work.len().to_i32().unwrap(); + + // Transpose if a is C-continuous + let mut a_t = None; + let a_layout = match self.a_layout { + MatrixLayout::C { .. } => { + let (layout, t) = transpose(self.a_layout, a); + a_t = Some(t); + layout + } + MatrixLayout::F { .. } => self.a_layout, + }; + + // Transpose if b is C-continuous + let mut b_t = None; + let b_layout = match self.b_layout { + MatrixLayout::C { .. } => { + let (layout, t) = transpose(self.b_layout, b); + b_t = Some(t); + layout + } + MatrixLayout::F { .. } => self.b_layout, + }; + + let rcond: ::Real = -1.; + let mut rank: i32 = 0; + + let mut info = 0; + unsafe { + $lsd( + &m, + &n, + &nrhs, + AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), + &a_layout.lda(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &b_layout.lda(), + AsPtr::as_mut_ptr(&mut self.singular_values), + &rcond, + &mut rank, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(&mut self.iwork), + &mut info, + ); + } + info.as_lapack_result()?; + + let singular_values = unsafe { self.singular_values.slice_assume_init_ref() }; + + // Skip a_t -> a transpose because A has been destroyed + // Re-transpose b + if let Some(b_t) = b_t { + transpose_over(b_layout, &b_t, b); + } + + Ok(LeastSquaresRef { + singular_values, + rank, + }) + } + + fn eval( + mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let LeastSquaresRef { rank, .. } = self.calc(a, b)?; + let singular_values = unsafe { self.singular_values.assume_init() }; + Ok(LeastSquaresOwned { + singular_values, + rank, + }) + } + } + }; } +impl_least_squares_work_r!(f64, lapack_sys::dgelsd_); +impl_least_squares_work_r!(f32, lapack_sys::sgelsd_); macro_rules! impl_least_squares { (@real, $scalar:ty, $gelsd:path) => { From b379b92737412b0aaa6207710c875bff21542c36 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 28 Sep 2022 21:44:59 +0900 Subject: [PATCH 117/165] Merge LeastSquaresSvdDivideConquer_ into Lapack trait --- lax/src/least_squares.rs | 162 ---------------------------- lax/src/lib.rs | 46 ++++++-- ndarray-linalg/src/least_squares.rs | 2 +- 3 files changed, 39 insertions(+), 171 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 44445316..9f8d4561 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -20,26 +20,6 @@ pub struct LeastSquaresRef<'work, A: Scalar> { pub rank: i32, } -#[cfg_attr(doc, katexit::katexit)] -/// Solve least square problem -pub trait LeastSquaresSvdDivideConquer_: Scalar { - /// Compute a vector $x$ which minimizes Euclidian norm $\| Ax - b\|$ - /// for a given matrix $A$ and a vector $b$. - fn least_squares( - a_layout: MatrixLayout, - a: &mut [Self], - b: &mut [Self], - ) -> Result>; - - /// Solve least square problems $\argmin_X \| AX - B\|$ - fn least_squares_nrhs( - a_layout: MatrixLayout, - a: &mut [Self], - b_layout: MatrixLayout, - b: &mut [Self], - ) -> Result>; -} - pub struct LeastSquaresWork { pub a_layout: MatrixLayout, pub b_layout: MatrixLayout, @@ -356,145 +336,3 @@ macro_rules! impl_least_squares_work_r { } impl_least_squares_work_r!(f64, lapack_sys::dgelsd_); impl_least_squares_work_r!(f32, lapack_sys::sgelsd_); - -macro_rules! impl_least_squares { - (@real, $scalar:ty, $gelsd:path) => { - impl_least_squares!(@body, $scalar, $gelsd, ); - }; - (@complex, $scalar:ty, $gelsd:path) => { - impl_least_squares!(@body, $scalar, $gelsd, rwork); - }; - - (@body, $scalar:ty, $gelsd:path, $($rwork:ident),*) => { - impl LeastSquaresSvdDivideConquer_ for $scalar { - fn least_squares( - l: MatrixLayout, - a: &mut [Self], - b: &mut [Self], - ) -> Result> { - let b_layout = l.resized(b.len() as i32, 1); - Self::least_squares_nrhs(l, a, b_layout, b) - } - - fn least_squares_nrhs( - a_layout: MatrixLayout, - a: &mut [Self], - b_layout: MatrixLayout, - b: &mut [Self], - ) -> Result> { - // Minimize |b - Ax|_2 - // - // where - // A : (m, n) - // b : (max(m, n), nrhs) // `b` has to store `x` on exit - // x : (n, nrhs) - let (m, n) = a_layout.size(); - let (m_, nrhs) = b_layout.size(); - let k = m.min(n); - assert!(m_ >= m); - - // Transpose if a is C-continuous - let mut a_t = None; - let a_layout = match a_layout { - MatrixLayout::C { .. } => { - let (layout, t) = transpose(a_layout, a); - a_t = Some(t); - layout - } - MatrixLayout::F { .. } => a_layout, - }; - - // Transpose if b is C-continuous - let mut b_t = None; - let b_layout = match b_layout { - MatrixLayout::C { .. } => { - let (layout, t) = transpose(b_layout, b); - b_t = Some(t); - layout - } - MatrixLayout::F { .. } => b_layout, - }; - - let rcond: Self::Real = -1.; - let mut singular_values: Vec> = vec_uninit( k as usize); - let mut rank: i32 = 0; - - // eval work size - let mut info = 0; - let mut work_size = [Self::zero()]; - let mut iwork_size = [0]; - $( - let mut $rwork = [Self::Real::zero()]; - )* - unsafe { - $gelsd( - &m, - &n, - &nrhs, - AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), - &a_layout.lda(), - AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), - &b_layout.lda(), - AsPtr::as_mut_ptr(&mut singular_values), - &rcond, - &mut rank, - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - $(AsPtr::as_mut_ptr(&mut $rwork),)* - iwork_size.as_mut_ptr(), - &mut info, - ) - }; - info.as_lapack_result()?; - - // calc - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - let liwork = iwork_size[0].to_usize().unwrap(); - let mut iwork: Vec> = vec_uninit(liwork); - $( - let lrwork = $rwork[0].to_usize().unwrap(); - let mut $rwork: Vec> = vec_uninit(lrwork); - )* - unsafe { - $gelsd( - &m, - &n, - &nrhs, - AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), - &a_layout.lda(), - AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), - &b_layout.lda(), - AsPtr::as_mut_ptr(&mut singular_values), - &rcond, - &mut rank, - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - $(AsPtr::as_mut_ptr(&mut $rwork),)* - AsPtr::as_mut_ptr(&mut iwork), - &mut info, - ); - } - info.as_lapack_result()?; - - let singular_values = unsafe { singular_values.assume_init() }; - - // Skip a_t -> a transpose because A has been destroyed - // Re-transpose b - if let Some(b_t) = b_t { - transpose_over(b_layout, &b_t, b); - } - - Ok(LeastSquaresOwned { - singular_values, - rank, - }) - } - } - }; -} - -impl_least_squares!(@real, f64, lapack_sys::dgelsd_); -impl_least_squares!(@real, f32, lapack_sys::sgelsd_); -impl_least_squares!(@complex, c64, lapack_sys::zgelsd_); -impl_least_squares!(@complex, c32, lapack_sys::cgelsd_); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 00ad27b3..09d15c46 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -120,16 +120,10 @@ use std::mem::MaybeUninit; pub type Pivot = Vec; +#[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines pub trait Lapack: - OperatorNorm_ - + Solve_ - + Solveh_ - + Cholesky_ - + Triangular_ - + Tridiagonal_ - + Rcond_ - + LeastSquaresSvdDivideConquer_ + OperatorNorm_ + Solve_ + Solveh_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( @@ -172,6 +166,22 @@ pub trait Lapack: /// Compute singular value decomposition (SVD) with divide-and-conquer algorithm fn svddc(layout: MatrixLayout, jobz: JobSvd, a: &mut [Self]) -> Result>; + + /// Compute a vector $x$ which minimizes Euclidian norm $\| Ax - b\|$ + /// for a given matrix $A$ and a vector $b$. + fn least_squares( + a_layout: MatrixLayout, + a: &mut [Self], + b: &mut [Self], + ) -> Result>; + + /// Solve least square problems $\argmin_X \| AX - B\|$ + fn least_squares_nrhs( + a_layout: MatrixLayout, + a: &mut [Self], + b_layout: MatrixLayout, + b: &mut [Self], + ) -> Result>; } macro_rules! impl_lapack { @@ -247,6 +257,26 @@ macro_rules! impl_lapack { let work = SvdDcWork::<$s>::new(layout, jobz)?; work.eval(a) } + + fn least_squares( + l: MatrixLayout, + a: &mut [Self], + b: &mut [Self], + ) -> Result> { + let b_layout = l.resized(b.len() as i32, 1); + Self::least_squares_nrhs(l, a, b_layout, b) + } + + fn least_squares_nrhs( + a_layout: MatrixLayout, + a: &mut [Self], + b_layout: MatrixLayout, + b: &mut [Self], + ) -> Result> { + use least_squares::*; + let work = LeastSquaresWork::<$s>::new(a_layout, b_layout)?; + work.eval(a, b) + } } }; } diff --git a/ndarray-linalg/src/least_squares.rs b/ndarray-linalg/src/least_squares.rs index 0042ef74..f376f569 100644 --- a/ndarray-linalg/src/least_squares.rs +++ b/ndarray-linalg/src/least_squares.rs @@ -340,7 +340,7 @@ fn compute_residual_scalar>( /// valid representation for `ArrayBase` (over `E`). impl LeastSquaresSvdInPlace for ArrayBase where - E: Scalar + Lapack + LeastSquaresSvdDivideConquer_, + E: Scalar + Lapack, D1: DataMut, D2: DataMut, { From 6a218d389a7db1a62dac882ae06f23f4d5658457 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 29 Sep 2022 00:39:52 +0900 Subject: [PATCH 118/165] Fix refactoring bug --- lax/src/least_squares.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lax/src/least_squares.rs b/lax/src/least_squares.rs index 9f8d4561..d0bb7def 100644 --- a/lax/src/least_squares.rs +++ b/lax/src/least_squares.rs @@ -70,9 +70,9 @@ macro_rules! impl_least_squares_work_c { &n, &nrhs, std::ptr::null_mut(), - &a_layout.lda(), + &m, std::ptr::null_mut(), - &b_layout.lda(), + &m_, AsPtr::as_mut_ptr(&mut singular_values), &rcond, &mut rank, @@ -116,7 +116,7 @@ macro_rules! impl_least_squares_work_c { // Transpose if a is C-continuous let mut a_t = None; - let a_layout = match self.a_layout { + let _ = match self.a_layout { MatrixLayout::C { .. } => { let (layout, t) = transpose(self.a_layout, a); a_t = Some(t); @@ -146,9 +146,9 @@ macro_rules! impl_least_squares_work_c { &n, &nrhs, AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), - &a_layout.lda(), + &m, AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), - &b_layout.lda(), + &m_, AsPtr::as_mut_ptr(&mut self.singular_values), &rcond, &mut rank, @@ -218,9 +218,9 @@ macro_rules! impl_least_squares_work_r { &n, &nrhs, std::ptr::null_mut(), - &a_layout.lda(), + &m, std::ptr::null_mut(), - &b_layout.lda(), + &m_, AsPtr::as_mut_ptr(&mut singular_values), &rcond, &mut rank, @@ -261,7 +261,7 @@ macro_rules! impl_least_squares_work_r { // Transpose if a is C-continuous let mut a_t = None; - let a_layout = match self.a_layout { + let _ = match self.a_layout { MatrixLayout::C { .. } => { let (layout, t) = transpose(self.a_layout, a); a_t = Some(t); @@ -291,9 +291,9 @@ macro_rules! impl_least_squares_work_r { &n, &nrhs, AsPtr::as_mut_ptr(a_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(a)), - &a_layout.lda(), + &m, AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), - &b_layout.lda(), + &m_, AsPtr::as_mut_ptr(&mut self.singular_values), &rcond, &mut rank, From d75c693fd23d7c57a3cf6939006cf48f6b4497c7 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 29 Sep 2022 00:48:09 +0900 Subject: [PATCH 119/165] Make least_squares submodule public, fix document --- lax/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 09d15c46..199f2dc2 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -67,8 +67,7 @@ //! //! - [svd] module for singular value decomposition (SVD) for general matrix //! - [svddc] module for singular value decomposition (SVD) with divided-and-conquer algorithm for general matrix -//! - [LeastSquaresSvdDivideConquer_] trait provides methods -//! for solving least square problem by SVD +//! - [least_squares] module for solving least square problem using SVD //! #![deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)] @@ -89,13 +88,13 @@ pub mod layout; pub mod eig; pub mod eigh; pub mod eigh_generalized; +pub mod least_squares; pub mod qr; pub mod svd; pub mod svddc; mod alloc; mod cholesky; -mod least_squares; mod opnorm; mod rcond; mod solve; @@ -105,7 +104,7 @@ mod tridiagonal; pub use self::cholesky::*; pub use self::flags::*; -pub use self::least_squares::*; +pub use self::least_squares::LeastSquaresOwned; pub use self::opnorm::*; pub use self::rcond::*; pub use self::solve::*; From 77235f4f3fbb705d177fc36cadf2b247e913bef9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 29 Sep 2022 20:54:44 +0900 Subject: [PATCH 120/165] InvWork --- lax/src/solve.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index d0f764fd..89dc1113 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -64,6 +64,69 @@ pub trait Solve_: Scalar + Sized { fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } +pub struct InvWork { + pub layout: MatrixLayout, + pub work: Vec>, +} + +pub trait InvWorkImpl: Sized { + type Elem: Scalar; + fn new(layout: MatrixLayout) -> Result; + fn calc(&mut self, a: &mut [Self::Elem], p: &Pivot) -> Result<()>; +} + +macro_rules! impl_inv_work { + ($s:ty, $tri:path) => { + impl InvWorkImpl for InvWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let (n, _) = layout.size(); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $tri( + &n, + std::ptr::null_mut(), + &layout.lda(), + std::ptr::null(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(InvWork { layout, work }) + } + + fn calc(&mut self, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $tri( + &self.layout.len(), + AsPtr::as_mut_ptr(a), + &self.layout.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(()) + } + } + }; +} + +impl_inv_work!(c64, lapack_sys::zgetri_); +impl_inv_work!(c32, lapack_sys::cgetri_); +impl_inv_work!(f64, lapack_sys::dgetri_); +impl_inv_work!(f32, lapack_sys::sgetri_); + macro_rules! impl_solve { ($scalar:ty, $getrf:path, $getri:path, $getrs:path) => { impl Solve_ for $scalar { From 842ad79f4d445931f070ef3633b11fbd25abcef0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 01:01:10 +0900 Subject: [PATCH 121/165] LuImpl, SolveImpl --- lax/src/lib.rs | 73 +++++++++++++- lax/src/solve.rs | 258 +++++++++++++++-------------------------------- 2 files changed, 154 insertions(+), 177 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 199f2dc2..6ed8498c 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -90,6 +90,7 @@ pub mod eigh; pub mod eigh_generalized; pub mod least_squares; pub mod qr; +pub mod solve; pub mod svd; pub mod svddc; @@ -97,7 +98,6 @@ mod alloc; mod cholesky; mod opnorm; mod rcond; -mod solve; mod solveh; mod triangular; mod tridiagonal; @@ -107,7 +107,6 @@ pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; pub use self::opnorm::*; pub use self::rcond::*; -pub use self::solve::*; pub use self::solveh::*; pub use self::svd::{SvdOwned, SvdRef}; pub use self::triangular::*; @@ -122,7 +121,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines pub trait Lapack: - OperatorNorm_ + Solve_ + Solveh_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ + OperatorNorm_ + Solveh_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( @@ -181,6 +180,51 @@ pub trait Lapack: b_layout: MatrixLayout, b: &mut [Self], ) -> Result>; + + /// Computes the LU decomposition of a general $m \times n$ matrix + /// with partial pivoting with row interchanges. + /// + /// Output + /// ------- + /// - $U$ and $L$ are stored in `a` after LU decomposition has succeeded. + /// - $P$ is returned as [Pivot] + /// + /// Error + /// ------ + /// - if the matrix is singular + /// - On this case, `return_code` in [Error::LapackComputationalFailure] means + /// `return_code`-th diagonal element of $U$ becomes zero. + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetrf | dgetrf | cgetrf | zgetrf | + /// + fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; + + /// Compute inverse matrix $A^{-1}$ from the output of LU-decomposition + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetri | dgetri | cgetri | zgetri | + /// + fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()>; + + /// Solve linear equations $Ax = b$ using the output of LU-decomposition + /// + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetrs | dgetrs | cgetrs | zgetrs | + /// + fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } macro_rules! impl_lapack { @@ -276,6 +320,29 @@ macro_rules! impl_lapack { let work = LeastSquaresWork::<$s>::new(a_layout, b_layout)?; work.eval(a, b) } + + fn lu(l: MatrixLayout, a: &mut [Self]) -> Result { + use solve::*; + LuImpl::lu(l, a) + } + + fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()> { + use solve::*; + let mut work = InvWork::<$s>::new(l)?; + work.calc(a, p)?; + Ok(()) + } + + fn solve( + l: MatrixLayout, + t: Transpose, + a: &[Self], + p: &Pivot, + b: &mut [Self], + ) -> Result<()> { + use solve::*; + SolveImpl::solve(l, t, a, p, b) + } } }; } diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 89dc1113..ba67c847 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -17,119 +17,13 @@ use num_traits::{ToPrimitive, Zero}; /// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ /// using the output of LU decomposition. /// -pub trait Solve_: Scalar + Sized { - /// Computes the LU decomposition of a general $m \times n$ matrix - /// with partial pivoting with row interchanges. - /// - /// Output - /// ------- - /// - $U$ and $L$ are stored in `a` after LU decomposition has succeeded. - /// - $P$ is returned as [Pivot] - /// - /// Error - /// ------ - /// - if the matrix is singular - /// - On this case, `return_code` in [Error::LapackComputationalFailure] means - /// `return_code`-th diagonal element of $U$ becomes zero. - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetrf | dgetrf | cgetrf | zgetrf | - /// +pub trait LuImpl: Scalar { fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; - - /// Compute inverse matrix $A^{-1}$ from the output of LU-decomposition - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetri | dgetri | cgetri | zgetri | - /// - fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()>; - - /// Solve linear equations $Ax = b$ using the output of LU-decomposition - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetrs | dgetrs | cgetrs | zgetrs | - /// - fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; -} - -pub struct InvWork { - pub layout: MatrixLayout, - pub work: Vec>, -} - -pub trait InvWorkImpl: Sized { - type Elem: Scalar; - fn new(layout: MatrixLayout) -> Result; - fn calc(&mut self, a: &mut [Self::Elem], p: &Pivot) -> Result<()>; -} - -macro_rules! impl_inv_work { - ($s:ty, $tri:path) => { - impl InvWorkImpl for InvWork<$s> { - type Elem = $s; - - fn new(layout: MatrixLayout) -> Result { - let (n, _) = layout.size(); - let mut info = 0; - let mut work_size = [Self::Elem::zero()]; - unsafe { - $tri( - &n, - std::ptr::null_mut(), - &layout.lda(), - std::ptr::null(), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ) - }; - info.as_lapack_result()?; - let lwork = work_size[0].to_usize().unwrap(); - let work = vec_uninit(lwork); - Ok(InvWork { layout, work }) - } - - fn calc(&mut self, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()> { - let lwork = self.work.len().to_i32().unwrap(); - let mut info = 0; - unsafe { - $tri( - &self.layout.len(), - AsPtr::as_mut_ptr(a), - &self.layout.lda(), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(&mut self.work), - &lwork, - &mut info, - ) - }; - info.as_lapack_result()?; - Ok(()) - } - } - }; } -impl_inv_work!(c64, lapack_sys::zgetri_); -impl_inv_work!(c32, lapack_sys::cgetri_); -impl_inv_work!(f64, lapack_sys::dgetri_); -impl_inv_work!(f32, lapack_sys::sgetri_); - -macro_rules! impl_solve { - ($scalar:ty, $getrf:path, $getri:path, $getrs:path) => { - impl Solve_ for $scalar { +macro_rules! impl_lu { + ($scalar:ty, $getrf:path) => { + impl LuImpl for $scalar { fn lu(l: MatrixLayout, a: &mut [Self]) -> Result { let (row, col) = l.size(); assert_eq!(a.len() as i32, row * col); @@ -154,49 +48,22 @@ macro_rules! impl_solve { let ipiv = unsafe { ipiv.assume_init() }; Ok(ipiv) } + } + }; +} - fn inv(l: MatrixLayout, a: &mut [Self], ipiv: &Pivot) -> Result<()> { - let (n, _) = l.size(); - if n == 0 { - // Do nothing for empty matrices. - return Ok(()); - } - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $getri( - &n, - AsPtr::as_mut_ptr(a), - &l.lda(), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ) - }; - info.as_lapack_result()?; - - // actual - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - $getri( - &l.len(), - AsPtr::as_mut_ptr(a), - &l.lda(), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ) - }; - info.as_lapack_result()?; +impl_lu!(c64, lapack_sys::zgetrf_); +impl_lu!(c32, lapack_sys::cgetrf_); +impl_lu!(f64, lapack_sys::dgetrf_); +impl_lu!(f32, lapack_sys::sgetrf_); - Ok(()) - } +pub trait SolveImpl: Scalar { + fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; +} +macro_rules! impl_solve { + ($scalar:ty, $getrs:path) => { + impl SolveImpl for $scalar { fn solve( l: MatrixLayout, t: Transpose, @@ -266,27 +133,70 @@ macro_rules! impl_solve { }; } // impl_solve! -impl_solve!( - f64, - lapack_sys::dgetrf_, - lapack_sys::dgetri_, - lapack_sys::dgetrs_ -); -impl_solve!( - f32, - lapack_sys::sgetrf_, - lapack_sys::sgetri_, - lapack_sys::sgetrs_ -); -impl_solve!( - c64, - lapack_sys::zgetrf_, - lapack_sys::zgetri_, - lapack_sys::zgetrs_ -); -impl_solve!( - c32, - lapack_sys::cgetrf_, - lapack_sys::cgetri_, - lapack_sys::cgetrs_ -); +impl_solve!(f64, lapack_sys::dgetrs_); +impl_solve!(f32, lapack_sys::sgetrs_); +impl_solve!(c64, lapack_sys::zgetrs_); +impl_solve!(c32, lapack_sys::cgetrs_); + +pub struct InvWork { + pub layout: MatrixLayout, + pub work: Vec>, +} + +pub trait InvWorkImpl: Sized { + type Elem: Scalar; + fn new(layout: MatrixLayout) -> Result; + fn calc(&mut self, a: &mut [Self::Elem], p: &Pivot) -> Result<()>; +} + +macro_rules! impl_inv_work { + ($s:ty, $tri:path) => { + impl InvWorkImpl for InvWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let (n, _) = layout.size(); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $tri( + &n, + std::ptr::null_mut(), + &layout.lda(), + std::ptr::null(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(InvWork { layout, work }) + } + + fn calc(&mut self, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $tri( + &self.layout.len(), + AsPtr::as_mut_ptr(a), + &self.layout.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(()) + } + } + }; +} + +impl_inv_work!(c64, lapack_sys::zgetri_); +impl_inv_work!(c32, lapack_sys::cgetri_); +impl_inv_work!(f64, lapack_sys::dgetri_); +impl_inv_work!(f32, lapack_sys::sgetri_); From db395057690061d5a36f162f709649e47ddfd235 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 01:31:14 +0900 Subject: [PATCH 122/165] Fix 0-sized case --- lax/src/solve.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index ba67c847..6dfa5433 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -176,6 +176,9 @@ macro_rules! impl_inv_work { } fn calc(&mut self, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()> { + if self.layout.len() == 0 { + return Ok(()); + } let lwork = self.work.len().to_i32().unwrap(); let mut info = 0; unsafe { From 23baa44c29295872d26eef35d4ca426afcb41459 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 01:31:28 +0900 Subject: [PATCH 123/165] Update documents --- lax/src/lib.rs | 64 +++++++++++++++++--------------------- lax/src/solve.rs | 80 +++++++++++++++++++++++++++++------------------- 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 6ed8498c..49ee1702 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -1,21 +1,24 @@ -//! ndarray-free safe Rust wrapper for LAPACK FFI +//! Safe Rust wrapper for LAPACK without external dependency. //! -//! `Lapack` trait and sub-traits -//! ------------------------------- +//! [Lapack] trait +//! ---------------- //! -//! This crates provides LAPACK wrapper as `impl` of traits to base scalar types. -//! For example, LU decomposition to double-precision matrix is provided like: +//! This crates provides LAPACK wrapper as a traits. +//! For example, LU decomposition of general matrices is provided like: //! -//! ```ignore -//! impl Solve_ for f64 { -//! fn lu(l: MatrixLayout, a: &mut [Self]) -> Result { ... } +//! ``` +//! pub trait Lapack{ +//! fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; //! } //! ``` //! -//! see [Solve_] for detail. You can use it like `f64::lu`: +//! see [Lapack] for detail. +//! This trait is implemented for [f32], [f64], [c32] which is an alias to `num::Complex`, +//! and [c64] which is an alias to `num::Complex`. +//! You can use it like `f64::lu`: //! //! ``` -//! use lax::{Solve_, layout::MatrixLayout, Transpose}; +//! use lax::{Lapack, layout::MatrixLayout, Transpose}; //! //! let mut a = vec![ //! 1.0, 2.0, @@ -31,9 +34,9 @@ //! this trait can be used as a trait bound: //! //! ``` -//! use lax::{Solve_, layout::MatrixLayout, Transpose}; +//! use lax::{Lapack, layout::MatrixLayout, Transpose}; //! -//! fn solve_at_once(layout: MatrixLayout, a: &mut [T], b: &mut [T]) -> Result<(), lax::error::Error> { +//! fn solve_at_once(layout: MatrixLayout, a: &mut [T], b: &mut [T]) -> Result<(), lax::error::Error> { //! let pivot = T::lu(layout, a)?; //! T::solve(layout, Transpose::No, a, &pivot, b)?; //! Ok(()) @@ -48,7 +51,7 @@ //! //! According to the property input metrix, several types of triangular decomposition are used: //! -//! - [Solve_] trait provides methods for LU-decomposition for general matrix. +//! - [solve] module provides methods for LU-decomposition for general matrix. //! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. //! - [Cholesky_] triat provides methods for Cholesky decomposition for symmetric/hermite positive dinite matrix. //! @@ -184,6 +187,18 @@ pub trait Lapack: /// Computes the LU decomposition of a general $m \times n$ matrix /// with partial pivoting with row interchanges. /// + /// For a given matrix $A$, LU decomposition is described as $A = PLU$ where: + /// + /// - $L$ is lower matrix + /// - $U$ is upper matrix + /// - $P$ is permutation matrix represented by [Pivot] + /// + /// This is designed as two step computation according to LAPACK API: + /// + /// 1. Factorize input matrix $A$ into $L$, $U$, and $P$. + /// 2. Solve linear equation $Ax = b$ by [Lapack::solve] + /// or compute inverse matrix $A^{-1}$ by [Lapack::inv] using the output of LU decomposition. + /// /// Output /// ------- /// - $U$ and $L$ are stored in `a` after LU decomposition has succeeded. @@ -195,35 +210,12 @@ pub trait Lapack: /// - On this case, `return_code` in [Error::LapackComputationalFailure] means /// `return_code`-th diagonal element of $U$ becomes zero. /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetrf | dgetrf | cgetrf | zgetrf | - /// fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; /// Compute inverse matrix $A^{-1}$ from the output of LU-decomposition - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetri | dgetri | cgetri | zgetri | - /// fn inv(l: MatrixLayout, a: &mut [Self], p: &Pivot) -> Result<()>; /// Solve linear equations $Ax = b$ using the output of LU-decomposition - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | sgetrs | dgetrs | cgetrs | zgetrs | - /// fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 6dfa5433..1b3239f5 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -1,21 +1,17 @@ +//! Solve linear equations using LU-decomposition + use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; -#[cfg_attr(doc, katexit::katexit)] -/// Solve linear equations using LU-decomposition -/// -/// For a given matrix $A$, LU decomposition is described as $A = PLU$ where: -/// -/// - $L$ is lower matrix -/// - $U$ is upper matrix -/// - $P$ is permutation matrix represented by [Pivot] +/// Helper trait to abstract `*getrf` LAPACK routines for implementing [Lapack::lu] /// -/// This is designed as two step computation according to LAPACK API: +/// LAPACK correspondance +/// ---------------------- /// -/// 1. Factorize input matrix $A$ into $L$, $U$, and $P$. -/// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ -/// using the output of LU decomposition. +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | sgetrf | dgetrf | cgetrf | zgetrf | /// pub trait LuImpl: Scalar { fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; @@ -57,6 +53,36 @@ impl_lu!(c32, lapack_sys::cgetrf_); impl_lu!(f64, lapack_sys::dgetrf_); impl_lu!(f32, lapack_sys::sgetrf_); +/// Helper trait to abstract `*getrs` LAPACK routines for implementing [Lapack::solve] +/// +/// If the array has C layout, then it needs to be handled +/// specially, since LAPACK expects a Fortran-layout array. +/// Reinterpreting a C layout array as Fortran layout is +/// equivalent to transposing it. So, we can handle the "no +/// transpose" and "transpose" cases by swapping to "transpose" +/// or "no transpose", respectively. For the "Hermite" case, we +/// can take advantage of the following: +/// +/// ```text +/// A^H x = b +/// ⟺ conj(A^T) x = b +/// ⟺ conj(conj(A^T) x) = conj(b) +/// ⟺ conj(conj(A^T)) conj(x) = conj(b) +/// ⟺ A^T conj(x) = conj(b) +/// ``` +/// +/// So, we can handle this case by switching to "no transpose" +/// (which is equivalent to transposing the array since it will +/// be reinterpreted as Fortran layout) and applying the +/// elementwise conjugate to `x` and `b`. +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | sgetrs | dgetrs | cgetrs | zgetrs | +/// pub trait SolveImpl: Scalar { fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } @@ -71,26 +97,6 @@ macro_rules! impl_solve { ipiv: &Pivot, b: &mut [Self], ) -> Result<()> { - // If the array has C layout, then it needs to be handled - // specially, since LAPACK expects a Fortran-layout array. - // Reinterpreting a C layout array as Fortran layout is - // equivalent to transposing it. So, we can handle the "no - // transpose" and "transpose" cases by swapping to "transpose" - // or "no transpose", respectively. For the "Hermite" case, we - // can take advantage of the following: - // - // ```text - // A^H x = b - // ⟺ conj(A^T) x = b - // ⟺ conj(conj(A^T) x) = conj(b) - // ⟺ conj(conj(A^T)) conj(x) = conj(b) - // ⟺ A^T conj(x) = conj(b) - // ``` - // - // So, we can handle this case by switching to "no transpose" - // (which is equivalent to transposing the array since it will - // be reinterpreted as Fortran layout) and applying the - // elementwise conjugate to `x` and `b`. let (t, conj) = match l { MatrixLayout::C { .. } => match t { Transpose::No => (Transpose::Transpose, false), @@ -138,11 +144,21 @@ impl_solve!(f32, lapack_sys::sgetrs_); impl_solve!(c64, lapack_sys::zgetrs_); impl_solve!(c32, lapack_sys::cgetrs_); +/// Working memory for computing inverse matrix pub struct InvWork { pub layout: MatrixLayout, pub work: Vec>, } +/// Helper trait to abstract `*getri` LAPACK rotuines for implementing [Lapack::inv] +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | sgetri | dgetri | cgetri | zgetri | +/// pub trait InvWorkImpl: Sized { type Elem: Scalar; fn new(layout: MatrixLayout) -> Result; From 3dcf19b78a8ff97e411b970d99d70f81f363f0d8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 21:24:29 +0900 Subject: [PATCH 124/165] Add BkWork, InvhWork, SolvehImpl --- lax/src/solveh.rs | 161 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index bbc6f363..e357a195 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -2,6 +2,167 @@ use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; +pub struct BkWork { + pub layout: MatrixLayout, + pub work: Vec>, + pub ipiv: Vec>, +} + +pub trait BkWorkImpl: Sized { + type Elem: Scalar; + fn new(l: MatrixLayout) -> Result; + fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem]) -> Result<&[i32]>; + fn eval(self, uplo: UPLO, a: &mut [Self::Elem]) -> Result; +} + +macro_rules! impl_bk_work { + ($s:ty, $trf:path) => { + impl BkWorkImpl for BkWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let (n, _) = layout.size(); + let ipiv = vec_uninit(n as usize); + let mut info = 0; + let mut work_size = [Self::Elem::zero()]; + unsafe { + $trf( + UPLO::Upper.as_ptr(), + &n, + std::ptr::null_mut(), + &layout.lda(), + std::ptr::null_mut(), + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + Ok(BkWork { layout, work, ipiv }) + } + + fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem]) -> Result<&[i32]> { + let (n, _) = self.layout.size(); + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $trf( + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &self.layout.lda(), + AsPtr::as_mut_ptr(&mut self.ipiv), + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(unsafe { self.ipiv.slice_assume_init_ref() }) + } + + fn eval(mut self, uplo: UPLO, a: &mut [Self::Elem]) -> Result { + let _ref = self.calc(uplo, a)?; + Ok(unsafe { self.ipiv.assume_init() }) + } + } + }; +} +impl_bk_work!(c64, lapack_sys::zhetrf_); +impl_bk_work!(c32, lapack_sys::chetrf_); +impl_bk_work!(f64, lapack_sys::dsytrf_); +impl_bk_work!(f32, lapack_sys::ssytrf_); + +pub struct InvhWork { + pub layout: MatrixLayout, + pub work: Vec>, +} + +pub trait InvhWorkImpl: Sized { + type Elem; + fn new(layout: MatrixLayout) -> Result; + fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()>; +} + +macro_rules! impl_invh_work { + ($s:ty, $tri:path) => { + impl InvhWorkImpl for InvhWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Result { + let (n, _) = layout.size(); + let work = vec_uninit(n as usize); + Ok(InvhWork { layout, work }) + } + + fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem], ipiv: &Pivot) -> Result<()> { + let (n, _) = self.layout.size(); + let mut info = 0; + unsafe { + $tri( + uplo.as_ptr(), + &n, + AsPtr::as_mut_ptr(a), + &self.layout.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(&mut self.work), + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(()) + } + } + }; +} +impl_invh_work!(c64, lapack_sys::zhetri_); +impl_invh_work!(c32, lapack_sys::chetri_); +impl_invh_work!(f64, lapack_sys::dsytri_); +impl_invh_work!(f32, lapack_sys::ssytri_); + +pub trait SolvehImpl: Scalar { + fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; +} + +macro_rules! impl_solveh_ { + ($s:ty, $trs:path) => { + impl SolvehImpl for $s { + fn solveh( + l: MatrixLayout, + uplo: UPLO, + a: &[Self], + ipiv: &Pivot, + b: &mut [Self], + ) -> Result<()> { + let (n, _) = l.size(); + let mut info = 0; + unsafe { + $trs( + uplo.as_ptr(), + &n, + &1, + AsPtr::as_ptr(a), + &l.lda(), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(b), + &n, + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(()) + } + } + }; +} + +impl_solveh_!(c64, lapack_sys::zhetrs_); +impl_solveh_!(c32, lapack_sys::chetrs_); +impl_solveh_!(f64, lapack_sys::dsytrs_); +impl_solveh_!(f32, lapack_sys::ssytrs_); + #[cfg_attr(doc, katexit::katexit)] /// Solve symmetric/hermite indefinite linear problem using the [Bunch-Kaufman diagonal pivoting method][BK]. /// From 4f7404d85c5e93c519c4104b70afe5be489104e8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 21:41:58 +0900 Subject: [PATCH 125/165] Merge Solveh_ into Lapack trait --- lax/src/lib.rs | 51 +++++++++++- lax/src/solveh.rs | 198 +++++++--------------------------------------- 2 files changed, 75 insertions(+), 174 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 49ee1702..30421d74 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -123,9 +123,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: - OperatorNorm_ + Solveh_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ -{ +pub trait Lapack: OperatorNorm_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -217,6 +215,30 @@ pub trait Lapack: /// Solve linear equations $Ax = b$ using the output of LU-decomposition fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; + + /// Factorize symmetric/Hermitian matrix using Bunch-Kaufman diagonal pivoting method + /// + /// + /// For a given symmetric matrix $A$, + /// this method factorizes $A = U^T D U$ or $A = L D L^T$ where + /// + /// - $U$ (or $L$) are is a product of permutation and unit upper (lower) triangular matrices + /// - $D$ is symmetric and block diagonal with 1-by-1 and 2-by-2 diagonal blocks. + /// + /// This takes two-step approach based in LAPACK: + /// + /// 1. Factorize given matrix $A$ into upper ($U$) or lower ($L$) form with diagonal matrix $D$ + /// 2. Then solve linear equation $Ax = b$, and/or calculate inverse matrix $A^{-1}$ + /// + /// [BK]: https://doi.org/10.2307/2005787 + /// + fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; + + /// Compute inverse matrix $A^{-1}$ of symmetric/Hermitian matrix using factroized result + fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()>; + + /// Solve symmetric/Hermitian linear equation $Ax = b$ using factroized result + fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; } macro_rules! impl_lapack { @@ -335,6 +357,29 @@ macro_rules! impl_lapack { use solve::*; SolveImpl::solve(l, t, a, p, b) } + + fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result { + use solveh::*; + let work = BkWork::<$s>::new(l)?; + work.eval(uplo, a) + } + + fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { + use solveh::*; + let mut work = InvhWork::<$s>::new(l)?; + work.calc(uplo, a, ipiv) + } + + fn solveh( + l: MatrixLayout, + uplo: UPLO, + a: &[Self], + ipiv: &Pivot, + b: &mut [Self], + ) -> Result<()> { + use solveh::*; + SolvehImpl::solveh(l, uplo, a, ipiv, b) + } } }; } diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index e357a195..e9af59b6 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -8,6 +8,15 @@ pub struct BkWork { pub ipiv: Vec>, } +/// Factorize symmetric/Hermitian matrix using Bunch-Kaufman diagonal pivoting method +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | ssytrf | dsytrf | chetrf | zhetrf | +/// pub trait BkWorkImpl: Sized { type Elem: Scalar; fn new(l: MatrixLayout) -> Result; @@ -80,6 +89,15 @@ pub struct InvhWork { pub work: Vec>, } +/// Compute inverse matrix of symmetric/Hermitian matrix +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | ssytri | dsytri | chetri | zhetri | +/// pub trait InvhWorkImpl: Sized { type Elem; fn new(layout: MatrixLayout) -> Result; @@ -122,6 +140,15 @@ impl_invh_work!(c32, lapack_sys::chetri_); impl_invh_work!(f64, lapack_sys::dsytri_); impl_invh_work!(f32, lapack_sys::ssytri_); +/// Solve symmetric/Hermitian linear equation +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | ssytrs | dsytrs | chetrs | zhetrs | +/// pub trait SolvehImpl: Scalar { fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; } @@ -162,174 +189,3 @@ impl_solveh_!(c64, lapack_sys::zhetrs_); impl_solveh_!(c32, lapack_sys::chetrs_); impl_solveh_!(f64, lapack_sys::dsytrs_); impl_solveh_!(f32, lapack_sys::ssytrs_); - -#[cfg_attr(doc, katexit::katexit)] -/// Solve symmetric/hermite indefinite linear problem using the [Bunch-Kaufman diagonal pivoting method][BK]. -/// -/// For a given symmetric matrix $A$, -/// this method factorizes $A = U^T D U$ or $A = L D L^T$ where -/// -/// - $U$ (or $L$) are is a product of permutation and unit upper (lower) triangular matrices -/// - $D$ is symmetric and block diagonal with 1-by-1 and 2-by-2 diagonal blocks. -/// -/// This takes two-step approach based in LAPACK: -/// -/// 1. Factorize given matrix $A$ into upper ($U$) or lower ($L$) form with diagonal matrix $D$ -/// 2. Then solve linear equation $Ax = b$, and/or calculate inverse matrix $A^{-1}$ -/// -/// [BK]: https://doi.org/10.2307/2005787 -/// -pub trait Solveh_: Sized { - /// Factorize input matrix using Bunch-Kaufman diagonal pivoting method - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | ssytrf | dsytrf | chetrf | zhetrf | - /// - fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; - - /// Compute inverse matrix $A^{-1}$ from factroized result - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | ssytri | dsytri | chetri | zhetri | - /// - fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()>; - - /// Solve linear equation $Ax = b$ using factroized result - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | ssytrs | dsytrs | chetrs | zhetrs | - /// - fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; -} - -macro_rules! impl_solveh { - ($scalar:ty, $trf:path, $tri:path, $trs:path) => { - impl Solveh_ for $scalar { - fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result { - let (n, _) = l.size(); - let mut ipiv = vec_uninit(n as usize); - if n == 0 { - return Ok(Vec::new()); - } - - // calc work size - let mut info = 0; - let mut work_size = [Self::zero()]; - unsafe { - $trf( - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &l.lda(), - AsPtr::as_mut_ptr(&mut ipiv), - AsPtr::as_mut_ptr(&mut work_size), - &(-1), - &mut info, - ) - }; - info.as_lapack_result()?; - - // actual - let lwork = work_size[0].to_usize().unwrap(); - let mut work: Vec> = vec_uninit(lwork); - unsafe { - $trf( - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &l.lda(), - AsPtr::as_mut_ptr(&mut ipiv), - AsPtr::as_mut_ptr(&mut work), - &(lwork as i32), - &mut info, - ) - }; - info.as_lapack_result()?; - let ipiv = unsafe { ipiv.assume_init() }; - Ok(ipiv) - } - - fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()> { - let (n, _) = l.size(); - let mut info = 0; - let mut work: Vec> = vec_uninit(n as usize); - unsafe { - $tri( - uplo.as_ptr(), - &n, - AsPtr::as_mut_ptr(a), - &l.lda(), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(&mut work), - &mut info, - ) - }; - info.as_lapack_result()?; - Ok(()) - } - - fn solveh( - l: MatrixLayout, - uplo: UPLO, - a: &[Self], - ipiv: &Pivot, - b: &mut [Self], - ) -> Result<()> { - let (n, _) = l.size(); - let mut info = 0; - unsafe { - $trs( - uplo.as_ptr(), - &n, - &1, - AsPtr::as_ptr(a), - &l.lda(), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(b), - &n, - &mut info, - ) - }; - info.as_lapack_result()?; - Ok(()) - } - } - }; -} // impl_solveh! - -impl_solveh!( - f64, - lapack_sys::dsytrf_, - lapack_sys::dsytri_, - lapack_sys::dsytrs_ -); -impl_solveh!( - f32, - lapack_sys::ssytrf_, - lapack_sys::ssytri_, - lapack_sys::ssytrs_ -); -impl_solveh!( - c64, - lapack_sys::zhetrf_, - lapack_sys::zhetri_, - lapack_sys::zhetrs_ -); -impl_solveh!( - c32, - lapack_sys::chetrf_, - lapack_sys::chetri_, - lapack_sys::chetrs_ -); From 608010c55a3749e0042ded6d07c136f0170c9f6e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 30 Sep 2022 21:44:29 +0900 Subject: [PATCH 126/165] Make solveh submodule public --- lax/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 30421d74..8b0be740 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -52,7 +52,7 @@ //! According to the property input metrix, several types of triangular decomposition are used: //! //! - [solve] module provides methods for LU-decomposition for general matrix. -//! - [Solveh_] triat provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. +//! - [solveh] module provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. //! - [Cholesky_] triat provides methods for Cholesky decomposition for symmetric/hermite positive dinite matrix. //! //! Eigenvalue Problem @@ -94,6 +94,7 @@ pub mod eigh_generalized; pub mod least_squares; pub mod qr; pub mod solve; +pub mod solveh; pub mod svd; pub mod svddc; @@ -101,7 +102,6 @@ mod alloc; mod cholesky; mod opnorm; mod rcond; -mod solveh; mod triangular; mod tridiagonal; @@ -110,7 +110,6 @@ pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; pub use self::opnorm::*; pub use self::rcond::*; -pub use self::solveh::*; pub use self::svd::{SvdOwned, SvdRef}; pub use self::triangular::*; pub use self::tridiagonal::*; From d42fc3ec231ad73ffdad0f4b809dda18cf600951 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:14:13 +0900 Subject: [PATCH 127/165] Add CholeskyImpl, InvCholeskyImpl, SolveCholeskyImpl --- lax/src/cholesky.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 9b213246..1c0d3612 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -2,6 +2,116 @@ use super::*; use crate::{error::*, layout::*}; use cauchy::*; +pub trait CholeskyImpl: Scalar { + fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; +} + +macro_rules! impl_cholesky_ { + ($s:ty, $trf:path) => { + impl CholeskyImpl for $s { + fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { + let (n, _) = l.size(); + if matches!(l, MatrixLayout::C { .. }) { + square_transpose(l, a); + } + let mut info = 0; + unsafe { + $trf(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &n, &mut info); + } + info.as_lapack_result()?; + if matches!(l, MatrixLayout::C { .. }) { + square_transpose(l, a); + } + Ok(()) + } + } + }; +} +impl_cholesky_!(c64, lapack_sys::zpotrf_); +impl_cholesky_!(c32, lapack_sys::cpotrf_); +impl_cholesky_!(f64, lapack_sys::dpotrf_); +impl_cholesky_!(f32, lapack_sys::spotrf_); + +pub trait InvCholeskyImpl: Scalar { + fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; +} + +macro_rules! impl_inv_cholesky { + ($s:ty, $tri:path) => { + impl InvCholeskyImpl for $s { + fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { + let (n, _) = l.size(); + if matches!(l, MatrixLayout::C { .. }) { + square_transpose(l, a); + } + let mut info = 0; + unsafe { + $tri(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &l.lda(), &mut info); + } + info.as_lapack_result()?; + if matches!(l, MatrixLayout::C { .. }) { + square_transpose(l, a); + } + Ok(()) + } + } + }; +} +impl_inv_cholesky!(c64, lapack_sys::zpotri_); +impl_inv_cholesky!(c32, lapack_sys::cpotri_); +impl_inv_cholesky!(f64, lapack_sys::dpotri_); +impl_inv_cholesky!(f32, lapack_sys::spotri_); + +pub trait SolveCholeskyImpl: Scalar { + fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; +} + +macro_rules! impl_solve_cholesky { + ($s:ty, $trs:path) => { + impl SolveCholeskyImpl for $s { + fn solve_cholesky( + l: MatrixLayout, + mut uplo: UPLO, + a: &[Self], + b: &mut [Self], + ) -> Result<()> { + let (n, _) = l.size(); + let nrhs = 1; + let mut info = 0; + if matches!(l, MatrixLayout::C { .. }) { + uplo = uplo.t(); + for val in b.iter_mut() { + *val = val.conj(); + } + } + unsafe { + $trs( + uplo.as_ptr(), + &n, + &nrhs, + AsPtr::as_ptr(a), + &l.lda(), + AsPtr::as_mut_ptr(b), + &n, + &mut info, + ); + } + info.as_lapack_result()?; + if matches!(l, MatrixLayout::C { .. }) { + for val in b.iter_mut() { + *val = val.conj(); + } + } + Ok(()) + } + } + }; +} +impl_solve_cholesky!(c64, lapack_sys::zpotrs_); +impl_solve_cholesky!(c32, lapack_sys::cpotrs_); +impl_solve_cholesky!(f64, lapack_sys::dpotrs_); +impl_solve_cholesky!(f32, lapack_sys::spotrs_); + #[cfg_attr(doc, katexit::katexit)] /// Solve symmetric/hermite positive-definite linear equations using Cholesky decomposition /// From 062a345b383ddc88fb6541bbf94c90860a63dee7 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:19:10 +0900 Subject: [PATCH 128/165] Merge Cholesky_ into Lapack --- lax/src/cholesky.rs | 175 +++++++------------------------------------- lax/src/lib.rs | 43 ++++++++++- 2 files changed, 69 insertions(+), 149 deletions(-) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 1c0d3612..0f853173 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -2,6 +2,15 @@ use super::*; use crate::{error::*, layout::*}; use cauchy::*; +/// Compute Cholesky decomposition according to [UPLO] +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | spotrf | dpotrf | cpotrf | zpotrf | +/// pub trait CholeskyImpl: Scalar { fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; } @@ -32,6 +41,15 @@ impl_cholesky_!(c32, lapack_sys::cpotrf_); impl_cholesky_!(f64, lapack_sys::dpotrf_); impl_cholesky_!(f32, lapack_sys::spotrf_); +/// Compute inverse matrix using Cholesky factroization result +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | spotri | dpotri | cpotri | zpotri | +/// pub trait InvCholeskyImpl: Scalar { fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; } @@ -62,6 +80,15 @@ impl_inv_cholesky!(c32, lapack_sys::cpotri_); impl_inv_cholesky!(f64, lapack_sys::dpotri_); impl_inv_cholesky!(f32, lapack_sys::spotri_); +/// Solve linear equation using Cholesky factroization result +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | spotrs | dpotrs | cpotrs | zpotrs | +/// pub trait SolveCholeskyImpl: Scalar { fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; } @@ -111,151 +138,3 @@ impl_solve_cholesky!(c64, lapack_sys::zpotrs_); impl_solve_cholesky!(c32, lapack_sys::cpotrs_); impl_solve_cholesky!(f64, lapack_sys::dpotrs_); impl_solve_cholesky!(f32, lapack_sys::spotrs_); - -#[cfg_attr(doc, katexit::katexit)] -/// Solve symmetric/hermite positive-definite linear equations using Cholesky decomposition -/// -/// For a given positive definite matrix $A$, -/// Cholesky decomposition is described as $A = U^T U$ or $A = LL^T$ where -/// -/// - $L$ is lower matrix -/// - $U$ is upper matrix -/// -/// This is designed as two step computation according to LAPACK API -/// -/// 1. Factorize input matrix $A$ into $L$ or $U$ -/// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ -/// using $U$ or $L$. -pub trait Cholesky_: Sized { - /// Compute Cholesky decomposition $A = U^T U$ or $A = L L^T$ according to [UPLO] - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | spotrf | dpotrf | cpotrf | zpotrf | - /// - fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - - /// Compute inverse matrix $A^{-1}$ using $U$ or $L$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | spotri | dpotri | cpotri | zpotri | - /// - fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - - /// Solve linear equation $Ax = b$ using $U$ or $L$ - /// - /// LAPACK correspondance - /// ---------------------- - /// - /// | f32 | f64 | c32 | c64 | - /// |:-------|:-------|:-------|:-------| - /// | spotrs | dpotrs | cpotrs | zpotrs | - /// - fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; -} - -macro_rules! impl_cholesky { - ($scalar:ty, $trf:path, $tri:path, $trs:path) => { - impl Cholesky_ for $scalar { - fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { - let (n, _) = l.size(); - if matches!(l, MatrixLayout::C { .. }) { - square_transpose(l, a); - } - let mut info = 0; - unsafe { - $trf(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &n, &mut info); - } - info.as_lapack_result()?; - if matches!(l, MatrixLayout::C { .. }) { - square_transpose(l, a); - } - Ok(()) - } - - fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { - let (n, _) = l.size(); - if matches!(l, MatrixLayout::C { .. }) { - square_transpose(l, a); - } - let mut info = 0; - unsafe { - $tri(uplo.as_ptr(), &n, AsPtr::as_mut_ptr(a), &l.lda(), &mut info); - } - info.as_lapack_result()?; - if matches!(l, MatrixLayout::C { .. }) { - square_transpose(l, a); - } - Ok(()) - } - - fn solve_cholesky( - l: MatrixLayout, - mut uplo: UPLO, - a: &[Self], - b: &mut [Self], - ) -> Result<()> { - let (n, _) = l.size(); - let nrhs = 1; - let mut info = 0; - if matches!(l, MatrixLayout::C { .. }) { - uplo = uplo.t(); - for val in b.iter_mut() { - *val = val.conj(); - } - } - unsafe { - $trs( - uplo.as_ptr(), - &n, - &nrhs, - AsPtr::as_ptr(a), - &l.lda(), - AsPtr::as_mut_ptr(b), - &n, - &mut info, - ); - } - info.as_lapack_result()?; - if matches!(l, MatrixLayout::C { .. }) { - for val in b.iter_mut() { - *val = val.conj(); - } - } - Ok(()) - } - } - }; -} // end macro_rules - -impl_cholesky!( - f64, - lapack_sys::dpotrf_, - lapack_sys::dpotri_, - lapack_sys::dpotrs_ -); -impl_cholesky!( - f32, - lapack_sys::spotrf_, - lapack_sys::spotri_, - lapack_sys::spotrs_ -); -impl_cholesky!( - c64, - lapack_sys::zpotrf_, - lapack_sys::zpotri_, - lapack_sys::zpotrs_ -); -impl_cholesky!( - c32, - lapack_sys::cpotrf_, - lapack_sys::cpotri_, - lapack_sys::cpotrs_ -); diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 8b0be740..21190ab9 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -122,7 +122,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: OperatorNorm_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond_ { +pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -238,6 +238,27 @@ pub trait Lapack: OperatorNorm_ + Cholesky_ + Triangular_ + Tridiagonal_ + Rcond /// Solve symmetric/Hermitian linear equation $Ax = b$ using factroized result fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; + + /// Solve symmetric/hermite positive-definite linear equations using Cholesky decomposition + /// + /// For a given positive definite matrix $A$, + /// Cholesky decomposition is described as $A = U^T U$ or $A = LL^T$ where + /// + /// - $L$ is lower matrix + /// - $U$ is upper matrix + /// + /// This is designed as two step computation according to LAPACK API + /// + /// 1. Factorize input matrix $A$ into $L$ or $U$ + /// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ + /// using $U$ or $L$. + fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; + + /// Compute inverse matrix $A^{-1}$ using $U$ or $L$ + fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; + + /// Solve linear equation $Ax = b$ using $U$ or $L$ + fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; } macro_rules! impl_lapack { @@ -379,6 +400,26 @@ macro_rules! impl_lapack { use solveh::*; SolvehImpl::solveh(l, uplo, a, ipiv, b) } + + fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { + use cholesky::*; + CholeskyImpl::cholesky(l, uplo, a) + } + + fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()> { + use cholesky::*; + InvCholeskyImpl::inv_cholesky(l, uplo, a) + } + + fn solve_cholesky( + l: MatrixLayout, + uplo: UPLO, + a: &[Self], + b: &mut [Self], + ) -> Result<()> { + use cholesky::*; + SolveCholeskyImpl::solve_cholesky(l, uplo, a, b) + } } }; } From 29c848c66c3ca4ceb354c788f3a32fe3780c02f1 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:29:24 +0900 Subject: [PATCH 129/165] Make cholesky submodule public, update documents --- lax/src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 21190ab9..d3e713ce 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -52,8 +52,8 @@ //! According to the property input metrix, several types of triangular decomposition are used: //! //! - [solve] module provides methods for LU-decomposition for general matrix. -//! - [solveh] module provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/hermite indefinite matrix. -//! - [Cholesky_] triat provides methods for Cholesky decomposition for symmetric/hermite positive dinite matrix. +//! - [solveh] module provides methods for Bunch-Kaufman diagonal pivoting method for symmetric/Hermitian indefinite matrix. +//! - [cholesky] module provides methods for Cholesky decomposition for symmetric/Hermitian positive dinite matrix. //! //! Eigenvalue Problem //! ------------------- @@ -62,8 +62,8 @@ //! there are several types of eigenvalue problem API //! //! - [eig] module for eigenvalue problem for general matrix. -//! - [eigh] module for eigenvalue problem for symmetric/hermite matrix. -//! - [eigh_generalized] module for generalized eigenvalue problem for symmetric/hermite matrix. +//! - [eigh] module for eigenvalue problem for symmetric/Hermitian matrix. +//! - [eigh_generalized] module for generalized eigenvalue problem for symmetric/Hermitian matrix. //! //! Singular Value Decomposition //! ----------------------------- @@ -88,6 +88,7 @@ pub mod error; pub mod flags; pub mod layout; +pub mod cholesky; pub mod eig; pub mod eigh; pub mod eigh_generalized; @@ -99,7 +100,6 @@ pub mod svd; pub mod svddc; mod alloc; -mod cholesky; mod opnorm; mod rcond; mod triangular; @@ -130,7 +130,7 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { a: &mut [Self], ) -> Result<(Vec, Vec)>; - /// Compute right eigenvalue and eigenvectors for a symmetric or hermite matrix + /// Compute right eigenvalue and eigenvectors for a symmetric or Hermitian matrix fn eigh( calc_eigenvec: bool, layout: MatrixLayout, @@ -138,7 +138,7 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { a: &mut [Self], ) -> Result>; - /// Compute right eigenvalue and eigenvectors for a symmetric or hermite matrix + /// Compute right eigenvalue and eigenvectors for a symmetric or Hermitian matrix fn eigh_generalized( calc_eigenvec: bool, layout: MatrixLayout, @@ -217,7 +217,6 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Factorize symmetric/Hermitian matrix using Bunch-Kaufman diagonal pivoting method /// - /// /// For a given symmetric matrix $A$, /// this method factorizes $A = U^T D U$ or $A = L D L^T$ where /// @@ -233,13 +232,13 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; - /// Compute inverse matrix $A^{-1}$ of symmetric/Hermitian matrix using factroized result + /// Compute inverse matrix $A^{-1}$ using the result of [Lapack::bk] fn invh(l: MatrixLayout, uplo: UPLO, a: &mut [Self], ipiv: &Pivot) -> Result<()>; - /// Solve symmetric/Hermitian linear equation $Ax = b$ using factroized result + /// Solve symmetric/Hermitian linear equation $Ax = b$ using the result of [Lapack::bk] fn solveh(l: MatrixLayout, uplo: UPLO, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()>; - /// Solve symmetric/hermite positive-definite linear equations using Cholesky decomposition + /// Solve symmetric/Hermitian positive-definite linear equations using Cholesky decomposition /// /// For a given positive definite matrix $A$, /// Cholesky decomposition is described as $A = U^T U$ or $A = LL^T$ where @@ -250,14 +249,15 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// This is designed as two step computation according to LAPACK API /// /// 1. Factorize input matrix $A$ into $L$ or $U$ - /// 2. Solve linear equation $Ax = b$ or compute inverse matrix $A^{-1}$ - /// using $U$ or $L$. + /// 2. Solve linear equation $Ax = b$ by [Lapack::solve_cholesky] + /// or compute inverse matrix $A^{-1}$ by [Lapack::inv_cholesky] + /// fn cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - /// Compute inverse matrix $A^{-1}$ using $U$ or $L$ + /// Compute inverse matrix $A^{-1}$ using $U$ or $L$ calculated by [Lapack::cholesky] fn inv_cholesky(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result<()>; - /// Solve linear equation $Ax = b$ using $U$ or $L$ + /// Solve linear equation $Ax = b$ using $U$ or $L$ calculated by [Lapack::cholesky] fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; } From 5824df154c26c72d1c4d8e7a86cd022019bc07ea Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:45:39 +0900 Subject: [PATCH 130/165] Ignore first doctest --- lax/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index d3e713ce..e844b1da 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -6,8 +6,8 @@ //! This crates provides LAPACK wrapper as a traits. //! For example, LU decomposition of general matrices is provided like: //! -//! ``` -//! pub trait Lapack{ +//! ```ignore +//! pub trait Lapack { //! fn lu(l: MatrixLayout, a: &mut [Self]) -> Result; //! } //! ``` From e381bf2d3e4b9ff118e35a02d8ca422d3111705a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:45:56 +0900 Subject: [PATCH 131/165] Fix for 0-sized case --- lax/src/solveh.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index e9af59b6..4587d8e8 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -55,6 +55,9 @@ macro_rules! impl_bk_work { fn calc(&mut self, uplo: UPLO, a: &mut [Self::Elem]) -> Result<&[i32]> { let (n, _) = self.layout.size(); let lwork = self.work.len().to_i32().unwrap(); + if lwork == 0 { + return Ok(&[]); + } let mut info = 0; unsafe { $trf( From a621b1b0c0eaada181f3acb28a3a0be8387776f9 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 15:54:50 +0900 Subject: [PATCH 132/165] Update module-level documents --- lax/src/cholesky.rs | 2 ++ lax/src/lib.rs | 2 -- lax/src/solveh.rs | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lax/src/cholesky.rs b/lax/src/cholesky.rs index 0f853173..785f6e5e 100644 --- a/lax/src/cholesky.rs +++ b/lax/src/cholesky.rs @@ -1,3 +1,5 @@ +//! Factorize positive-definite symmetric/Hermitian matrices using Cholesky algorithm + use super::*; use crate::{error::*, layout::*}; use cauchy::*; diff --git a/lax/src/lib.rs b/lax/src/lib.rs index e844b1da..e673d261 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -228,8 +228,6 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// 1. Factorize given matrix $A$ into upper ($U$) or lower ($L$) form with diagonal matrix $D$ /// 2. Then solve linear equation $Ax = b$, and/or calculate inverse matrix $A^{-1}$ /// - /// [BK]: https://doi.org/10.2307/2005787 - /// fn bk(l: MatrixLayout, uplo: UPLO, a: &mut [Self]) -> Result; /// Compute inverse matrix $A^{-1}$ using the result of [Lapack::bk] diff --git a/lax/src/solveh.rs b/lax/src/solveh.rs index 4587d8e8..abb75cb8 100644 --- a/lax/src/solveh.rs +++ b/lax/src/solveh.rs @@ -1,3 +1,8 @@ +//! Factorize symmetric/Hermitian matrix using [Bunch-Kaufman diagonal pivoting method][BK] +//! +//! [BK]: https://doi.org/10.2307/2005787 +//! + use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::{ToPrimitive, Zero}; From 1b1bc821d5b3741e2e7433e11a0bff81cb86f7c0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 16:08:40 +0900 Subject: [PATCH 133/165] Rewrite transpose note into KaTeX --- lax/src/solve.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 1b3239f5..df6372f9 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -53,6 +53,7 @@ impl_lu!(c32, lapack_sys::cgetrf_); impl_lu!(f64, lapack_sys::dgetrf_); impl_lu!(f32, lapack_sys::sgetrf_); +#[cfg_attr(doc, katexit::katexit)] /// Helper trait to abstract `*getrs` LAPACK routines for implementing [Lapack::solve] /// /// If the array has C layout, then it needs to be handled @@ -63,13 +64,15 @@ impl_lu!(f32, lapack_sys::sgetrf_); /// or "no transpose", respectively. For the "Hermite" case, we /// can take advantage of the following: /// -/// ```text -/// A^H x = b -/// ⟺ conj(A^T) x = b -/// ⟺ conj(conj(A^T) x) = conj(b) -/// ⟺ conj(conj(A^T)) conj(x) = conj(b) -/// ⟺ A^T conj(x) = conj(b) -/// ``` +/// $$ +/// \begin{align*} +/// A^H x &= b \\\\ +/// \Leftrightarrow \overline{A^T} x &= b \\\\ +/// \Leftrightarrow \overline{\overline{A^T} x} &= \overline{b} \\\\ +/// \Leftrightarrow \overline{\overline{A^T}} \overline{x} &= \overline{b} \\\\ +/// \Leftrightarrow A^T \overline{x} &= \overline{b} +/// \end{align*} +/// $$ /// /// So, we can handle this case by switching to "no transpose" /// (which is equivalent to transposing the array since it will From 7e61539e4a36ed0de858ca3d81a2c0ccdcf95390 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 16:12:28 +0900 Subject: [PATCH 134/165] Fix markdown table --- lax/src/solve.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index df6372f9..63f69983 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -79,14 +79,14 @@ impl_lu!(f32, lapack_sys::sgetrf_); /// be reinterpreted as Fortran layout) and applying the /// elementwise conjugate to `x` and `b`. /// -/// LAPACK correspondance -/// ---------------------- -/// -/// | f32 | f64 | c32 | c64 | -/// |:-------|:-------|:-------|:-------| -/// | sgetrs | dgetrs | cgetrs | zgetrs | -/// pub trait SolveImpl: Scalar { + /// LAPACK correspondance + /// ---------------------- + /// + /// | f32 | f64 | c32 | c64 | + /// |:-------|:-------|:-------|:-------| + /// | sgetrs | dgetrs | cgetrs | zgetrs | + /// fn solve(l: MatrixLayout, t: Transpose, a: &[Self], p: &Pivot, b: &mut [Self]) -> Result<()>; } From 09f92262a66dc0e1dcf2b201d47be3f3fe84c651 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 17:21:39 +0900 Subject: [PATCH 135/165] Add RcondWork --- lax/src/rcond.rs | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 6cc72749..09ce75a8 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -2,6 +2,125 @@ use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::Zero; +pub struct RcondWork { + pub layout: MatrixLayout, + pub work: Vec>, + pub rwork: Option>>, + pub iwork: Option>>, +} + +pub trait RcondWorkImpl { + type Elem: Scalar; + fn new(l: MatrixLayout) -> Self; + fn calc( + &mut self, + a: &[Self::Elem], + anorm: ::Real, + ) -> Result<::Real>; +} + +macro_rules! impl_rcond_work_c { + ($c:ty, $con:path) => { + impl RcondWorkImpl for RcondWork<$c> { + type Elem = $c; + + fn new(layout: MatrixLayout) -> Self { + let (n, _) = layout.size(); + let work = vec_uninit(2 * n as usize); + let rwork = vec_uninit(2 * n as usize); + RcondWork { + layout, + work, + rwork: Some(rwork), + iwork: None, + } + } + + fn calc( + &mut self, + a: &[Self::Elem], + anorm: ::Real, + ) -> Result<::Real> { + let (n, _) = self.layout.size(); + let mut rcond = ::Real::zero(); + let mut info = 0; + let norm_type = match self.layout { + MatrixLayout::C { .. } => NormType::Infinity, + MatrixLayout::F { .. } => NormType::One, + }; + unsafe { + $con( + norm_type.as_ptr(), + &n, + AsPtr::as_ptr(a), + &self.layout.lda(), + &anorm, + &mut rcond, + AsPtr::as_mut_ptr(&mut self.work), + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(rcond) + } + } + }; +} +impl_rcond_work_c!(c64, lapack_sys::zgecon_); +impl_rcond_work_c!(c32, lapack_sys::cgecon_); + +macro_rules! impl_rcond_work_r { + ($r:ty, $con:path) => { + impl RcondWorkImpl for RcondWork<$r> { + type Elem = $r; + + fn new(layout: MatrixLayout) -> Self { + let (n, _) = layout.size(); + let work = vec_uninit(4 * n as usize); + let iwork = vec_uninit(n as usize); + RcondWork { + layout, + work, + rwork: None, + iwork: Some(iwork), + } + } + + fn calc( + &mut self, + a: &[Self::Elem], + anorm: ::Real, + ) -> Result<::Real> { + let (n, _) = self.layout.size(); + let mut rcond = ::Real::zero(); + let mut info = 0; + let norm_type = match self.layout { + MatrixLayout::C { .. } => NormType::Infinity, + MatrixLayout::F { .. } => NormType::One, + }; + unsafe { + $con( + norm_type.as_ptr(), + &n, + AsPtr::as_ptr(a), + &self.layout.lda(), + &anorm, + &mut rcond, + AsPtr::as_mut_ptr(&mut self.work), + AsPtr::as_mut_ptr(self.iwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(rcond) + } + } + }; +} +impl_rcond_work_r!(f64, lapack_sys::dgecon_); +impl_rcond_work_r!(f32, lapack_sys::sgecon_); + pub trait Rcond_: Scalar + Sized { /// Estimates the the reciprocal of the condition number of the matrix in 1-norm. /// From a118bf0b05308438e47887db0b3a482561b6d340 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 17:24:27 +0900 Subject: [PATCH 136/165] Merge Rcond_ into Lapack trait --- lax/src/lib.rs | 16 ++++++++-- lax/src/rcond.rs | 82 ------------------------------------------------ 2 files changed, 13 insertions(+), 85 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index e673d261..dbe68578 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -94,6 +94,7 @@ pub mod eigh; pub mod eigh_generalized; pub mod least_squares; pub mod qr; +pub mod rcond; pub mod solve; pub mod solveh; pub mod svd; @@ -101,7 +102,6 @@ pub mod svddc; mod alloc; mod opnorm; -mod rcond; mod triangular; mod tridiagonal; @@ -109,7 +109,6 @@ pub use self::cholesky::*; pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; pub use self::opnorm::*; -pub use self::rcond::*; pub use self::svd::{SvdOwned, SvdRef}; pub use self::triangular::*; pub use self::tridiagonal::*; @@ -122,7 +121,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { +pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -257,6 +256,11 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ + Rcond_ { /// Solve linear equation $Ax = b$ using $U$ or $L$ calculated by [Lapack::cholesky] fn solve_cholesky(l: MatrixLayout, uplo: UPLO, a: &[Self], b: &mut [Self]) -> Result<()>; + + /// Estimates the the reciprocal of the condition number of the matrix in 1-norm. + /// + /// `anorm` should be the 1-norm of the matrix `a`. + fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result; } macro_rules! impl_lapack { @@ -418,6 +422,12 @@ macro_rules! impl_lapack { use cholesky::*; SolveCholeskyImpl::solve_cholesky(l, uplo, a, b) } + + fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result { + use rcond::*; + let mut work = RcondWork::<$s>::new(l); + work.calc(a, anorm) + } } }; } diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 09ce75a8..1cf86286 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -120,85 +120,3 @@ macro_rules! impl_rcond_work_r { } impl_rcond_work_r!(f64, lapack_sys::dgecon_); impl_rcond_work_r!(f32, lapack_sys::sgecon_); - -pub trait Rcond_: Scalar + Sized { - /// Estimates the the reciprocal of the condition number of the matrix in 1-norm. - /// - /// `anorm` should be the 1-norm of the matrix `a`. - fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result; -} - -macro_rules! impl_rcond_real { - ($scalar:ty, $gecon:path) => { - impl Rcond_ for $scalar { - fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result { - let (n, _) = l.size(); - let mut rcond = Self::Real::zero(); - let mut info = 0; - - let mut work: Vec> = vec_uninit(4 * n as usize); - let mut iwork: Vec> = vec_uninit(n as usize); - let norm_type = match l { - MatrixLayout::C { .. } => NormType::Infinity, - MatrixLayout::F { .. } => NormType::One, - }; - unsafe { - $gecon( - norm_type.as_ptr(), - &n, - AsPtr::as_ptr(a), - &l.lda(), - &anorm, - &mut rcond, - AsPtr::as_mut_ptr(&mut work), - AsPtr::as_mut_ptr(&mut iwork), - &mut info, - ) - }; - info.as_lapack_result()?; - - Ok(rcond) - } - } - }; -} - -impl_rcond_real!(f32, lapack_sys::sgecon_); -impl_rcond_real!(f64, lapack_sys::dgecon_); - -macro_rules! impl_rcond_complex { - ($scalar:ty, $gecon:path) => { - impl Rcond_ for $scalar { - fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result { - let (n, _) = l.size(); - let mut rcond = Self::Real::zero(); - let mut info = 0; - let mut work: Vec> = vec_uninit(2 * n as usize); - let mut rwork: Vec> = vec_uninit(2 * n as usize); - let norm_type = match l { - MatrixLayout::C { .. } => NormType::Infinity, - MatrixLayout::F { .. } => NormType::One, - }; - unsafe { - $gecon( - norm_type.as_ptr(), - &n, - AsPtr::as_ptr(a), - &l.lda(), - &anorm, - &mut rcond, - AsPtr::as_mut_ptr(&mut work), - AsPtr::as_mut_ptr(&mut rwork), - &mut info, - ) - }; - info.as_lapack_result()?; - - Ok(rcond) - } - } - }; -} - -impl_rcond_complex!(c32, lapack_sys::cgecon_); -impl_rcond_complex!(c64, lapack_sys::zgecon_); From dfc720af3eb286dedb2c7165f1f02fcba6c724c2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 17:53:46 +0900 Subject: [PATCH 137/165] Add OperatorNormWork --- lax/src/opnorm.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index 60933489..8a08c79f 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -4,6 +4,59 @@ use super::{AsPtr, NormType}; use crate::{layout::MatrixLayout, *}; use cauchy::*; +pub struct OperatorNormWork { + pub ty: NormType, + pub layout: MatrixLayout, + pub work: Vec>, +} + +pub trait OperatorNormWorkImpl { + type Elem: Scalar; + fn new(t: NormType, l: MatrixLayout) -> Self; + fn calc(&mut self, a: &[Self::Elem]) -> ::Real; +} + +macro_rules! impl_operator_norm { + ($s:ty, $lange:path) => { + impl OperatorNormWorkImpl for OperatorNormWork<$s> { + type Elem = $s; + + fn new(ty: NormType, layout: MatrixLayout) -> Self { + let m = layout.lda(); + let work = match (ty, layout) { + (NormType::Infinity, MatrixLayout::F { .. }) + | (NormType::One, MatrixLayout::C { .. }) => vec_uninit(m as usize), + _ => Vec::new(), + }; + OperatorNormWork { ty, layout, work } + } + + fn calc(&mut self, a: &[Self::Elem]) -> ::Real { + let m = self.layout.lda(); + let n = self.layout.len(); + let t = match self.layout { + MatrixLayout::F { .. } => self.ty, + MatrixLayout::C { .. } => self.ty.transpose(), + }; + unsafe { + $lange( + t.as_ptr(), + &m, + &n, + AsPtr::as_ptr(a), + &m, + AsPtr::as_mut_ptr(&mut self.work), + ) + } + } + } + }; +} +impl_operator_norm!(c64, lapack_sys::zlange_); +impl_operator_norm!(c32, lapack_sys::clange_); +impl_operator_norm!(f64, lapack_sys::dlange_); +impl_operator_norm!(f32, lapack_sys::slange_); + pub trait OperatorNorm_: Scalar { fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real; } From 52a504fe1d23ac16136a34d0d7aa2d9ea51ac7df Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 1 Oct 2022 18:35:48 +0900 Subject: [PATCH 138/165] Merge OperatorNorm_ into Lapack trait --- lax/src/lib.rs | 13 +++++++++++-- lax/src/opnorm.rs | 39 --------------------------------------- 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index dbe68578..2a7c28d2 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -93,6 +93,7 @@ pub mod eig; pub mod eigh; pub mod eigh_generalized; pub mod least_squares; +pub mod opnorm; pub mod qr; pub mod rcond; pub mod solve; @@ -101,7 +102,6 @@ pub mod svd; pub mod svddc; mod alloc; -mod opnorm; mod triangular; mod tridiagonal; @@ -121,7 +121,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ { +pub trait Lapack: Triangular_ + Tridiagonal_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -261,6 +261,9 @@ pub trait Lapack: OperatorNorm_ + Triangular_ + Tridiagonal_ { /// /// `anorm` should be the 1-norm of the matrix `a`. fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result; + + /// Compute operator norm of a matrix + fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real; } macro_rules! impl_lapack { @@ -428,6 +431,12 @@ macro_rules! impl_lapack { let mut work = RcondWork::<$s>::new(l); work.calc(a, anorm) } + + fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real { + use opnorm::*; + let mut work = OperatorNormWork::<$s>::new(t, l); + work.calc(a) + } } }; } diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index 8a08c79f..0bfc94f2 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -56,42 +56,3 @@ impl_operator_norm!(c64, lapack_sys::zlange_); impl_operator_norm!(c32, lapack_sys::clange_); impl_operator_norm!(f64, lapack_sys::dlange_); impl_operator_norm!(f32, lapack_sys::slange_); - -pub trait OperatorNorm_: Scalar { - fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real; -} - -macro_rules! impl_opnorm { - ($scalar:ty, $lange:path) => { - impl OperatorNorm_ for $scalar { - fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real { - let m = l.lda(); - let n = l.len(); - let t = match l { - MatrixLayout::F { .. } => t, - MatrixLayout::C { .. } => t.transpose(), - }; - let mut work: Vec> = if matches!(t, NormType::Infinity) { - vec_uninit(m as usize) - } else { - Vec::new() - }; - unsafe { - $lange( - t.as_ptr(), - &m, - &n, - AsPtr::as_ptr(a), - &m, - AsPtr::as_mut_ptr(&mut work), - ) - } - } - } - }; -} // impl_opnorm! - -impl_opnorm!(f64, lapack_sys::dlange_); -impl_opnorm!(f32, lapack_sys::slange_); -impl_opnorm!(c64, lapack_sys::zlange_); -impl_opnorm!(c32, lapack_sys::clange_); From 1abe240a6da8d83566a825aaa6c8693ad0ffe725 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 2 Oct 2022 21:29:31 +0900 Subject: [PATCH 139/165] Add document for opnorm --- lax/src/lib.rs | 36 +++++++++++++++++++++++++++++++++++- lax/src/opnorm.rs | 2 +- lax/src/rcond.rs | 2 ++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 2a7c28d2..4989fe15 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -262,7 +262,41 @@ pub trait Lapack: Triangular_ + Tridiagonal_ { /// `anorm` should be the 1-norm of the matrix `a`. fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result; - /// Compute operator norm of a matrix + /// Compute norm of matrices + /// + /// For a $n \times m$ matrix + /// $$ + /// A = \begin{pmatrix} + /// a_{11} & \cdots & a_{1m} \\\\ + /// \vdots & \ddots & \vdots \\\\ + /// a_{n1} & \cdots & a_{nm} + /// \end{pmatrix} + /// $$ + /// LAPACK can compute three types of norms: + /// + /// - Operator norm based on 1-norm for its domain linear space: + /// $$ + /// \Vert A \Vert_1 = \sup_{\Vert x \Vert_1 = 1} \Vert Ax \Vert_1 + /// = \max_{1 \le j \le m } \sum_{i=1}^n |a_{ij}| + /// $$ + /// where + /// $\Vert x\Vert_1 = \sum_{j=1}^m |x_j|$ + /// is 1-norm for a vector $x$. + /// + /// - Operator norm based on $\infty$-norm for its domain linear space: + /// $$ + /// \Vert A \Vert_\infty = \sup_{\Vert x \Vert_\infty = 1} \Vert Ax \Vert_\infty + /// = \max_{1 \le i \le n } \sum_{j=1}^m |a_{ij}| + /// $$ + /// where + /// $\Vert x\Vert_\infty = \max_{j=1}^m |x_j|$ + /// is $\infty$-norm for a vector $x$. + /// + /// - Frobenious norm + /// $$ + /// \Vert A \Vert_F = \sqrt{\mathrm{Tr} \left(AA^\dagger\right)} = \sqrt{\sum_{i=1}^n \sum_{j=1}^m |a_{ij}|^2} + /// $$ + /// fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real; } diff --git a/lax/src/opnorm.rs b/lax/src/opnorm.rs index 0bfc94f2..1789f385 100644 --- a/lax/src/opnorm.rs +++ b/lax/src/opnorm.rs @@ -1,4 +1,4 @@ -//! Operator norms of matrices +//! Operator norm use super::{AsPtr, NormType}; use crate::{layout::MatrixLayout, *}; diff --git a/lax/src/rcond.rs b/lax/src/rcond.rs index 1cf86286..4d4a4c92 100644 --- a/lax/src/rcond.rs +++ b/lax/src/rcond.rs @@ -1,3 +1,5 @@ +//! Reciprocal conditional number + use crate::{error::*, layout::MatrixLayout, *}; use cauchy::*; use num_traits::Zero; From 27c7fa11230424c59c61d45b9957e89afe1e4032 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 3 Oct 2022 21:56:16 +0900 Subject: [PATCH 140/165] Merge Triangular_ into Lapack --- lax/src/lib.rs | 26 +++++++++++++++++++++++--- lax/src/triangular.rs | 26 +++++++++++++++++--------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 4989fe15..4e886ea4 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -100,9 +100,9 @@ pub mod solve; pub mod solveh; pub mod svd; pub mod svddc; +pub mod triangular; mod alloc; -mod triangular; mod tridiagonal; pub use self::cholesky::*; @@ -110,7 +110,6 @@ pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; pub use self::opnorm::*; pub use self::svd::{SvdOwned, SvdRef}; -pub use self::triangular::*; pub use self::tridiagonal::*; use self::{alloc::*, error::*, layout::*}; @@ -121,7 +120,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: Triangular_ + Tridiagonal_ { +pub trait Lapack: Tridiagonal_ { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -298,6 +297,15 @@ pub trait Lapack: Triangular_ + Tridiagonal_ { /// $$ /// fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real; + + fn solve_triangular( + al: MatrixLayout, + bl: MatrixLayout, + uplo: UPLO, + d: Diag, + a: &[Self], + b: &mut [Self], + ) -> Result<()>; } macro_rules! impl_lapack { @@ -471,6 +479,18 @@ macro_rules! impl_lapack { let mut work = OperatorNormWork::<$s>::new(t, l); work.calc(a) } + + fn solve_triangular( + al: MatrixLayout, + bl: MatrixLayout, + uplo: UPLO, + d: Diag, + a: &[Self], + b: &mut [Self], + ) -> Result<()> { + use triangular::*; + SolveTriangularImpl::solve_triangular(al, bl, uplo, d, a, b) + } } }; } diff --git a/lax/src/triangular.rs b/lax/src/triangular.rs index 14f29807..da4dc4cf 100644 --- a/lax/src/triangular.rs +++ b/lax/src/triangular.rs @@ -1,10 +1,18 @@ -//! Implement linear solver and inverse matrix +//! Linear problem for triangular matrices use crate::{error::*, layout::*, *}; use cauchy::*; -/// Wraps `*trtri` and `*trtrs` -pub trait Triangular_: Scalar { +/// Solve linear problem for triangular matrices +/// +/// LAPACK correspondance +/// ---------------------- +/// +/// | f32 | f64 | c32 | c64 | +/// |:-------|:-------|:-------|:-------| +/// | strtrs | dtrtrs | ctrtrs | ztrtrs | +/// +pub trait SolveTriangularImpl: Scalar { fn solve_triangular( al: MatrixLayout, bl: MatrixLayout, @@ -16,8 +24,8 @@ pub trait Triangular_: Scalar { } macro_rules! impl_triangular { - ($scalar:ty, $trtri:path, $trtrs:path) => { - impl Triangular_ for $scalar { + ($scalar:ty, $trtrs:path) => { + impl SolveTriangularImpl for $scalar { fn solve_triangular( a_layout: MatrixLayout, b_layout: MatrixLayout, @@ -79,7 +87,7 @@ macro_rules! impl_triangular { }; } // impl_triangular! -impl_triangular!(f64, lapack_sys::dtrtri_, lapack_sys::dtrtrs_); -impl_triangular!(f32, lapack_sys::strtri_, lapack_sys::strtrs_); -impl_triangular!(c64, lapack_sys::ztrtri_, lapack_sys::ztrtrs_); -impl_triangular!(c32, lapack_sys::ctrtri_, lapack_sys::ctrtrs_); +impl_triangular!(f64, lapack_sys::dtrtrs_); +impl_triangular!(f32, lapack_sys::strtrs_); +impl_triangular!(c64, lapack_sys::ztrtrs_); +impl_triangular!(c32, lapack_sys::ctrtrs_); From 17f9fb85723d5d49afdca560abfefd0a698178e0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 16:41:37 +0900 Subject: [PATCH 141/165] Split definition of tridiagonal matrix --- lax/src/tridiagonal/matrix.rs | 106 +++++++++++++++++ .../{tridiagonal.rs => tridiagonal/mod.rs} | 107 +----------------- 2 files changed, 110 insertions(+), 103 deletions(-) create mode 100644 lax/src/tridiagonal/matrix.rs rename lax/src/{tridiagonal.rs => tridiagonal/mod.rs} (68%) diff --git a/lax/src/tridiagonal/matrix.rs b/lax/src/tridiagonal/matrix.rs new file mode 100644 index 00000000..d009a0b5 --- /dev/null +++ b/lax/src/tridiagonal/matrix.rs @@ -0,0 +1,106 @@ +use crate::layout::*; +use cauchy::*; +use num_traits::Zero; +use std::ops::{Index, IndexMut}; + +/// Represents a tridiagonal matrix as 3 one-dimensional vectors. +/// +/// ```text +/// [d0, u1, 0, ..., 0, +/// l1, d1, u2, ..., +/// 0, l2, d2, +/// ... ..., u{n-1}, +/// 0, ..., l{n-1}, d{n-1},] +/// ``` +#[derive(Clone, PartialEq, Eq)] +pub struct Tridiagonal { + /// layout of raw matrix + pub l: MatrixLayout, + /// (n-1) sub-diagonal elements of matrix. + pub dl: Vec, + /// (n) diagonal elements of matrix. + pub d: Vec, + /// (n-1) super-diagonal elements of matrix. + pub du: Vec, +} + +impl Tridiagonal { + pub fn opnorm_one(&self) -> A::Real { + let mut col_sum: Vec = self.d.iter().map(|val| val.abs()).collect(); + for i in 0..col_sum.len() { + if i < self.dl.len() { + col_sum[i] += self.dl[i].abs(); + } + if i > 0 { + col_sum[i] += self.du[i - 1].abs(); + } + } + let mut max = A::Real::zero(); + for &val in &col_sum { + if max < val { + max = val; + } + } + max + } +} + +impl Index<(i32, i32)> for Tridiagonal { + type Output = A; + #[inline] + fn index(&self, (row, col): (i32, i32)) -> &A { + let (n, _) = self.l.size(); + assert!( + std::cmp::max(row, col) < n, + "ndarray: index {:?} is out of bounds for array of shape {}", + [row, col], + n + ); + match row - col { + 0 => &self.d[row as usize], + 1 => &self.dl[col as usize], + -1 => &self.du[row as usize], + _ => panic!( + "ndarray-linalg::tridiagonal: index {:?} is not tridiagonal element", + [row, col] + ), + } + } +} + +impl Index<[i32; 2]> for Tridiagonal { + type Output = A; + #[inline] + fn index(&self, [row, col]: [i32; 2]) -> &A { + &self[(row, col)] + } +} + +impl IndexMut<(i32, i32)> for Tridiagonal { + #[inline] + fn index_mut(&mut self, (row, col): (i32, i32)) -> &mut A { + let (n, _) = self.l.size(); + assert!( + std::cmp::max(row, col) < n, + "ndarray: index {:?} is out of bounds for array of shape {}", + [row, col], + n + ); + match row - col { + 0 => &mut self.d[row as usize], + 1 => &mut self.dl[col as usize], + -1 => &mut self.du[row as usize], + _ => panic!( + "ndarray-linalg::tridiagonal: index {:?} is not tridiagonal element", + [row, col] + ), + } + } +} + +impl IndexMut<[i32; 2]> for Tridiagonal { + #[inline] + fn index_mut(&mut self, [row, col]: [i32; 2]) -> &mut A { + &mut self[(row, col)] + } +} diff --git a/lax/src/tridiagonal.rs b/lax/src/tridiagonal/mod.rs similarity index 68% rename from lax/src/tridiagonal.rs rename to lax/src/tridiagonal/mod.rs index 3d28c63a..715ccb9c 100644 --- a/lax/src/tridiagonal.rs +++ b/lax/src/tridiagonal/mod.rs @@ -1,52 +1,13 @@ //! Implement linear solver using LU decomposition //! for tridiagonal matrix +mod matrix; + +pub use matrix::*; + use crate::{error::*, layout::*, *}; use cauchy::*; use num_traits::Zero; -use std::ops::{Index, IndexMut}; - -/// Represents a tridiagonal matrix as 3 one-dimensional vectors. -/// -/// ```text -/// [d0, u1, 0, ..., 0, -/// l1, d1, u2, ..., -/// 0, l2, d2, -/// ... ..., u{n-1}, -/// 0, ..., l{n-1}, d{n-1},] -/// ``` -#[derive(Clone, PartialEq, Eq)] -pub struct Tridiagonal { - /// layout of raw matrix - pub l: MatrixLayout, - /// (n-1) sub-diagonal elements of matrix. - pub dl: Vec, - /// (n) diagonal elements of matrix. - pub d: Vec, - /// (n-1) super-diagonal elements of matrix. - pub du: Vec, -} - -impl Tridiagonal { - fn opnorm_one(&self) -> A::Real { - let mut col_sum: Vec = self.d.iter().map(|val| val.abs()).collect(); - for i in 0..col_sum.len() { - if i < self.dl.len() { - col_sum[i] += self.dl[i].abs(); - } - if i > 0 { - col_sum[i] += self.du[i - 1].abs(); - } - } - let mut max = A::Real::zero(); - for &val in &col_sum { - if max < val { - max = val; - } - } - max - } -} /// Represents the LU factorization of a tridiagonal matrix `A` as `A = P*L*U`. #[derive(Clone, PartialEq)] @@ -65,66 +26,6 @@ pub struct LUFactorizedTridiagonal { a_opnorm_one: A::Real, } -impl Index<(i32, i32)> for Tridiagonal { - type Output = A; - #[inline] - fn index(&self, (row, col): (i32, i32)) -> &A { - let (n, _) = self.l.size(); - assert!( - std::cmp::max(row, col) < n, - "ndarray: index {:?} is out of bounds for array of shape {}", - [row, col], - n - ); - match row - col { - 0 => &self.d[row as usize], - 1 => &self.dl[col as usize], - -1 => &self.du[row as usize], - _ => panic!( - "ndarray-linalg::tridiagonal: index {:?} is not tridiagonal element", - [row, col] - ), - } - } -} - -impl Index<[i32; 2]> for Tridiagonal { - type Output = A; - #[inline] - fn index(&self, [row, col]: [i32; 2]) -> &A { - &self[(row, col)] - } -} - -impl IndexMut<(i32, i32)> for Tridiagonal { - #[inline] - fn index_mut(&mut self, (row, col): (i32, i32)) -> &mut A { - let (n, _) = self.l.size(); - assert!( - std::cmp::max(row, col) < n, - "ndarray: index {:?} is out of bounds for array of shape {}", - [row, col], - n - ); - match row - col { - 0 => &mut self.d[row as usize], - 1 => &mut self.dl[col as usize], - -1 => &mut self.du[row as usize], - _ => panic!( - "ndarray-linalg::tridiagonal: index {:?} is not tridiagonal element", - [row, col] - ), - } - } -} - -impl IndexMut<[i32; 2]> for Tridiagonal { - #[inline] - fn index_mut(&mut self, [row, col]: [i32; 2]) -> &mut A { - &mut self[(row, col)] - } -} - /// Wraps `*gttrf`, `*gtcon` and `*gttrs` pub trait Tridiagonal_: Scalar + Sized { /// Computes the LU factorization of a tridiagonal `m x n` matrix `a` using From a8550c20a173896fae6b9bb09672d36d9964f453 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 16:49:41 +0900 Subject: [PATCH 142/165] SolveTridiagonalImpl --- lax/src/tridiagonal/mod.rs | 2 ++ lax/src/tridiagonal/solve.rs | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 lax/src/tridiagonal/solve.rs diff --git a/lax/src/tridiagonal/mod.rs b/lax/src/tridiagonal/mod.rs index 715ccb9c..98be1afd 100644 --- a/lax/src/tridiagonal/mod.rs +++ b/lax/src/tridiagonal/mod.rs @@ -2,8 +2,10 @@ //! for tridiagonal matrix mod matrix; +mod solve; pub use matrix::*; +pub use solve::*; use crate::{error::*, layout::*, *}; use cauchy::*; diff --git a/lax/src/tridiagonal/solve.rs b/lax/src/tridiagonal/solve.rs new file mode 100644 index 00000000..43f7d120 --- /dev/null +++ b/lax/src/tridiagonal/solve.rs @@ -0,0 +1,64 @@ +use crate::{error::*, layout::*, *}; +use cauchy::*; + +pub trait SolveTridiagonalImpl: Scalar { + fn solve_tridiagonal( + lu: &LUFactorizedTridiagonal, + bl: MatrixLayout, + t: Transpose, + b: &mut [Self], + ) -> Result<()>; +} + +macro_rules! impl_solve_tridiagonal { + ($s:ty, $trs:path) => { + impl SolveTridiagonalImpl for $s { + fn solve_tridiagonal( + lu: &LUFactorizedTridiagonal, + b_layout: MatrixLayout, + t: Transpose, + b: &mut [Self], + ) -> Result<()> { + let (n, _) = lu.a.l.size(); + let ipiv = &lu.ipiv; + // Transpose if b is C-continuous + let mut b_t = None; + let b_layout = match b_layout { + MatrixLayout::C { .. } => { + let (layout, t) = transpose(b_layout, b); + b_t = Some(t); + layout + } + MatrixLayout::F { .. } => b_layout, + }; + let (ldb, nrhs) = b_layout.size(); + let mut info = 0; + unsafe { + $trs( + t.as_ptr(), + &n, + &nrhs, + AsPtr::as_ptr(&lu.a.dl), + AsPtr::as_ptr(&lu.a.d), + AsPtr::as_ptr(&lu.a.du), + AsPtr::as_ptr(&lu.du2), + ipiv.as_ptr(), + AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), + &ldb, + &mut info, + ); + } + info.as_lapack_result()?; + if let Some(b_t) = b_t { + transpose_over(b_layout, &b_t, b); + } + Ok(()) + } + } + }; +} + +impl_solve_tridiagonal!(c64, lapack_sys::zgttrs_); +impl_solve_tridiagonal!(c32, lapack_sys::cgttrs_); +impl_solve_tridiagonal!(f64, lapack_sys::dgttrs_); +impl_solve_tridiagonal!(f32, lapack_sys::sgttrs_); From 6a35654f53c08910a1a3321bde72270007100681 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 17:07:35 +0900 Subject: [PATCH 143/165] RcondTridiagonalWork --- lax/src/tridiagonal/mod.rs | 2 + lax/src/tridiagonal/rcond.rs | 109 +++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 lax/src/tridiagonal/rcond.rs diff --git a/lax/src/tridiagonal/mod.rs b/lax/src/tridiagonal/mod.rs index 98be1afd..dfbd9be2 100644 --- a/lax/src/tridiagonal/mod.rs +++ b/lax/src/tridiagonal/mod.rs @@ -2,9 +2,11 @@ //! for tridiagonal matrix mod matrix; +mod rcond; mod solve; pub use matrix::*; +pub use rcond::*; pub use solve::*; use crate::{error::*, layout::*, *}; diff --git a/lax/src/tridiagonal/rcond.rs b/lax/src/tridiagonal/rcond.rs new file mode 100644 index 00000000..a309cae4 --- /dev/null +++ b/lax/src/tridiagonal/rcond.rs @@ -0,0 +1,109 @@ +use crate::*; +use cauchy::*; +use num_traits::Zero; + +pub struct RcondTridiagonalWork { + pub work: Vec>, + pub iwork: Option>>, +} + +pub trait RcondTridiagonalWorkImpl { + type Elem: Scalar; + fn new(layout: MatrixLayout) -> Self; + fn calc( + &mut self, + lu: &LUFactorizedTridiagonal, + ) -> Result<::Real>; +} + +macro_rules! impl_rcond_tridiagonal_work_c { + ($c:ty, $gtcon:path) => { + impl RcondTridiagonalWorkImpl for RcondTridiagonalWork<$c> { + type Elem = $c; + + fn new(layout: MatrixLayout) -> Self { + let (n, _) = layout.size(); + let work = vec_uninit(2 * n as usize); + RcondTridiagonalWork { work, iwork: None } + } + + fn calc( + &mut self, + lu: &LUFactorizedTridiagonal, + ) -> Result<::Real> { + let (n, _) = lu.a.l.size(); + let ipiv = &lu.ipiv; + let mut rcond = ::Real::zero(); + let mut info = 0; + unsafe { + $gtcon( + NormType::One.as_ptr(), + &n, + AsPtr::as_ptr(&lu.a.dl), + AsPtr::as_ptr(&lu.a.d), + AsPtr::as_ptr(&lu.a.du), + AsPtr::as_ptr(&lu.du2), + ipiv.as_ptr(), + &lu.a_opnorm_one, + &mut rcond, + AsPtr::as_mut_ptr(&mut self.work), + &mut info, + ); + } + info.as_lapack_result()?; + Ok(rcond) + } + } + }; +} + +impl_rcond_tridiagonal_work_c!(c64, lapack_sys::zgtcon_); +impl_rcond_tridiagonal_work_c!(c32, lapack_sys::cgtcon_); + +macro_rules! impl_rcond_tridiagonal_work_r { + ($c:ty, $gtcon:path) => { + impl RcondTridiagonalWorkImpl for RcondTridiagonalWork<$c> { + type Elem = $c; + + fn new(layout: MatrixLayout) -> Self { + let (n, _) = layout.size(); + let work = vec_uninit(2 * n as usize); + let iwork = vec_uninit(n as usize); + RcondTridiagonalWork { + work, + iwork: Some(iwork), + } + } + + fn calc( + &mut self, + lu: &LUFactorizedTridiagonal, + ) -> Result<::Real> { + let (n, _) = lu.a.l.size(); + let mut rcond = ::Real::zero(); + let mut info = 0; + unsafe { + $gtcon( + NormType::One.as_ptr(), + &n, + AsPtr::as_ptr(&lu.a.dl), + AsPtr::as_ptr(&lu.a.d), + AsPtr::as_ptr(&lu.a.du), + AsPtr::as_ptr(&lu.du2), + AsPtr::as_ptr(&lu.ipiv), + &lu.a_opnorm_one, + &mut rcond, + AsPtr::as_mut_ptr(&mut self.work), + AsPtr::as_mut_ptr(self.iwork.as_mut().unwrap()), + &mut info, + ); + } + info.as_lapack_result()?; + Ok(rcond) + } + } + }; +} + +impl_rcond_tridiagonal_work_r!(f64, lapack_sys::dgtcon_); +impl_rcond_tridiagonal_work_r!(f32, lapack_sys::sgtcon_); From d9c52a2940ea85596648676e0a1390f030d51714 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 17:45:41 +0900 Subject: [PATCH 144/165] LuTridiagonalWork --- lax/src/tridiagonal/lu.rs | 101 ++++++++++++++++++++++++++++++++++ lax/src/tridiagonal/matrix.rs | 22 -------- lax/src/tridiagonal/mod.rs | 19 +------ 3 files changed, 103 insertions(+), 39 deletions(-) create mode 100644 lax/src/tridiagonal/lu.rs diff --git a/lax/src/tridiagonal/lu.rs b/lax/src/tridiagonal/lu.rs new file mode 100644 index 00000000..e159bec6 --- /dev/null +++ b/lax/src/tridiagonal/lu.rs @@ -0,0 +1,101 @@ +use crate::*; +use cauchy::*; +use num_traits::Zero; + +/// Represents the LU factorization of a tridiagonal matrix `A` as `A = P*L*U`. +#[derive(Clone, PartialEq)] +pub struct LUFactorizedTridiagonal { + /// A tridiagonal matrix which consists of + /// - l : layout of raw matrix + /// - dl: (n-1) multipliers that define the matrix L. + /// - d : (n) diagonal elements of the upper triangular matrix U. + /// - du: (n-1) elements of the first super-diagonal of U. + pub a: Tridiagonal, + /// (n-2) elements of the second super-diagonal of U. + pub du2: Vec, + /// The pivot indices that define the permutation matrix `P`. + pub ipiv: Pivot, + + pub a_opnorm_one: A::Real, +} + +impl Tridiagonal { + fn opnorm_one(&self) -> A::Real { + let mut col_sum: Vec = self.d.iter().map(|val| val.abs()).collect(); + for i in 0..col_sum.len() { + if i < self.dl.len() { + col_sum[i] += self.dl[i].abs(); + } + if i > 0 { + col_sum[i] += self.du[i - 1].abs(); + } + } + let mut max = A::Real::zero(); + for &val in &col_sum { + if max < val { + max = val; + } + } + max + } +} + +pub struct LuTridiagonalWork { + pub layout: MatrixLayout, + pub du2: Vec>, + pub ipiv: Vec>, +} + +pub trait LuTridiagonalWorkImpl { + type Elem: Scalar; + fn new(layout: MatrixLayout) -> Self; + fn eval(self, a: Tridiagonal) -> Result>; +} + +macro_rules! impl_lu_tridiagonal_work { + ($s:ty, $trf:path) => { + impl LuTridiagonalWorkImpl for LuTridiagonalWork<$s> { + type Elem = $s; + + fn new(layout: MatrixLayout) -> Self { + let (n, _) = layout.size(); + let du2 = vec_uninit((n - 2) as usize); + let ipiv = vec_uninit(n as usize); + LuTridiagonalWork { layout, du2, ipiv } + } + + fn eval( + mut self, + mut a: Tridiagonal, + ) -> Result> { + let (n, _) = self.layout.size(); + // We have to calc one-norm before LU factorization + let a_opnorm_one = a.opnorm_one(); + let mut info = 0; + unsafe { + $trf( + &n, + AsPtr::as_mut_ptr(&mut a.dl), + AsPtr::as_mut_ptr(&mut a.d), + AsPtr::as_mut_ptr(&mut a.du), + AsPtr::as_mut_ptr(&mut self.du2), + AsPtr::as_mut_ptr(&mut self.ipiv), + &mut info, + ) + }; + info.as_lapack_result()?; + Ok(LUFactorizedTridiagonal { + a, + du2: unsafe { self.du2.assume_init() }, + ipiv: unsafe { self.ipiv.assume_init() }, + a_opnorm_one, + }) + } + } + }; +} + +impl_lu_tridiagonal_work!(c64, lapack_sys::zgttrf_); +impl_lu_tridiagonal_work!(c32, lapack_sys::cgttrf_); +impl_lu_tridiagonal_work!(f64, lapack_sys::dgttrf_); +impl_lu_tridiagonal_work!(f32, lapack_sys::sgttrf_); diff --git a/lax/src/tridiagonal/matrix.rs b/lax/src/tridiagonal/matrix.rs index d009a0b5..47401430 100644 --- a/lax/src/tridiagonal/matrix.rs +++ b/lax/src/tridiagonal/matrix.rs @@ -1,6 +1,5 @@ use crate::layout::*; use cauchy::*; -use num_traits::Zero; use std::ops::{Index, IndexMut}; /// Represents a tridiagonal matrix as 3 one-dimensional vectors. @@ -24,27 +23,6 @@ pub struct Tridiagonal { pub du: Vec, } -impl Tridiagonal { - pub fn opnorm_one(&self) -> A::Real { - let mut col_sum: Vec = self.d.iter().map(|val| val.abs()).collect(); - for i in 0..col_sum.len() { - if i < self.dl.len() { - col_sum[i] += self.dl[i].abs(); - } - if i > 0 { - col_sum[i] += self.du[i - 1].abs(); - } - } - let mut max = A::Real::zero(); - for &val in &col_sum { - if max < val { - max = val; - } - } - max - } -} - impl Index<(i32, i32)> for Tridiagonal { type Output = A; #[inline] diff --git a/lax/src/tridiagonal/mod.rs b/lax/src/tridiagonal/mod.rs index dfbd9be2..20809a63 100644 --- a/lax/src/tridiagonal/mod.rs +++ b/lax/src/tridiagonal/mod.rs @@ -1,10 +1,12 @@ //! Implement linear solver using LU decomposition //! for tridiagonal matrix +mod lu; mod matrix; mod rcond; mod solve; +pub use lu::*; pub use matrix::*; pub use rcond::*; pub use solve::*; @@ -13,23 +15,6 @@ use crate::{error::*, layout::*, *}; use cauchy::*; use num_traits::Zero; -/// Represents the LU factorization of a tridiagonal matrix `A` as `A = P*L*U`. -#[derive(Clone, PartialEq)] -pub struct LUFactorizedTridiagonal { - /// A tridiagonal matrix which consists of - /// - l : layout of raw matrix - /// - dl: (n-1) multipliers that define the matrix L. - /// - d : (n) diagonal elements of the upper triangular matrix U. - /// - du: (n-1) elements of the first super-diagonal of U. - pub a: Tridiagonal, - /// (n-2) elements of the second super-diagonal of U. - pub du2: Vec, - /// The pivot indices that define the permutation matrix `P`. - pub ipiv: Pivot, - - a_opnorm_one: A::Real, -} - /// Wraps `*gttrf`, `*gtcon` and `*gttrs` pub trait Tridiagonal_: Scalar + Sized { /// Computes the LU factorization of a tridiagonal `m x n` matrix `a` using From 4913818f40a8f28ac8e83a16ad69fa45ffef1bb4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 17:52:40 +0900 Subject: [PATCH 145/165] Merge Tridiagonal_ into Lapack --- lax/src/lib.rs | 53 +++++++++++--- lax/src/tridiagonal/mod.rs | 137 ------------------------------------- 2 files changed, 42 insertions(+), 148 deletions(-) diff --git a/lax/src/lib.rs b/lax/src/lib.rs index 4e886ea4..af48f257 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -84,14 +84,14 @@ extern crate openblas_src as _src; #[cfg(any(feature = "netlib-system", feature = "netlib-static"))] extern crate netlib_src as _src; -pub mod error; -pub mod flags; -pub mod layout; - +pub mod alloc; pub mod cholesky; pub mod eig; pub mod eigh; pub mod eigh_generalized; +pub mod error; +pub mod flags; +pub mod layout; pub mod least_squares; pub mod opnorm; pub mod qr; @@ -101,16 +101,12 @@ pub mod solveh; pub mod svd; pub mod svddc; pub mod triangular; +pub mod tridiagonal; -mod alloc; -mod tridiagonal; - -pub use self::cholesky::*; pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; -pub use self::opnorm::*; pub use self::svd::{SvdOwned, SvdRef}; -pub use self::tridiagonal::*; +pub use self::tridiagonal::{LUFactorizedTridiagonal, Tridiagonal}; use self::{alloc::*, error::*, layout::*}; use cauchy::*; @@ -120,7 +116,7 @@ pub type Pivot = Vec; #[cfg_attr(doc, katexit::katexit)] /// Trait for primitive types which implements LAPACK subroutines -pub trait Lapack: Tridiagonal_ { +pub trait Lapack: Scalar { /// Compute right eigenvalue and eigenvectors for a general matrix fn eig( calc_v: bool, @@ -306,6 +302,19 @@ pub trait Lapack: Tridiagonal_ { a: &[Self], b: &mut [Self], ) -> Result<()>; + + /// Computes the LU factorization of a tridiagonal `m x n` matrix `a` using + /// partial pivoting with row interchanges. + fn lu_tridiagonal(a: Tridiagonal) -> Result>; + + fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result; + + fn solve_tridiagonal( + lu: &LUFactorizedTridiagonal, + bl: MatrixLayout, + t: Transpose, + b: &mut [Self], + ) -> Result<()>; } macro_rules! impl_lapack { @@ -491,6 +500,28 @@ macro_rules! impl_lapack { use triangular::*; SolveTriangularImpl::solve_triangular(al, bl, uplo, d, a, b) } + + fn lu_tridiagonal(a: Tridiagonal) -> Result> { + use tridiagonal::*; + let work = LuTridiagonalWork::<$s>::new(a.l); + work.eval(a) + } + + fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { + use tridiagonal::*; + let mut work = RcondTridiagonalWork::<$s>::new(lu.a.l); + work.calc(lu) + } + + fn solve_tridiagonal( + lu: &LUFactorizedTridiagonal, + bl: MatrixLayout, + t: Transpose, + b: &mut [Self], + ) -> Result<()> { + use tridiagonal::*; + SolveTridiagonalImpl::solve_tridiagonal(lu, bl, t, b) + } } }; } diff --git a/lax/src/tridiagonal/mod.rs b/lax/src/tridiagonal/mod.rs index 20809a63..834ddd8a 100644 --- a/lax/src/tridiagonal/mod.rs +++ b/lax/src/tridiagonal/mod.rs @@ -10,140 +10,3 @@ pub use lu::*; pub use matrix::*; pub use rcond::*; pub use solve::*; - -use crate::{error::*, layout::*, *}; -use cauchy::*; -use num_traits::Zero; - -/// Wraps `*gttrf`, `*gtcon` and `*gttrs` -pub trait Tridiagonal_: Scalar + Sized { - /// Computes the LU factorization of a tridiagonal `m x n` matrix `a` using - /// partial pivoting with row interchanges. - fn lu_tridiagonal(a: Tridiagonal) -> Result>; - - fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result; - - fn solve_tridiagonal( - lu: &LUFactorizedTridiagonal, - bl: MatrixLayout, - t: Transpose, - b: &mut [Self], - ) -> Result<()>; -} - -macro_rules! impl_tridiagonal { - (@real, $scalar:ty, $gttrf:path, $gtcon:path, $gttrs:path) => { - impl_tridiagonal!(@body, $scalar, $gttrf, $gtcon, $gttrs, iwork); - }; - (@complex, $scalar:ty, $gttrf:path, $gtcon:path, $gttrs:path) => { - impl_tridiagonal!(@body, $scalar, $gttrf, $gtcon, $gttrs, ); - }; - (@body, $scalar:ty, $gttrf:path, $gtcon:path, $gttrs:path, $($iwork:ident)*) => { - impl Tridiagonal_ for $scalar { - fn lu_tridiagonal(mut a: Tridiagonal) -> Result> { - let (n, _) = a.l.size(); - let mut du2 = vec_uninit( (n - 2) as usize); - let mut ipiv = vec_uninit( n as usize); - // We have to calc one-norm before LU factorization - let a_opnorm_one = a.opnorm_one(); - let mut info = 0; - unsafe { - $gttrf( - &n, - AsPtr::as_mut_ptr(&mut a.dl), - AsPtr::as_mut_ptr(&mut a.d), - AsPtr::as_mut_ptr(&mut a.du), - AsPtr::as_mut_ptr(&mut du2), - AsPtr::as_mut_ptr(&mut ipiv), - &mut info, - ) - }; - info.as_lapack_result()?; - let du2 = unsafe { du2.assume_init() }; - let ipiv = unsafe { ipiv.assume_init() }; - Ok(LUFactorizedTridiagonal { - a, - du2, - ipiv, - a_opnorm_one, - }) - } - - fn rcond_tridiagonal(lu: &LUFactorizedTridiagonal) -> Result { - let (n, _) = lu.a.l.size(); - let ipiv = &lu.ipiv; - let mut work: Vec> = vec_uninit(2 * n as usize); - $( - let mut $iwork: Vec> = vec_uninit(n as usize); - )* - let mut rcond = Self::Real::zero(); - let mut info = 0; - unsafe { - $gtcon( - NormType::One.as_ptr(), - &n, - AsPtr::as_ptr(&lu.a.dl), - AsPtr::as_ptr(&lu.a.d), - AsPtr::as_ptr(&lu.a.du), - AsPtr::as_ptr(&lu.du2), - ipiv.as_ptr(), - &lu.a_opnorm_one, - &mut rcond, - AsPtr::as_mut_ptr(&mut work), - $(AsPtr::as_mut_ptr(&mut $iwork),)* - &mut info, - ); - } - info.as_lapack_result()?; - Ok(rcond) - } - - fn solve_tridiagonal( - lu: &LUFactorizedTridiagonal, - b_layout: MatrixLayout, - t: Transpose, - b: &mut [Self], - ) -> Result<()> { - let (n, _) = lu.a.l.size(); - let ipiv = &lu.ipiv; - // Transpose if b is C-continuous - let mut b_t = None; - let b_layout = match b_layout { - MatrixLayout::C { .. } => { - let (layout, t) = transpose(b_layout, b); - b_t = Some(t); - layout - } - MatrixLayout::F { .. } => b_layout, - }; - let (ldb, nrhs) = b_layout.size(); - let mut info = 0; - unsafe { - $gttrs( - t.as_ptr(), - &n, - &nrhs, - AsPtr::as_ptr(&lu.a.dl), - AsPtr::as_ptr(&lu.a.d), - AsPtr::as_ptr(&lu.a.du), - AsPtr::as_ptr(&lu.du2), - ipiv.as_ptr(), - AsPtr::as_mut_ptr(b_t.as_mut().map(|v| v.as_mut_slice()).unwrap_or(b)), - &ldb, - &mut info, - ); - } - info.as_lapack_result()?; - if let Some(b_t) = b_t { - transpose_over(b_layout, &b_t, b); - } - Ok(()) - } - } - }; -} // impl_tridiagonal! - -impl_tridiagonal!(@real, f64, lapack_sys::dgttrf_, lapack_sys::dgtcon_, lapack_sys::dgttrs_); -impl_tridiagonal!(@real, f32, lapack_sys::sgttrf_, lapack_sys::sgtcon_, lapack_sys::sgttrs_); -impl_tridiagonal!(@complex, c64, lapack_sys::zgttrf_, lapack_sys::zgtcon_, lapack_sys::zgttrs_); -impl_tridiagonal!(@complex, c32, lapack_sys::cgttrf_, lapack_sys::cgtcon_, lapack_sys::cgttrs_); From f9032288e467be9bb7a5105ca5b1c143a43c213c Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 18:28:33 +0900 Subject: [PATCH 146/165] (cargo-release) version 0.16.0-rc.0 --- lax/Cargo.toml | 2 +- ndarray-linalg/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index d4e7773c..72e7392c 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.15.0" +version = "0.16.0-rc.0" authors = ["Toshiki Teramura "] edition = "2018" diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index c7006cb2..e833ff3f 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -42,7 +42,7 @@ features = ["blas", "approx", "std"] default-features = false [dependencies.lax] -version = "0.15.0-rc.0" +version = "0.16.0-rc.0" path = "../lax" default-features = false From 0bc1e71023c05638dd090ad7d572b1e4e330da49 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 18:28:33 +0900 Subject: [PATCH 147/165] (cargo-release) version 0.16.0-rc.0 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index e833ff3f..267db861 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.15.0" +version = "0.16.0-rc.0" authors = ["Toshiki Teramura "] edition = "2018" From a1e63aab14478ee53e08da8ffc8406be6ff0f873 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 19:03:42 +0900 Subject: [PATCH 148/165] (cargo-release) version 0.16.0 --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 72e7392c..2f095ea8 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.16.0-rc.0" +version = "0.16.0" authors = ["Toshiki Teramura "] edition = "2018" From 3e13736c740c2959745bfdf20aa69a4cde988d3c Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 4 Oct 2022 19:03:43 +0900 Subject: [PATCH 149/165] (cargo-release) version 0.16.0 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 267db861..e06fa675 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.16.0-rc.0" +version = "0.16.0" authors = ["Toshiki Teramura "] edition = "2018" From 293655b31e3255f6b3926fb5c6330d72fa63d42e Mon Sep 17 00:00:00 2001 From: Michael Kefeder Date: Fri, 5 May 2023 14:17:00 +0200 Subject: [PATCH 150/165] use platform dependent c_char instead of hardcoded i8 --- lax/Cargo.toml | 1 + lax/src/flags.rs | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 2f095ea8..f8813329 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -34,6 +34,7 @@ cauchy = "0.4.0" num-traits = "0.2.14" lapack-sys = "0.14.0" katexit = "0.1.2" +libc = "0.2.142" [dependencies.intel-mkl-src] version = "0.8.1" diff --git a/lax/src/flags.rs b/lax/src/flags.rs index dd9dde3d..951cb2c5 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -17,8 +17,8 @@ impl UPLO { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const UPLO as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const UPLO as *const libc::c_char } } @@ -32,8 +32,8 @@ pub enum Transpose { impl Transpose { /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const Transpose as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const Transpose as *const libc::c_char } } @@ -55,8 +55,8 @@ impl NormType { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const NormType as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const NormType as *const libc::c_char } } @@ -87,8 +87,8 @@ impl JobEv { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const i8 { - self as *const JobEv as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const JobEv as *const libc::c_char } } @@ -117,8 +117,8 @@ impl JobSvd { } } - pub fn as_ptr(&self) -> *const i8 { - self as *const JobSvd as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const JobSvd as *const libc::c_char } } @@ -133,7 +133,7 @@ pub enum Diag { } impl Diag { - pub fn as_ptr(&self) -> *const i8 { - self as *const Diag as *const i8 + pub fn as_ptr(&self) -> *const libc::c_char { + self as *const Diag as *const libc::c_char } } From cef6aa5e95d8d595e546fe82d09e83d1e1861f5a Mon Sep 17 00:00:00 2001 From: Michael Kefeder Date: Tue, 12 Mar 2024 09:08:18 +0100 Subject: [PATCH 151/165] remove dependency on libc --- lax/Cargo.toml | 1 - lax/src/flags.rs | 25 +++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index f8813329..2f095ea8 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -34,7 +34,6 @@ cauchy = "0.4.0" num-traits = "0.2.14" lapack-sys = "0.14.0" katexit = "0.1.2" -libc = "0.2.142" [dependencies.intel-mkl-src] version = "0.8.1" diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 951cb2c5..60364d5e 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -1,4 +1,5 @@ //! Charactor flags, e.g. `'T'`, used in LAPACK API +use std::ffi::c_char; /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -17,8 +18,8 @@ impl UPLO { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const UPLO as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const UPLO as *const c_char } } @@ -32,8 +33,8 @@ pub enum Transpose { impl Transpose { /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const Transpose as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const Transpose as *const c_char } } @@ -55,8 +56,8 @@ impl NormType { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const NormType as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const NormType as *const c_char } } @@ -87,8 +88,8 @@ impl JobEv { } /// To use Fortran LAPACK API in lapack-sys crate - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const JobEv as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const JobEv as *const c_char } } @@ -117,8 +118,8 @@ impl JobSvd { } } - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const JobSvd as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const JobSvd as *const c_char } } @@ -133,7 +134,7 @@ pub enum Diag { } impl Diag { - pub fn as_ptr(&self) -> *const libc::c_char { - self as *const Diag as *const libc::c_char + pub fn as_ptr(&self) -> *const c_char { + self as *const Diag as *const c_char } } From 7213ae839d533c8c25dadf90c0a70034248dfbee Mon Sep 17 00:00:00 2001 From: Michael Kefeder Date: Mon, 11 Nov 2024 18:45:25 +0100 Subject: [PATCH 152/165] use core instead of std --- lax/src/flags.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/src/flags.rs b/lax/src/flags.rs index 60364d5e..f9dea20d 100644 --- a/lax/src/flags.rs +++ b/lax/src/flags.rs @@ -1,5 +1,5 @@ //! Charactor flags, e.g. `'T'`, used in LAPACK API -use std::ffi::c_char; +use core::ffi::c_char; /// Upper/Lower specification for seveal usages #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] From 6b9181e2d254c2e44dec591354ad1979a63e5760 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 18 Nov 2024 21:44:19 +0900 Subject: [PATCH 153/165] Add a feature to turn on/off blas backend ndarray by default turns off blas backend, but ndarray-linalg turns it on by default and there is no way to turn it off. This change adds a cargo feature to control it. It's a default feature, so users who don't want to use blas backend need `default-features = false` in their Cargo.toml. --- ndarray-linalg/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index e06fa675..c904fe6d 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -13,7 +13,8 @@ readme = "../README.md" categories = ["algorithms", "science"] [features] -default = [] +default = ["blas"] +blas = ["ndarray/blas"] netlib = ["lax/netlib"] openblas = ["lax/openblas"] @@ -38,7 +39,7 @@ thiserror = "1.0.24" [dependencies.ndarray] version = "0.15.2" -features = ["blas", "approx", "std"] +features = ["approx", "std"] default-features = false [dependencies.lax] From fb1b6d6779ad644b9f4c7c5a0b145d0c7237991f Mon Sep 17 00:00:00 2001 From: Gorka Kobeaga Date: Sat, 10 Feb 2024 01:11:26 +0100 Subject: [PATCH 154/165] Fix: rcond_hilbert test --- ndarray-linalg/tests/solve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/tests/solve.rs b/ndarray-linalg/tests/solve.rs index 57c29b67..074350ce 100644 --- a/ndarray-linalg/tests/solve.rs +++ b/ndarray-linalg/tests/solve.rs @@ -254,7 +254,7 @@ fn rcond_hilbert() { macro_rules! rcond_hilbert { ($elem:ty, $rows:expr, $atol:expr) => { let a = Array2::<$elem>::from_shape_fn(($rows, $rows), |(i, j)| { - 1. / (i as $elem + j as $elem - 1.) + 1. / (i as $elem + j as $elem + 1.) }); assert_aclose!(a.rcond().unwrap(), 0., $atol); assert_aclose!(a.rcond_into().unwrap(), 0., $atol); From 6fb720783b9a1e94c1f9cd68aaf7b8f7c719d33a Mon Sep 17 00:00:00 2001 From: Gemingyang Date: Sat, 14 Dec 2024 22:18:00 +0000 Subject: [PATCH 155/165] fix CI --- .github/workflows/gh-pages.yml | 14 +++++----- .github/workflows/intel-mkl.yml | 48 +++++++-------------------------- .github/workflows/netlib.yml | 12 +++------ .github/workflows/openblas.yml | 22 ++++++--------- .github/workflows/rust.yml | 46 +++++++++++++++---------------- 5 files changed, 50 insertions(+), 92 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index f8509459..4123d4a6 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -33,23 +33,21 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Generate cargo-doc - - uses: actions-rs/cargo@v1 - with: - command: doc - args: --no-deps + - name: Generate documentation + run: cargo doc --no-deps - name: Setup Pages - uses: actions/configure-pages@v2 + uses: actions/configure-pages@v5 # Upload target/doc directory - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: 'target/doc' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/intel-mkl.yml b/.github/workflows/intel-mkl.yml index fcb8c845..14a0b443 100644 --- a/.github/workflows/intel-mkl.yml +++ b/.github/workflows/intel-mkl.yml @@ -5,44 +5,16 @@ on: branches: - master pull_request: {} + workflow_dispatch: jobs: - windows: - runs-on: windows-2019 + intel-mkl: + strategy: + fail-fast: false + matrix: + system: [ubuntu-22.04, windows-latest] + runs-on: ${{ matrix.system }} steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: test - args: > - --manifest-path=ndarray-linalg/Cargo.toml - --no-default-features - --features=intel-mkl-static - - linux: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - name: cargo test - with: - command: test - args: > - --manifest-path=ndarray-linalg/Cargo.toml - --no-default-features - --features=intel-mkl-static - - linux-container: - runs-on: ubuntu-22.04 - container: - image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - name: cargo test - with: - command: test - args: > - --manifest-path=ndarray-linalg/Cargo.toml - --no-default-features - --features=intel-mkl-system + - uses: actions/checkout@v4 + - name: cargo test + run: cargo test --manifest-path=ndarray-linalg/Cargo.toml --no-default-features --features=intel-mkl-static --verbose diff --git a/.github/workflows/netlib.yml b/.github/workflows/netlib.yml index d4df49a5..70048503 100644 --- a/.github/workflows/netlib.yml +++ b/.github/workflows/netlib.yml @@ -5,6 +5,7 @@ on: branches: - master pull_request: {} + workflow_dispatch: jobs: linux: @@ -15,15 +16,10 @@ jobs: - static runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: apt install gfortran run: | sudo apt update sudo apt install -y gfortran - - uses: actions-rs/cargo@v1 - with: - command: test - args: > - --manifest-path=ndarray-linalg/Cargo.toml - --no-default-features - --features=netlib-${{ matrix.feature }} + - name: cargo test + run: cargo test --manifest-path=ndarray-linalg/Cargo.toml --no-default-features --features=netlib-${{ matrix.feature }} diff --git a/.github/workflows/openblas.yml b/.github/workflows/openblas.yml index 4e717ba9..5375be70 100644 --- a/.github/workflows/openblas.yml +++ b/.github/workflows/openblas.yml @@ -5,12 +5,11 @@ on: branches: - master pull_request: {} + workflow_dispatch: jobs: linux: runs-on: ubuntu-22.04 - container: - image: rust strategy: fail-fast: false matrix: @@ -18,20 +17,15 @@ jobs: - static - system steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: apt install gfortran run: | - apt update - apt install -y gfortran + sudo apt update + sudo apt install -y gfortran - name: Install OpenBLAS by apt run: | - apt update - apt install -y libopenblas-dev + sudo apt update + sudo apt install -y libopenblas-dev if: ${{ contains(matrix.feature, 'system') }} - - uses: actions-rs/cargo@v1 - with: - command: test - args: > - --manifest-path=ndarray-linalg/Cargo.toml - --no-default-features - --features=openblas-${{ matrix.feature }} + - name: cargo test + run: cargo test --manifest-path=ndarray-linalg/Cargo.toml --no-default-features --features=openblas-${{ matrix.feature }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9cd3919b..66ca1553 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,52 +5,50 @@ on: branches: - master pull_request: {} + workflow_dispatch: jobs: check-format: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check + - uses: actions/checkout@v4 + - name: fmt + run: cargo fmt -- --check check: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-targets + - uses: actions/checkout@v4 + - name: cargo check + run: cargo check --all-targets check-doc: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: doc - args: --no-deps + - uses: actions/checkout@v4 + - name: cargo doc + run: cargo doc --no-deps clippy: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: clippy + - uses: actions/checkout@v4 + - name: cargo clippy + run: cargo clippy coverage: runs-on: ubuntu-22.04 container: - image: ghcr.io/rust-math/rust-mkl:1.63.0-2020.1 + image: xd009642/tarpaulin:develop-nightly options: --security-opt seccomp=unconfined steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Install Cross + uses: taiki-e/install-action@v2 + with: + tool: cargo-tarpaulin - name: Generate code coverage - run: | - cargo tarpaulin --features=intel-mkl-system --out Xml + run: cargo +nightly tarpaulin --features=intel-mkl-static --out xml - name: Upload to codecov.io - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v5 From c1a3284d6216c3a1b1a642ecdf8df0a840432438 Mon Sep 17 00:00:00 2001 From: Dirreke Date: Thu, 15 Aug 2024 00:40:42 +0800 Subject: [PATCH 156/165] Upgrade deps --- ndarray-linalg/Cargo.toml | 6 +++--- ndarray-linalg/src/convert.rs | 10 +++++----- ndarray-linalg/tests/det.rs | 4 ++-- ndarray-linalg/tests/deth.rs | 4 ++-- ndarray-linalg/tests/inv.rs | 2 +- ndarray-linalg/tests/opnorm.rs | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index e06fa675..fd524609 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -37,7 +37,7 @@ rand = "0.8.3" thiserror = "1.0.24" [dependencies.ndarray] -version = "0.15.2" +version = "0.16.0" features = ["blas", "approx", "std"] default-features = false @@ -48,9 +48,9 @@ default-features = false [dev-dependencies] paste = "1.0.5" -criterion = "0.3.4" +criterion = "0.5.1" # Keep the same version as ndarray's dependency! -approx = { version = "0.4.0", features = ["num-complex"] } +approx = { version = "0.5", features = ["num-complex"] } rand_pcg = "0.3.1" [[bench]] diff --git a/ndarray-linalg/src/convert.rs b/ndarray-linalg/src/convert.rs index 43002966..c808211e 100644 --- a/ndarray-linalg/src/convert.rs +++ b/ndarray-linalg/src/convert.rs @@ -12,7 +12,7 @@ where S: Data, { let n = a.len(); - a.into_shape((n, 1)).unwrap() + a.into_shape_with_order((n, 1)).unwrap() } pub fn into_row(a: ArrayBase) -> ArrayBase @@ -20,7 +20,7 @@ where S: Data, { let n = a.len(); - a.into_shape((1, n)).unwrap() + a.into_shape_with_order((1, n)).unwrap() } pub fn flatten(a: ArrayBase) -> ArrayBase @@ -28,7 +28,7 @@ where S: Data, { let n = a.len(); - a.into_shape(n).unwrap() + a.into_shape_with_order(n).unwrap() } pub fn into_matrix(l: MatrixLayout, a: Vec) -> Result> @@ -99,9 +99,9 @@ where // https://github.com/bluss/rust-ndarray/issues/325 let strides: Vec = a.strides().to_vec(); let new = if a.is_standard_layout() { - ArrayBase::from_shape_vec(a.dim(), a.into_raw_vec()).unwrap() + ArrayBase::from_shape_vec(a.dim(), a.into_raw_vec_and_offset().0).unwrap() } else { - ArrayBase::from_shape_vec(a.dim().f(), a.into_raw_vec()).unwrap() + ArrayBase::from_shape_vec(a.dim().f(), a.into_raw_vec_and_offset().0).unwrap() }; assert_eq!( new.strides(), diff --git a/ndarray-linalg/tests/det.rs b/ndarray-linalg/tests/det.rs index d14fc8d0..40dafd57 100644 --- a/ndarray-linalg/tests/det.rs +++ b/ndarray-linalg/tests/det.rs @@ -94,7 +94,7 @@ fn det_zero_nonsquare() { assert!(a.sln_det_into().is_err()); }; } - for &shape in &[(1, 2).into_shape(), (1, 2).f()] { + for &shape in &[(1, 2).into_shape_with_order(), (1, 2).f()] { det_zero_nonsquare!(f64, shape); det_zero_nonsquare!(f32, shape); det_zero_nonsquare!(c64, shape); @@ -186,7 +186,7 @@ fn det_nonsquare() { }; } for &dims in &[(1, 0), (1, 2), (2, 1), (2, 3)] { - for &shape in &[dims.into_shape(), dims.f()] { + for &shape in &[dims.into_shape_with_order(), dims.f()] { det_nonsquare!(f64, shape); det_nonsquare!(f32, shape); det_nonsquare!(c64, shape); diff --git a/ndarray-linalg/tests/deth.rs b/ndarray-linalg/tests/deth.rs index 4785aadc..9079c342 100644 --- a/ndarray-linalg/tests/deth.rs +++ b/ndarray-linalg/tests/deth.rs @@ -60,7 +60,7 @@ fn deth_zero_nonsquare() { assert!(a.sln_deth_into().is_err()); }; } - for &shape in &[(1, 2).into_shape(), (1, 2).f()] { + for &shape in &[(1, 2).into_shape_with_order(), (1, 2).f()] { deth_zero_nonsquare!(f64, shape); deth_zero_nonsquare!(f32, shape); deth_zero_nonsquare!(c64, shape); @@ -138,7 +138,7 @@ fn deth_nonsquare() { }; } for &dims in &[(1, 0), (1, 2), (2, 1), (2, 3)] { - for &shape in &[dims.into_shape(), dims.f()] { + for &shape in &[dims.into_shape_with_order(), dims.f()] { deth_nonsquare!(f64, shape); deth_nonsquare!(f32, shape); deth_nonsquare!(c64, shape); diff --git a/ndarray-linalg/tests/inv.rs b/ndarray-linalg/tests/inv.rs index 030773c1..7dab3e01 100644 --- a/ndarray-linalg/tests/inv.rs +++ b/ndarray-linalg/tests/inv.rs @@ -106,7 +106,7 @@ fn inv_into_random_complex() { #[should_panic] fn inv_error() { // do not have inverse - let a = Array::::zeros(9).into_shape((3, 3)).unwrap(); + let a = Array::::zeros(9).into_shape_with_order((3, 3)).unwrap(); let a_inv = a.inv().unwrap(); println!("{:?}", a_inv); } diff --git a/ndarray-linalg/tests/opnorm.rs b/ndarray-linalg/tests/opnorm.rs index abd41748..b00c149f 100644 --- a/ndarray-linalg/tests/opnorm.rs +++ b/ndarray-linalg/tests/opnorm.rs @@ -14,11 +14,11 @@ fn gen(i: usize, j: usize, rev: bool) -> Array2 { let n = (i * j + 1) as f64; if rev { Array::range(1., n, 1.) - .into_shape((j, i)) + .into_shape_with_order((j, i)) .unwrap() .reversed_axes() } else { - Array::range(1., n, 1.).into_shape((i, j)).unwrap() + Array::range(1., n, 1.).into_shape_with_order((i, j)).unwrap() } } From 271e0411f32383e1cf29cb42bda244430e227740 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 23 Nov 2024 18:32:10 +0100 Subject: [PATCH 157/165] ndarray-linalg: Update to lax 0.16.0 --- ndarray-linalg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index fd524609..bda7a89c 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -42,7 +42,7 @@ features = ["blas", "approx", "std"] default-features = false [dependencies.lax] -version = "0.16.0-rc.0" +version = "0.16.0" path = "../lax" default-features = false From 373192d69d6087610ce540c9b29c8befdfc56c5b Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 23 Nov 2024 18:39:52 +0100 Subject: [PATCH 158/165] Run cargo fmt --- ndarray-linalg/tests/inv.rs | 4 +++- ndarray-linalg/tests/opnorm.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ndarray-linalg/tests/inv.rs b/ndarray-linalg/tests/inv.rs index 7dab3e01..93ff1aad 100644 --- a/ndarray-linalg/tests/inv.rs +++ b/ndarray-linalg/tests/inv.rs @@ -106,7 +106,9 @@ fn inv_into_random_complex() { #[should_panic] fn inv_error() { // do not have inverse - let a = Array::::zeros(9).into_shape_with_order((3, 3)).unwrap(); + let a = Array::::zeros(9) + .into_shape_with_order((3, 3)) + .unwrap(); let a_inv = a.inv().unwrap(); println!("{:?}", a_inv); } diff --git a/ndarray-linalg/tests/opnorm.rs b/ndarray-linalg/tests/opnorm.rs index b00c149f..cd45d258 100644 --- a/ndarray-linalg/tests/opnorm.rs +++ b/ndarray-linalg/tests/opnorm.rs @@ -18,7 +18,9 @@ fn gen(i: usize, j: usize, rev: bool) -> Array2 { .unwrap() .reversed_axes() } else { - Array::range(1., n, 1.).into_shape_with_order((i, j)).unwrap() + Array::range(1., n, 1.) + .into_shape_with_order((i, j)) + .unwrap() } } From 598e246a347e61f19b790eef46ce209d31b2bc9f Mon Sep 17 00:00:00 2001 From: Dirreke Date: Wed, 29 Jan 2025 23:10:22 +0800 Subject: [PATCH 159/165] Bump thiserror from 1.0 to 2.0 --- lax/Cargo.toml | 2 +- ndarray-linalg/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 2f095ea8..8d7540e3 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -29,7 +29,7 @@ intel-mkl-static = ["intel-mkl-src/mkl-static-lp64-seq"] intel-mkl-system = ["intel-mkl-src/mkl-dynamic-lp64-seq"] [dependencies] -thiserror = "1.0.24" +thiserror = "2.0.0" cauchy = "0.4.0" num-traits = "0.2.14" lapack-sys = "0.14.0" diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 5439a9a1..aca807cb 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -35,7 +35,7 @@ katexit = "0.1.2" num-complex = "0.4.0" num-traits = "0.2.14" rand = "0.8.3" -thiserror = "1.0.24" +thiserror = "2.0.0" [dependencies.ndarray] version = "0.16.0" From ec3edd33e466b02c3da3ef3aeee11cd2dbc5b222 Mon Sep 17 00:00:00 2001 From: Dirreke Date: Wed, 29 Jan 2025 23:17:47 +0800 Subject: [PATCH 160/165] release 0.17.0 --- lax/Cargo.toml | 2 +- ndarray-linalg/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 8d7540e3..b3320221 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.16.0" +version = "0.17.0" authors = ["Toshiki Teramura "] edition = "2018" diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index aca807cb..8c5c83a8 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.16.0" +version = "0.17.0" authors = ["Toshiki Teramura "] edition = "2018" @@ -43,7 +43,7 @@ features = ["approx", "std"] default-features = false [dependencies.lax] -version = "0.16.0" +version = "0.17.0" path = "../lax" default-features = false From e30c22c5aafdc59c08b3824d3fd22a67a2c0b91b Mon Sep 17 00:00:00 2001 From: Eric Berquist Date: Sat, 19 Apr 2025 15:47:02 -0400 Subject: [PATCH 161/165] Bump lapack-sys from 0.14.0 to 0.15.0 --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index b3320221..cf302eba 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -32,7 +32,7 @@ intel-mkl-system = ["intel-mkl-src/mkl-dynamic-lp64-seq"] thiserror = "2.0.0" cauchy = "0.4.0" num-traits = "0.2.14" -lapack-sys = "0.14.0" +lapack-sys = "0.15.0" katexit = "0.1.2" [dependencies.intel-mkl-src] From ccd537982a4b78cd58cbfda8c2f1082c5f512d06 Mon Sep 17 00:00:00 2001 From: Eric Berquist Date: Sat, 19 Apr 2025 15:47:33 -0400 Subject: [PATCH 162/165] Bump netlib-src from 0.8.0 to 0.9.0 --- lax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index cf302eba..076d5cf2 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -41,7 +41,7 @@ default-features = false optional = true [dependencies.netlib-src] -version = "0.8.0" +version = "0.9.0" optional = true features = ["cblas"] default-features = false From c7ff9dcba8cde23f98394558280f8e78872e25a3 Mon Sep 17 00:00:00 2001 From: Dirreke Date: Fri, 6 Jun 2025 21:47:42 +0800 Subject: [PATCH 163/165] fix test --- ndarray-linalg/src/assert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/src/assert.rs b/ndarray-linalg/src/assert.rs index 670aabc8..74c1618b 100644 --- a/ndarray-linalg/src/assert.rs +++ b/ndarray-linalg/src/assert.rs @@ -86,7 +86,7 @@ where } macro_rules! generate_assert { - ($assert:ident, $close:path) => { + ($assert:ident, $close:tt) => { #[macro_export] macro_rules! $assert { ($test: expr,$truth: expr,$tol: expr) => { From e4bfc4289f91a8c4249e3ab4649a28145419a747 Mon Sep 17 00:00:00 2001 From: "Bang C. Huynh" <49149246+bangconghuynh@users.noreply.github.com> Date: Wed, 6 Aug 2025 09:57:22 +0100 Subject: [PATCH 164/165] Implement generalized eigenvalue solver for general square matrices (LAPACK's ?ggev) (#403) * Implement generalized eigenvalue solver for general square matrices (LAPACK's ?ggev) --- lax/src/eig.rs | 2 +- lax/src/eig_generalized.rs | 520 ++++++++++++++++++++++ lax/src/lib.rs | 35 ++ ndarray-linalg/benches/eig_generalized.rs | 40 ++ ndarray-linalg/src/eig.rs | 84 ++++ ndarray-linalg/tests/eig_generalized.rs | 190 ++++++++ 6 files changed, 870 insertions(+), 1 deletion(-) create mode 100644 lax/src/eig_generalized.rs create mode 100644 ndarray-linalg/benches/eig_generalized.rs create mode 100644 ndarray-linalg/tests/eig_generalized.rs diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 710beb9c..f02035bb 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -404,7 +404,7 @@ impl_eig_work_r!(f64, lapack_sys::dgeev_); /// /// In the C-layout case, we need the conjugates of the left /// eigenvectors, so the signs should be reversed. -fn reconstruct_eigenvectors( +pub(crate) fn reconstruct_eigenvectors( take_hermite_conjugate: bool, eig_im: &[T], vr: &[T], diff --git a/lax/src/eig_generalized.rs b/lax/src/eig_generalized.rs new file mode 100644 index 00000000..ea99dbdb --- /dev/null +++ b/lax/src/eig_generalized.rs @@ -0,0 +1,520 @@ +//! Generalized eigenvalue problem for general matrices +//! +//! LAPACK correspondance +//! ---------------------- +//! +//! | f32 | f64 | c32 | c64 | +//! |:------|:------|:------|:------| +//! | sggev | dggev | cggev | zggev | +//! +use std::mem::MaybeUninit; + +use crate::eig::reconstruct_eigenvectors; +use crate::{error::*, layout::MatrixLayout, *}; +use cauchy::*; +use num_traits::{ToPrimitive, Zero}; + +#[derive(Clone, PartialEq, Eq)] +pub enum GeneralizedEigenvalue { + /// Finite generalized eigenvalue: `Finite(α/β, (α, β))` + Finite(T, (T, T)), + + /// Indeterminate generalized eigenvalue: `Indeterminate((α, β))` + Indeterminate((T, T)), +} + +impl std::fmt::Display for GeneralizedEigenvalue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Finite(e, (a, b)) => write!(f, "{e:.3e} ({a:.3e}/{b:.3e})"), + Self::Indeterminate((a, b)) => write!(f, "∞ ({a:.3e}/{b:.3e})"), + } + } +} + +#[non_exhaustive] +pub struct EigGeneralizedWork { + /// Problem size + pub n: i32, + /// Compute right eigenvectors or not + pub jobvr: JobEv, + /// Compute left eigenvectors or not + pub jobvl: JobEv, + + /// Eigenvalues: alpha (numerators) + pub alpha: Vec>, + /// Eigenvalues: beta (denominators) + pub beta: Vec>, + /// Real part of alpha (eigenvalue numerators) used in real routines + pub alpha_re: Option>>, + /// Imaginary part of alpha (eigenvalue numerators) used in real routines + pub alpha_im: Option>>, + /// Real part of beta (eigenvalue denominators) used in real routines + pub beta_re: Option>>, + /// Imaginary part of beta (eigenvalue denominators) used in real routines + pub beta_im: Option>>, + + /// Left eigenvectors + pub vc_l: Option>>, + /// Left eigenvectors used in real routines + pub vr_l: Option>>, + /// Right eigenvectors + pub vc_r: Option>>, + /// Right eigenvectors used in real routines + pub vr_r: Option>>, + + /// Working memory + pub work: Vec>, + /// Working memory with `T::Real` + pub rwork: Option>>, +} + +impl EigGeneralizedWork +where + T: Scalar, + EigGeneralizedWork: EigGeneralizedWorkImpl, +{ + /// Create new working memory for eigenvalues compution. + pub fn new(calc_v: bool, l: MatrixLayout) -> Result { + EigGeneralizedWorkImpl::new(calc_v, l) + } + + /// Compute eigenvalues and vectors on this working memory. + pub fn calc(&mut self, a: &mut [T], b: &mut [T]) -> Result> { + EigGeneralizedWorkImpl::calc(self, a, b) + } + + /// Compute eigenvalues and vectors by consuming this working memory. + pub fn eval(self, a: &mut [T], b: &mut [T]) -> Result> { + EigGeneralizedWorkImpl::eval(self, a, b) + } +} + +/// Owned result of eigenvalue problem by [EigGeneralizedWork::eval] +#[derive(Debug, Clone, PartialEq)] +pub struct EigGeneralizedOwned { + /// Eigenvalues + pub alpha: Vec, + + pub beta: Vec, + + /// Right eigenvectors + pub vr: Option>, + + /// Left eigenvectors + pub vl: Option>, +} + +/// Reference result of eigenvalue problem by [EigGeneralizedWork::calc] +#[derive(Debug, Clone, PartialEq)] +pub struct EigGeneralizedRef<'work, T: Scalar> { + /// Eigenvalues + pub alpha: &'work [T::Complex], + + pub beta: &'work [T::Complex], + + /// Right eigenvectors + pub vr: Option<&'work [T::Complex]>, + + /// Left eigenvectors + pub vl: Option<&'work [T::Complex]>, +} + +/// Helper trait for implementing [EigGeneralizedWork] methods +pub trait EigGeneralizedWorkImpl: Sized { + type Elem: Scalar; + fn new(calc_v: bool, l: MatrixLayout) -> Result; + fn calc<'work>( + &'work mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result>; + fn eval( + self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result>; +} + +macro_rules! impl_eig_generalized_work_c { + ($f:ty, $c:ty, $ev:path) => { + impl EigGeneralizedWorkImpl for EigGeneralizedWork<$c> { + type Elem = $c; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut rwork = vec_uninit(8 * n as usize); + + let mut alpha = vec_uninit(n as usize); + let mut beta = vec_uninit(n as usize); + + let mut vc_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vc_r = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size = [<$c>::zero()]; + unsafe { + $ev( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut alpha), + AsPtr::as_mut_ptr(&mut beta), + AsPtr::as_mut_ptr(vc_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vc_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + AsPtr::as_mut_ptr(&mut rwork), + &mut info, + ) + }; + info.as_lapack_result()?; + + let lwork = work_size[0].to_usize().unwrap(); + let work: Vec> = vec_uninit(lwork); + Ok(Self { + n, + jobvl, + jobvr, + alpha, + beta, + alpha_re: None, + alpha_im: None, + beta_re: None, + beta_im: None, + rwork: Some(rwork), + vc_l, + vc_r, + vr_l: None, + vr_r: None, + work, + }) + } + + fn calc<'work>( + &'work mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(b), + &self.n, + AsPtr::as_mut_ptr(&mut self.alpha), + AsPtr::as_mut_ptr(&mut self.beta), + AsPtr::as_mut_ptr(self.vc_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vc_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + AsPtr::as_mut_ptr(self.rwork.as_mut().unwrap()), + &mut info, + ) + }; + info.as_lapack_result()?; + // Hermite conjugate + if let Some(vl) = self.vc_l.as_mut() { + for value in vl { + let value = unsafe { value.assume_init_mut() }; + value.im = -value.im; + } + } + Ok(EigGeneralizedRef { + alpha: unsafe { self.alpha.slice_assume_init_ref() }, + beta: unsafe { self.beta.slice_assume_init_ref() }, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) + } + + fn eval( + mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let _eig_generalized_ref = self.calc(a, b)?; + Ok(EigGeneralizedOwned { + alpha: unsafe { self.alpha.assume_init() }, + beta: unsafe { self.beta.assume_init() }, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) + } + } + + impl EigGeneralizedOwned<$c> { + pub fn calc_eigs(&self, thresh_opt: Option<$f>) -> Vec> { + self.alpha + .iter() + .zip(self.beta.iter()) + .map(|(alpha, beta)| { + if let Some(thresh) = thresh_opt { + if beta.abs() < thresh { + GeneralizedEigenvalue::Indeterminate((alpha.clone(), beta.clone())) + } else { + GeneralizedEigenvalue::Finite( + alpha / beta, + (alpha.clone(), beta.clone()), + ) + } + } else { + if beta.is_zero() { + GeneralizedEigenvalue::Indeterminate((alpha.clone(), beta.clone())) + } else { + GeneralizedEigenvalue::Finite( + alpha / beta, + (alpha.clone(), beta.clone()), + ) + } + } + }) + .collect::>() + } + } + }; +} + +impl_eig_generalized_work_c!(f32, c32, lapack_sys::cggev_); +impl_eig_generalized_work_c!(f64, c64, lapack_sys::zggev_); + +macro_rules! impl_eig_generalized_work_r { + ($f:ty, $c:ty, $ev:path) => { + impl EigGeneralizedWorkImpl for EigGeneralizedWork<$f> { + type Elem = $f; + + fn new(calc_v: bool, l: MatrixLayout) -> Result { + let (n, _) = l.size(); + let (jobvl, jobvr) = if calc_v { + match l { + MatrixLayout::C { .. } => (JobEv::All, JobEv::None), + MatrixLayout::F { .. } => (JobEv::None, JobEv::All), + } + } else { + (JobEv::None, JobEv::None) + }; + let mut alpha_re = vec_uninit(n as usize); + let mut alpha_im = vec_uninit(n as usize); + let mut beta_re = vec_uninit(n as usize); + let mut vr_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let mut vr_r = jobvr.then(|| vec_uninit((n * n) as usize)); + let vc_l = jobvl.then(|| vec_uninit((n * n) as usize)); + let vc_r = jobvr.then(|| vec_uninit((n * n) as usize)); + + // calc work size + let mut info = 0; + let mut work_size: [$f; 1] = [0.0]; + unsafe { + $ev( + jobvl.as_ptr(), + jobvr.as_ptr(), + &n, + std::ptr::null_mut(), + &n, + std::ptr::null_mut(), + &n, + AsPtr::as_mut_ptr(&mut alpha_re), + AsPtr::as_mut_ptr(&mut alpha_im), + AsPtr::as_mut_ptr(&mut beta_re), + AsPtr::as_mut_ptr(vr_l.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(vr_r.as_deref_mut().unwrap_or(&mut [])), + &n, + AsPtr::as_mut_ptr(&mut work_size), + &(-1), + &mut info, + ) + }; + info.as_lapack_result()?; + + // actual ev + let lwork = work_size[0].to_usize().unwrap(); + let work = vec_uninit(lwork); + + Ok(Self { + n, + jobvr, + jobvl, + alpha: vec_uninit(n as usize), + beta: vec_uninit(n as usize), + alpha_re: Some(alpha_re), + alpha_im: Some(alpha_im), + beta_re: Some(beta_re), + beta_im: None, + rwork: None, + vr_l, + vr_r, + vc_l, + vc_r, + work, + }) + } + + fn calc<'work>( + &'work mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let lwork = self.work.len().to_i32().unwrap(); + let mut info = 0; + unsafe { + $ev( + self.jobvl.as_ptr(), + self.jobvr.as_ptr(), + &self.n, + AsPtr::as_mut_ptr(a), + &self.n, + AsPtr::as_mut_ptr(b), + &self.n, + AsPtr::as_mut_ptr(self.alpha_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.alpha_im.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.beta_re.as_mut().unwrap()), + AsPtr::as_mut_ptr(self.vr_l.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(self.vr_r.as_deref_mut().unwrap_or(&mut [])), + &self.n, + AsPtr::as_mut_ptr(&mut self.work), + &lwork, + &mut info, + ) + }; + info.as_lapack_result()?; + + let alpha_re = self + .alpha_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let alpha_im = self + .alpha_im + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + let beta_re = self + .beta_re + .as_ref() + .map(|e| unsafe { e.slice_assume_init_ref() }) + .unwrap(); + reconstruct_eigs_optional_im(alpha_re, Some(alpha_im), &mut self.alpha); + reconstruct_eigs_optional_im(beta_re, None, &mut self.beta); + + if let Some(v) = self.vr_l.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(true, alpha_im, v, self.vc_l.as_mut().unwrap()); + } + if let Some(v) = self.vr_r.as_ref() { + let v = unsafe { v.slice_assume_init_ref() }; + reconstruct_eigenvectors(false, alpha_im, v, self.vc_r.as_mut().unwrap()); + } + + Ok(EigGeneralizedRef { + alpha: unsafe { self.alpha.slice_assume_init_ref() }, + beta: unsafe { self.beta.slice_assume_init_ref() }, + vl: self + .vc_l + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + vr: self + .vc_r + .as_ref() + .map(|v| unsafe { v.slice_assume_init_ref() }), + }) + } + + fn eval( + mut self, + a: &mut [Self::Elem], + b: &mut [Self::Elem], + ) -> Result> { + let _eig_generalized_ref = self.calc(a, b)?; + Ok(EigGeneralizedOwned { + alpha: unsafe { self.alpha.assume_init() }, + beta: unsafe { self.beta.assume_init() }, + vl: self.vc_l.map(|v| unsafe { v.assume_init() }), + vr: self.vc_r.map(|v| unsafe { v.assume_init() }), + }) + } + } + + impl EigGeneralizedOwned<$f> { + pub fn calc_eigs(&self, thresh_opt: Option<$f>) -> Vec> { + self.alpha + .iter() + .zip(self.beta.iter()) + .map(|(alpha, beta)| { + if let Some(thresh) = thresh_opt { + if beta.abs() < thresh { + GeneralizedEigenvalue::Indeterminate((alpha.clone(), beta.clone())) + } else { + GeneralizedEigenvalue::Finite( + alpha / beta, + (alpha.clone(), beta.clone()), + ) + } + } else { + if beta.is_zero() { + GeneralizedEigenvalue::Indeterminate((alpha.clone(), beta.clone())) + } else { + GeneralizedEigenvalue::Finite( + alpha / beta, + (alpha.clone(), beta.clone()), + ) + } + } + }) + .collect::>() + } + } + }; +} +impl_eig_generalized_work_r!(f32, c32, lapack_sys::sggev_); +impl_eig_generalized_work_r!(f64, c64, lapack_sys::dggev_); + +/// Create complex eigenvalues from real and optional imaginary parts. +fn reconstruct_eigs_optional_im( + re: &[T], + im_opt: Option<&[T]>, + eigs: &mut [MaybeUninit], +) { + let n = eigs.len(); + assert_eq!(re.len(), n); + + if let Some(im) = im_opt { + assert_eq!(im.len(), n); + for i in 0..n { + eigs[i].write(T::complex(re[i], im[i])); + } + } else { + for i in 0..n { + eigs[i].write(T::complex(re[i], T::zero())); + } + } +} diff --git a/lax/src/lib.rs b/lax/src/lib.rs index af48f257..680ff0db 100644 --- a/lax/src/lib.rs +++ b/lax/src/lib.rs @@ -62,6 +62,7 @@ //! there are several types of eigenvalue problem API //! //! - [eig] module for eigenvalue problem for general matrix. +//! - [eig_generalized] module for generalized eigenvalue problem for general matrix. //! - [eigh] module for eigenvalue problem for symmetric/Hermitian matrix. //! - [eigh_generalized] module for generalized eigenvalue problem for symmetric/Hermitian matrix. //! @@ -87,6 +88,7 @@ extern crate netlib_src as _src; pub mod alloc; pub mod cholesky; pub mod eig; +pub mod eig_generalized; pub mod eigh; pub mod eigh_generalized; pub mod error; @@ -103,6 +105,8 @@ pub mod svddc; pub mod triangular; pub mod tridiagonal; +pub use crate::eig_generalized::GeneralizedEigenvalue; + pub use self::flags::*; pub use self::least_squares::LeastSquaresOwned; pub use self::svd::{SvdOwned, SvdRef}; @@ -124,6 +128,18 @@ pub trait Lapack: Scalar { a: &mut [Self], ) -> Result<(Vec, Vec)>; + /// Compute right eigenvalue and eigenvectors for a general matrix + fn eig_generalized( + calc_v: bool, + l: MatrixLayout, + a: &mut [Self], + b: &mut [Self], + thresh_opt: Option, + ) -> Result<( + Vec>, + Vec, + )>; + /// Compute right eigenvalue and eigenvectors for a symmetric or Hermitian matrix fn eigh( calc_eigenvec: bool, @@ -331,6 +347,25 @@ macro_rules! impl_lapack { Ok((eigs, vr.or(vl).unwrap_or_default())) } + fn eig_generalized( + calc_v: bool, + l: MatrixLayout, + a: &mut [Self], + b: &mut [Self], + thresh_opt: Option, + ) -> Result<( + Vec>, + Vec, + )> { + use eig_generalized::*; + let work = EigGeneralizedWork::<$s>::new(calc_v, l)?; + let eig_generalized_owned = work.eval(a, b)?; + let eigs = eig_generalized_owned.calc_eigs(thresh_opt); + let vr = eig_generalized_owned.vr; + let vl = eig_generalized_owned.vl; + Ok((eigs, vr.or(vl).unwrap_or_default())) + } + fn eigh( calc_eigenvec: bool, layout: MatrixLayout, diff --git a/ndarray-linalg/benches/eig_generalized.rs b/ndarray-linalg/benches/eig_generalized.rs new file mode 100644 index 00000000..d1f5621b --- /dev/null +++ b/ndarray-linalg/benches/eig_generalized.rs @@ -0,0 +1,40 @@ +use criterion::*; +use ndarray::*; +use ndarray_linalg::*; + +fn eig_generalized_small(c: &mut Criterion) { + let mut group = c.benchmark_group("eig"); + for &n in &[4, 8, 16, 32, 64, 128] { + group.bench_with_input(BenchmarkId::new("vecs/C/r", n), &n, |c, n| { + let a: Array2 = random((*n, *n)); + let b: Array2 = random((*n, *n)); + c.iter(|| { + let (_e, _vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + }) + }); + group.bench_with_input(BenchmarkId::new("vecs/F/r", n), &n, |c, n| { + let a: Array2 = random((*n, *n).f()); + let b: Array2 = random((*n, *n).f()); + c.iter(|| { + let (_e, _vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + }) + }); + group.bench_with_input(BenchmarkId::new("vecs/C/c", n), &n, |c, n| { + let a: Array2 = random((*n, *n)); + let b: Array2 = random((*n, *n)); + c.iter(|| { + let (_e, _vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + }) + }); + group.bench_with_input(BenchmarkId::new("vecs/F/c", n), &n, |c, n| { + let a: Array2 = random((*n, *n).f()); + let b: Array2 = random((*n, *n).f()); + c.iter(|| { + let (_e, _vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + }) + }); + } +} + +criterion_group!(eig, eig_generalized_small); +criterion_main!(eig); diff --git a/ndarray-linalg/src/eig.rs b/ndarray-linalg/src/eig.rs index bde5bd7d..03e3ee03 100644 --- a/ndarray-linalg/src/eig.rs +++ b/ndarray-linalg/src/eig.rs @@ -3,6 +3,7 @@ use crate::error::*; use crate::layout::*; use crate::types::*; +pub use lax::GeneralizedEigenvalue; use ndarray::*; #[cfg_attr(doc, katexit::katexit)] @@ -77,3 +78,86 @@ where Ok(ArrayBase::from(s)) } } + +#[cfg_attr(doc, katexit::katexit)] +/// Eigenvalue decomposition of general matrix reference +pub trait EigGeneralized { + /// EigVec is the right eivenvector + type EigVal; + type EigVec; + type Real; + /// Calculate eigenvalues with the right eigenvector + /// + /// $$ A u_i = \lambda_i B u_i $$ + /// + /// ``` + /// use ndarray::*; + /// use ndarray_linalg::*; + /// + /// let a: Array2 = array![ + /// [-1.01, 0.86, -4.60, 3.31, -4.81], + /// [ 3.98, 0.53, -7.04, 5.29, 3.55], + /// [ 3.30, 8.26, -3.89, 8.20, -1.51], + /// [ 4.43, 4.96, -7.66, -7.33, 6.18], + /// [ 7.31, -6.43, -6.16, 2.47, 5.58], + /// ]; + /// let b: Array2 = array![ + /// [ 1.23, -4.56, 7.89, 0.12, -3.45], + /// [ 6.78, -9.01, 2.34, -5.67, 8.90], + /// [-1.11, 3.33, -6.66, 9.99, -2.22], + /// [ 4.44, -7.77, 0.00, 1.11, 5.55], + /// [-8.88, 6.66, -3.33, 2.22, -9.99], + /// ]; + /// let (geneigs, vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + /// + /// let a = a.map(|v| v.as_c()); + /// let b = b.map(|v| v.as_c()); + /// for (ge, vec) in geneigs.iter().zip(vecs.axis_iter(Axis(1))) { + /// if let GeneralizedEigenvalue::Finite(e, _) = ge { + /// let ebv = b.dot(&vec).map(|v| v * e); + /// let av = a.dot(&vec); + /// assert_close_l2!(&av, &ebv, 1e-5); + /// } + /// } + /// ``` + /// + /// # Arguments + /// + /// * `thresh_opt` - An optional threshold for determining approximate zero |β| values when + /// computing the eigenvalues as α/β. If `None`, no approximate comparisons to zero will be + /// made. + fn eig_generalized( + &self, + thresh_opt: Option, + ) -> Result<(Self::EigVal, Self::EigVec)>; +} + +impl EigGeneralized for (ArrayBase, ArrayBase) +where + A: Scalar + Lapack, + S: Data, +{ + type EigVal = Array1>; + type EigVec = Array2; + type Real = A::Real; + + fn eig_generalized( + &self, + thresh_opt: Option, + ) -> Result<(Self::EigVal, Self::EigVec)> { + let (mut a, mut b) = (self.0.to_owned(), self.1.to_owned()); + let layout = a.square_layout()?; + let (s, t) = A::eig_generalized( + true, + layout, + a.as_allocated_mut()?, + b.as_allocated_mut()?, + thresh_opt, + )?; + let n = layout.len() as usize; + Ok(( + ArrayBase::from(s), + Array2::from_shape_vec((n, n).f(), t).unwrap(), + )) + } +} diff --git a/ndarray-linalg/tests/eig_generalized.rs b/ndarray-linalg/tests/eig_generalized.rs new file mode 100644 index 00000000..06df81ec --- /dev/null +++ b/ndarray-linalg/tests/eig_generalized.rs @@ -0,0 +1,190 @@ +use ndarray::*; +use ndarray_linalg::*; + +#[test] +fn generalized_eigenvalue_fmt() { + let ge0 = GeneralizedEigenvalue::Finite(0.1, (1.0, 10.0)); + assert_eq!(ge0.to_string(), "1.000e-1 (1.000e0/1.000e1)".to_string()); + + let ge1 = GeneralizedEigenvalue::Indeterminate((1.0, 0.0)); + assert_eq!(ge1.to_string(), "∞ (1.000e0/0.000e0)".to_string()); +} + +#[test] +fn real_a_real_b_3x3_full_rank() { + #[rustfmt::skip] + let a = array![ + [ 2.0, 1.0, 8.0], + [-2.0, 0.0, 3.0], + [ 7.0, 6.0, 5.0], + ]; + #[rustfmt::skip] + let b = array![ + [ 1.0, 2.0, -7.0], + [-3.0, 1.0, 6.0], + [ 4.0, -5.0, 1.0], + ]; + let (geneigvals, eigvecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + + let a = a.map(|v| v.as_c()); + let b = b.map(|v| v.as_c()); + for (ge, vec) in geneigvals.iter().zip(eigvecs.columns()) { + if let GeneralizedEigenvalue::Finite(e, _) = ge { + let ebv = b.dot(&vec).map(|v| v * e); + let av = a.dot(&vec); + assert_close_l2!(&av, &ebv, 1e-7); + } + } + + let mut eigvals = geneigvals + .iter() + .filter_map(|ge: &GeneralizedEigenvalue| match ge { + GeneralizedEigenvalue::Finite(e, _) => Some(e.clone()), + GeneralizedEigenvalue::Indeterminate(_) => None, + }) + .collect::>(); + eigvals.sort_by(|a, b| a.re().partial_cmp(&b.re()).unwrap()); + let eigvals = Array1::from_vec(eigvals); + // Reference eigenvalues from Mathematica + assert_close_l2!( + &eigvals, + &array![-0.4415795111, 0.5619249537, 50.87965456].map(c64::from), + 1e-7 + ); +} + +#[test] +fn real_a_real_b_3x3_nullity_1() { + #[rustfmt::skip] + let a = array![ + [ 2.0, 1.0, 8.0], + [-2.0, 0.0, 3.0], + [ 7.0, 6.0, 5.0], + ]; + #[rustfmt::skip] + let b = array![ + [1.0, 2.0, 3.0], + [0.0, 1.0, 1.0], + [1.0, -1.0, 0.0], + ]; + let (geneigvals, eigvecs) = (a.clone(), b.clone()).eig_generalized(Some(1e-4)).unwrap(); + + let a = a.map(|v| v.as_c()); + let b = b.map(|v| v.as_c()); + for (ge, vec) in geneigvals.iter().zip(eigvecs.columns()) { + if let GeneralizedEigenvalue::Finite(e, _) = ge { + let ebv = b.dot(&vec).map(|v| v * e); + let av = a.dot(&vec); + assert_close_l2!(&av, &ebv, 1e-7); + } + } + + let mut eigvals = geneigvals + .iter() + .filter_map(|ge: &GeneralizedEigenvalue| match ge { + GeneralizedEigenvalue::Finite(e, _) => Some(e.clone()), + GeneralizedEigenvalue::Indeterminate(_) => None, + }) + .collect::>(); + eigvals.sort_by(|a, b| a.re().partial_cmp(&b.re()).unwrap()); + let eigvals = Array1::from_vec(eigvals); + // Reference eigenvalues from Mathematica + assert_close_l2!( + &eigvals, + &array![-12.91130192, 3.911301921].map(c64::from), + 1e-7 + ); +} + +#[test] +fn complex_a_complex_b_3x3_full_rank() { + #[rustfmt::skip] + let a = array![ + [c64::new(1.0, 2.0), c64::new(-3.0, 0.5), c64::new( 0.0, -1.0)], + [c64::new(2.5, -4.0), c64::new( 1.0, 1.0), c64::new(-1.5, 2.5)], + [c64::new(0.0, 0.0), c64::new( 3.0, -2.0), c64::new( 4.0, 4.0)], + ]; + #[rustfmt::skip] + let b = array![ + [c64::new(-2.0, 1.0), c64::new( 3.5, -1.0), c64::new( 1.0, 1.0)], + [c64::new( 0.0, -3.0), c64::new( 2.0, 2.0), c64::new(-4.0, 0.0)], + [c64::new( 5.0, 5.0), c64::new(-1.5, 1.5), c64::new( 0.0, -2.0)], + ]; + let (geneigvals, eigvecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap(); + + let a = a.map(|v| v.as_c()); + let b = b.map(|v| v.as_c()); + for (ge, vec) in geneigvals.iter().zip(eigvecs.columns()) { + if let GeneralizedEigenvalue::Finite(e, _) = ge { + let ebv = b.dot(&vec).map(|v| v * e); + let av = a.dot(&vec); + assert_close_l2!(&av, &ebv, 1e-7); + } + } + + let mut eigvals = geneigvals + .iter() + .filter_map(|ge: &GeneralizedEigenvalue| match ge { + GeneralizedEigenvalue::Finite(e, _) => Some(e.clone()), + GeneralizedEigenvalue::Indeterminate(_) => None, + }) + .collect::>(); + eigvals.sort_by(|a, b| a.re().partial_cmp(&b.re()).unwrap()); + let eigvals = Array1::from_vec(eigvals); + // Reference eigenvalues from Mathematica + assert_close_l2!( + &eigvals, + &array![ + c64::new(-0.701598, -1.71262), + c64::new(-0.67899, -0.0172468), + c64::new(0.59059, 0.276034) + ], + 1e-5 + ); +} + +#[test] +fn complex_a_complex_b_3x3_nullity_1() { + #[rustfmt::skip] + let a = array![ + [c64::new(1.0, 2.0), c64::new(-3.0, 0.5), c64::new( 0.0, -1.0)], + [c64::new(2.5, -4.0), c64::new( 1.0, 1.0), c64::new(-1.5, 2.5)], + [c64::new(0.0, 0.0), c64::new( 3.0, -2.0), c64::new( 4.0, 4.0)], + ]; + #[rustfmt::skip] + let b = array![ + [c64::new(-2.55604, -4.10176), c64::new(9.03944, 3.745000), c64::new(35.4641, 21.1704)], + [c64::new( 7.85029, 7.02144), c64::new(9.23225, -0.479451), c64::new(13.9507, -16.5402)], + [c64::new(-4.47803, 3.98981), c64::new(9.44434, -4.519970), c64::new(40.9006, -23.5060)], + ]; + let (geneigvals, eigvecs) = (a.clone(), b.clone()).eig_generalized(Some(1e-4)).unwrap(); + + let a = a.map(|v| v.as_c()); + let b = b.map(|v| v.as_c()); + for (ge, vec) in geneigvals.iter().zip(eigvecs.columns()) { + if let GeneralizedEigenvalue::Finite(e, _) = ge { + let ebv = b.dot(&vec).map(|v| v * e); + let av = a.dot(&vec); + assert_close_l2!(&av, &ebv, 1e-7); + } + } + + let mut eigvals = geneigvals + .iter() + .filter_map(|ge: &GeneralizedEigenvalue| match ge { + GeneralizedEigenvalue::Finite(e, _) => Some(e.clone()), + GeneralizedEigenvalue::Indeterminate(_) => None, + }) + .collect::>(); + eigvals.sort_by(|a, b| a.re().partial_cmp(&b.re()).unwrap()); + let eigvals = Array1::from_vec(eigvals); + // Reference eigenvalues from Mathematica + assert_close_l2!( + &eigvals, + &array![ + c64::new(-0.0620674, -0.270016), + c64::new(0.0218236, 0.0602709), + ], + 1e-5 + ); +} From f1098e63a3331cfa17e14e88d66c79f9065aa32f Mon Sep 17 00:00:00 2001 From: Nigel Delaney Date: Thu, 7 Aug 2025 11:13:15 -0700 Subject: [PATCH 165/165] fix: Add Helping Verb to README Because we all get by with a little help from our friends. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4478dca3..68c81b0c 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Supported features are following: ### For library developer -If you creating a library depending on this crate, we encourage you not to link any backend: +If you are creating a library depending on this crate, we encourage you not to link any backend: ```toml [dependencies]