Skip to content

Commit f8b1395

Browse files
committed
Add new lobpcg integration test and move Marchenko-Pastur into it
1 parent d201ca1 commit f8b1395

File tree

3 files changed

+70
-56
lines changed

3 files changed

+70
-56
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,8 @@ rand_xoshiro = { version = "0.6" }
2525
[features]
2626
default = ["iterative"]
2727
iterative = ["rand"]
28+
29+
[[test]]
30+
name = "lobpcg"
31+
path = "tests/lobpcg.rs"
32+
required-features = ["iterative"]

src/lobpcg/svd.rs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -318,60 +318,4 @@ mod tests {
318318

319319
assert_abs_diff_eq!(&a, &reconstructed, epsilon = 1e-5);
320320
}
321-
322-
/// Eigenvalue structure in high dimensions
323-
///
324-
/// This test checks that the eigenvalues are following the Marchensko-Pastur law. The data is
325-
/// standard uniformly distributed (i.e. E(x) = 0, E^2(x) = 1) and we have twice the amount of
326-
/// data when compared to features. The probability density of the eigenvalues should then follow
327-
/// a special densitiy function, described by the Marchenko-Pastur law.
328-
///
329-
/// See also https://en.wikipedia.org/wiki/Marchenko%E2%80%93Pastur_distribution
330-
#[test]
331-
fn test_marchenko_pastur() {
332-
// create random number generator
333-
let mut rng = Xoshiro256Plus::seed_from_u64(3);
334-
335-
// generate normal distribution random data with N >> p
336-
let data = Array2::random_using((1000, 500), StandardNormal, &mut rng) / 1000f64.sqrt();
337-
338-
let res =
339-
TruncatedSvd::new_with_rng(data, Order::Largest, Xoshiro256Plus::seed_from_u64(42))
340-
.precision(1e-3)
341-
.decompose(500)
342-
.unwrap();
343-
344-
let sv = res.values().mapv(|x: f64| x * x);
345-
346-
// we have created a random spectrum and can apply the Marchenko-Pastur law
347-
// with variance 1 and p/n = 0.5
348-
let (a, b) = (
349-
1. * (1. - 0.5f64.sqrt()).powf(2.0),
350-
1. * (1. + 0.5f64.sqrt()).powf(2.0),
351-
);
352-
353-
// check that the spectrum has correct boundaries
354-
assert_abs_diff_eq!(b, sv[0], epsilon = 0.1);
355-
assert_abs_diff_eq!(a, sv[sv.len() - 1], epsilon = 0.1);
356-
357-
// estimate density empirical and compare with Marchenko-Pastur law
358-
let mut i = 0;
359-
'outer: for th in Array1::linspace(0.1f64, 2.8, 28).slice(s![..;-1]) {
360-
let mut count = 0;
361-
while sv[i] >= *th {
362-
count += 1;
363-
i += 1;
364-
365-
if i == sv.len() {
366-
break 'outer;
367-
}
368-
}
369-
370-
let x = th + 0.05;
371-
let mp_law = ((b - x) * (x - a)).sqrt() / std::f64::consts::PI / x;
372-
let empirical = count as f64 / 500. / ((2.8 - 0.1) / 28.);
373-
374-
assert_abs_diff_eq!(mp_law, empirical, epsilon = 0.05);
375-
}
376-
}
377321
}

tests/lobpcg.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use approx::assert_abs_diff_eq;
2+
use ndarray::prelude::*;
3+
use ndarray_rand::{rand_distr::StandardNormal, RandomExt};
4+
use proptest::prelude::*;
5+
6+
use ndarray_linalg_rs::lobpcg::*;
7+
use rand::SeedableRng;
8+
use rand_xoshiro::Xoshiro256Plus;
9+
10+
mod common;
11+
12+
/// Eigenvalue structure in high dimensions
13+
///
14+
/// This test checks that the eigenvalues are following the Marchensko-Pastur law. The data is
15+
/// standard uniformly distributed (i.e. E(x) = 0, E^2(x) = 1) and we have twice the amount of
16+
/// data when compared to features. The probability density of the eigenvalues should then follow
17+
/// a special densitiy function, described by the Marchenko-Pastur law.
18+
///
19+
/// See also https://en.wikipedia.org/wiki/Marchenko%E2%80%93Pastur_distribution
20+
#[test]
21+
fn test_marchenko_pastur() {
22+
// create random number generator
23+
let mut rng = Xoshiro256Plus::seed_from_u64(3);
24+
25+
// generate normal distribution random data with N >> p
26+
let data = Array2::random_using((1000, 500), StandardNormal, &mut rng) / 1000f64.sqrt();
27+
28+
let res = TruncatedSvd::new_with_rng(data, Order::Largest, Xoshiro256Plus::seed_from_u64(42))
29+
.precision(1e-3)
30+
.decompose(500)
31+
.unwrap();
32+
33+
let sv = res.values().mapv(|x: f64| x * x);
34+
35+
// we have created a random spectrum and can apply the Marchenko-Pastur law
36+
// with variance 1 and p/n = 0.5
37+
let (a, b) = (
38+
1. * (1. - 0.5f64.sqrt()).powf(2.0),
39+
1. * (1. + 0.5f64.sqrt()).powf(2.0),
40+
);
41+
42+
// check that the spectrum has correct boundaries
43+
assert_abs_diff_eq!(b, sv[0], epsilon = 0.1);
44+
assert_abs_diff_eq!(a, sv[sv.len() - 1], epsilon = 0.1);
45+
46+
// estimate density empirical and compare with Marchenko-Pastur law
47+
let mut i = 0;
48+
'outer: for th in Array1::linspace(0.1f64, 2.8, 28).slice(s![..;-1]) {
49+
let mut count = 0;
50+
while sv[i] >= *th {
51+
count += 1;
52+
i += 1;
53+
54+
if i == sv.len() {
55+
break 'outer;
56+
}
57+
}
58+
59+
let x = th + 0.05;
60+
let mp_law = ((b - x) * (x - a)).sqrt() / std::f64::consts::PI / x;
61+
let empirical = count as f64 / 500. / ((2.8 - 0.1) / 28.);
62+
63+
assert_abs_diff_eq!(mp_law, empirical, epsilon = 0.05);
64+
}
65+
}

0 commit comments

Comments
 (0)