Add nalgebra-lapack as a crate on this workspace.
This commit is contained in:
parent
3f70af97dd
commit
02767fa894
|
@ -0,0 +1,22 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## [0.4.0] - 2016-09-07
|
||||||
|
|
||||||
|
* Made all traits use associated types for their output type parameters. This
|
||||||
|
simplifies usage of the traits and is consistent with the concept of
|
||||||
|
associated types used as output type parameters (not input type parameters) as
|
||||||
|
described in [the associated type
|
||||||
|
RFC](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md).
|
||||||
|
* Implemented `check_info!` macro to check all LAPACK calls.
|
||||||
|
* Implemented error handling with [error_chain](https://crates.io/crates/error-chain).
|
||||||
|
|
||||||
|
## [0.3.0] - 2016-09-06
|
||||||
|
|
||||||
|
* Documentation is hosted at https://docs.rs/nalgebra-lapack/
|
||||||
|
* Updated `nalgebra` to 0.10.
|
||||||
|
* Rename traits `HasSVD` to `SVD` and `HasEigensystem` to `Eigensystem`.
|
||||||
|
* Added `Solve` trait for solving a linear matrix equation.
|
||||||
|
* Added `Inverse` for computing the multiplicative inverse of a matrix.
|
||||||
|
* Added `Cholesky` for decomposing a positive-definite matrix.
|
||||||
|
* The `Eigensystem` and `SVD` traits are now generic over types. The
|
||||||
|
associated types have been removed.
|
|
@ -0,0 +1,40 @@
|
||||||
|
[package]
|
||||||
|
name = "nalgebra-lapack"
|
||||||
|
version = "0.11.2"
|
||||||
|
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
|
||||||
|
|
||||||
|
description = "Linear algebra library with transformations and satically-sized or dynamically-sized matrices."
|
||||||
|
documentation = "http://nalgebra.org/doc/nalgebra/index.html"
|
||||||
|
homepage = "http://nalgebra.org"
|
||||||
|
repository = "https://github.com/sebcrozet/nalgebra"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = [ "linear", "algebra", "matrix", "vector" ]
|
||||||
|
license = "BSD-3-Clause"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
serde-serialize = [ "serde", "serde_derive" ]
|
||||||
|
|
||||||
|
# For BLAS/LAPACK
|
||||||
|
default = ["openblas"]
|
||||||
|
openblas = ["lapack/openblas"]
|
||||||
|
netlib = ["lapack/netlib"]
|
||||||
|
accelerate = ["lapack/accelerate"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nalgebra = { version = "0.12", path = ".." }
|
||||||
|
num-traits = "0.1"
|
||||||
|
num-complex = "0.1"
|
||||||
|
alga = "0.5"
|
||||||
|
serde = { version = "0.9", optional = true }
|
||||||
|
serde_derive = { version = "0.9", optional = true }
|
||||||
|
# clippy = "*"
|
||||||
|
|
||||||
|
[dependencies.lapack]
|
||||||
|
version = "0.11"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nalgebra = { version = "0.12", path = "..", features = [ "arbitrary" ] }
|
||||||
|
quickcheck = "0.4"
|
||||||
|
approx = "0.1"
|
||||||
|
rand = "0.3"
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Andrew D. Straw
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,11 @@
|
||||||
|
all:
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
doc:
|
||||||
|
cargo doc --all --no-deps
|
||||||
|
|
||||||
|
bench:
|
||||||
|
cargo bench
|
|
@ -0,0 +1,53 @@
|
||||||
|
# nalgebra-lapack [![Version][version-img]][version-url] [![Status][status-img]][status-url] [![Doc][doc-img]][doc-url]
|
||||||
|
|
||||||
|
Rust library for linear algebra using nalgebra and LAPACK
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Documentation is available [here](https://docs.rs/nalgebra-lapack/).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Cargo features to select lapack provider
|
||||||
|
|
||||||
|
Like the [lapack crate](https://crates.io/crates/lapack) from which this
|
||||||
|
behavior is inherited, nalgebra-lapack uses [cargo
|
||||||
|
features](http://doc.crates.io/manifest.html#the-[features]-section) to select
|
||||||
|
which lapack provider (or implementation) is used. Command line arguments to
|
||||||
|
cargo are the easiest way to do this, and the best provider depends on your
|
||||||
|
particular system. In some cases, the providers can be further tuned with
|
||||||
|
environment variables.
|
||||||
|
|
||||||
|
Below are given examples of how to invoke `cargo build` on two different systems
|
||||||
|
using two different providers. The `--no-default-features --features "provider"`
|
||||||
|
arguments will be consistent for other `cargo` commands.
|
||||||
|
|
||||||
|
### Ubuntu
|
||||||
|
|
||||||
|
As tested on Ubuntu 12.04, do this to build the lapack package against
|
||||||
|
the system installation of netlib without LAPACKE (note the E) or
|
||||||
|
CBLAS:
|
||||||
|
|
||||||
|
sudo apt-get install gfortran libblas3gf liblapack3gf
|
||||||
|
export CARGO_FEATURE_SYSTEM_NETLIB=1
|
||||||
|
export CARGO_FEATURE_EXCLUDE_LAPACKE=1
|
||||||
|
export CARGO_FEATURE_EXCLUDE_CBLAS=1
|
||||||
|
|
||||||
|
export CARGO_FEATURES='--no-default-features --features netlib'
|
||||||
|
cargo build ${CARGO_FEATURES}
|
||||||
|
|
||||||
|
### Mac OS X
|
||||||
|
|
||||||
|
On Mac OS X, do this to use Apple's Accelerate framework:
|
||||||
|
|
||||||
|
export CARGO_FEATURES='--no-default-features --features accelerate'
|
||||||
|
cargo build ${CARGO_FEATURES}
|
||||||
|
|
||||||
|
[version-img]: https://img.shields.io/crates/v/nalgebra-lapack.svg
|
||||||
|
[version-url]: https://crates.io/crates/nalgebra-lapack
|
||||||
|
[status-img]: https://travis-ci.org/strawlab/nalgebra-lapack.svg?branch=master
|
||||||
|
[status-url]: https://travis-ci.org/strawlab/nalgebra-lapack
|
||||||
|
[doc-img]: https://docs.rs/nalgebra-lapack/badge.svg
|
||||||
|
[doc-url]: https://docs.rs/nalgebra-lapack/
|
|
@ -0,0 +1,8 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
extern crate nalgebra_lapack as nl;
|
||||||
|
|
||||||
|
mod linalg;
|
|
@ -0,0 +1,21 @@
|
||||||
|
use test::{self, Bencher};
|
||||||
|
use na::{DMatrix, Matrix4};
|
||||||
|
use nl::Hessenberg;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn hessenberg_decompose_100x100(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(100, 100);
|
||||||
|
bh.iter(|| test::black_box(Hessenberg::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn hessenberg_decompose_4x4(bh: &mut Bencher) {
|
||||||
|
let m = Matrix4::<f64>::new_random();
|
||||||
|
bh.iter(|| test::black_box(Hessenberg::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn hessenberg_decompose_500x500(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(500, 500);
|
||||||
|
bh.iter(|| test::black_box(Hessenberg::new(m.clone())))
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
use test::{self, Bencher};
|
||||||
|
use na::{DMatrix, Matrix4};
|
||||||
|
use nl::LU;
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn lu_decompose_100x100(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(100, 100);
|
||||||
|
bh.iter(|| test::black_box(LU::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn lu_decompose_100x500(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(100, 500);
|
||||||
|
bh.iter(|| test::black_box(LU::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn lu_decompose_4x4(bh: &mut Bencher) {
|
||||||
|
let m = Matrix4::<f64>::new_random();
|
||||||
|
bh.iter(|| test::black_box(LU::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn lu_decompose_500x100(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(500, 100);
|
||||||
|
bh.iter(|| test::black_box(LU::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn lu_decompose_500x500(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(500, 500);
|
||||||
|
bh.iter(|| test::black_box(LU::new(m.clone())))
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
mod qr;
|
||||||
|
mod lu;
|
||||||
|
mod hessenberg;
|
|
@ -0,0 +1,33 @@
|
||||||
|
use test::{self, Bencher};
|
||||||
|
use na::{DMatrix, Matrix4};
|
||||||
|
use nl::QR;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn qr_decompose_100x100(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(100, 100);
|
||||||
|
bh.iter(|| test::black_box(QR::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn qr_decompose_100x500(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(100, 500);
|
||||||
|
bh.iter(|| test::black_box(QR::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn qr_decompose_4x4(bh: &mut Bencher) {
|
||||||
|
let m = Matrix4::<f64>::new_random();
|
||||||
|
bh.iter(|| test::black_box(QR::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn qr_decompose_500x100(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(500, 100);
|
||||||
|
bh.iter(|| test::black_box(QR::new(m.clone())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn qr_decompose_500x500(bh: &mut Bencher) {
|
||||||
|
let m = DMatrix::<f64>::new_random(500, 500);
|
||||||
|
bh.iter(|| test::black_box(QR::new(m.clone())))
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
use num::Zero;
|
||||||
|
use num_complex::Complex;
|
||||||
|
|
||||||
|
use na::{Scalar, DefaultAllocator, MatrixN, MatrixMN};
|
||||||
|
use na::dimension::Dim;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
/// The cholesky decomposion of a symmetric-definite-positive matrix.
|
||||||
|
pub struct Cholesky<N: Scalar, D: Dim>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> {
|
||||||
|
l: MatrixN<N, D>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: CholeskyScalar + Zero, D: Dim> Cholesky<N, D>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> {
|
||||||
|
|
||||||
|
/// Complutes the cholesky decomposition of the given symmetric-definite-positive square
|
||||||
|
/// matrix.
|
||||||
|
///
|
||||||
|
/// Only the lower-triangular part of the input matrix is considered.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(mut m: MatrixN<N, D>) -> Option<Self> {
|
||||||
|
// FIXME: check symmetry as well?
|
||||||
|
assert!(m.is_square(), "Unable to compute the cholesky decomposition of a non-square matrix.");
|
||||||
|
|
||||||
|
let uplo = b'L';
|
||||||
|
let dim = m.nrows() as i32;
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
N::xpotrf(uplo, dim, m.as_mut_slice(), dim, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some(Cholesky { l: m })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpack(mut self) -> MatrixN<N, D> {
|
||||||
|
self.l.fill_upper_triangle(Zero::zero(), 1);
|
||||||
|
self.l
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn l(&self) -> MatrixN<N, D> {
|
||||||
|
let mut res = self.l.clone();
|
||||||
|
res.fill_upper_triangle(Zero::zero(), 1);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solves the symmetric-definite-positive linear system `self * x = b`, where `x` is the
|
||||||
|
/// unknown to be determined.
|
||||||
|
pub fn solve<R2: Dim, C2: Dim>(&self, mut b: MatrixMN<N, R2, C2>)
|
||||||
|
-> Option<MatrixMN<N, R2, C2>>
|
||||||
|
where DefaultAllocator: Allocator<N, R2, C2> {
|
||||||
|
|
||||||
|
let dim = self.l.nrows();
|
||||||
|
|
||||||
|
assert!(b.nrows() == dim, "The number of rows of `b` must be equal to the dimension of the matrix `a`.");
|
||||||
|
|
||||||
|
let nrhs = b.ncols() as i32;
|
||||||
|
let lda = dim as i32;
|
||||||
|
let ldb = dim as i32;
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
N::xpotrs(b'L', dim as i32, nrhs, self.l.as_slice(), lda, b.as_mut_slice(), ldb, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the inverse of the decomposed matrix.
|
||||||
|
pub fn inverse(mut self) -> Option<MatrixN<N, D>> {
|
||||||
|
let dim = self.l.nrows();
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
N::xpotri(b'L', dim as i32, self.l.as_mut_slice(), dim as i32, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
// Copy lower triangle to upper triangle.
|
||||||
|
for i in 0 .. dim {
|
||||||
|
for j in i + 1 .. dim {
|
||||||
|
unsafe { *self.l.get_unchecked_mut(i, j) = *self.l.get_unchecked(j, i) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self.l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lapack functions dispatch.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/// Trait implemented by floats (`f32`, `f64`) and complex floats (`Complex<f32>`, `Complex<f64>`)
|
||||||
|
/// supported by the cholesky decompotition.
|
||||||
|
pub trait CholeskyScalar: Scalar {
|
||||||
|
fn xpotrf(uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32);
|
||||||
|
fn xpotrs(uplo: u8, n: i32, nrhs: i32, a: &[Self], lda: i32, b: &mut [Self], ldb: i32, info: &mut i32);
|
||||||
|
fn xpotri(uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cholesky_scalar_impl(
|
||||||
|
($N: ty, $xpotrf: path, $xpotrs: path, $xpotri: path) => (
|
||||||
|
impl CholeskyScalar for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xpotrf(uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32) {
|
||||||
|
$xpotrf(uplo, n, a, lda, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xpotrs(uplo: u8, n: i32, nrhs: i32, a: &[Self], lda: i32,
|
||||||
|
b: &mut [Self], ldb: i32, info: &mut i32) {
|
||||||
|
$xpotrs(uplo, n, nrhs, a, lda, b, ldb, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xpotri(uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32) {
|
||||||
|
$xpotri(uplo, n, a, lda, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
cholesky_scalar_impl!(f32, interface::spotrf, interface::spotrs, interface::spotri);
|
||||||
|
cholesky_scalar_impl!(f64, interface::dpotrf, interface::dpotrs, interface::dpotri);
|
||||||
|
cholesky_scalar_impl!(Complex<f32>, interface::cpotrf, interface::cpotrs, interface::cpotri);
|
||||||
|
cholesky_scalar_impl!(Complex<f64>, interface::zpotrf, interface::zpotrs, interface::zpotri);
|
|
@ -0,0 +1,182 @@
|
||||||
|
use num::Zero;
|
||||||
|
|
||||||
|
use alga::general::Real;
|
||||||
|
|
||||||
|
use ::ComplexHelper;
|
||||||
|
use na::{Scalar, DefaultAllocator, Matrix, VectorN, MatrixN};
|
||||||
|
use na::dimension::{Dim, U1};
|
||||||
|
use na::storage::Storage;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
/// Eigendecomposition of a real square matrix with real eigenvalues.
|
||||||
|
pub struct RealEigensystem<N: Scalar, D: Dim>
|
||||||
|
where DefaultAllocator: Allocator<N, D> +
|
||||||
|
Allocator<N, D, D> {
|
||||||
|
pub eigenvalues: VectorN<N, D>,
|
||||||
|
pub eigenvectors: Option<MatrixN<N, D>>,
|
||||||
|
pub left_eigenvectors: Option<MatrixN<N, D>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<N: RealEigensystemScalar + Real, D: Dim> RealEigensystem<N, D>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> +
|
||||||
|
Allocator<N, D> {
|
||||||
|
/// Computes the eigenvalues and eigenvectors of the square matrix `m`.
|
||||||
|
///
|
||||||
|
/// If `eigenvectors` is `false` then, the eigenvectors are not computed explicitly.
|
||||||
|
pub fn new(mut m: MatrixN<N, D>, left_eigenvectors: bool, eigenvectors: bool)
|
||||||
|
-> Option<RealEigensystem<N, D>> {
|
||||||
|
|
||||||
|
assert!(m.is_square(), "Unable to compute the eigenvalue decomposition of a non-square matrix.");
|
||||||
|
|
||||||
|
let ljob = if left_eigenvectors { b'V' } else { b'N' };
|
||||||
|
let rjob = if eigenvectors { b'V' } else { b'N' };
|
||||||
|
|
||||||
|
let (nrows, ncols) = m.data.shape();
|
||||||
|
let n = nrows.value();
|
||||||
|
|
||||||
|
let lda = n as i32;
|
||||||
|
|
||||||
|
let mut wr = unsafe { Matrix::new_uninitialized_generic(nrows, U1) };
|
||||||
|
// FIXME: Tap into the workspace.
|
||||||
|
let mut wi = unsafe { Matrix::new_uninitialized_generic(nrows, U1) };
|
||||||
|
|
||||||
|
|
||||||
|
let mut info = 0;
|
||||||
|
let mut placeholder1 = [ N::zero() ];
|
||||||
|
let mut placeholder2 = [ N::zero() ];
|
||||||
|
|
||||||
|
let lwork = N::xgeev_work_size(ljob, rjob, n as i32, m.as_mut_slice(), lda,
|
||||||
|
wr.as_mut_slice(), wi.as_mut_slice(), &mut placeholder1,
|
||||||
|
n as i32, &mut placeholder2, n as i32, &mut info);
|
||||||
|
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
let mut work = unsafe { ::uninitialized_vec(lwork as usize) };
|
||||||
|
|
||||||
|
match (left_eigenvectors, eigenvectors) {
|
||||||
|
(true, true) => {
|
||||||
|
let mut vl = unsafe { Matrix::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
let mut vr = unsafe { Matrix::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
|
||||||
|
N::xgeev(ljob, rjob, n as i32, m.as_mut_slice(), lda, wr.as_mut_slice(),
|
||||||
|
wi.as_mut_slice(), &mut vl.as_mut_slice(), n as i32, &mut vr.as_mut_slice(),
|
||||||
|
n as i32, &mut work, lwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
if wi.iter().all(|e| e.is_zero()) {
|
||||||
|
return Some(RealEigensystem {
|
||||||
|
eigenvalues: wr, left_eigenvectors: Some(vl), eigenvectors: Some(vr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(true, false) => {
|
||||||
|
let mut vl = unsafe { Matrix::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
|
||||||
|
N::xgeev(ljob, rjob, n as i32, m.as_mut_slice(), lda, wr.as_mut_slice(),
|
||||||
|
wi.as_mut_slice(), &mut vl.as_mut_slice(), n as i32, &mut placeholder2,
|
||||||
|
1 as i32, &mut work, lwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
if wi.iter().all(|e| e.is_zero()) {
|
||||||
|
return Some(RealEigensystem {
|
||||||
|
eigenvalues: wr, left_eigenvectors: Some(vl), eigenvectors: None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(false, true) => {
|
||||||
|
let mut vr = unsafe { Matrix::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
|
||||||
|
N::xgeev(ljob, rjob, n as i32, m.as_mut_slice(), lda, wr.as_mut_slice(),
|
||||||
|
wi.as_mut_slice(), &mut placeholder1, 1 as i32, &mut vr.as_mut_slice(),
|
||||||
|
n as i32, &mut work, lwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
if wi.iter().all(|e| e.is_zero()) {
|
||||||
|
return Some(RealEigensystem {
|
||||||
|
eigenvalues: wr, left_eigenvectors: None, eigenvectors: Some(vr)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(false, false) => {
|
||||||
|
N::xgeev(ljob, rjob, n as i32, m.as_mut_slice(), lda, wr.as_mut_slice(),
|
||||||
|
wi.as_mut_slice(), &mut placeholder1, 1 as i32, &mut placeholder2,
|
||||||
|
1 as i32, &mut work, lwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
if wi.iter().all(|e| e.is_zero()) {
|
||||||
|
return Some(RealEigensystem {
|
||||||
|
eigenvalues: wr, left_eigenvectors: None, eigenvectors: None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The determinant of the decomposed matrix.
|
||||||
|
#[inline]
|
||||||
|
pub fn determinant(&self) -> N {
|
||||||
|
let mut det = N::one();
|
||||||
|
for e in self.eigenvalues.iter() {
|
||||||
|
det *= *e;
|
||||||
|
}
|
||||||
|
|
||||||
|
det
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lapack functions dispatch.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pub trait RealEigensystemScalar: Scalar {
|
||||||
|
fn xgeev(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
|
||||||
|
wr: &mut [Self], wi: &mut [Self],
|
||||||
|
vl: &mut [Self], ldvl: i32, vr: &mut [Self], ldvr: i32,
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32);
|
||||||
|
fn xgeev_work_size(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
|
||||||
|
wr: &mut [Self], wi: &mut [Self], vl: &mut [Self], ldvl: i32,
|
||||||
|
vr: &mut [Self], ldvr: i32, info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! real_eigensystem_scalar_impl (
|
||||||
|
($N: ty, $xgeev: path) => (
|
||||||
|
impl RealEigensystemScalar for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xgeev(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
|
||||||
|
wr: &mut [Self], wi: &mut [Self],
|
||||||
|
vl: &mut [Self], ldvl: i32, vr: &mut [Self], ldvr: i32,
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xgeev(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgeev_work_size(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
|
||||||
|
wr: &mut [Self], wi: &mut [Self], vl: &mut [Self], ldvl: i32,
|
||||||
|
vr: &mut [Self], ldvr: i32, info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xgeev(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
real_eigensystem_scalar_impl!(f32, interface::sgeev);
|
||||||
|
real_eigensystem_scalar_impl!(f64, interface::dgeev);
|
||||||
|
|
||||||
|
//// FIXME: decomposition of complex matrix and matrices with complex eigenvalues.
|
||||||
|
// eigensystem_complex_impl!(f32, interface::cgeev);
|
||||||
|
// eigensystem_complex_impl!(f64, interface::zgeev);
|
|
@ -0,0 +1,155 @@
|
||||||
|
use num::Zero;
|
||||||
|
use num_complex::Complex;
|
||||||
|
|
||||||
|
use ::ComplexHelper;
|
||||||
|
use na::{Scalar, Matrix, DefaultAllocator, VectorN, MatrixN};
|
||||||
|
use na::dimension::{DimSub, DimDiff, U1};
|
||||||
|
use na::storage::Storage;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
|
||||||
|
/// The Hessenberg decomposition of a general matrix.
|
||||||
|
pub struct Hessenberg<N: Scalar, D: DimSub<U1>>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> +
|
||||||
|
Allocator<N, DimDiff<D, U1>> {
|
||||||
|
h: MatrixN<N, D>,
|
||||||
|
tau: VectorN<N, DimDiff<D, U1>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<N: HessenbergScalar + Zero, D: DimSub<U1>> Hessenberg<N, D>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> +
|
||||||
|
Allocator<N, DimDiff<D, U1>> {
|
||||||
|
/// Computes the hessenberg decomposition of the matrix `m`.
|
||||||
|
pub fn new(mut m: MatrixN<N, D>) -> Hessenberg<N, D> {
|
||||||
|
let nrows = m.data.shape().0;
|
||||||
|
let n = nrows.value() as i32;
|
||||||
|
|
||||||
|
assert!(m.is_square(), "Unable to compute the hessenberg decomposition of a non-square matrix.");
|
||||||
|
assert!(!m.is_empty(), "Unable to compute the hessenberg decomposition of an empty matrix.");
|
||||||
|
|
||||||
|
let mut tau = unsafe { Matrix::new_uninitialized_generic(nrows.sub(U1), U1) };
|
||||||
|
|
||||||
|
let mut info = 0;
|
||||||
|
let lwork = N::xgehrd_work_size(n, 1, n, m.as_mut_slice(), n, tau.as_mut_slice(), &mut info);
|
||||||
|
let mut work = unsafe { ::uninitialized_vec(lwork as usize) };
|
||||||
|
|
||||||
|
lapack_panic!(info);
|
||||||
|
|
||||||
|
N::xgehrd(n, 1, n, m.as_mut_slice(), n, tau.as_mut_slice(), &mut work, lwork, &mut info);
|
||||||
|
lapack_panic!(info);
|
||||||
|
|
||||||
|
Hessenberg { h: m, tau: tau }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the hessenberg matrix of this decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn h(&self) -> MatrixN<N, D> {
|
||||||
|
let mut h = self.h.clone_owned();
|
||||||
|
h.fill_lower_triangle(N::zero(), 2);
|
||||||
|
|
||||||
|
h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: HessenbergReal + Zero, D: DimSub<U1>> Hessenberg<N, D>
|
||||||
|
where DefaultAllocator: Allocator<N, D, D> +
|
||||||
|
Allocator<N, DimDiff<D, U1>> {
|
||||||
|
/// Computes the matrices `(Q, H)` of this decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn unpack(self) -> (MatrixN<N, D>, MatrixN<N, D>) {
|
||||||
|
(self.q(), self.h())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the unitary matrix `Q` of this decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn q(&self) -> MatrixN<N, D> {
|
||||||
|
let n = self.h.nrows() as i32;
|
||||||
|
let mut q = self.h.clone_owned();
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
let lwork = N::xorghr_work_size(n, 1, n, q.as_mut_slice(), n, self.tau.as_slice(), &mut info);
|
||||||
|
let mut work = vec![ N::zero(); lwork as usize ];
|
||||||
|
|
||||||
|
N::xorghr(n, 1, n, q.as_mut_slice(), n, self.tau.as_slice(), &mut work, lwork, &mut info);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lapack functions dispatch.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pub trait HessenbergScalar: Scalar {
|
||||||
|
fn xgehrd(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &mut [Self], work: &mut [Self], lwork: i32, info: &mut i32);
|
||||||
|
fn xgehrd_work_size(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &mut [Self], info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HessenbergReal: HessenbergScalar {
|
||||||
|
fn xorghr(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32, tau: &[Self],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32);
|
||||||
|
fn xorghr_work_size(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &[Self], info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! hessenberg_scalar_impl(
|
||||||
|
($N: ty, $xgehrd: path) => (
|
||||||
|
impl HessenbergScalar for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xgehrd(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &mut [Self], work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xgehrd(n, ilo, ihi, a, lda, tau, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgehrd_work_size(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &mut [Self], info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xgehrd(n, ilo, ihi, a, lda, tau, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! hessenberg_real_impl(
|
||||||
|
($N: ty, $xorghr: path) => (
|
||||||
|
impl HessenbergReal for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xorghr(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32, tau: &[Self],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xorghr(n, ilo, ihi, a, lda, tau, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xorghr_work_size(n: i32, ilo: i32, ihi: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &[Self], info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xorghr(n, ilo, ihi, a, lda, tau, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
hessenberg_scalar_impl!(f32, interface::sgehrd);
|
||||||
|
hessenberg_scalar_impl!(f64, interface::dgehrd);
|
||||||
|
hessenberg_scalar_impl!(Complex<f32>, interface::cgehrd);
|
||||||
|
hessenberg_scalar_impl!(Complex<f64>, interface::zgehrd);
|
||||||
|
|
||||||
|
hessenberg_real_impl!(f32, interface::sorghr);
|
||||||
|
hessenberg_real_impl!(f64, interface::dorghr);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
macro_rules! lapack_check(
|
||||||
|
($info: expr) => (
|
||||||
|
// FIXME: return a richer error.
|
||||||
|
if $info != 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// if $info < 0 {
|
||||||
|
// return Err(Error::from(ErrorKind::LapackIllegalArgument(-$info)));
|
||||||
|
// } else if $info > 0 {
|
||||||
|
// return Err(Error::from(ErrorKind::LapackFailure($info)));
|
||||||
|
// }
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! lapack_panic(
|
||||||
|
($info: expr) => (
|
||||||
|
assert!($info == 0, "Lapack error.");
|
||||||
|
);
|
||||||
|
);
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
#![deny(non_camel_case_types)]
|
||||||
|
#![deny(unused_parens)]
|
||||||
|
#![deny(non_upper_case_globals)]
|
||||||
|
#![deny(unused_qualifications)]
|
||||||
|
#![deny(unused_results)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![doc(html_root_url = "http://nalgebra.org/rustdoc")]
|
||||||
|
|
||||||
|
extern crate num_traits as num;
|
||||||
|
extern crate num_complex;
|
||||||
|
extern crate lapack;
|
||||||
|
extern crate alga;
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
mod lapack_check;
|
||||||
|
mod svd;
|
||||||
|
mod eigen;
|
||||||
|
mod cholesky;
|
||||||
|
mod lu;
|
||||||
|
mod qr;
|
||||||
|
mod hessenberg;
|
||||||
|
|
||||||
|
use num_complex::Complex;
|
||||||
|
|
||||||
|
pub use self::svd::SVD;
|
||||||
|
pub use self::cholesky::{Cholesky, CholeskyScalar};
|
||||||
|
pub use self::lu::{LU, LUScalar};
|
||||||
|
pub use self::eigen::RealEigensystem;
|
||||||
|
pub use self::qr::QR;
|
||||||
|
pub use self::hessenberg::Hessenberg;
|
||||||
|
|
||||||
|
|
||||||
|
trait ComplexHelper {
|
||||||
|
type RealPart;
|
||||||
|
|
||||||
|
fn real_part(self) -> Self::RealPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComplexHelper for f32 {
|
||||||
|
type RealPart = f32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn real_part(self) -> Self::RealPart {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComplexHelper for f64 {
|
||||||
|
type RealPart = f64;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn real_part(self) -> Self::RealPart {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComplexHelper for Complex<f32> {
|
||||||
|
type RealPart = f32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn real_part(self) -> Self::RealPart {
|
||||||
|
self.re
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComplexHelper for Complex<f64> {
|
||||||
|
type RealPart = f64;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn real_part(self) -> Self::RealPart {
|
||||||
|
self.re
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn uninitialized_vec<T: Copy>(n: usize) -> Vec<T> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
res.reserve_exact(n);
|
||||||
|
res.set_len(n);
|
||||||
|
res
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
use num::{Zero, One};
|
||||||
|
use num_complex::Complex;
|
||||||
|
|
||||||
|
use ::ComplexHelper;
|
||||||
|
use na::{Scalar, DefaultAllocator, Matrix, MatrixMN, MatrixN, VectorN};
|
||||||
|
use na::dimension::{Dim, DimMin, DimMinimum, U1};
|
||||||
|
use na::storage::Storage;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
/// LU decomposition with partial pivoting.
|
||||||
|
///
|
||||||
|
/// This decomposes a matrix `M` with m rows and n columns into three parts:
|
||||||
|
/// * `L` which is a `m × min(m, n)` lower-triangular matrix.
|
||||||
|
/// * `U` which is a `min(m, n) × n` upper-triangular matrix.
|
||||||
|
/// * `P` which is a `m * m` permutation matrix.
|
||||||
|
///
|
||||||
|
/// Those are such that `M == P * L * U`.
|
||||||
|
pub struct LU<N: Scalar, R: DimMin<C>, C: Dim>
|
||||||
|
where DefaultAllocator: Allocator<i32, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, R, C> {
|
||||||
|
lu: MatrixMN<N, R, C>,
|
||||||
|
p: VectorN<i32, DimMinimum<R, C>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: LUScalar, R: Dim, C: Dim> LU<N, R, C>
|
||||||
|
where N: Zero + One,
|
||||||
|
R: DimMin<C>,
|
||||||
|
DefaultAllocator: Allocator<N, R, C> +
|
||||||
|
Allocator<N, R, R> +
|
||||||
|
Allocator<N, R, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, DimMinimum<R, C>, C> +
|
||||||
|
Allocator<i32, DimMinimum<R, C>> {
|
||||||
|
|
||||||
|
pub fn new(mut m: MatrixMN<N, R, C>) -> Self {
|
||||||
|
let (nrows, ncols) = m.data.shape();
|
||||||
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
|
let nrows = nrows.value() as i32;
|
||||||
|
let ncols = ncols.value() as i32;
|
||||||
|
|
||||||
|
let mut ipiv: VectorN<i32, _> = Matrix::zeros_generic(min_nrows_ncols, U1);
|
||||||
|
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
N::xgetrf(nrows, ncols, m.as_mut_slice(), nrows, ipiv.as_mut_slice(), &mut info);
|
||||||
|
lapack_panic!(info);
|
||||||
|
|
||||||
|
LU { lu: m, p: ipiv }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the lower-triangular matrix part of the decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn l(&self) -> MatrixMN<N, R, DimMinimum<R, C>> {
|
||||||
|
let (nrows, ncols) = self.lu.data.shape();
|
||||||
|
let mut res = self.lu.columns_generic(0, nrows.min(ncols)).into_owned();
|
||||||
|
|
||||||
|
res.fill_upper_triangle(Zero::zero(), 1);
|
||||||
|
res.fill_diagonal(One::one());
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the upper-triangular matrix part of the decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn u(&self) -> MatrixMN<N, DimMinimum<R, C>, C> {
|
||||||
|
let (nrows, ncols) = self.lu.data.shape();
|
||||||
|
let mut res = self.lu.rows_generic(0, nrows.min(ncols)).into_owned();
|
||||||
|
|
||||||
|
res.fill_lower_triangle(Zero::zero(), 1);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the row permutation matrix of this decomposition.
|
||||||
|
///
|
||||||
|
/// Computing the permutation matrix explicitly is costly and usually not necessary.
|
||||||
|
/// To permute rows of a matrix or vector, use the method `self.permute(...)` instead.
|
||||||
|
#[inline]
|
||||||
|
pub fn p(&self) -> MatrixN<N, R> {
|
||||||
|
let (dim, _) = self.lu.data.shape();
|
||||||
|
let mut id = Matrix::identity_generic(dim, dim);
|
||||||
|
self.permute(&mut id);
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: when we support resizing a matrix, we could add unwrap_u/unwrap_l that would
|
||||||
|
// re-use the memory from the internal matrix!
|
||||||
|
|
||||||
|
/// Gets the LAPACK permutation indices.
|
||||||
|
#[inline]
|
||||||
|
pub fn permutation_indices(&self) -> &VectorN<i32, DimMinimum<R, C>> {
|
||||||
|
&self.p
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the permutation matrix to a given matrix or vector in-place.
|
||||||
|
#[inline]
|
||||||
|
pub fn permute<C2: Dim>(&self, rhs: &mut MatrixMN<N, R, C2>)
|
||||||
|
where DefaultAllocator: Allocator<N, R, C2> {
|
||||||
|
|
||||||
|
let (nrows, ncols) = rhs.shape();
|
||||||
|
|
||||||
|
N::xlaswp(ncols as i32, rhs.as_mut_slice(), nrows as i32,
|
||||||
|
1, self.p.len() as i32, self.p.as_slice(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_solve<R2: Dim, C2: Dim>(&self, trans: u8, mut b: MatrixMN<N, R2, C2>)
|
||||||
|
-> Option<MatrixMN<N, R2, C2>>
|
||||||
|
where DefaultAllocator: Allocator<N, R2, C2> +
|
||||||
|
Allocator<i32, R2> {
|
||||||
|
|
||||||
|
let dim = self.lu.nrows();
|
||||||
|
|
||||||
|
assert!(self.lu.is_square(), "Unable to solve a set of under/over-determined equations.");
|
||||||
|
assert!(b.nrows() == dim, "The number of rows of `b` must be equal to the dimension of the matrix `a`.");
|
||||||
|
|
||||||
|
let nrhs = b.ncols() as i32;
|
||||||
|
let lda = dim as i32;
|
||||||
|
let ldb = dim as i32;
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
N::xgetrs(trans, dim as i32, nrhs, self.lu.as_slice(), lda, self.p.as_slice(),
|
||||||
|
b.as_mut_slice(), ldb, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solves the linear system `self * x = b`, where `x` is the unknown to be determined.
|
||||||
|
pub fn solve<R2: Dim, C2: Dim>(&self, b: MatrixMN<N, R2, C2>)
|
||||||
|
-> Option<MatrixMN<N, R2, C2>>
|
||||||
|
where DefaultAllocator: Allocator<N, R2, C2> +
|
||||||
|
Allocator<i32, R2> {
|
||||||
|
|
||||||
|
self.generic_solve(b'N', b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solves the linear system `self.transpose() * x = b`, where `x` is the unknown to be
|
||||||
|
/// determined.
|
||||||
|
pub fn solve_transpose<R2: Dim, C2: Dim>(&self, b: MatrixMN<N, R2, C2>)
|
||||||
|
-> Option<MatrixMN<N, R2, C2>>
|
||||||
|
where DefaultAllocator: Allocator<N, R2, C2> +
|
||||||
|
Allocator<i32, R2> {
|
||||||
|
|
||||||
|
self.generic_solve(b'T', b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solves the linear system `self.conjugate_transpose() * x = b`, where `x` is the unknown to
|
||||||
|
/// be determined.
|
||||||
|
pub fn solve_conjugate_transpose<R2: Dim, C2: Dim>(&self, b: MatrixMN<N, R2, C2>)
|
||||||
|
-> Option<MatrixMN<N, R2, C2>>
|
||||||
|
where DefaultAllocator: Allocator<N, R2, C2> +
|
||||||
|
Allocator<i32, R2> {
|
||||||
|
|
||||||
|
self.generic_solve(b'T', b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: LUScalar, D: Dim> LU<N, D, D>
|
||||||
|
where N: Zero + One,
|
||||||
|
D: DimMin<D, Output = D>,
|
||||||
|
DefaultAllocator: Allocator<N, D, D> +
|
||||||
|
Allocator<i32, D> {
|
||||||
|
/// Computes the inverse of the decomposed matrix.
|
||||||
|
pub fn inverse(mut self) -> Option<MatrixN<N, D>> {
|
||||||
|
let dim = self.lu.nrows() as i32;
|
||||||
|
let mut info = 0;
|
||||||
|
let lwork = N::xgetri_work_size(dim, self.lu.as_mut_slice(),
|
||||||
|
dim, self.p.as_mut_slice(),
|
||||||
|
&mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
let mut work = unsafe { ::uninitialized_vec(lwork as usize) };
|
||||||
|
|
||||||
|
N::xgetri(dim, self.lu.as_mut_slice(), dim, self.p.as_mut_slice(),
|
||||||
|
&mut work, lwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some(self.lu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lapack functions dispatch.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pub trait LUScalar: Scalar {
|
||||||
|
fn xgetrf(m: i32, n: i32, a: &mut [Self], lda: i32, ipiv: &mut [i32], info: &mut i32);
|
||||||
|
fn xlaswp(n: i32, a: &mut [Self], lda: i32, k1: i32, k2: i32, ipiv: &[i32], incx: i32);
|
||||||
|
fn xgetrs(trans: u8, n: i32, nrhs: i32, a: &[Self], lda: i32, ipiv: &[i32],
|
||||||
|
b: &mut [Self], ldb: i32, info: &mut i32);
|
||||||
|
fn xgetri(n: i32, a: &mut [Self], lda: i32, ipiv: &[i32],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32);
|
||||||
|
fn xgetri_work_size(n: i32, a: &mut [Self], lda: i32, ipiv: &[i32], info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! lup_scalar_impl(
|
||||||
|
($N: ty, $xgetrf: path, $xlaswp: path, $xgetrs: path, $xgetri: path) => (
|
||||||
|
impl LUScalar for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xgetrf(m: i32, n: i32, a: &mut [Self], lda: i32, ipiv: &mut [i32], info: &mut i32) {
|
||||||
|
$xgetrf(m, n, a, lda, ipiv, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xlaswp(n: i32, a: &mut [Self], lda: i32, k1: i32, k2: i32, ipiv: &[i32], incx: i32) {
|
||||||
|
$xlaswp(n, a, lda, k1, k2, ipiv, incx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgetrs(trans: u8, n: i32, nrhs: i32, a: &[Self], lda: i32, ipiv: &[i32],
|
||||||
|
b: &mut [Self], ldb: i32, info: &mut i32) {
|
||||||
|
$xgetrs(trans, n, nrhs, a, lda, ipiv, b, ldb, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgetri(n: i32, a: &mut [Self], lda: i32, ipiv: &[i32],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xgetri(n, a, lda, ipiv, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgetri_work_size(n: i32, a: &mut [Self], lda: i32, ipiv: &[i32], info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xgetri(n, a, lda, ipiv, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
lup_scalar_impl!(f32, interface::sgetrf, interface::slaswp, interface::sgetrs, interface::sgetri);
|
||||||
|
lup_scalar_impl!(f64, interface::dgetrf, interface::dlaswp, interface::dgetrs, interface::dgetri);
|
||||||
|
lup_scalar_impl!(Complex<f32>, interface::cgetrf, interface::claswp, interface::cgetrs, interface::cgetri);
|
||||||
|
lup_scalar_impl!(Complex<f64>, interface::zgetrf, interface::zlaswp, interface::zgetrs, interface::zgetri);
|
|
@ -0,0 +1,171 @@
|
||||||
|
use num_complex::Complex;
|
||||||
|
use num::Zero;
|
||||||
|
|
||||||
|
use ::ComplexHelper;
|
||||||
|
use na::{Scalar, DefaultAllocator, Matrix, VectorN, MatrixMN};
|
||||||
|
use na::dimension::{Dim, DimMin, DimMinimum, U1};
|
||||||
|
use na::storage::Storage;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
|
||||||
|
/// The QR decomposition of a general matrix.
|
||||||
|
pub struct QR<N: Scalar, R: DimMin<C>, C: Dim>
|
||||||
|
where DefaultAllocator: Allocator<N, R, C> +
|
||||||
|
Allocator<N, DimMinimum<R, C>> {
|
||||||
|
qr: MatrixMN<N, R, C>,
|
||||||
|
tau: VectorN<N, DimMinimum<R, C>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: QRScalar + Zero, R: DimMin<C>, C: Dim> QR<N, R, C>
|
||||||
|
where DefaultAllocator: Allocator<N, R, C> +
|
||||||
|
Allocator<N, R, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, DimMinimum<R, C>, C> +
|
||||||
|
Allocator<N, DimMinimum<R, C>> {
|
||||||
|
/// Computes the QR decomposition of the matrix `m`.
|
||||||
|
pub fn new(mut m: MatrixMN<N, R, C>) -> QR<N, R, C> {
|
||||||
|
let (nrows, ncols) = m.data.shape();
|
||||||
|
|
||||||
|
let mut info = 0;
|
||||||
|
let mut tau = unsafe { Matrix::new_uninitialized_generic(nrows.min(ncols), U1) };
|
||||||
|
|
||||||
|
if nrows.value() == 0 || ncols.value() == 0 {
|
||||||
|
return QR { qr: m, tau: tau };
|
||||||
|
}
|
||||||
|
|
||||||
|
let lwork = N::xgeqrf_work_size(nrows.value() as i32, ncols.value() as i32,
|
||||||
|
m.as_mut_slice(), nrows.value() as i32,
|
||||||
|
tau.as_mut_slice(), &mut info);
|
||||||
|
|
||||||
|
let mut work = unsafe { ::uninitialized_vec(lwork as usize) };
|
||||||
|
|
||||||
|
N::xgeqrf(nrows.value() as i32, ncols.value() as i32, m.as_mut_slice(),
|
||||||
|
nrows.value() as i32, tau.as_mut_slice(), &mut work, lwork, &mut info);
|
||||||
|
|
||||||
|
QR { qr: m, tau: tau }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn r(&self) -> MatrixMN<N, DimMinimum<R, C>, C> {
|
||||||
|
let (nrows, ncols) = self.qr.data.shape();
|
||||||
|
self.qr.rows_generic(0, nrows.min(ncols)).upper_triangle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: QRReal + Zero, R: DimMin<C>, C: Dim> QR<N, R, C>
|
||||||
|
where DefaultAllocator: Allocator<N, R, C> +
|
||||||
|
Allocator<N, R, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, DimMinimum<R, C>, C> +
|
||||||
|
Allocator<N, DimMinimum<R, C>> {
|
||||||
|
/// Retrieves the matrices `(Q, R)` of this decompositions.
|
||||||
|
pub fn unpack(self) -> (MatrixMN<N, R, DimMinimum<R, C>>, MatrixMN<N, DimMinimum<R, C>, C>) {
|
||||||
|
(self.q(), self.r())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Computes the orthogonal matrix `Q` of this decomposition.
|
||||||
|
#[inline]
|
||||||
|
pub fn q(&self) -> MatrixMN<N, R, DimMinimum<R, C>> {
|
||||||
|
let (nrows, ncols) = self.qr.data.shape();
|
||||||
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
|
|
||||||
|
if min_nrows_ncols.value() == 0 {
|
||||||
|
return MatrixMN::from_element_generic(nrows, min_nrows_ncols, N::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut q = self.qr.generic_slice((0, 0), (nrows, min_nrows_ncols)).into_owned();
|
||||||
|
|
||||||
|
let mut info = 0;
|
||||||
|
let nrows = nrows.value() as i32;
|
||||||
|
|
||||||
|
let lwork = N::xorgqr_work_size(nrows, min_nrows_ncols.value() as i32,
|
||||||
|
self.tau.len() as i32, q.as_mut_slice(), nrows,
|
||||||
|
self.tau.as_slice(), &mut info);
|
||||||
|
|
||||||
|
let mut work = vec![ N::zero(); lwork as usize ];
|
||||||
|
|
||||||
|
N::xorgqr(nrows, min_nrows_ncols.value() as i32, self.tau.len() as i32, q.as_mut_slice(),
|
||||||
|
nrows, self.tau.as_slice(), &mut work, lwork, &mut info);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lapack functions dispatch.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pub trait QRScalar: Scalar {
|
||||||
|
fn xgeqrf(m: i32, n: i32, a: &mut [Self], lda: i32, tau: &mut [Self],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32);
|
||||||
|
|
||||||
|
fn xgeqrf_work_size(m: i32, n: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &mut [Self], info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait QRReal: QRScalar {
|
||||||
|
fn xorgqr(m: i32, n: i32, k: i32, a: &mut [Self], lda: i32, tau: &[Self], work: &mut [Self],
|
||||||
|
lwork: i32, info: &mut i32);
|
||||||
|
|
||||||
|
fn xorgqr_work_size(m: i32, n: i32, k: i32, a: &mut [Self], lda: i32,
|
||||||
|
tau: &[Self], info: &mut i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! qr_scalar_impl(
|
||||||
|
($N: ty, $xgeqrf: path) => (
|
||||||
|
impl QRScalar for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xgeqrf(m: i32, n: i32, a: &mut [Self], lda: i32, tau: &mut [Self],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xgeqrf(m, n, a, lda, tau, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xgeqrf_work_size(m: i32, n: i32, a: &mut [Self], lda: i32, tau: &mut [Self],
|
||||||
|
info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xgeqrf(m, n, a, lda, tau, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! qr_real_impl(
|
||||||
|
($N: ty, $xorgqr: path) => (
|
||||||
|
impl QRReal for $N {
|
||||||
|
#[inline]
|
||||||
|
fn xorgqr(m: i32, n: i32, k: i32, a: &mut [Self], lda: i32, tau: &[Self],
|
||||||
|
work: &mut [Self], lwork: i32, info: &mut i32) {
|
||||||
|
$xorgqr(m, n, k, a, lda, tau, work, lwork, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn xorgqr_work_size(m: i32, n: i32, k: i32, a: &mut [Self], lda: i32, tau: &[Self],
|
||||||
|
info: &mut i32) -> i32 {
|
||||||
|
let mut work = [ Zero::zero() ];
|
||||||
|
let lwork = -1 as i32;
|
||||||
|
|
||||||
|
$xorgqr(m, n, k, a, lda, tau, &mut work, lwork, info);
|
||||||
|
ComplexHelper::real_part(work[0]) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
qr_scalar_impl!(f32, interface::sgeqrf);
|
||||||
|
qr_scalar_impl!(f64, interface::dgeqrf);
|
||||||
|
qr_scalar_impl!(Complex<f32>, interface::cgeqrf);
|
||||||
|
qr_scalar_impl!(Complex<f64>, interface::zgeqrf);
|
||||||
|
|
||||||
|
qr_real_impl!(f32, interface::sorgqr);
|
||||||
|
qr_real_impl!(f64, interface::dorgqr);
|
|
@ -0,0 +1,247 @@
|
||||||
|
use std::cmp;
|
||||||
|
use num::Signed;
|
||||||
|
|
||||||
|
use na::{Scalar, Matrix, VectorN, MatrixN, MatrixMN,
|
||||||
|
DefaultAllocator};
|
||||||
|
use na::dimension::{Dim, DimMin, DimMinimum, U1};
|
||||||
|
use na::storage::Storage;
|
||||||
|
use na::allocator::Allocator;
|
||||||
|
|
||||||
|
use lapack::fortran as interface;
|
||||||
|
|
||||||
|
|
||||||
|
/// The SVD decomposition of a general matrix.
|
||||||
|
pub struct SVD<N: Scalar, R: DimMin<C>, C: Dim>
|
||||||
|
where DefaultAllocator: Allocator<N, R, R> +
|
||||||
|
Allocator<N, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, C, C> {
|
||||||
|
pub u: MatrixN<N, R>,
|
||||||
|
pub s: VectorN<N, DimMinimum<R, C>>,
|
||||||
|
pub vt: MatrixN<N, C>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Trait implemented by floats (`f32`, `f64`) and complex floats (`Complex<f32>`, `Complex<f64>`)
|
||||||
|
/// supported by the Singular Value Decompotition.
|
||||||
|
pub trait SVDScalar<R: DimMin<C>, C: Dim>: Scalar
|
||||||
|
where DefaultAllocator: Allocator<Self, R, R> +
|
||||||
|
Allocator<Self, R, C> +
|
||||||
|
Allocator<Self, DimMinimum<R, C>> +
|
||||||
|
Allocator<Self, C, C> {
|
||||||
|
/// Computes the SVD decomposition of `m`.
|
||||||
|
fn compute(m: MatrixMN<Self, R, C>) -> Option<SVD<Self, R, C>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SVDScalar<R, C>, R: DimMin<C>, C: Dim> SVD<N, R, C>
|
||||||
|
where DefaultAllocator: Allocator<N, R, R> +
|
||||||
|
Allocator<N, R, C> +
|
||||||
|
Allocator<N, DimMinimum<R, C>> +
|
||||||
|
Allocator<N, C, C> {
|
||||||
|
pub fn new(m: MatrixMN<N, R, C>) -> Option<Self> {
|
||||||
|
N::compute(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! svd_impl(
|
||||||
|
($t: ty, $lapack_func: path) => (
|
||||||
|
impl<R: Dim, C: Dim> SVDScalar<R, C> for $t
|
||||||
|
where R: DimMin<C>,
|
||||||
|
DefaultAllocator: Allocator<$t, R, C> +
|
||||||
|
Allocator<$t, R, R> +
|
||||||
|
Allocator<$t, C, C> +
|
||||||
|
Allocator<$t, DimMinimum<R, C>> {
|
||||||
|
|
||||||
|
fn compute(mut m: MatrixMN<$t, R, C>) -> Option<SVD<$t, R, C>> {
|
||||||
|
let (nrows, ncols) = m.data.shape();
|
||||||
|
|
||||||
|
if nrows.value() == 0 || ncols.value() == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let job = b'A';
|
||||||
|
|
||||||
|
let lda = nrows.value() as i32;
|
||||||
|
|
||||||
|
let mut u = unsafe { Matrix::new_uninitialized_generic(nrows, nrows) };
|
||||||
|
let mut s = unsafe { Matrix::new_uninitialized_generic(nrows.min(ncols), U1) };
|
||||||
|
let mut vt = unsafe { Matrix::new_uninitialized_generic(ncols, ncols) };
|
||||||
|
|
||||||
|
let ldu = nrows.value();
|
||||||
|
let ldvt = ncols.value();
|
||||||
|
|
||||||
|
let mut work = [ 0.0 ];
|
||||||
|
let mut lwork = -1 as i32;
|
||||||
|
let mut info = 0;
|
||||||
|
let mut iwork = unsafe { ::uninitialized_vec(8 * cmp::min(nrows.value(), ncols.value())) };
|
||||||
|
|
||||||
|
$lapack_func(job, nrows.value() as i32, ncols.value() as i32, m.as_mut_slice(),
|
||||||
|
lda, &mut s.as_mut_slice(), u.as_mut_slice(), ldu as i32, vt.as_mut_slice(),
|
||||||
|
ldvt as i32, &mut work, lwork, &mut iwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
lwork = work[0] as i32;
|
||||||
|
let mut work = unsafe { ::uninitialized_vec(lwork as usize) };
|
||||||
|
|
||||||
|
$lapack_func(job, nrows.value() as i32, ncols.value() as i32, m.as_mut_slice(),
|
||||||
|
lda, &mut s.as_mut_slice(), u.as_mut_slice(), ldu as i32, vt.as_mut_slice(),
|
||||||
|
ldvt as i32, &mut work, lwork, &mut iwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some(SVD { u: u, s: s, vt: vt })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: DimMin<C>, C: Dim> SVD<$t, R, C>
|
||||||
|
// FIXME: All those bounds…
|
||||||
|
where DefaultAllocator: Allocator<$t, R, C> +
|
||||||
|
Allocator<$t, C, R> +
|
||||||
|
Allocator<$t, U1, R> +
|
||||||
|
Allocator<$t, U1, C> +
|
||||||
|
Allocator<$t, R, R> +
|
||||||
|
Allocator<$t, DimMinimum<R, C>> +
|
||||||
|
Allocator<$t, DimMinimum<R, C>, R> +
|
||||||
|
Allocator<$t, DimMinimum<R, C>, C> +
|
||||||
|
Allocator<$t, R, DimMinimum<R, C>> +
|
||||||
|
Allocator<$t, C, C> {
|
||||||
|
/// Reconstructs the matrix from its decomposition.
|
||||||
|
///
|
||||||
|
/// Useful if some components (e.g. some singular values) of this decomposition have
|
||||||
|
/// been manually changed by the user.
|
||||||
|
#[inline]
|
||||||
|
pub fn matrix(&self) -> MatrixMN<$t, R, C> {
|
||||||
|
let nrows = self.u.data.shape().0;
|
||||||
|
let ncols = self.vt.data.shape().1;
|
||||||
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
|
|
||||||
|
let mut res: MatrixMN<_, R, C> = Matrix::zeros_generic(nrows, ncols);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut sres = res.generic_slice_mut((0, 0), (min_nrows_ncols, ncols));
|
||||||
|
sres.copy_from(&self.vt.rows_generic(0, min_nrows_ncols));
|
||||||
|
|
||||||
|
for i in 0 .. min_nrows_ncols.value() {
|
||||||
|
let eigval = self.s[i];
|
||||||
|
let mut row = sres.row_mut(i);
|
||||||
|
row *= eigval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&self.u * res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the pseudo-inverse of the decomposed matrix.
|
||||||
|
///
|
||||||
|
/// All singular value bellow epsilon will be set to zero instead of being inverted.
|
||||||
|
#[inline]
|
||||||
|
pub fn pseudo_inverse(&self, epsilon: $t) -> MatrixMN<$t, C, R> {
|
||||||
|
let nrows = self.u.data.shape().0;
|
||||||
|
let ncols = self.vt.data.shape().1;
|
||||||
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
|
|
||||||
|
let mut res: MatrixMN<_, C, R> = Matrix::zeros_generic(ncols, nrows);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut sres = res.generic_slice_mut((0, 0), (min_nrows_ncols, nrows));
|
||||||
|
self.u.columns_generic(0, min_nrows_ncols).transpose_to(&mut sres);
|
||||||
|
|
||||||
|
for i in 0 .. min_nrows_ncols.value() {
|
||||||
|
let eigval = self.s[i];
|
||||||
|
let mut row = sres.row_mut(i);
|
||||||
|
|
||||||
|
if eigval.abs() > epsilon {
|
||||||
|
row /= eigval
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
row.fill(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vt.tr_mul(&res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The rank of the decomposed matrix.
|
||||||
|
///
|
||||||
|
/// This is the number of singular values that are not too small (i.e. greater than
|
||||||
|
/// the given `epsilon`).
|
||||||
|
#[inline]
|
||||||
|
pub fn rank(&self, epsilon: $t) -> usize {
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
for e in self.s.as_slice().iter() {
|
||||||
|
if e.abs() > epsilon {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: add methods to retrieve the null-space and column-space? (Respectively
|
||||||
|
// corresponding to the zero and non-zero singular values).
|
||||||
|
}
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
macro_rules! svd_complex_impl(
|
||||||
|
($name: ident, $t: ty, $lapack_func: path) => (
|
||||||
|
impl SVDScalar for Complex<$t> {
|
||||||
|
fn compute<R: Dim, C: Dim, S>(mut m: Matrix<$t, R, C, S>) -> Option<SVD<$t, R, C, S::Alloc>>
|
||||||
|
Option<(MatrixN<Complex<$t>, R, S::Alloc>,
|
||||||
|
VectorN<$t, DimMinimum<R, C>, S::Alloc>,
|
||||||
|
MatrixN<Complex<$t>, C, S::Alloc>)>
|
||||||
|
where R: DimMin<C>,
|
||||||
|
S: ContiguousStorage<Complex<$t>, R, C>,
|
||||||
|
S::Alloc: OwnedAllocator<Complex<$t>, R, C, S> +
|
||||||
|
Allocator<Complex<$t>, R, R> +
|
||||||
|
Allocator<Complex<$t>, C, C> +
|
||||||
|
Allocator<$t, DimMinimum<R, C>> {
|
||||||
|
let (nrows, ncols) = m.data.shape();
|
||||||
|
|
||||||
|
if nrows.value() == 0 || ncols.value() == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let jobu = b'A';
|
||||||
|
let jobvt = b'A';
|
||||||
|
|
||||||
|
let lda = nrows.value() as i32;
|
||||||
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
|
|
||||||
|
|
||||||
|
let mut u = unsafe { Matrix::new_uninitialized_generic(nrows, nrows) };
|
||||||
|
let mut s = unsafe { Matrix::new_uninitialized_generic(min_nrows_ncols, U1) };
|
||||||
|
let mut vt = unsafe { Matrix::new_uninitialized_generic(ncols, ncols) };
|
||||||
|
|
||||||
|
let ldu = nrows.value();
|
||||||
|
let ldvt = ncols.value();
|
||||||
|
|
||||||
|
let mut work = [ Complex::new(0.0, 0.0) ];
|
||||||
|
let mut lwork = -1 as i32;
|
||||||
|
let mut rwork = vec![ 0.0; (5 * min_nrows_ncols.value()) ];
|
||||||
|
let mut info = 0;
|
||||||
|
|
||||||
|
$lapack_func(jobu, jobvt, nrows.value() as i32, ncols.value() as i32, m.as_mut_slice(),
|
||||||
|
lda, s.as_mut_slice(), u.as_mut_slice(), ldu as i32, vt.as_mut_slice(),
|
||||||
|
ldvt as i32, &mut work, lwork, &mut rwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
lwork = work[0].re as i32;
|
||||||
|
let mut work = vec![Complex::new(0.0, 0.0); lwork as usize];
|
||||||
|
|
||||||
|
$lapack_func(jobu, jobvt, nrows.value() as i32, ncols.value() as i32, m.as_mut_slice(),
|
||||||
|
lda, s.as_mut_slice(), u.as_mut_slice(), ldu as i32, vt.as_mut_slice(),
|
||||||
|
ldvt as i32, &mut work, lwork, &mut rwork, &mut info);
|
||||||
|
lapack_check!(info);
|
||||||
|
|
||||||
|
Some((u, s, vt))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
svd_impl!(f32, interface::sgesdd);
|
||||||
|
svd_impl!(f64, interface::dgesdd);
|
||||||
|
// svd_complex_impl!(lapack_svd_complex_f32, f32, interface::cgesvd);
|
||||||
|
// svd_complex_impl!(lapack_svd_complex_f64, f64, interface::zgesvd);
|
|
@ -0,0 +1,9 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quickcheck;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate approx;
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
extern crate nalgebra_lapack as nl;
|
||||||
|
|
||||||
|
|
||||||
|
mod linalg;
|
|
@ -0,0 +1,101 @@
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use nl::Cholesky;
|
||||||
|
use na::{DMatrix, DVector, Vector4, Matrix3, Matrix4x3, Matrix4};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn cholesky(m: DMatrix<f64>) -> bool {
|
||||||
|
if m.len() != 0 {
|
||||||
|
let m = &m * m.transpose();
|
||||||
|
if let Some(chol) = Cholesky::new(m.clone()) {
|
||||||
|
let l = chol.unpack();
|
||||||
|
let reconstructed_m = &l * l.transpose();
|
||||||
|
|
||||||
|
return relative_eq!(reconstructed_m, m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cholesky_static(m: Matrix3<f64>) -> bool {
|
||||||
|
let m = &m * m.transpose();
|
||||||
|
if let Some(chol) = Cholesky::new(m) {
|
||||||
|
let l = chol.unpack();
|
||||||
|
let reconstructed_m = &l * l.transpose();
|
||||||
|
|
||||||
|
relative_eq!(reconstructed_m, m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cholesky_solve(n: usize, nb: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 15); // To avoid slowing down the test too much.
|
||||||
|
let nb = cmp::min(nb, 15); // To avoid slowing down the test too much.
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
let m = &m * m.transpose();
|
||||||
|
|
||||||
|
if let Some(chol) = Cholesky::new(m.clone()) {
|
||||||
|
let b1 = DVector::new_random(n);
|
||||||
|
let b2 = DMatrix::new_random(n, nb);
|
||||||
|
|
||||||
|
let sol1 = chol.solve(b1.clone()).unwrap();
|
||||||
|
let sol2 = chol.solve(b2.clone()).unwrap();
|
||||||
|
|
||||||
|
return relative_eq!(&m * sol1, b1, epsilon = 1.0e-6) &&
|
||||||
|
relative_eq!(&m * sol2, b2, epsilon = 1.0e-6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cholesky_solve_static(m: Matrix4<f64>) -> bool {
|
||||||
|
let m = &m * m.transpose();
|
||||||
|
match Cholesky::new(m) {
|
||||||
|
Some(chol) => {
|
||||||
|
let b1 = Vector4::new_random();
|
||||||
|
let b2 = Matrix4x3::new_random();
|
||||||
|
|
||||||
|
let sol1 = chol.solve(b1).unwrap();
|
||||||
|
let sol2 = chol.solve(b2).unwrap();
|
||||||
|
|
||||||
|
relative_eq!(m * sol1, b1, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m * sol2, b2, epsilon = 1.0e-7)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cholesky_inverse(n: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 15); // To avoid slowing down the test too much.
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
let m = &m * m.transpose();
|
||||||
|
|
||||||
|
if let Some(m1) = Cholesky::new(m.clone()).unwrap().inverse() {
|
||||||
|
let id1 = &m * &m1;
|
||||||
|
let id2 = &m1 * &m;
|
||||||
|
|
||||||
|
return id1.is_identity(1.0e-6) && id2.is_identity(1.0e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cholesky_inverse_static(m: Matrix4<f64>) -> bool {
|
||||||
|
let m = m * m.transpose();
|
||||||
|
match Cholesky::new(m.clone()).unwrap().inverse() {
|
||||||
|
Some(m1) => {
|
||||||
|
let id1 = &m * &m1;
|
||||||
|
let id2 = &m1 * &m;
|
||||||
|
|
||||||
|
id1.is_identity(1.0e-5) && id2.is_identity(1.0e-5)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use nl::Hessenberg;
|
||||||
|
use na::{DMatrix, Matrix4};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn hessenberg(n: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 25);
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
|
||||||
|
match Hessenberg::new(m.clone()) {
|
||||||
|
Some(hess) => {
|
||||||
|
let h = hess.h();
|
||||||
|
let p = hess.p();
|
||||||
|
|
||||||
|
relative_eq!(m, &p * h * p.transpose(), epsilon = 1.0e-7)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hessenberg_static(m: Matrix4<f64>) -> bool {
|
||||||
|
match Hessenberg::new(m) {
|
||||||
|
Some(hess) => {
|
||||||
|
let h = hess.h();
|
||||||
|
let p = hess.p();
|
||||||
|
|
||||||
|
relative_eq!(m, p * h * p.transpose(), epsilon = 1.0e-7)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use nl::LU;
|
||||||
|
use na::{DMatrix, DVector, Matrix4, Matrix4x3, Matrix3x4, Vector4};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn lup(m: DMatrix<f64>) -> bool {
|
||||||
|
if m.len() != 0 {
|
||||||
|
let lup = LU::new(m.clone());
|
||||||
|
let l = lup.l();
|
||||||
|
let u = lup.u();
|
||||||
|
let mut computed1 = &l * &u;
|
||||||
|
lup.permute(&mut computed1);
|
||||||
|
|
||||||
|
let computed2 = lup.p() * l * u;
|
||||||
|
|
||||||
|
relative_eq!(computed1, m, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(computed2, m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lu_static(m: Matrix3x4<f64>) -> bool {
|
||||||
|
let lup = LU::new(m);
|
||||||
|
let l = lup.l();
|
||||||
|
let u = lup.u();
|
||||||
|
let mut computed1 = l * u;
|
||||||
|
lup.permute(&mut computed1);
|
||||||
|
|
||||||
|
let computed2 = lup.p() * l * u;
|
||||||
|
|
||||||
|
relative_eq!(computed1, m, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(computed2, m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lu_solve(n: usize, nb: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 25); // To avoid slowing down the test too much.
|
||||||
|
let nb = cmp::min(nb, 25); // To avoid slowing down the test too much.
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
|
||||||
|
let lup = LU::new(m.clone());
|
||||||
|
let b1 = DVector::new_random(n);
|
||||||
|
let b2 = DMatrix::new_random(n, nb);
|
||||||
|
|
||||||
|
let sol1 = lup.solve(b1.clone()).unwrap();
|
||||||
|
let sol2 = lup.solve(b2.clone()).unwrap();
|
||||||
|
|
||||||
|
let tr_sol1 = lup.solve_transpose(b1.clone()).unwrap();
|
||||||
|
let tr_sol2 = lup.solve_transpose(b2.clone()).unwrap();
|
||||||
|
|
||||||
|
relative_eq!(&m * sol1, b1, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(&m * sol2, b2, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m.transpose() * tr_sol1, b1, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m.transpose() * tr_sol2, b2, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lu_solve_static(m: Matrix4<f64>) -> bool {
|
||||||
|
let lup = LU::new(m);
|
||||||
|
let b1 = Vector4::new_random();
|
||||||
|
let b2 = Matrix4x3::new_random();
|
||||||
|
|
||||||
|
let sol1 = lup.solve(b1).unwrap();
|
||||||
|
let sol2 = lup.solve(b2).unwrap();
|
||||||
|
let tr_sol1 = lup.solve_transpose(b1).unwrap();
|
||||||
|
let tr_sol2 = lup.solve_transpose(b2).unwrap();
|
||||||
|
|
||||||
|
relative_eq!(m * sol1, b1, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m * sol2, b2, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m.transpose() * tr_sol1, b1, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(m.transpose() * tr_sol2, b2, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lu_inverse(n: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 25); // To avoid slowing down the test too much.
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
|
||||||
|
if let Some(m1) = LU::new(m.clone()).inverse() {
|
||||||
|
let id1 = &m * &m1;
|
||||||
|
let id2 = &m1 * &m;
|
||||||
|
|
||||||
|
return id1.is_identity(1.0e-7) && id2.is_identity(1.0e-7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lu_inverse_static(m: Matrix4<f64>) -> bool {
|
||||||
|
match LU::new(m.clone()).inverse() {
|
||||||
|
Some(m1) => {
|
||||||
|
let id1 = &m * &m1;
|
||||||
|
let id2 = &m1 * &m;
|
||||||
|
|
||||||
|
id1.is_identity(1.0e-5) && id2.is_identity(1.0e-5)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod real_eigensystem;
|
||||||
|
mod cholesky;
|
||||||
|
mod lu;
|
||||||
|
mod qr;
|
||||||
|
mod svd;
|
|
@ -0,0 +1,20 @@
|
||||||
|
use nl::QR;
|
||||||
|
use na::{DMatrix, Matrix4x3};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn qr(m: DMatrix<f64>) -> bool {
|
||||||
|
let qr = QR::new(m.clone());
|
||||||
|
let q = qr.q();
|
||||||
|
let r = qr.r();
|
||||||
|
|
||||||
|
relative_eq!(m, q * r, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn qr_static(m: Matrix4x3<f64>) -> bool {
|
||||||
|
let qr = QR::new(m);
|
||||||
|
let q = qr.q();
|
||||||
|
let r = qr.r();
|
||||||
|
|
||||||
|
relative_eq!(m, q * r, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use nl::RealEigensystem;
|
||||||
|
use na::{DMatrix, Matrix4};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn eigensystem(n: usize) -> bool {
|
||||||
|
if n != 0 {
|
||||||
|
let n = cmp::min(n, 25);
|
||||||
|
let m = DMatrix::<f64>::new_random(n, n);
|
||||||
|
|
||||||
|
match RealEigensystem::new(m.clone(), true, true) {
|
||||||
|
Some(eig) => {
|
||||||
|
let eigvals = DMatrix::from_diagonal(&eig.eigenvalues);
|
||||||
|
let transformed_eigvectors = &m * eig.eigenvectors.as_ref().unwrap();
|
||||||
|
let scaled_eigvectors = eig.eigenvectors.as_ref().unwrap() * &eigvals;
|
||||||
|
|
||||||
|
let transformed_left_eigvectors = m.transpose() * eig.left_eigenvectors.as_ref().unwrap();
|
||||||
|
let scaled_left_eigvectors = eig.left_eigenvectors.as_ref().unwrap() * &eigvals;
|
||||||
|
|
||||||
|
relative_eq!(transformed_eigvectors, scaled_eigvectors, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(transformed_left_eigvectors, scaled_left_eigvectors, epsilon = 1.0e-7)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eigensystem_static(m: Matrix4<f64>) -> bool {
|
||||||
|
match RealEigensystem::new(m, true, true) {
|
||||||
|
Some(eig) => {
|
||||||
|
let eigvals = Matrix4::from_diagonal(&eig.eigenvalues);
|
||||||
|
let transformed_eigvectors = m * eig.eigenvectors.unwrap();
|
||||||
|
let scaled_eigvectors = eig.eigenvectors.unwrap() * eigvals;
|
||||||
|
|
||||||
|
let transformed_left_eigvectors = m.transpose() * eig.left_eigenvectors.unwrap();
|
||||||
|
let scaled_left_eigvectors = eig.left_eigenvectors.unwrap() * eigvals;
|
||||||
|
|
||||||
|
relative_eq!(transformed_eigvectors, scaled_eigvectors, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(transformed_left_eigvectors, scaled_left_eigvectors, epsilon = 1.0e-7)
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
use nl::SVD;
|
||||||
|
use na::{DMatrix, Matrix3x4};
|
||||||
|
|
||||||
|
quickcheck!{
|
||||||
|
fn svd(m: DMatrix<f64>) -> bool {
|
||||||
|
if m.nrows() != 0 && m.ncols() != 0 {
|
||||||
|
let svd = SVD::new(m.clone()).unwrap();
|
||||||
|
let sm = DMatrix::from_partial_diagonal(m.nrows(), m.ncols(), svd.s.as_slice());
|
||||||
|
|
||||||
|
let reconstructed_m = &svd.u * sm * &svd.vt;
|
||||||
|
let reconstructed_m2 = svd.matrix();
|
||||||
|
|
||||||
|
relative_eq!(reconstructed_m, m, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(reconstructed_m2, reconstructed_m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn svd_static(m: Matrix3x4<f64>) -> bool {
|
||||||
|
let svd = SVD::new(m).unwrap();
|
||||||
|
let sm = Matrix3x4::from_partial_diagonal(svd.s.as_slice());
|
||||||
|
|
||||||
|
let reconstructed_m = &svd.u * &sm * &svd.vt;
|
||||||
|
let reconstructed_m2 = svd.matrix();
|
||||||
|
|
||||||
|
relative_eq!(reconstructed_m, m, epsilon = 1.0e-7) &&
|
||||||
|
relative_eq!(reconstructed_m2, m, epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pseudo_inverse(m: DMatrix<f64>) -> bool {
|
||||||
|
if m.nrows() == 0 || m.ncols() == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let svd = SVD::new(m.clone()).unwrap();
|
||||||
|
let im = svd.pseudo_inverse(1.0e-7);
|
||||||
|
|
||||||
|
if m.nrows() <= m.ncols() {
|
||||||
|
return (&m * &im).is_identity(1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.nrows() >= m.ncols() {
|
||||||
|
return (im * m).is_identity(1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pseudo_inverse_static(m: Matrix3x4<f64>) -> bool {
|
||||||
|
let svd = SVD::new(m).unwrap();
|
||||||
|
let im = svd.pseudo_inverse(1.0e-7);
|
||||||
|
|
||||||
|
(m * im).is_identity(1.0e-7)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue