Skip to content

Commit 17f66b7

Browse files
committed
Add .rcond() and .rcond_into()
This commit also changes the character representations of `NormType` to capital letters to be compatible with `*gecon`.
1 parent 4635131 commit 17f66b7

File tree

4 files changed

+144
-9
lines changed

4 files changed

+144
-9
lines changed

src/lapack_traits/opnorm.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use types::*;
88

99
#[repr(u8)]
1010
pub enum NormType {
11-
One = b'o',
12-
Infinity = b'i',
13-
Frobenius = b'f',
11+
One = b'O',
12+
Infinity = b'I',
13+
Frobenius = b'F',
1414
}
1515

1616
impl NormType {

src/lapack_traits/solve.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ use lapack::c;
44

55
use error::*;
66
use layout::MatrixLayout;
7+
use num_traits::Zero;
78
use types::*;
89

910
use super::{Pivot, Transpose, into_result};
11+
use super::opnorm::NormType;
1012

1113
/// Wraps `*getrf`, `*getri`, and `*getrs`
12-
pub trait Solve_: Sized {
14+
pub trait Solve_: AssociatedReal + Sized {
1315
/// Computes the LU factorization of a general `m x n` matrix `a` using
1416
/// partial pivoting with row interchanges.
1517
///
@@ -20,11 +22,12 @@ pub trait Solve_: Sized {
2022
/// if it is used to solve a system of equations.
2123
unsafe fn lu(MatrixLayout, a: &mut [Self]) -> Result<Pivot>;
2224
unsafe fn inv(MatrixLayout, a: &mut [Self], &Pivot) -> Result<()>;
25+
unsafe fn rcond(MatrixLayout, a: &[Self], anorm: Self::Real) -> Result<Self::Real>;
2326
unsafe fn solve(MatrixLayout, Transpose, a: &[Self], &Pivot, b: &mut [Self]) -> Result<()>;
2427
}
2528

2629
macro_rules! impl_solve {
27-
($scalar:ty, $getrf:path, $getri:path, $getrs:path) => {
30+
($scalar:ty, $getrf:path, $getri:path, $gecon:path, $getrs:path) => {
2831

2932
impl Solve_ for $scalar {
3033
unsafe fn lu(l: MatrixLayout, a: &mut [Self]) -> Result<Pivot> {
@@ -41,6 +44,13 @@ impl Solve_ for $scalar {
4144
into_result(info, ())
4245
}
4346

47+
unsafe fn rcond(l: MatrixLayout, a: &[Self], anorm: Self::Real) -> Result<Self::Real> {
48+
let (n, _) = l.size();
49+
let mut rcond = Self::Real::zero();
50+
let info = $gecon(l.lapacke_layout(), NormType::One as u8, n, a, l.lda(), anorm, &mut rcond);
51+
into_result(info, rcond)
52+
}
53+
4454
unsafe fn solve(l: MatrixLayout, t: Transpose, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()> {
4555
let (n, _) = l.size();
4656
let nrhs = 1;
@@ -52,7 +62,7 @@ impl Solve_ for $scalar {
5262

5363
}} // impl_solve!
5464

55-
impl_solve!(f64, c::dgetrf, c::dgetri, c::dgetrs);
56-
impl_solve!(f32, c::sgetrf, c::sgetri, c::sgetrs);
57-
impl_solve!(c64, c::zgetrf, c::zgetri, c::zgetrs);
58-
impl_solve!(c32, c::cgetrf, c::cgetri, c::cgetrs);
65+
impl_solve!(f64, c::dgetrf, c::dgetri, c::dgecon, c::dgetrs);
66+
impl_solve!(f32, c::sgetrf, c::sgetri, c::sgecon, c::sgetrs);
67+
impl_solve!(c64, c::zgetrf, c::zgetri, c::zgecon, c::zgetrs);
68+
impl_solve!(c32, c::cgetrf, c::cgetri, c::cgecon, c::cgetrs);

src/solve.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ use ndarray::*;
4949

5050
use super::convert::*;
5151
use super::error::*;
52+
use super::lapack_traits::opnorm::NormType;
5253
use super::layout::*;
54+
use super::opnorm::OperationNorm;
5355
use super::types::*;
5456

5557
pub use lapack_traits::{Pivot, Transpose};
@@ -419,3 +421,77 @@ where
419421
}
420422
}
421423
}
424+
425+
/// An interface for *estimating* the reciprocal condition number of matrix refs.
426+
pub trait ReciprocalConditionNum<A: Scalar> {
427+
/// *Estimates* the reciprocal of the condition number of the matrix in
428+
/// 1-norm.
429+
///
430+
/// This method uses the LAPACK `*gecon` routines, which *estimate*
431+
/// `self.inv().opnorm(One)` and then compute `rcond = 1. /
432+
/// (self.opnorm(One) * self.inv().opnorm(One))`.
433+
///
434+
/// * If `rcond` is near `0.`, the matrix is badly conditioned.
435+
/// * If `rcond` is near `1.`, the matrix is well conditioned.
436+
fn rcond(&self) -> Result<A::Real>;
437+
}
438+
439+
/// An interface for *estimating* the reciprocal condition number of matrices.
440+
pub trait ReciprocalConditionNumInto<A: Scalar> {
441+
/// *Estimates* the reciprocal of the condition number of the matrix in
442+
/// 1-norm.
443+
///
444+
/// This method uses the LAPACK `*gecon` routines, which *estimate*
445+
/// `self.inv().opnorm(One)` and then compute `rcond = 1. /
446+
/// (self.opnorm(One) * self.inv().opnorm(One))`.
447+
///
448+
/// * If `rcond` is near `0.`, the matrix is badly conditioned.
449+
/// * If `rcond` is near `1.`, the matrix is well conditioned.
450+
fn rcond_into(self) -> Result<A::Real>;
451+
}
452+
453+
impl<A, S> ReciprocalConditionNum<A> for LUFactorized<S>
454+
where
455+
A: Scalar,
456+
S: Data<Elem = A>,
457+
{
458+
fn rcond(&self) -> Result<A::Real> {
459+
unsafe {
460+
A::rcond(
461+
self.a.layout()?,
462+
self.a.as_allocated()?,
463+
self.a.opnorm(NormType::One)?,
464+
)
465+
}
466+
}
467+
}
468+
469+
impl<A, S> ReciprocalConditionNumInto<A> for LUFactorized<S>
470+
where
471+
A: Scalar,
472+
S: Data<Elem = A>,
473+
{
474+
fn rcond_into(self) -> Result<A::Real> {
475+
self.rcond()
476+
}
477+
}
478+
479+
impl<A, S> ReciprocalConditionNum<A> for ArrayBase<S, Ix2>
480+
where
481+
A: Scalar,
482+
S: Data<Elem = A>,
483+
{
484+
fn rcond(&self) -> Result<A::Real> {
485+
self.factorize()?.rcond_into()
486+
}
487+
}
488+
489+
impl<A, S> ReciprocalConditionNumInto<A> for ArrayBase<S, Ix2>
490+
where
491+
A: Scalar,
492+
S: DataMut<Elem = A>,
493+
{
494+
fn rcond_into(self) -> Result<A::Real> {
495+
self.factorize_into()?.rcond_into()
496+
}
497+
}

tests/solve.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ extern crate num_traits;
55

66
use ndarray::*;
77
use ndarray_linalg::*;
8+
use ndarray_linalg::lapack_traits::opnorm::NormType;
89

910
#[test]
1011
fn solve_random() {
@@ -23,3 +24,51 @@ fn solve_random_t() {
2324
let y = a.solve_into(b).unwrap();
2425
assert_close_l2!(&x, &y, 1e-7);
2526
}
27+
28+
#[test]
29+
fn rcond() {
30+
macro_rules! rcond {
31+
($elem:ty, $rows:expr, $atol:expr) => {
32+
let a: Array2<$elem> = random(($rows, $rows));
33+
let rcond = 1. / (a.opnorm(NormType::One).unwrap() * a.inv().unwrap().opnorm(NormType::One).unwrap());
34+
assert_aclose!(a.rcond().unwrap(), rcond, $atol);
35+
assert_aclose!(a.rcond_into().unwrap(), rcond, $atol);
36+
}
37+
}
38+
for rows in 1..6 {
39+
rcond!(f64, rows, 0.2);
40+
rcond!(f32, rows, 0.5);
41+
rcond!(c64, rows, 0.2);
42+
rcond!(c32, rows, 0.5);
43+
}
44+
}
45+
46+
#[test]
47+
fn rcond_hilbert() {
48+
macro_rules! rcond_hilbert {
49+
($elem:ty, $rows:expr, $atol:expr) => {
50+
let a = Array2::<$elem>::from_shape_fn(($rows, $rows), |(i, j)| 1. / (i as $elem + j as $elem - 1.));
51+
assert_aclose!(a.rcond().unwrap(), 0., $atol);
52+
assert_aclose!(a.rcond_into().unwrap(), 0., $atol);
53+
}
54+
}
55+
rcond_hilbert!(f64, 10, 1e-9);
56+
rcond_hilbert!(f32, 10, 1e-3);
57+
}
58+
59+
#[test]
60+
fn rcond_identity() {
61+
macro_rules! rcond_identity {
62+
($elem:ty, $rows:expr, $atol:expr) => {
63+
let a = Array2::<$elem>::eye($rows);
64+
assert_aclose!(a.rcond().unwrap(), 1., $atol);
65+
assert_aclose!(a.rcond_into().unwrap(), 1., $atol);
66+
}
67+
}
68+
for rows in 1..6 {
69+
rcond_identity!(f64, rows, 1e-9);
70+
rcond_identity!(f32, rows, 1e-3);
71+
rcond_identity!(c64, rows, 1e-9);
72+
rcond_identity!(c32, rows, 1e-3);
73+
}
74+
}

0 commit comments

Comments
 (0)