From 256e5f715e3be3c5544a2e663bd3a8b80ba0c774 Mon Sep 17 00:00:00 2001 From: Benjamin Kay Date: Fri, 12 Feb 2021 23:02:24 -0600 Subject: [PATCH 001/225] Workaround for issue with ndarray_linalg trace() --- ndarray-linalg/src/trace.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ndarray-linalg/src/trace.rs b/ndarray-linalg/src/trace.rs index 3020a9a5..feb119f2 100644 --- a/ndarray-linalg/src/trace.rs +++ b/ndarray-linalg/src/trace.rs @@ -4,7 +4,6 @@ use ndarray::*; use std::iter::Sum; use super::error::*; -use super::layout::*; use super::types::*; pub trait Trace { @@ -20,7 +19,13 @@ where type Output = A; fn trace(&self) -> Result { - let (n, _) = self.square_layout()?.size(); + let n = match self.is_square() { + true => Ok(self.nrows()), + false => Err(LinalgError::NotSquare { + rows: self.nrows() as i32, + cols: self.ncols() as i32, + }), + }?; Ok((0..n as usize).map(|i| self[(i, i)]).sum()) } } From de7988604d57b90c47cd82c3290ce8c95da8a12b Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 20 Feb 2021 23:52:05 +0900 Subject: [PATCH 002/225] Update changelog for #263 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15ccf81..ec159d27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Unreleased ----------- +Changed +-------- +- Not require continious layout for trace https://github.com/rust-ndarray/ndarray-linalg/pull/263 + 0.13.0 - 20 Feb 2021 ===================== From bcf61d46111d9bf2b49f74caf83ebb82118ccf31 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 15:39:17 +0900 Subject: [PATCH 003/225] Move katex-header into ndarray-linalg crate region --- katex-header.html => ndarray-linalg/katex-header.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename katex-header.html => ndarray-linalg/katex-header.html (100%) diff --git a/katex-header.html b/ndarray-linalg/katex-header.html similarity index 100% rename from katex-header.html rename to ndarray-linalg/katex-header.html From 343e2c964e85d5d1a78fa8ed6c6db2cf33542534 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 15:40:19 +0900 Subject: [PATCH 004/225] (cargo-release) version 0.13.1-alpha.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 63fa3e86..252fb810 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.13.0" +version = "0.13.1-alpha.1" authors = ["Toshiki Teramura "] edition = "2018" From af5ba12f07642c21ddf6531e0db5cd87ebe5b3e5 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 15:57:36 +0900 Subject: [PATCH 005/225] (cargo-release) version 0.13.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 252fb810..559cec06 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.13.1-alpha.1" +version = "0.13.1" authors = ["Toshiki Teramura "] edition = "2018" From 5d38f5e286ab83132d115c4f12fb17fdf5e4cdf7 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 15:58:56 +0900 Subject: [PATCH 006/225] Update CHANGELOG for 0.13.1 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec159d27..9cbfaba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,17 @@ Unreleased ----------- +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 ===================== From b526f30094fa9d1bd8140c7ae652213f9b6b1792 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 16:03:41 +0900 Subject: [PATCH 007/225] Copy header for KaTeX integration for lax crate --- lax/katex-header.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 lax/katex-header.html diff --git a/lax/katex-header.html b/lax/katex-header.html new file mode 100644 index 00000000..6e10c052 --- /dev/null +++ b/lax/katex-header.html @@ -0,0 +1,16 @@ + + + + + From 66dd86f23397b646300c5370ace362461a488969 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 16:04:15 +0900 Subject: [PATCH 008/225] Add docs.rs section --- lax/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index 812ad126..fccf952a 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -51,5 +51,8 @@ optional = true default-features = false features = ["cblas"] +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", "katex-header.html"] + [package.metadata.release] no-dev-version = true From 7925aa6c6fd123b7cf4aa89c39800201fe396dbb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 16:04:59 +0900 Subject: [PATCH 009/225] Fix katex-header path in CI --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index e63fc81f..fce128da 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v2 - name: Generate code coverage run: | - RUSTDOCFLAGS="--html-in-header katex-header.html" cargo doc --no-deps + 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 From a22c83602a9c679e29aac0ce8482422ab765a554 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 13 Mar 2021 16:06:32 +0900 Subject: [PATCH 010/225] Fix minor format spec of rustfmt --- ndarray-linalg/src/solve.rs | 6 ++++-- ndarray-linalg/src/solveh.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index 2277d227..dc71bf66 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -468,7 +468,8 @@ where self.ensure_square()?; match self.factorize() { Ok(fac) => fac.sln_det(), - Err(LinalgError::Lapack(e)) if matches!(e, lax::error::Error::LapackComputationalFailure {..}) => + Err(LinalgError::Lapack(e)) + if matches!(e, lax::error::Error::LapackComputationalFailure { .. }) => { // The determinant is zero. Ok((A::zero(), A::Real::neg_infinity())) @@ -487,7 +488,8 @@ where self.ensure_square()?; match self.factorize_into() { Ok(fac) => fac.sln_det_into(), - Err(LinalgError::Lapack(e)) if matches!(e, lax::error::Error::LapackComputationalFailure { .. }) => + Err(LinalgError::Lapack(e)) + if matches!(e, lax::error::Error::LapackComputationalFailure { .. }) => { // The determinant is zero. Ok((A::zero(), A::Real::neg_infinity())) diff --git a/ndarray-linalg/src/solveh.rs b/ndarray-linalg/src/solveh.rs index c70138d4..fcfc6933 100644 --- a/ndarray-linalg/src/solveh.rs +++ b/ndarray-linalg/src/solveh.rs @@ -426,7 +426,8 @@ where fn sln_deth(&self) -> Result<(A::Real, A::Real)> { match self.factorizeh() { Ok(fac) => Ok(fac.sln_deth()), - Err(LinalgError::Lapack(e)) if matches!(e, lax::error::Error::LapackComputationalFailure {..}) => + Err(LinalgError::Lapack(e)) + if matches!(e, lax::error::Error::LapackComputationalFailure { .. }) => { // Determinant is zero. Ok((A::Real::zero(), A::Real::neg_infinity())) @@ -451,7 +452,8 @@ where fn sln_deth_into(self) -> Result<(A::Real, A::Real)> { match self.factorizeh_into() { Ok(fac) => Ok(fac.sln_deth_into()), - Err(LinalgError::Lapack(e)) if matches!(e, lax::error::Error::LapackComputationalFailure {..}) => + Err(LinalgError::Lapack(e)) + if matches!(e, lax::error::Error::LapackComputationalFailure { .. }) => { // Determinant is zero. Ok((A::Real::zero(), A::Real::neg_infinity())) From a23224f6550b345440c7fef60847a9142fb7fb92 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Mon, 5 Apr 2021 14:51:28 +0200 Subject: [PATCH 011/225] Relax type bounds for LeastSquaresSvd family --- ndarray-linalg/src/least_squares.rs | 99 +++++++++++++++++++---------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/ndarray-linalg/src/least_squares.rs b/ndarray-linalg/src/least_squares.rs index 03583a25..25e700e4 100644 --- a/ndarray-linalg/src/least_squares.rs +++ b/ndarray-linalg/src/least_squares.rs @@ -149,12 +149,13 @@ where /// Solve least squares for immutable references and a single /// column vector as a right-hand side. -/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any -/// valid representation for `ArrayBase`. -impl LeastSquaresSvd for ArrayBase +/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D1`, `D2` can be any +/// valid representation for `ArrayBase` (over `E`). +impl LeastSquaresSvd for ArrayBase where E: Scalar + Lapack, - D: Data, + D1: Data, + D2: Data, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(&rhs)`, where `rhs` is a @@ -163,7 +164,7 @@ where /// `A` and `rhs` must have the same layout, i.e. they must /// be both either row- or column-major format, otherwise a /// `IncompatibleShape` error is raised. - fn least_squares(&self, rhs: &ArrayBase) -> Result> { + fn least_squares(&self, rhs: &ArrayBase) -> Result> { let a = self.to_owned(); let b = rhs.to_owned(); a.least_squares_into(b) @@ -172,12 +173,13 @@ where /// Solve least squares for immutable references and matrix /// (=mulitipe vectors) as a right-hand side. -/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any -/// valid representation for `ArrayBase`. -impl LeastSquaresSvd for ArrayBase +/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D1`, `D2` can be any +/// valid representation for `ArrayBase` (over `E`). +impl LeastSquaresSvd for ArrayBase where E: Scalar + Lapack, - D: Data, + D1: Data, + D2: Data, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(&rhs)`, where `rhs` is @@ -186,7 +188,7 @@ where /// `A` and `rhs` must have the same layout, i.e. they must /// be both either row- or column-major format, otherwise a /// `IncompatibleShape` error is raised. - fn least_squares(&self, rhs: &ArrayBase) -> Result> { + fn least_squares(&self, rhs: &ArrayBase) -> Result> { let a = self.to_owned(); let b = rhs.to_owned(); a.least_squares_into(b) @@ -199,10 +201,11 @@ where /// /// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any /// valid representation for `ArrayBase`. -impl LeastSquaresSvdInto for ArrayBase +impl LeastSquaresSvdInto for ArrayBase where E: Scalar + Lapack, - D: DataMut, + D1: DataMut, + D2: DataMut, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(rhs)`, where `rhs` is a @@ -213,7 +216,7 @@ where /// `IncompatibleShape` error is raised. fn least_squares_into( mut self, - mut rhs: ArrayBase, + mut rhs: ArrayBase, ) -> Result> { self.least_squares_in_place(&mut rhs) } @@ -223,12 +226,13 @@ where /// as a right-hand side. The matrix and the RHS matrix /// are consumed. /// -/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any -/// valid representation for `ArrayBase`. -impl LeastSquaresSvdInto for ArrayBase +/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D1`, `D2` can be any +/// valid representation for `ArrayBase` (over `E`). +impl LeastSquaresSvdInto for ArrayBase where E: Scalar + Lapack, - D: DataMut, + D1: DataMut, + D2: DataMut, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(rhs)`, where `rhs` is a @@ -239,7 +243,7 @@ where /// `IncompatibleShape` error is raised. fn least_squares_into( mut self, - mut rhs: ArrayBase, + mut rhs: ArrayBase, ) -> Result> { self.least_squares_in_place(&mut rhs) } @@ -249,12 +253,13 @@ where /// as a right-hand side. Both values are overwritten in the /// call. /// -/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any -/// valid representation for `ArrayBase`. -impl LeastSquaresSvdInPlace for ArrayBase +/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D1`, `D2` can be any +/// valid representation for `ArrayBase` (over `E`). +impl LeastSquaresSvdInPlace for ArrayBase where E: Scalar + Lapack, - D: DataMut, + D1: DataMut, + D2: DataMut, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(rhs)`, where `rhs` is a @@ -265,7 +270,7 @@ where /// `IncompatibleShape` error is raised. fn least_squares_in_place( &mut self, - rhs: &mut ArrayBase, + rhs: &mut ArrayBase, ) -> Result> { if self.shape()[0] != rhs.shape()[0] { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape).into()); @@ -331,12 +336,13 @@ fn compute_residual_scalar>( /// as a right-hand side. Both values are overwritten in the /// call. /// -/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D` can be any -/// valid representation for `ArrayBase`. -impl LeastSquaresSvdInPlace for ArrayBase +/// `E` is one of `f32`, `f64`, `c32`, `c64`. `D1`, `D2` can be any +/// valid representation for `ArrayBase` (over `E`). +impl LeastSquaresSvdInPlace for ArrayBase where E: Scalar + Lapack + LeastSquaresSvdDivideConquer_, - D: DataMut, + D1: DataMut, + D2: DataMut, { /// Solve a least squares problem of the form `Ax = rhs` /// by calling `A.least_squares(rhs)`, where `rhs` is a @@ -347,7 +353,7 @@ where /// `IncompatibleShape` error is raised. fn least_squares_in_place( &mut self, - rhs: &mut ArrayBase, + rhs: &mut ArrayBase, ) -> Result> { if self.shape()[0] != rhs.shape()[0] { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape).into()); @@ -425,7 +431,7 @@ mod tests { use ndarray::*; // - // Test that the different lest squares traits work as intended on the + // Test that the different least squares traits work as intended on the // different array types. // // | least_squares | ls_into | ls_in_place | @@ -437,9 +443,9 @@ mod tests { // ArrayViewMut | yes | no | yes | // - fn assert_result>( - a: &ArrayBase, - b: &ArrayBase, + fn assert_result, D2: Data>( + a: &ArrayBase, + b: &ArrayBase, res: &LeastSquaresResult, ) { assert_eq!(res.rank, 2); @@ -487,6 +493,15 @@ mod tests { assert_result(&av, &bv, &res); } + #[test] + fn on_cow_view() { + let a = CowArray::from(array![[1., 2.], [4., 5.], [3., 4.]]); + let b: Array1 = array![1., 2., 3.]; + let bv = b.view(); + let res = a.least_squares(&bv).unwrap(); + assert_result(&a, &bv, &res); + } + #[test] fn into_on_owned() { let a: Array2 = array![[1., 2.], [4., 5.], [3., 4.]]; @@ -517,6 +532,16 @@ mod tests { assert_result(&a, &b, &res); } + #[test] + fn into_on_owned_cow() { + let a: Array2 = array![[1., 2.], [4., 5.], [3., 4.]]; + let b = CowArray::from(array![1., 2., 3.]); + let ac = a.clone(); + let b2 = b.clone(); + let res = ac.least_squares_into(b2).unwrap(); + assert_result(&a, &b, &res); + } + #[test] fn in_place_on_owned() { let a = array![[1., 2.], [4., 5.], [3., 4.]]; @@ -549,6 +574,16 @@ mod tests { assert_result(&a, &b, &res); } + #[test] + fn in_place_on_owned_cow() { + let a = array![[1., 2.], [4., 5.], [3., 4.]]; + let b = CowArray::from(array![1., 2., 3.]); + let mut a2 = a.clone(); + let mut b2 = b.clone(); + let res = a2.least_squares_in_place(&mut b2).unwrap(); + assert_result(&a, &b, &res); + } + // // Testing error cases // From 06af362c1bc779672f11b9aeeaa511ab047655bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Tue, 6 Apr 2021 16:09:38 +0200 Subject: [PATCH 012/225] Support ndarray 0.15 Adding `std` feature is required since ndarray 0.15 made `impl std::error::Error for ShapeError` require std. Bump MSRV to 1.49 in linux-container CI to match the one of ndarray (1.43 was giving errors in ndarray). --- .github/workflows/intel-mkl.yml | 2 +- ndarray-linalg/Cargo.toml | 4 ++-- ndarray-linalg/src/cholesky.rs | 4 ++-- ndarray-linalg/src/solve.rs | 2 +- ndarray-linalg/src/solveh.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/intel-mkl.yml b/.github/workflows/intel-mkl.yml index e7e06f2f..fec201c1 100644 --- a/.github/workflows/intel-mkl.yml +++ b/.github/workflows/intel-mkl.yml @@ -34,7 +34,7 @@ jobs: linux-container: runs-on: ubuntu-18.04 - container: rustmath/mkl-rust:1.43.0 + container: rustmath/mkl-rust:1.49.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 559cec06..a0823da1 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -36,8 +36,8 @@ rand = "0.7.3" thiserror = "1.0.20" [dependencies.ndarray] -version = "0.14" -features = ["blas", "approx"] +version = ">=0.14,<0.16" +features = ["blas", "approx", "std"] default-features = false [dependencies.lax] diff --git a/ndarray-linalg/src/cholesky.rs b/ndarray-linalg/src/cholesky.rs index 8ce0da84..58cc5cee 100644 --- a/ndarray-linalg/src/cholesky.rs +++ b/ndarray-linalg/src/cholesky.rs @@ -26,7 +26,7 @@ //! //! // Obtain `L` //! let lower = a.cholesky(UPLO::Lower).unwrap(); -//! assert!(lower.all_close(&array![ +//! assert!(lower.abs_diff_eq(&array![ //! [ 2., 0., 0.], //! [ 6., 1., 0.], //! [-8., 5., 3.] @@ -39,7 +39,7 @@ //! // Solve `A * x = b` //! let b = array![4., 13., -11.]; //! let x = a.solvec(&b).unwrap(); -//! assert!(x.all_close(&array![-2., 1., 0.], 1e-9)); +//! assert!(x.abs_diff_eq(&array![-2., 1., 0.], 1e-9)); //! # } //! ``` diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index dc71bf66..7e695a8c 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -16,7 +16,7 @@ //! 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.all_close(&array![1., -2., -2.], 1e-9)); +//! assert!(x.abs_diff_eq(&array![1., -2., -2.], 1e-9)); //! //! # } //! ``` diff --git a/ndarray-linalg/src/solveh.rs b/ndarray-linalg/src/solveh.rs index fcfc6933..26329509 100644 --- a/ndarray-linalg/src/solveh.rs +++ b/ndarray-linalg/src/solveh.rs @@ -23,7 +23,7 @@ //! ]; //! let b: Array1 = array![11., -12., 1.]; //! let x = a.solveh_into(b).unwrap(); -//! assert!(x.all_close(&array![1., 3., -2.], 1e-9)); +//! assert!(x.abs_diff_eq(&array![1., 3., -2.], 1e-9)); //! //! # } //! ``` From 62a1b3c9767cb37e3e7c11109d16af1f59774230 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Apr 2021 13:46:38 +0900 Subject: [PATCH 013/225] Update lax dependencies --- lax/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lax/Cargo.toml b/lax/Cargo.toml index fccf952a..a8e74044 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -29,10 +29,10 @@ intel-mkl-static = ["intel-mkl-src/mkl-static-lp64-seq", "intel-mkl-src/download intel-mkl-system = ["intel-mkl-src/mkl-dynamic-lp64-seq"] [dependencies] -thiserror = "1.0.23" -cauchy = "0.3.0" +thiserror = "1.0.24" +cauchy = "0.4.0" num-traits = "0.2.14" -lapack = "0.17.0" +lapack = "0.18.0" [dependencies.intel-mkl-src] version = "0.6.0" @@ -46,7 +46,7 @@ features = ["cblas"] default-features = false [dependencies.openblas-src] -version = "0.10.2" +version = "0.10.4" optional = true default-features = false features = ["cblas"] From c91862f5e1ade69e4db58dc7cfdcc44f9ed7c17e Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Apr 2021 13:49:40 +0900 Subject: [PATCH 014/225] Upgrade ndarray-linalg dependencies --- ndarray-linalg/Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index a0823da1..1c3ab554 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -29,14 +29,14 @@ intel-mkl-static = ["lax/intel-mkl-static"] intel-mkl-system = ["lax/intel-mkl-system"] [dependencies] -cauchy = "0.3.0" -num-complex = "0.3.1" -num-traits = "0.2.11" -rand = "0.7.3" -thiserror = "1.0.20" +cauchy = "0.4.0" +num-complex = "0.4.0" +num-traits = "0.2.14" +rand = "0.8.3" +thiserror = "1.0.24" [dependencies.ndarray] -version = ">=0.14,<0.16" +version = "0.15.1" features = ["blas", "approx", "std"] default-features = false @@ -46,10 +46,10 @@ path = "../lax" default-features = false [dev-dependencies] -paste = "1.0" -criterion = "0.3" +paste = "1.0.5" +criterion = "0.3.4" # Keep the same version as ndarray's dependency! -approx = { version = "0.4", features = ["num-complex"] } +approx = { version = "0.4.0", features = ["num-complex"] } [[bench]] name = "truncated_eig" From c760a7eb48a070e70bba849e54077a1a9eb678c4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Apr 2021 13:59:44 +0900 Subject: [PATCH 015/225] Fix renamed deprecated updates --- ndarray-linalg/src/eigh.rs | 1 - ndarray-linalg/src/lobpcg/eig.rs | 4 ++-- ndarray-linalg/src/lobpcg/lobpcg.rs | 4 ++-- ndarray-linalg/src/lobpcg/svd.rs | 4 ++-- ndarray-linalg/tests/cholesky.rs | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ndarray-linalg/src/eigh.rs b/ndarray-linalg/src/eigh.rs index 86f1fb46..580c2b7e 100644 --- a/ndarray-linalg/src/eigh.rs +++ b/ndarray-linalg/src/eigh.rs @@ -8,7 +8,6 @@ use crate::layout::*; use crate::operator::LinearOperator; use crate::types::*; use crate::UPLO; -use std::iter::FromIterator; /// Eigenvalue decomposition of Hermite matrix reference pub trait Eigh { diff --git a/ndarray-linalg/src/lobpcg/eig.rs b/ndarray-linalg/src/lobpcg/eig.rs index 0b5a60e8..e60adb04 100644 --- a/ndarray-linalg/src/lobpcg/eig.rs +++ b/ndarray-linalg/src/lobpcg/eig.rs @@ -139,9 +139,9 @@ impl Iterator // add the new eigenvector to the internal constrain matrix let new_constraints = if let Some(ref constraints) = self.eig.constraints { let eigvecs_arr: Vec<_> = constraints - .gencolumns() + .columns() .into_iter() - .chain(vecs.gencolumns().into_iter()) + .chain(vecs.columns().into_iter()) .collect(); stack(Axis(1), &eigvecs_arr).unwrap() diff --git a/ndarray-linalg/src/lobpcg/lobpcg.rs b/ndarray-linalg/src/lobpcg/lobpcg.rs index a10e5961..1bc4f2ad 100644 --- a/ndarray-linalg/src/lobpcg/lobpcg.rs +++ b/ndarray-linalg/src/lobpcg/lobpcg.rs @@ -81,7 +81,7 @@ fn apply_constraints( let gram_yv = y.t().dot(&v); let u = gram_yv - .gencolumns() + .columns() .into_iter() .map(|x| { let res = cholesky_yy.solvec(&x).unwrap(); @@ -222,7 +222,7 @@ pub fn lobpcg< // calculate L2 norm of error for every eigenvalue let residual_norms = r - .gencolumns() + .columns() .into_iter() .map(|x| x.norm()) .collect::>(); diff --git a/ndarray-linalg/src/lobpcg/svd.rs b/ndarray-linalg/src/lobpcg/svd.rs index a796364d..7a326293 100644 --- a/ndarray-linalg/src/lobpcg/svd.rs +++ b/ndarray-linalg/src/lobpcg/svd.rs @@ -64,7 +64,7 @@ impl + 'static + MagnitudeCorrection> Trunc let mut ularge = self.problem.dot(&vlarge); ularge - .gencolumns_mut() + .columns_mut() .into_iter() .zip(values.iter()) .for_each(|(mut a, b)| a.mapv_inplace(|x| x / *b)); @@ -75,7 +75,7 @@ impl + 'static + MagnitudeCorrection> Trunc let mut vlarge = self.problem.t().dot(&ularge); vlarge - .gencolumns_mut() + .columns_mut() .into_iter() .zip(values.iter()) .for_each(|(mut a, b)| a.mapv_inplace(|x| x / *b)); diff --git a/ndarray-linalg/tests/cholesky.rs b/ndarray-linalg/tests/cholesky.rs index b45afb5c..a498afc3 100644 --- a/ndarray-linalg/tests/cholesky.rs +++ b/ndarray-linalg/tests/cholesky.rs @@ -140,7 +140,7 @@ macro_rules! cholesky_det { .eigvalsh(UPLO::Upper) .unwrap() .mapv(|elem| elem.ln()) - .scalar_sum(); + .sum(); let det = ln_det.exp(); assert_aclose!(a.factorizec(UPLO::Upper).unwrap().detc(), det, $atol); assert_aclose!(a.factorizec(UPLO::Upper).unwrap().ln_detc(), ln_det, $atol); From fbac1a9abf31fc6534584f1006126c911e20ffdd Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Apr 2021 15:37:34 +0900 Subject: [PATCH 016/225] Use ghcr.io container --- .github/workflows/intel-mkl.yml | 2 +- .github/workflows/rust.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/intel-mkl.yml b/.github/workflows/intel-mkl.yml index fec201c1..2144049f 100644 --- a/.github/workflows/intel-mkl.yml +++ b/.github/workflows/intel-mkl.yml @@ -34,7 +34,7 @@ jobs: linux-container: runs-on: ubuntu-18.04 - container: rustmath/mkl-rust:1.49.0 + container: ghcr.io/rust-math/intel-mkl-src/mkl-rust:1.49.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/cargo@v1 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b1c2bc1f..f4aa30ae 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: coverage: runs-on: ubuntu-18.04 container: - image: rustmath/mkl-rust:1.43.0 + image: ghcr.io/rust-math/intel-mkl-src/mkl-rust:1.49.0 options: --security-opt seccomp=unconfined steps: - uses: actions/checkout@v2 From 59dd9f084b5a47de30d5026f5b2b28d6a129c37a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sun, 11 Apr 2021 16:03:28 +0900 Subject: [PATCH 017/225] CHANGELOG #273 #277 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cbfaba8..83b90cfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Unreleased ----------- +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/277 + 0.13.1 - 13 March 2021 ===================== From aee87d9352e6935ab769d375b939f956f4b73709 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 12 Apr 2021 01:32:43 +0900 Subject: [PATCH 018/225] Fix link in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b90cfd..853aff07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Unreleased 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/277 +- cauchy 0.4 (num-complex 0.4, rand 0.8), lapack 0.18 https://github.com/rust-ndarray/ndarray-linalg/pull/276 0.13.1 - 13 March 2021 ===================== From a561e5a9d998738b4461599c511d7795962381be Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Apr 2021 16:50:11 +0900 Subject: [PATCH 019/225] CHANGELOG of #272 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 853aff07..79972f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ 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 +Changed +-------- +- Relax type bounds for LeastSquaresSvd family https://github.com/rust-ndarray/ndarray-linalg/pull/272 + 0.13.1 - 13 March 2021 ===================== From 91b2be2c4cf08f0b9552b5077e632fbb28ef4ab1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 11 May 2021 19:57:18 -0400 Subject: [PATCH 020/225] Include EigVals::eigvals in tests --- ndarray-linalg/tests/eig.rs | 55 +++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/ndarray-linalg/tests/eig.rs b/ndarray-linalg/tests/eig.rs index 28314b8a..d5a6e635 100644 --- a/ndarray-linalg/tests/eig.rs +++ b/ndarray-linalg/tests/eig.rs @@ -2,8 +2,11 @@ use ndarray::*; use ndarray_linalg::*; // Test Av_i = e_i v_i for i = 0..n -fn test_eig(a: Array2, eigs: Array1, vecs: Array2) -where +fn test_eig( + a: ArrayView2<'_, T>, + eigs: ArrayView1<'_, T::Complex>, + vecs: ArrayView2<'_, T::Complex>, +) where T::Complex: Lapack, { println!("a\n{:+.4}", &a); @@ -205,29 +208,37 @@ macro_rules! impl_test_real { #[test] fn [<$real _eigvals >]() { let a = test_matrix_real::<$real>(); - let (e, _vecs) = a.eig().unwrap(); - assert_close_l2!(&e, &answer_eig_real::<$real>(), 1.0e-3); + let (e1, _vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + assert_close_l2!(&e1, &answer_eig_real::<$real>(), 1.0e-3); + assert_close_l2!(&e2, &answer_eig_real::<$real>(), 1.0e-3); } #[test] fn [<$real _eigvals_t>]() { let a = test_matrix_real_t::<$real>(); - let (e, _vecs) = a.eig().unwrap(); - assert_close_l2!(&e, &answer_eig_real::<$real>(), 1.0e-3); + let (e1, _vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + assert_close_l2!(&e1, &answer_eig_real::<$real>(), 1.0e-3); + assert_close_l2!(&e2, &answer_eig_real::<$real>(), 1.0e-3); } #[test] fn [<$real _eig>]() { let a = test_matrix_real::<$real>(); - let (e, vecs) = a.eig().unwrap(); - test_eig(a, e, vecs); + let (e1, vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + test_eig(a.view(), e1.view(), vecs.view()); + test_eig(a.view(), e2.view(), vecs.view()); } #[test] fn [<$real _eig_t>]() { let a = test_matrix_real_t::<$real>(); - let (e, vecs) = a.eig().unwrap(); - test_eig(a, e, vecs); + let (e1, vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + test_eig(a.view(), e1.view(), vecs.view()); + test_eig(a.view(), e2.view(), vecs.view()); } } // paste::item! @@ -243,15 +254,19 @@ macro_rules! impl_test_complex { #[test] fn [<$complex _eigvals >]() { let a = test_matrix_complex::<$complex>(); - let (e, _vecs) = a.eig().unwrap(); - assert_close_l2!(&e, &answer_eig_complex::<$complex>(), 1.0e-3); + let (e1, _vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + assert_close_l2!(&e1, &answer_eig_complex::<$complex>(), 1.0e-3); + assert_close_l2!(&e2, &answer_eig_complex::<$complex>(), 1.0e-3); } #[test] fn [<$complex _eigvals_t>]() { let a = test_matrix_complex_t::<$complex>(); - let (e, _vecs) = a.eig().unwrap(); - assert_close_l2!(&e, &answer_eig_complex::<$complex>(), 1.0e-3); + let (e1, _vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + assert_close_l2!(&e1, &answer_eig_complex::<$complex>(), 1.0e-3); + assert_close_l2!(&e2, &answer_eig_complex::<$complex>(), 1.0e-3); } #[test] @@ -271,15 +286,19 @@ macro_rules! impl_test_complex { #[test] fn [<$complex _eig>]() { let a = test_matrix_complex::<$complex>(); - let (e, vecs) = a.eig().unwrap(); - test_eig(a, e, vecs); + let (e1, vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + test_eig(a.view(), e1.view(), vecs.view()); + test_eig(a.view(), e2.view(), vecs.view()); } #[test] fn [<$complex _eig_t>]() { let a = test_matrix_complex_t::<$complex>(); - let (e, vecs) = a.eig().unwrap(); - test_eig(a, e, vecs); + let (e1, vecs) = a.eig().unwrap(); + let e2 = a.eigvals().unwrap(); + test_eig(a.view(), e1.view(), vecs.view()); + test_eig(a.view(), e2.view(), vecs.view()); } } // paste::item! }; From 6eb1d85cde8e1775a13556e795224804b6be4ce8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 11 May 2021 19:57:59 -0400 Subject: [PATCH 021/225] Avoid calculating eigenvectors unless requested --- ndarray-linalg/src/eig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/src/eig.rs b/ndarray-linalg/src/eig.rs index 17f5a1e8..ad9ac685 100644 --- a/ndarray-linalg/src/eig.rs +++ b/ndarray-linalg/src/eig.rs @@ -72,7 +72,7 @@ where fn eigvals(&self) -> Result { let mut a = self.to_owned(); - let (s, _) = A::eig(true, a.square_layout()?, a.as_allocated_mut()?)?; + let (s, _) = A::eig(false, a.square_layout()?, a.as_allocated_mut()?)?; Ok(ArrayBase::from(s)) } } From 484cabe148cf70cd50c4a43cf6c61e943de9ca78 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 11 May 2021 20:13:53 -0400 Subject: [PATCH 022/225] Relax bound on EigVals from DataMut to Data --- ndarray-linalg/src/eig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-linalg/src/eig.rs b/ndarray-linalg/src/eig.rs index ad9ac685..833f6d49 100644 --- a/ndarray-linalg/src/eig.rs +++ b/ndarray-linalg/src/eig.rs @@ -66,7 +66,7 @@ pub trait EigVals { impl EigVals for ArrayBase where A: Scalar + Lapack, - S: DataMut, + S: Data, { type EigVal = Array1; From b0d85290cf48e1475ffdbe9f1a662919ff5ad58a Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 13 May 2021 10:51:41 +0200 Subject: [PATCH 023/225] Port usage of uninitialized to build_uninit build_uninit is necessary for abstracting over any kind of owned array. The version of take_slice_upper is still inefficient in one way (overwriting after copying), but could likely still be an improvement upon the previous version. --- ndarray-linalg/Cargo.toml | 2 +- ndarray-linalg/src/convert.rs | 37 +++++++++++++++-------------------- ndarray-linalg/src/qr.rs | 16 +++++++-------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 1c3ab554..9a7bea43 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -36,7 +36,7 @@ rand = "0.8.3" thiserror = "1.0.24" [dependencies.ndarray] -version = "0.15.1" +version = "0.15.2" features = ["blas", "approx", "std"] default-features = false diff --git a/ndarray-linalg/src/convert.rs b/ndarray-linalg/src/convert.rs index e1446e96..43002966 100644 --- a/ndarray-linalg/src/convert.rs +++ b/ndarray-linalg/src/convert.rs @@ -46,21 +46,6 @@ where } } -fn uninitialized(l: MatrixLayout) -> ArrayBase -where - A: Copy, - S: DataOwned, -{ - match l { - MatrixLayout::C { row, lda } => unsafe { - ArrayBase::uninitialized((row as usize, lda as usize)) - }, - MatrixLayout::F { col, lda } => unsafe { - ArrayBase::uninitialized((lda as usize, col as usize).f()) - }, - } -} - pub fn replicate(a: &ArrayBase) -> ArrayBase where A: Copy, @@ -68,9 +53,12 @@ where So: DataOwned + DataMut, D: Dimension, { - let mut b = unsafe { ArrayBase::uninitialized(a.dim()) }; - b.assign(a); - b + unsafe { + let ret = ArrayBase::::build_uninit(a.dim(), |view| { + a.assign_to(view); + }); + ret.assume_init() + } } fn clone_with_layout(l: MatrixLayout, a: &ArrayBase) -> ArrayBase @@ -79,9 +67,16 @@ where Si: Data, So: DataOwned + DataMut, { - let mut b = uninitialized(l); - b.assign(a); - b + let shape_builder = match l { + MatrixLayout::C { row, lda } => (row as usize, lda as usize).set_f(false), + MatrixLayout::F { col, lda } => (lda as usize, col as usize).set_f(true), + }; + unsafe { + let ret = ArrayBase::::build_uninit(shape_builder, |view| { + a.assign_to(view); + }); + ret.assume_init() + } } pub fn transpose_data(a: &mut ArrayBase) -> Result<&mut ArrayBase> diff --git a/ndarray-linalg/src/qr.rs b/ndarray-linalg/src/qr.rs index 441e7cef..4bf2f0ec 100644 --- a/ndarray-linalg/src/qr.rs +++ b/ndarray-linalg/src/qr.rs @@ -135,9 +135,7 @@ where S2: DataMut + DataOwned, { let av = a.slice(s![..n as isize, ..m as isize]); - let mut a = unsafe { ArrayBase::uninitialized((n, m)) }; - a.assign(&av); - a + replicate(&av) } fn take_slice_upper(a: &ArrayBase, n: usize, m: usize) -> ArrayBase @@ -146,10 +144,12 @@ where S1: Data, S2: DataMut + DataOwned, { - let av = a.slice(s![..n as isize, ..m as isize]); - let mut a = unsafe { ArrayBase::uninitialized((n, m)) }; - for ((i, j), val) in a.indexed_iter_mut() { - *val = if i <= j { av[(i, j)] } else { A::zero() }; - } + let av = a.slice(s![..n, ..m]); + let mut a = replicate(&av); + Zip::indexed(&mut a).for_each(|(i, j), elt| { + if i > j { + *elt = A::zero() + } + }); a } From 178b3021c2838e139e30b2c84f6bf011ce1634c0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:04:16 -0400 Subject: [PATCH 024/225] Add tests for matching shape checks in Solve --- ndarray-linalg/tests/solve.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ndarray-linalg/tests/solve.rs b/ndarray-linalg/tests/solve.rs index d069ec7a..6a612fcb 100644 --- a/ndarray-linalg/tests/solve.rs +++ b/ndarray-linalg/tests/solve.rs @@ -1,6 +1,14 @@ use ndarray::*; use ndarray_linalg::*; +#[should_panic] +#[test] +fn solve_shape_mismatch() { + let a: Array2 = random((3, 3)); + let b: Array1 = random(2); + let _ = a.solve_into(b); +} + #[test] fn solve_random() { let a: Array2 = random((3, 3)); @@ -10,6 +18,14 @@ fn solve_random() { assert_close_l2!(&x, &y, 1e-7); } +#[should_panic] +#[test] +fn solve_t_shape_mismatch() { + let a: Array2 = random((3, 3).f()); + let b: Array1 = random(4); + let _ = a.solve_into(b); +} + #[test] fn solve_random_t() { let a: Array2 = random((3, 3).f()); @@ -19,6 +35,15 @@ fn solve_random_t() { assert_close_l2!(&x, &y, 1e-7); } +#[should_panic] +#[test] +fn solve_factorized_shape_mismatch() { + let a: Array2 = random((3, 3)); + let b: Array1 = random(4); + let f = a.factorize_into().unwrap(); + let _ = f.solve_into(b); +} + #[test] fn solve_factorized() { let a: Array2 = random((3, 3)); @@ -29,6 +54,15 @@ fn solve_factorized() { assert_close_l2!(&x, &ans, 1e-7); } +#[should_panic] +#[test] +fn solve_factorized_t_shape_mismatch() { + let a: Array2 = random((3, 3).f()); + let b: Array1 = random(4); + let f = a.factorize_into().unwrap(); + let _ = f.solve_into(b); +} + #[test] fn solve_factorized_t() { let a: Array2 = random((3, 3).f()); From 866c3596b3f28bf7d2737b2795272fc26c378397 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:04:47 -0400 Subject: [PATCH 025/225] Add checks in Solve for compatible shapes Before, mismatched shapes would lead to segfaults or other memory errors. --- ndarray-linalg/src/solve.rs | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index 7e695a8c..fe7f7034 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -77,13 +77,24 @@ pub use lax::{Pivot, Transpose}; pub trait Solve { /// Solves a system of linear equations `A * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solve>(&self, b: &ArrayBase) -> Result> { let mut b = replicate(b); self.solve_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solve_into>( &self, mut b: ArrayBase, @@ -91,8 +102,14 @@ pub trait Solve { self.solve_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solve_inplace<'a, S: DataMut>( &self, b: &'a mut ArrayBase, @@ -100,13 +117,24 @@ pub trait Solve { /// Solves a system of linear equations `A^T * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_t>(&self, b: &ArrayBase) -> Result> { let mut b = replicate(b); self.solve_t_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A^T * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_t_into>( &self, mut b: ArrayBase, @@ -114,8 +142,14 @@ pub trait Solve { self.solve_t_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A^T * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_t_inplace<'a, S: DataMut>( &self, b: &'a mut ArrayBase, @@ -123,6 +157,11 @@ pub trait Solve { /// Solves a system of linear equations `A^H * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_h>(&self, b: &ArrayBase) -> Result> { let mut b = replicate(b); self.solve_h_inplace(&mut b)?; @@ -130,6 +169,11 @@ pub trait Solve { } /// Solves a system of linear equations `A^H * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_h_into>( &self, mut b: ArrayBase, @@ -139,6 +183,11 @@ pub trait Solve { } /// Solves a system of linear equations `A^H * x = b` where `A` is `self`, `b` /// is the argument, and `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of rows of + /// `A`. fn solve_h_inplace<'a, S: DataMut>( &self, b: &'a mut ArrayBase, @@ -167,6 +216,11 @@ where where Sb: DataMut, { + assert_eq!( + rhs.len(), + self.a.len_of(Axis(1)), + "The length of `rhs` must be compatible with the shape of the factored matrix.", + ); A::solve( self.a.square_layout()?, Transpose::No, @@ -183,6 +237,11 @@ where where Sb: DataMut, { + assert_eq!( + rhs.len(), + self.a.len_of(Axis(0)), + "The length of `rhs` must be compatible with the shape of the factored matrix.", + ); A::solve( self.a.square_layout()?, Transpose::Transpose, @@ -199,6 +258,11 @@ where where Sb: DataMut, { + assert_eq!( + rhs.len(), + self.a.len_of(Axis(0)), + "The length of `rhs` must be compatible with the shape of the factored matrix.", + ); A::solve( self.a.square_layout()?, Transpose::Hermite, From 2364dac9412910a2c9b952b8d3a667b6c0aac2de Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:08:59 -0400 Subject: [PATCH 026/225] Add tests for matching shape checks in SolveH --- ndarray-linalg/tests/solveh.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ndarray-linalg/tests/solveh.rs b/ndarray-linalg/tests/solveh.rs index 1074057f..56d016c4 100644 --- a/ndarray-linalg/tests/solveh.rs +++ b/ndarray-linalg/tests/solveh.rs @@ -1,6 +1,23 @@ use ndarray::*; use ndarray_linalg::*; +#[should_panic] +#[test] +fn solveh_shape_mismatch() { + let a: Array2 = random_hpd(3); + let b: Array1 = random(2); + 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 f = a.factorizeh_into().unwrap(); + let _ = f.solveh_into(b); +} + #[test] fn solveh_random() { let a: Array2 = random_hpd(3); @@ -15,6 +32,23 @@ fn solveh_random() { assert_close_l2!(&x, &y, 1e-7); } +#[should_panic] +#[test] +fn solveh_t_shape_mismatch() { + let a: Array2 = random_hpd(3).reversed_axes(); + let b: Array1 = random(2); + 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 f = a.factorizeh_into().unwrap(); + let _ = f.solveh_into(b); +} + #[test] fn solveh_random_t() { let a: Array2 = random_hpd(3).reversed_axes(); From 1df28a2c6a613ee69ad3e63bbe6e8bcb93706aa8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:10:10 -0400 Subject: [PATCH 027/225] Add check in SolveH for compatible shapes --- ndarray-linalg/src/solveh.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ndarray-linalg/src/solveh.rs b/ndarray-linalg/src/solveh.rs index 26329509..c6dccba4 100644 --- a/ndarray-linalg/src/solveh.rs +++ b/ndarray-linalg/src/solveh.rs @@ -69,14 +69,25 @@ pub trait SolveH { /// Solves a system of linear equations `A * x = b` with Hermitian (or real /// symmetric) matrix `A`, where `A` is `self`, `b` is the argument, and /// `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solveh>(&self, b: &ArrayBase) -> Result> { let mut b = replicate(b); self.solveh_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A * x = b` with Hermitian (or real /// symmetric) matrix `A`, where `A` is `self`, `b` is the argument, and /// `x` is the successful result. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solveh_into>( &self, mut b: ArrayBase, @@ -84,10 +95,16 @@ pub trait SolveH { self.solveh_inplace(&mut b)?; Ok(b) } + /// Solves a system of linear equations `A * x = b` with Hermitian (or real /// symmetric) matrix `A`, where `A` is `self`, `b` is the argument, and /// `x` is the successful result. The value of `x` is also assigned to the /// argument. + /// + /// # Panics + /// + /// Panics if the length of `b` is not the equal to the number of columns + /// of `A`. fn solveh_inplace<'a, S: DataMut>( &self, b: &'a mut ArrayBase, @@ -113,6 +130,11 @@ where where Sb: DataMut, { + assert_eq!( + rhs.len(), + self.a.len_of(Axis(1)), + "The length of `rhs` must be compatible with the shape of the factored matrix.", + ); A::solveh( self.a.square_layout()?, UPLO::Upper, From 10d7b55b0ebca461912d477765f7bfc204ddc0e7 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:20:21 -0400 Subject: [PATCH 028/225] Add test for matching shape check in EighInplace --- ndarray-linalg/tests/eigh.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ndarray-linalg/tests/eigh.rs b/ndarray-linalg/tests/eigh.rs index 77be699b..dd1445be 100644 --- a/ndarray-linalg/tests/eigh.rs +++ b/ndarray-linalg/tests/eigh.rs @@ -1,6 +1,14 @@ use ndarray::*; use ndarray_linalg::*; +#[should_panic] +#[test] +fn eigh_generalized_shape_mismatch() { + let a = Array2::::eye(3); + let b = Array2::::eye(2); + let _ = (a, b).eigh_inplace(UPLO::Upper); +} + #[test] fn fixed() { let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0]]); From d7e56722300cd6768ef14fe02c4bfcadf69fef79 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 19 May 2021 23:22:02 -0400 Subject: [PATCH 029/225] Add check in EighInplace for compatible shapes --- ndarray-linalg/src/eigh.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ndarray-linalg/src/eigh.rs b/ndarray-linalg/src/eigh.rs index 580c2b7e..79c5d732 100644 --- a/ndarray-linalg/src/eigh.rs +++ b/ndarray-linalg/src/eigh.rs @@ -111,7 +111,17 @@ where { type EigVal = Array1; + /// Solves the generalized eigenvalue problem. + /// + /// # Panics + /// + /// Panics if the shapes of the matrices are different. fn eigh_inplace(&mut self, uplo: UPLO) -> Result<(Self::EigVal, &mut Self)> { + assert_eq!( + self.0.shape(), + self.1.shape(), + "The shapes of the matrices must be identical.", + ); let layout = self.0.square_layout()?; // XXX Force layout to be Fortran (see #146) match layout { From a1e6bc63805896dafe93c89581b77eb537034cc8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 5 May 2021 20:29:00 -0400 Subject: [PATCH 030/225] Fix LAPACK names in eigh_generalized docs --- lax/src/eigh.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lax/src/eigh.rs b/lax/src/eigh.rs index 46a3b131..ad8963dc 100644 --- a/lax/src/eigh.rs +++ b/lax/src/eigh.rs @@ -14,7 +14,7 @@ pub trait Eigh_: Scalar { a: &mut [Self], ) -> Result>; - /// Wraps `*syegv` for real and `*heegv` for complex + /// Wraps `*sygv` for real and `*hegv` for complex fn eigh_generalized( calc_eigenvec: bool, layout: MatrixLayout, From 3c4df7604f034826b8c6a502ccae2bebd8a63ecc Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 5 May 2021 20:29:54 -0400 Subject: [PATCH 031/225] Add basic documentation for eigh --- ndarray-linalg/src/eigh.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ndarray-linalg/src/eigh.rs b/ndarray-linalg/src/eigh.rs index 580c2b7e..850e6104 100644 --- a/ndarray-linalg/src/eigh.rs +++ b/ndarray-linalg/src/eigh.rs @@ -1,4 +1,15 @@ -//! Eigenvalue decomposition for Hermite matrices +//! Eigendecomposition for Hermitian matrices. +//! +//! For a Hermitian matrix `A`, this solves the eigenvalue problem `A V = V D` +//! for `D` and `V`, where `D` is the diagonal matrix of eigenvalues in +//! ascending order and `V` is the orthonormal matrix of corresponding +//! eigenvectors. +//! +//! For a pair of Hermitian matrices `A` and `B` where `B` is also positive +//! definite, this solves the generalized eigenvalue problem `A V = B V D`, +//! where `D` is the diagonal matrix of generalized eigenvalues in ascending +//! order and `V` is the matrix of corresponding generalized eigenvectors. The +//! matrix `V` is normalized such that `V^H B V = I`. use ndarray::*; From ab4cc024cc32087eecae21a3d614670085fa4f55 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 5 May 2021 23:42:43 -0400 Subject: [PATCH 032/225] Add example to eigh module --- ndarray-linalg/src/eigh.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ndarray-linalg/src/eigh.rs b/ndarray-linalg/src/eigh.rs index 850e6104..ec306ad6 100644 --- a/ndarray-linalg/src/eigh.rs +++ b/ndarray-linalg/src/eigh.rs @@ -10,6 +10,28 @@ //! where `D` is the diagonal matrix of generalized eigenvalues in ascending //! order and `V` is the matrix of corresponding generalized eigenvectors. The //! matrix `V` is normalized such that `V^H B V = I`. +//! +//! # Example +//! +//! Find the eigendecomposition of a Hermitian (or real symmetric) matrix. +//! +//! ``` +//! use approx::assert_abs_diff_eq; +//! use ndarray::{array, Array2}; +//! use ndarray_linalg::{Eigh, UPLO}; +//! +//! let a: Array2 = array![ +//! [2., 1.], +//! [1., 2.], +//! ]; +//! let (eigvals, eigvecs) = a.eigh(UPLO::Lower)?; +//! assert_abs_diff_eq!(eigvals, array![1., 3.]); +//! assert_abs_diff_eq!( +//! a.dot(&eigvecs), +//! eigvecs.dot(&Array2::from_diag(&eigvals)), +//! ); +//! # Ok::<(), Box>(()) +//! ``` use ndarray::*; From 3004354311602dbd6c5fe8c5c8c0f7df62de3055 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 27 May 2021 20:58:32 -0400 Subject: [PATCH 033/225] Add more tests for Solve --- ndarray-linalg/tests/solve.rs | 202 +++++++++++++++++++++++++++++----- 1 file changed, 174 insertions(+), 28 deletions(-) diff --git a/ndarray-linalg/tests/solve.rs b/ndarray-linalg/tests/solve.rs index d069ec7a..86e95bdc 100644 --- a/ndarray-linalg/tests/solve.rs +++ b/ndarray-linalg/tests/solve.rs @@ -1,42 +1,188 @@ -use ndarray::*; -use ndarray_linalg::*; +use ndarray::prelude::*; +use ndarray_linalg::{ + assert_aclose, assert_close_l2, c32, c64, random, random_hpd, solve::*, OperationNorm, Scalar, +}; + +macro_rules! test_solve { + ( + [$($elem_type:ty => $rtol:expr),*], + $a_ident:ident = $a:expr, + $x_ident:ident = $x:expr, + b = $b:expr, + $solve:ident, + ) => { + $({ + let $a_ident: Array2<$elem_type> = $a; + let $x_ident: Array1<$elem_type> = $x; + let b: Array1<$elem_type> = $b; + let a = $a_ident; + let x = $x_ident; + let rtol = $rtol; + assert_close_l2!(&a.$solve(&b).unwrap(), &x, rtol); + assert_close_l2!(&a.factorize().unwrap().$solve(&b).unwrap(), &x, rtol); + assert_close_l2!(&a.factorize_into().unwrap().$solve(&b).unwrap(), &x, rtol); + })* + }; +} + +macro_rules! test_solve_into { + ( + [$($elem_type:ty => $rtol:expr),*], + $a_ident:ident = $a:expr, + $x_ident:ident = $x:expr, + b = $b:expr, + $solve_into:ident, + ) => { + $({ + let $a_ident: Array2<$elem_type> = $a; + let $x_ident: Array1<$elem_type> = $x; + let b: Array1<$elem_type> = $b; + let a = $a_ident; + let x = $x_ident; + let rtol = $rtol; + assert_close_l2!(&a.$solve_into(b.clone()).unwrap(), &x, rtol); + assert_close_l2!(&a.factorize().unwrap().$solve_into(b.clone()).unwrap(), &x, rtol); + assert_close_l2!(&a.factorize_into().unwrap().$solve_into(b.clone()).unwrap(), &x, rtol); + })* + }; +} + +macro_rules! test_solve_inplace { + ( + [$($elem_type:ty => $rtol:expr),*], + $a_ident:ident = $a:expr, + $x_ident:ident = $x:expr, + b = $b:expr, + $solve_inplace:ident, + ) => { + $({ + let $a_ident: Array2<$elem_type> = $a; + let $x_ident: Array1<$elem_type> = $x; + let b: Array1<$elem_type> = $b; + let a = $a_ident; + let x = $x_ident; + let rtol = $rtol; + { + let mut b = b.clone(); + assert_close_l2!(&a.$solve_inplace(&mut b).unwrap(), &x, rtol); + assert_close_l2!(&b, &x, rtol); + } + { + let mut b = b.clone(); + assert_close_l2!(&a.factorize().unwrap().$solve_inplace(&mut b).unwrap(), &x, rtol); + assert_close_l2!(&b, &x, rtol); + } + { + let mut b = b.clone(); + assert_close_l2!(&a.factorize_into().unwrap().$solve_inplace(&mut b).unwrap(), &x, rtol); + assert_close_l2!(&b, &x, rtol); + } + })* + }; +} + +macro_rules! test_solve_all { + ( + [$($elem_type:ty => $rtol:expr),*], + $a_ident:ident = $a:expr, + $x_ident:ident = $x:expr, + b = $b:expr, + [$solve:ident, $solve_into:ident, $solve_inplace:ident], + ) => { + test_solve!([$($elem_type => $rtol),*], $a_ident = $a, $x_ident = $x, b = $b, $solve,); + test_solve_into!([$($elem_type => $rtol),*], $a_ident = $a, $x_ident = $x, b = $b, $solve_into,); + test_solve_inplace!([$($elem_type => $rtol),*], $a_ident = $a, $x_ident = $x, b = $b, $solve_inplace,); + }; +} + +#[test] +fn solve_random_float() { + 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), + b = a.dot(&x), + [solve, solve_into, solve_inplace], + ); + } + } +} + +#[test] +fn solve_random_complex() { + 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), + b = a.dot(&x), + [solve, solve_into, solve_inplace], + ); + } + } +} #[test] -fn solve_random() { - let a: Array2 = random((3, 3)); - let x: Array1 = random(3); - let b = a.dot(&x); - let y = a.solve_into(b).unwrap(); - assert_close_l2!(&x, &y, 1e-7); +fn solve_t_random_float() { + 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), + b = a.t().dot(&x), + [solve_t, solve_t_into, solve_t_inplace], + ); + } + } } #[test] -fn solve_random_t() { - let a: Array2 = random((3, 3).f()); - let x: Array1 = random(3); - let b = a.dot(&x); - let y = a.solve_into(b).unwrap(); - assert_close_l2!(&x, &y, 1e-7); +fn solve_t_random_complex() { + 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), + b = a.t().dot(&x), + [solve_t, solve_t_into, solve_t_inplace], + ); + } + } } #[test] -fn solve_factorized() { - let a: Array2 = random((3, 3)); - let ans: Array1 = random(3); - let b = a.dot(&ans); - let f = a.factorize_into().unwrap(); - let x = f.solve_into(b).unwrap(); - assert_close_l2!(&x, &ans, 1e-7); +fn solve_h_random_float() { + 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), + b = a.t().mapv(|x| x.conj()).dot(&x), + [solve_h, solve_h_into, solve_h_inplace], + ); + } + } } #[test] -fn solve_factorized_t() { - let a: Array2 = random((3, 3).f()); - let ans: Array1 = random(3); - let b = a.dot(&ans); - let f = a.factorize_into().unwrap(); - let x = f.solve_into(b).unwrap(); - assert_close_l2!(&x, &ans, 1e-7); +fn solve_h_random_complex() { + 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), + b = a.t().mapv(|x| x.conj()).dot(&x), + [solve_h, solve_h_into, solve_h_inplace], + ); + } + } } #[test] From ffe65cb1b9fd4dfe738020b0179909bed5fa5f23 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 27 May 2021 21:03:00 -0400 Subject: [PATCH 034/225] Fix Solve::solve_h for complex inputs with C layout --- lax/src/solve.rs | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 39498a04..3851bde2 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -75,18 +75,49 @@ macro_rules! impl_solve { ipiv: &Pivot, b: &mut [Self], ) -> Result<()> { - let t = match l { + // 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, - Transpose::Transpose | Transpose::Hermite => Transpose::No, + Transpose::No => (Transpose::Transpose, false), + Transpose::Transpose => (Transpose::No, false), + Transpose::Hermite => (Transpose::No, true), }, - _ => t, + MatrixLayout::F { .. } => (t, false), }; let (n, _) = l.size(); let nrhs = 1; let ldb = l.lda(); let mut info = 0; + if conj { + for b_elem in &mut *b { + *b_elem = b_elem.conj(); + } + } unsafe { $getrs(t as u8, n, nrhs, a, l.lda(), ipiv, b, ldb, &mut info) }; + if conj { + for b_elem in &mut *b { + *b_elem = b_elem.conj(); + } + } info.as_lapack_result()?; Ok(()) } From e48d1277edaaf4e384fdfec6deb1301d14607ce7 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 27 May 2021 21:26:40 -0400 Subject: [PATCH 035/225] Add more tests for Inverse and InverseInto traits --- ndarray-linalg/tests/inv.rs | 103 ++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/ndarray-linalg/tests/inv.rs b/ndarray-linalg/tests/inv.rs index cbbcffd0..71e8973a 100644 --- a/ndarray-linalg/tests/inv.rs +++ b/ndarray-linalg/tests/inv.rs @@ -1,20 +1,103 @@ use ndarray::*; use ndarray_linalg::*; +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 identity = Array2::eye(n); + assert_close_l2!(&a.inv().unwrap().dot(&a), &identity, rtol); + assert_close_l2!( + &a.factorize().unwrap().inv().unwrap().dot(&a), + &identity, + rtol + ); + assert_close_l2!( + &a.clone().factorize_into().unwrap().inv().unwrap().dot(&a), + &identity, + rtol + ); +} + +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 identity = Array2::eye(n); + assert_close_l2!(&a.clone().inv_into().unwrap().dot(&a), &identity, rtol); + assert_close_l2!( + &a.factorize().unwrap().inv_into().unwrap().dot(&a), + &identity, + rtol + ); + assert_close_l2!( + &a.clone() + .factorize_into() + .unwrap() + .inv_into() + .unwrap() + .dot(&a), + &identity, + rtol + ); +} + +#[test] +fn inv_empty() { + test_inv_random::(0, false, 0.); + test_inv_random::(0, false, 0.); + test_inv_random::(0, false, 0.); + test_inv_random::(0, false, 0.); +} + +#[test] +fn inv_random_float() { + for n in 1..=8 { + for &set_f in &[false, true] { + test_inv_random::(n, set_f, 1e-3); + test_inv_random::(n, set_f, 1e-9); + } + } +} + +#[test] +fn inv_random_complex() { + for n in 1..=8 { + for &set_f in &[false, true] { + test_inv_random::(n, set_f, 1e-3); + test_inv_random::(n, set_f, 1e-9); + } + } +} + +#[test] +fn inv_into_empty() { + test_inv_into_random::(0, false, 0.); + test_inv_into_random::(0, false, 0.); + test_inv_into_random::(0, false, 0.); + test_inv_into_random::(0, false, 0.); +} + #[test] -fn inv_random() { - let a: Array2 = random((3, 3)); - let ai: Array2<_> = (&a).inv().unwrap(); - let id = Array::eye(3); - assert_close_l2!(&ai.dot(&a), &id, 1e-7); +fn inv_into_random_float() { + for n in 1..=8 { + for &set_f in &[false, true] { + test_inv_into_random::(n, set_f, 1e-3); + test_inv_into_random::(n, set_f, 1e-9); + } + } } #[test] -fn inv_random_t() { - let a: Array2 = random((3, 3).f()); - let ai: Array2<_> = (&a).inv().unwrap(); - let id = Array::eye(3); - assert_close_l2!(&ai.dot(&a), &id, 1e-7); +fn inv_into_random_complex() { + for n in 1..=8 { + for &set_f in &[false, true] { + test_inv_into_random::(n, set_f, 1e-3); + test_inv_into_random::(n, set_f, 1e-9); + } + } } #[test] From ef8482fcfb28cd970b778e90f3ccd8ee291a0e41 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 27 May 2021 21:30:46 -0400 Subject: [PATCH 036/225] Fix Inverse/InverseInto for empty matrices --- lax/src/solve.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lax/src/solve.rs b/lax/src/solve.rs index 39498a04..d8fe3c92 100644 --- a/lax/src/solve.rs +++ b/lax/src/solve.rs @@ -42,6 +42,10 @@ macro_rules! impl_solve { 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; From 15b4a5116e98ef93cb29b8287bda2f84f8ff24e5 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 27 May 2021 23:45:34 -0400 Subject: [PATCH 037/225] Fix Inverse for LUFactorized This fixes the result of `a.factorize_into()?.inv()?` when `a` is column-major. --- ndarray-linalg/src/solve.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index 7e695a8c..9a1eb18b 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -323,8 +323,15 @@ where type Output = Array2; fn inv(&self) -> Result> { + // Preserve the existing layout. This is required to obtain the correct + // result, because the result of `A::inv` is layout-dependent. + let a = if self.a.is_standard_layout() { + replicate(&self.a) + } else { + replicate(&self.a.t()).reversed_axes() + }; let f = LUFactorized { - a: replicate(&self.a), + a, ipiv: self.ipiv.clone(), }; f.inv_into() From 6766c3129ef376800b4eae8e56f0da7299bc9556 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 28 May 2021 00:35:38 -0400 Subject: [PATCH 038/225] Make the fields of LUFactorized private The correct interpretation of the fields of LUFactorized is very complicated when the input to the factorization was row-major. --- ndarray-linalg/src/solve.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndarray-linalg/src/solve.rs b/ndarray-linalg/src/solve.rs index 9a1eb18b..c643d00a 100644 --- a/ndarray-linalg/src/solve.rs +++ b/ndarray-linalg/src/solve.rs @@ -150,9 +150,9 @@ pub trait Solve { pub struct LUFactorized { /// The factors `L` and `U`; the unit diagonal elements of `L` are not /// stored. - pub a: ArrayBase, + a: ArrayBase, /// The pivot indices that define the permutation matrix `P`. - pub ipiv: Pivot, + ipiv: Pivot, } impl Solve for LUFactorized From d9468da07e6377276cb08643306acc8cd35e4bf6 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 29 May 2021 01:13:24 -0400 Subject: [PATCH 039/225] Fix eig tests for column-major arrays --- ndarray-linalg/tests/eig.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ndarray-linalg/tests/eig.rs b/ndarray-linalg/tests/eig.rs index 28314b8a..288f26a2 100644 --- a/ndarray-linalg/tests/eig.rs +++ b/ndarray-linalg/tests/eig.rs @@ -87,7 +87,10 @@ fn test_matrix_real() -> Array2 { } fn test_matrix_real_t() -> Array2 { - test_matrix_real::().t().permuted_axes([1, 0]).to_owned() + let orig = test_matrix_real::(); + let mut out = Array2::zeros(orig.raw_dim().f()); + out.assign(&orig); + out } fn answer_eig_real() -> Array1 { @@ -154,10 +157,10 @@ fn test_matrix_complex() -> Array2 { } fn test_matrix_complex_t() -> Array2 { - test_matrix_complex::() - .t() - .permuted_axes([1, 0]) - .to_owned() + let orig = test_matrix_complex::(); + let mut out = Array2::zeros(orig.raw_dim().f()); + out.assign(&orig); + out } fn answer_eig_complex() -> Array1 { From 6063d7d8c57a852a0140f7487aeeb4f13e400c3c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 30 May 2021 01:18:02 -0400 Subject: [PATCH 040/225] Fix eig test to ignore ordering of eigvals --- ndarray-linalg/tests/eig.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ndarray-linalg/tests/eig.rs b/ndarray-linalg/tests/eig.rs index 288f26a2..d8144e68 100644 --- a/ndarray-linalg/tests/eig.rs +++ b/ndarray-linalg/tests/eig.rs @@ -1,6 +1,19 @@ use ndarray::*; use ndarray_linalg::*; +fn sorted_eigvals(eigvals: ArrayView1<'_, T>) -> Array1 { + let mut indices: Vec = (0..eigvals.len()).collect(); + indices.sort_by(|&ind1, &ind2| { + let e1 = eigvals[ind1]; + let e2 = eigvals[ind2]; + e1.re() + .partial_cmp(&e2.re()) + .unwrap() + .then(e1.im().partial_cmp(&e2.im()).unwrap()) + }); + indices.iter().map(|&ind| eigvals[ind]).collect() +} + // Test Av_i = e_i v_i for i = 0..n fn test_eig(a: Array2, eigs: Array1, vecs: Array2) where @@ -216,7 +229,11 @@ macro_rules! impl_test_real { fn [<$real _eigvals_t>]() { let a = test_matrix_real_t::<$real>(); let (e, _vecs) = a.eig().unwrap(); - assert_close_l2!(&e, &answer_eig_real::<$real>(), 1.0e-3); + assert_close_l2!( + &sorted_eigvals(e.view()), + &sorted_eigvals(answer_eig_real::<$real>().view()), + 1.0e-3 + ); } #[test] From d94482113763ce3e74ec0f9df1a2bf9378157cea Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 30 May 2021 01:33:56 -0400 Subject: [PATCH 041/225] Fix eig for column-major arrays with real elements --- lax/src/eig.rs | 78 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/lax/src/eig.rs b/lax/src/eig.rs index 53245de7..a6d6a16e 100644 --- a/lax/src/eig.rs +++ b/lax/src/eig.rs @@ -23,8 +23,16 @@ macro_rules! impl_eig_complex { mut a: &mut [Self], ) -> Result<(Vec, Vec)> { let (n, _) = l.size(); - // Because LAPACK assumes F-continious array, C-continious array should be taken Hermitian conjugate. - // However, we utilize a fact that left eigenvector of A^H corresponds to the right eigenvector of A + // 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 { .. } => (b'V', b'N'), @@ -118,8 +126,22 @@ macro_rules! impl_eig_real { mut a: &mut [Self], ) -> Result<(Vec, Vec)> { let (n, _) = l.size(); - // Because LAPACK assumes F-continious array, C-continious array should be taken Hermitian conjugate. - // However, we utilize a fact that left eigenvector of A^H corresponds to the right eigenvector of A + // 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 { .. } => (b'V', b'N'), @@ -211,40 +233,34 @@ macro_rules! impl_eig_real { // - v(j) = VR(:,j) + i*VR(:,j+1) // - v(j+1) = VR(:,j) - i*VR(:,j+1). // - // ``` - // j -> <----pair----> <----pair----> - // [ ... (real), (imag), (imag), (imag), (imag), ... ] : eigs - // ^ ^ ^ ^ ^ - // false false true false true : is_conjugate_pair - // ``` + // 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 = unsafe { vec_uninit(n * n) }; - let mut is_conjugate_pair = false; // flag for check `j` is complex conjugate - for j in 0..n { - if eig_im[j] == 0.0 { - // j-th eigenvalue is real - for i in 0..n { - eigvecs[i + j * n] = Self::complex(v[i + j * n], 0.0); + 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.); } + col += 1; } else { - // j-th eigenvalue is complex - // complex conjugated pair can be `j-1` or `j+1` - if is_conjugate_pair { - let j_pair = j - 1; - assert!(j_pair < n); - for i in 0..n { - eigvecs[i + j * n] = Self::complex(v[i + j_pair * n], v[i + j * n]); - } - } else { - let j_pair = j + 1; - assert!(j_pair < n); - for i in 0..n { - eigvecs[i + j * n] = - Self::complex(v[i + j * n], -v[i + j_pair * n]); + // 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 == b'V' { + im = -im; } + eigvecs[row + col * n] = Self::complex(re, im); + eigvecs[row + (col + 1) * n] = Self::complex(re, -im); } - is_conjugate_pair = !is_conjugate_pair; + col += 2; } } From 9d2814f08661a34ba710f72a717770e5876cfd72 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 15:52:34 +0900 Subject: [PATCH 042/225] Add CHANGELOG for #286 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79972f27..b0e11a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Updated dependencies Changed -------- +- Avoid unnecessary calculation of eigenvectors in EigVals::eigvals and relax the DataMut bound https://github.com/rust-ndarray/ndarray-linalg/pull/286 - Relax type bounds for LeastSquaresSvd family https://github.com/rust-ndarray/ndarray-linalg/pull/272 0.13.1 - 13 March 2021 From 452a299cd911682a23e8840e7107522283950efc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 15:53:53 +0900 Subject: [PATCH 043/225] CHANGELOG for #283 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e11a56..f8290ac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Updated dependencies 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 - Relax type bounds for LeastSquaresSvd family https://github.com/rust-ndarray/ndarray-linalg/pull/272 0.13.1 - 13 March 2021 From 15bda91224660f69b913569b8a6e0300214f8967 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 15:55:30 +0900 Subject: [PATCH 044/225] CHANGELOG for #287 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8290ac4..bc6beeb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 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 From db138386f1faf0662b9f785420f723eb44ebbd41 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 16:03:20 +0900 Subject: [PATCH 045/225] CHANGELOG for #296 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc6beeb1..0cfc81b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Updated dependencies Changed -------- +- Fix Solve::solve_h_* for complex inputs with standard layout https://github.com/rust-ndarray/ndarray-linalg/pull/296 - 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 From d10cc8e4e6b6a318b004d833bce0e3e55b7184b8 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 16:13:32 +0900 Subject: [PATCH 046/225] CHANGELOG for #290 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cfc81b3..f430ca83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Updated dependencies Changed -------- +- Add checks for matching shapes in Solve, SolveH, and EighInplace https://github.com/rust-ndarray/ndarray-linalg/pull/290 - Fix Solve::solve_h_* for complex inputs with standard layout https://github.com/rust-ndarray/ndarray-linalg/pull/296 - 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 From 685fc3cd73df1d694f1e01d39a78fd33a700ff37 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 16:16:19 +0900 Subject: [PATCH 047/225] Separate Fixed section in CHANGELOG --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f430ca83..a6ed1ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,13 @@ 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 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 -------- -- Add checks for matching shapes in Solve, SolveH, and EighInplace https://github.com/rust-ndarray/ndarray-linalg/pull/290 -- Fix Solve::solve_h_* for complex inputs with standard layout https://github.com/rust-ndarray/ndarray-linalg/pull/296 - 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 From 261fdd62edd7cf342d488c73f3234de8712476c3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 16:21:23 +0900 Subject: [PATCH 048/225] CHANGELOG of #298 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ed1ca7..5d283e63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Updated dependencies Fixed ----- +- 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 From c7f22aeaa238c3a20e06a9cc87dfa39f2b2c5775 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 12 Jun 2021 17:46:36 +0900 Subject: [PATCH 049/225] CHANGELOG of #297 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d283e63..d7fae67b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Updated dependencies 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 From 423b8fddb08345e6a344029aff1c4069a87231e2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 21:59:58 +0900 Subject: [PATCH 050/225] Changelog for lax crate --- lax/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lax/CHANGELOG.md diff --git a/lax/CHANGELOG.md b/lax/CHANGELOG.md new file mode 100644 index 00000000..a5ab0052 --- /dev/null +++ b/lax/CHANGELOG.md @@ -0,0 +1,12 @@ +0.2.0 - 17 July 2021 +===================== + +Updated dependencies +--------------------- +- 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 From 359bf5f8c177635d02bad4ed6fb6f2fa621a9f35 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:03:15 +0900 Subject: [PATCH 051/225] lax 0.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 a8e74044..53bde27f 100644 --- a/lax/Cargo.toml +++ b/lax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lax" -version = "0.1.0" +version = "0.2.0" authors = ["Toshiki Teramura "] edition = "2018" diff --git a/ndarray-linalg/Cargo.toml b/ndarray-linalg/Cargo.toml index 9a7bea43..9fccc075 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.1.0" +version = "0.2.0" path = "../lax" default-features = false From 45f5d89fad3c6e76bec20898b6396392ce0669e2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:15:44 +0900 Subject: [PATCH 052/225] Add badges in lax README --- lax/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lax/README.md b/lax/README.md index ed563735..fa35f56f 100644 --- a/lax/README.md +++ b/lax/README.md @@ -1,6 +1,9 @@ Linear Algebra eXtension (LAX) =============================== +[![crates.io](https://img.shields.io/badge/crates.io-lax-blue)](https://crates.io/crates/ndarray-linalg) +[![docs.rs](https://docs.rs/lax/badge.svg)](https://docs.rs/ndarray-linalg) + ndarray-free safe Rust wrapper for LAPACK FFI for implementing ndarray-linalg crate. This crate responsibles for From 67e9a5806b86a9736475d8a5540fcdd1089bb0e2 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:16:34 +0900 Subject: [PATCH 053/225] Use shields.io badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4478dca3..58cc22e2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ndarray-linalg =============== -[![Crate](http://meritbadge.herokuapp.com/ndarray-linalg)](https://crates.io/crates/ndarray-linalg) +[![crate](https://img.shields.io/badge/crates.io-ndarray--linalg-blue)](https://crates.io/crates/ndarray-linalg) [![docs.rs](https://docs.rs/ndarray-linalg/badge.svg)](https://docs.rs/ndarray-linalg) Linear algebra package for Rust with [ndarray](https://github.com/rust-ndarray/ndarray) based on external LAPACK implementations. From f3843c02c49e358393e600fb7f881f63169cc3a0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:17:44 +0900 Subject: [PATCH 054/225] Fix URL --- lax/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lax/README.md b/lax/README.md index fa35f56f..9dea6b32 100644 --- a/lax/README.md +++ b/lax/README.md @@ -1,8 +1,8 @@ Linear Algebra eXtension (LAX) =============================== -[![crates.io](https://img.shields.io/badge/crates.io-lax-blue)](https://crates.io/crates/ndarray-linalg) -[![docs.rs](https://docs.rs/lax/badge.svg)](https://docs.rs/ndarray-linalg) +[![crates.io](https://img.shields.io/badge/crates.io-lax-blue)](https://crates.io/crates/lax) +[![docs.rs](https://docs.rs/lax/badge.svg)](https://docs.rs/lax) ndarray-free safe Rust wrapper for LAPACK FFI for implementing ndarray-linalg crate. This crate responsibles for From 19d1e15f9b7cd32b983e554bd6d46e5bc2a10092 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:36:48 +0900 Subject: [PATCH 055/225] (cargo-release) version 0.14.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 9fccc075..d8dcb51b 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.13.1" +version = "0.14.0" authors = ["Toshiki Teramura "] edition = "2018" From 856e1c95f2dfdf83948cdc9ec4e919b5854366be Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 17 Jul 2021 22:38:42 +0900 Subject: [PATCH 056/225] CHANGELOG for ndarray-linalg 0.14.0 --- CHANGELOG.md | 3 +++ lax/CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7fae67b..4317389c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Unreleased ----------- +0.14.0 - 17 July 2021 +===================== + Updated dependencies --------------------- - ndarray 0.15 https://github.com/rust-ndarray/ndarray-linalg/pull/273 diff --git a/lax/CHANGELOG.md b/lax/CHANGELOG.md index a5ab0052..68e828b4 100644 --- a/lax/CHANGELOG.md +++ b/lax/CHANGELOG.md @@ -1,3 +1,6 @@ +Unreleased +----------- + 0.2.0 - 17 July 2021 ===================== From f4bde41c8f4af3e7a808f6c5e29d07d1e9f92e11 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 21 Jul 2021 00:30:42 -0400 Subject: [PATCH 057/225] Add crate:: prefix to top-level wildcard imports docs.rs failed to build `ndarray-linalg` 0.14.0. The [build log](https://docs.rs/crate/ndarray-linalg/0.14.0/builds/412464) seems to indicate that this change will fix the issue. --- ndarray-linalg/src/lib.rs | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/ndarray-linalg/src/lib.rs b/ndarray-linalg/src/lib.rs index ba9d10c5..8a8088c8 100644 --- a/ndarray-linalg/src/lib.rs +++ b/ndarray-linalg/src/lib.rs @@ -74,26 +74,26 @@ pub mod triangular; pub mod tridiagonal; pub mod types; -pub use assert::*; -pub use cholesky::*; -pub use convert::*; -pub use diagonal::*; -pub use eig::*; -pub use eigh::*; -pub use generate::*; -pub use inner::*; -pub use layout::*; -pub use least_squares::*; -pub use lobpcg::{TruncatedEig, TruncatedOrder, TruncatedSvd}; -pub use norm::*; -pub use operator::*; -pub use opnorm::*; -pub use qr::*; -pub use solve::*; -pub use solveh::*; -pub use svd::*; -pub use svddc::*; -pub use trace::*; -pub use triangular::*; -pub use tridiagonal::*; -pub use types::*; +pub use crate::assert::*; +pub use crate::cholesky::*; +pub use crate::convert::*; +pub use crate::diagonal::*; +pub use crate::eig::*; +pub use crate::eigh::*; +pub use crate::generate::*; +pub use crate::inner::*; +pub use crate::layout::*; +pub use crate::least_squares::*; +pub use crate::lobpcg::{TruncatedEig, TruncatedOrder, TruncatedSvd}; +pub use crate::norm::*; +pub use crate::operator::*; +pub use crate::opnorm::*; +pub use crate::qr::*; +pub use crate::solve::*; +pub use crate::solveh::*; +pub use crate::svd::*; +pub use crate::svddc::*; +pub use crate::trace::*; +pub use crate::triangular::*; +pub use crate::tridiagonal::*; +pub use crate::types::*; From 429d14e03d82fda9255f1af2e11c788c0f25c30e Mon Sep 17 00:00:00 2001 From: Benjamin Kay Date: Tue, 27 Jul 2021 16:49:10 -0500 Subject: [PATCH 058/225] 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 bc683aab6ac8cbfc8310e4d3a63df63db94437c3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 14 Aug 2021 15:13:49 +0900 Subject: [PATCH 059/225] (cargo-release) version 0.14.1-alpha.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 d8dcb51b..f9e832ff 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.14.0" +version = "0.14.1-alpha.1" authors = ["Toshiki Teramura "] edition = "2018" From fa1924bf8285c6e20a3389fc025d6a1f9b4afe71 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 14 Aug 2021 15:18:42 +0900 Subject: [PATCH 060/225] Add CHANGELOG for #302 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4317389c..3f01111f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ 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 ===================== From 55866d9f3fc7b6f9de62e35abf4251362e696298 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Sat, 14 Aug 2021 15:19:00 +0900 Subject: [PATCH 061/225] (cargo-release) version 0.14.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 f9e832ff..8d3aebff 100644 --- a/ndarray-linalg/Cargo.toml +++ b/ndarray-linalg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-linalg" -version = "0.14.1-alpha.1" +version = "0.14.1" authors = ["Toshiki Teramura "] edition = "2018" From 70b9227a188d938b45261a4d2f6c37da9a1c711e Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Tue, 7 Sep 2021 13:35:03 +0200 Subject: [PATCH 062/225] 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 063/225] 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 064/225] 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 065/225] 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 066/225] 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 067/225] 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 068/225] 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 069/225] (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 070/225] (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 071/225] 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 072/225] (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 073/225] (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 074/225] (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 075/225] (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 076/225] 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 077/225] 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 078/225] 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 079/225] 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 080/225] `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 081/225] 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 082/225] 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 083/225] 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 084/225] 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 085/225] 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 086/225] 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 087/225] 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 088/225] 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 089/225] 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 090/225] 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 091/225] 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 092/225] 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 093/225] 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 094/225] 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 095/225] 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 096/225] 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 097/225] 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 098/225] 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 099/225] 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 100/225] 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 101/225] 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 102/225] 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 103/225] 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 104/225] 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 105/225] 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 106/225] 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 107/225] 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 108/225] 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 109/225] 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 110/225] 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 111/225] 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 112/225] 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 113/225] 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 114/225] 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 115/225] 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 116/225] 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 117/225] 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 118/225] 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 119/225] 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 120/225] 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 121/225] 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 122/225] 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 123/225] 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 124/225] 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 125/225] 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 126/225] 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 127/225] 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 128/225] 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 129/225] 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 130/225] 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 131/225] 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 132/225] 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 133/225] 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 134/225] 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 135/225] 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 136/225] 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 137/225] 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 138/225] 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 139/225] 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 140/225] 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 141/225] 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 142/225] 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 143/225] 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 144/225] 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 145/225] 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 146/225] 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 147/225] 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 148/225] 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 149/225] 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 150/225] 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 151/225] 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 152/225] 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 153/225] 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 154/225] 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 155/225] 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 156/225] 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 157/225] 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 158/225] 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 159/225] 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 160/225] 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 161/225] 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 162/225] 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 163/225] 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 164/225] 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 165/225] 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 166/225] 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 167/225] 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 168/225] 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 169/225] 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 170/225] 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 171/225] 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 172/225] 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 173/225] 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 174/225] 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 175/225] 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 176/225] 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 177/225] 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 178/225] 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 179/225] 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 180/225] 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 181/225] 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 182/225] 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 183/225] 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 184/225] 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 185/225] 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 186/225] 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 187/225] 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 188/225] 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 189/225] 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 190/225] 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 191/225] 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 192/225] 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 193/225] 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 194/225] 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 195/225] 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 196/225] 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 197/225] 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 198/225] 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 199/225] 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 200/225] 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 201/225] 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 202/225] 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 203/225] 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 204/225] 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 205/225] 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 206/225] (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 207/225] (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 208/225] (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 209/225] (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 210/225] 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 211/225] 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 212/225] 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 213/225] 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 214/225] 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 215/225] 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 216/225] 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 217/225] 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 218/225] 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 219/225] 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 220/225] 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 221/225] 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 222/225] 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 223/225] 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 224/225] 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 225/225] 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]