1- use std :: cmp ;
2-
3- use na:: { DMatrix , DVector , Matrix4x3 , Vector4 } ;
1+ use crate :: proptest :: * ;
2+ use core :: f64 ;
3+ use na:: { Const , DMatrix , Matrix4 , Matrix4xX } ;
44use nl:: Cholesky ;
5+ use proptest:: prelude:: * ;
6+
7+ fn positive_definite_dmatrix ( ) -> impl Strategy < Value = DMatrix < f64 > > {
8+ // @note(geo-ant) to get positive definite matrices we use M*M^T + alpha*I,
9+ // where alpha is a constant that is chosen so that the eigenvales stay
10+ // positive.
11+ dmatrix ( ) . prop_map ( |m| {
12+ let alpha = f64:: EPSILON . sqrt ( ) * m. norm_squared ( ) ;
13+ let nrows = m. nrows ( ) ;
14+ & m * m. transpose ( ) + alpha * DMatrix :: identity ( nrows, nrows)
15+ } )
16+ }
517
6- use crate :: proptest:: * ;
7- use proptest:: { prop_assert, proptest} ;
18+ fn positive_definite_matrix4 ( ) -> impl Strategy < Value = Matrix4 < f64 > > {
19+ matrix4 ( ) . prop_map ( |m| {
20+ let alpha = f64:: EPSILON . sqrt ( ) * m. norm_squared ( ) ;
21+ & m * m. transpose ( ) + alpha * Matrix4 :: identity ( )
22+ } )
23+ }
24+
25+ fn positive_definite_linear_system ( ) -> impl Strategy < Value = ( DMatrix < f64 > , DMatrix < f64 > ) > {
26+ positive_definite_dmatrix ( ) . prop_flat_map ( |a| {
27+ let b = matrix ( PROPTEST_F64 , a. nrows ( ) , PROPTEST_MATRIX_DIM ) ;
28+ ( Just ( a) , b)
29+ } )
30+ }
31+
32+ fn positive_definite_linear_system_4 ( ) -> impl Strategy < Value = ( Matrix4 < f64 > , Matrix4xX < f64 > ) > {
33+ positive_definite_matrix4 ( ) . prop_flat_map ( |a| {
34+ let b = matrix ( PROPTEST_F64 , Const :: < 4 > , PROPTEST_MATRIX_DIM ) ;
35+ ( Just ( a) , b)
36+ } )
37+ }
838
939proptest ! {
1040 #[ test]
11- fn cholesky( m in dmatrix( ) ) {
12- let m = & m * m. transpose( ) ;
41+ fn cholesky( m in positive_definite_dmatrix( ) ) {
1342 if let Some ( chol) = Cholesky :: new( m. clone( ) ) {
1443 let l = chol. unpack( ) ;
1544 let reconstructed_m = & l * l. transpose( ) ;
@@ -19,8 +48,7 @@ proptest! {
1948 }
2049
2150 #[ test]
22- fn cholesky_static( m in matrix3( ) ) {
23- let m = & m * m. transpose( ) ;
51+ fn cholesky_static( m in positive_definite_matrix4( ) ) {
2452 if let Some ( chol) = Cholesky :: new( m) {
2553 let l = chol. unpack( ) ;
2654 let reconstructed_m = & l * l. transpose( ) ;
@@ -30,61 +58,37 @@ proptest! {
3058 }
3159
3260 #[ test]
33- fn cholesky_solve( n in PROPTEST_MATRIX_DIM , nb in PROPTEST_MATRIX_DIM ) {
34- let n = cmp:: min( n, 15 ) ; // To avoid slowing down the test too much.
35- let nb = cmp:: min( nb, 15 ) ; // To avoid slowing down the test too much.
36- let m = DMatrix :: <f64 >:: new_random( n, n) ;
37- let m = & m * m. transpose( ) ;
61+ fn cholesky_solve( ( a, b) in positive_definite_linear_system( ) ) {
3862
39- if let Some ( chol) = Cholesky :: new( m. clone( ) ) {
40- let b1 = DVector :: new_random( n) ;
41- let b2 = DMatrix :: new_random( n, nb) ;
42-
43- let sol1 = chol. solve( & b1) . unwrap( ) ;
44- let sol2 = chol. solve( & b2) . unwrap( ) ;
45-
46- prop_assert!( relative_eq!( & m * sol1, b1, epsilon = 1.0e-6 ) ) ;
47- prop_assert!( relative_eq!( & m * sol2, b2, epsilon = 1.0e-6 ) ) ;
63+ if let Some ( chol) = Cholesky :: new( a. clone( ) ) {
64+ let sol = chol. solve( & b) . unwrap( ) ;
65+ prop_assert!( relative_eq!( & a * sol, b, epsilon = 1.0e-5 ) ) ;
4866 }
4967 }
5068
5169 #[ test]
52- fn cholesky_solve_static( m in matrix4( ) ) {
53- let m = & m * m. transpose( ) ;
54- if let Some ( chol) = Cholesky :: new( m) {
55- let b1 = Vector4 :: new_random( ) ;
56- let b2 = Matrix4x3 :: new_random( ) ;
57-
58- let sol1 = chol. solve( & b1) . unwrap( ) ;
59- let sol2 = chol. solve( & b2) . unwrap( ) ;
60-
61- prop_assert!( relative_eq!( m * sol1, b1, epsilon = 1.0e-4 ) ) ;
62- prop_assert!( relative_eq!( m * sol2, b2, epsilon = 1.0e-4 ) ) ;
70+ fn cholesky_solve_static( ( a, b) in positive_definite_linear_system_4( ) ) {
71+ if let Some ( chol) = Cholesky :: new( a) {
72+ let sol = chol. solve( & b) . unwrap( ) ;
73+ prop_assert!( relative_eq!( a * sol, b, epsilon = 1.0e-5 ) ) ;
6374 }
6475 }
6576
6677 #[ test]
67- fn cholesky_inverse( n in PROPTEST_MATRIX_DIM ) {
68- let n = cmp:: min( n, 15 ) ; // To avoid slowing down the test too much.
69- let m = DMatrix :: <f64 >:: new_random( n, n) ;
70- let m = & m * m. transpose( ) ;
71-
72- if let Some ( m1) = Cholesky :: new( m. clone( ) ) . unwrap( ) . inverse( ) {
73- let id1 = & m * & m1;
74- let id2 = & m1 * & m;
78+ fn cholesky_inverse( a in positive_definite_dmatrix( ) ) {
79+ let minv = Cholesky :: new( a. clone( ) ) . unwrap( ) . inverse( ) . unwrap( ) ;
80+ let id1 = & a * & minv;
81+ let id2 = & minv * & a;
7582
76- prop_assert!( id1. is_identity( 1.0e-6 ) && id2. is_identity( 1.0e-6 ) ) ;
77- }
83+ prop_assert!( id1. is_identity( 1.0e-6 ) && id2. is_identity( 1.0e-6 ) ) ;
7884 }
7985
8086 #[ test]
81- fn cholesky_inverse_static( m in matrix4( ) ) {
82- let m = m * m. transpose( ) ;
83- if let Some ( m1) = Cholesky :: new( m. clone( ) ) . unwrap( ) . inverse( ) {
84- let id1 = & m * & m1;
85- let id2 = & m1 * & m;
87+ fn cholesky_inverse_static( a in positive_definite_matrix4( ) ) {
88+ let minv = Cholesky :: new( a. clone( ) ) . unwrap( ) . inverse( ) . unwrap( ) ;
89+ let id1 = & a * & minv;
90+ let id2 = & minv * & a;
8691
87- prop_assert!( id1. is_identity( 1.0e-4 ) && id2. is_identity( 1.0e-4 ) )
88- }
92+ prop_assert!( id1. is_identity( 1.0e-6 ) && id2. is_identity( 1.0e-6 ) ) ;
8993 }
9094}
0 commit comments