6
6
//!
7
7
//! # Example
8
8
//!
9
- //! Calculate `L` in the Cholesky decomposition `A = L * L^H` , where `A` is a
10
- //! Hermitian (or real symmetric) positive definite matrix:
9
+ //! Using the Cholesky decomposition of `A` for various operations , where `A`
10
+ //! is a Hermitian (or real symmetric) positive definite matrix:
11
11
//!
12
12
//! ```
13
13
//! #[macro_use]
14
14
//! extern crate ndarray;
15
15
//! extern crate ndarray_linalg;
16
16
//!
17
17
//! use ndarray::prelude::*;
18
- //! use ndarray_linalg::{CholeskyInto , UPLO};
18
+ //! use ndarray_linalg::{Cholesky , UPLO};
19
19
//! # fn main() {
20
20
//!
21
21
//! let a: Array2<f64> = array![
22
22
//! [ 4., 12., -16.],
23
23
//! [ 12., 37., -43.],
24
24
//! [-16., -43., 98.]
25
25
//! ];
26
- //! let lower = a.cholesky_into(UPLO::Lower).unwrap();
27
- //! assert!(lower.all_close(&array![
26
+ //! let chol_lower = a.cholesky(UPLO::Lower).unwrap();
27
+ //!
28
+ //! // Examine `L`
29
+ //! assert!(chol_lower.factor.all_close(&array![
28
30
//! [ 2., 0., 0.],
29
31
//! [ 6., 1., 0.],
30
32
//! [-8., 5., 3.]
31
33
//! ], 1e-9));
34
+ //!
35
+ //! // Find the determinant of `A`
36
+ //! let det = chol_lower.det();
37
+ //! assert!((det - 36.).abs() < 1e-9);
38
+ //!
39
+ //! // Solve `A * x = b`
40
+ //! let b = array![4., 13., -11.];
41
+ //! let x = chol_lower.solve(&b).unwrap();
42
+ //! assert!(x.all_close(&array![-2., 1., 0.], 1e-9));
32
43
//! # }
33
44
//! ```
34
45
35
46
use ndarray:: * ;
47
+ use num_traits:: Float ;
36
48
37
49
use super :: convert:: * ;
38
50
use super :: error:: * ;
@@ -42,79 +54,212 @@ use super::types::*;
42
54
43
55
pub use lapack_traits:: UPLO ;
44
56
57
+ /// Cholesky decomposition of Hermitian (or real symmetric) positive definite matrix
58
+ pub struct FactorizedCholesky < S >
59
+ where
60
+ S : Data ,
61
+ {
62
+ /// `L` from the decomposition `A = L * L^H` or `U` from the decomposition
63
+ /// `A = U^H * U`.
64
+ pub factor : ArrayBase < S , Ix2 > ,
65
+ /// If this is `UPLO::Lower`, then `self.factor` is `L`. If this is
66
+ /// `UPLO::Upper`, then `self.factor` is `U`.
67
+ pub uplo : UPLO ,
68
+ }
69
+
70
+ impl < A , S > FactorizedCholesky < S >
71
+ where
72
+ A : Scalar ,
73
+ S : DataMut < Elem = A > ,
74
+ {
75
+ /// Returns `L` from the Cholesky decomposition `A = L * L^H`.
76
+ ///
77
+ /// If `self.uplo == UPLO::Lower`, then no computations need to be
78
+ /// performed; otherwise, the conjugate transpose of `self.factor` is
79
+ /// calculated.
80
+ pub fn into_lower ( self ) -> ArrayBase < S , Ix2 > {
81
+ match self . uplo {
82
+ UPLO :: Lower => self . factor ,
83
+ UPLO :: Upper => self . factor . reversed_axes ( ) . mapv_into ( |elem| elem. conj ( ) ) ,
84
+ }
85
+ }
86
+
87
+ /// Returns `U` from the Cholesky decomposition `A = U^H * U`.
88
+ ///
89
+ /// If `self.uplo == UPLO::Upper`, then no computations need to be
90
+ /// performed; otherwise, the conjugate transpose of `self.factor` is
91
+ /// calculated.
92
+ pub fn into_upper ( self ) -> ArrayBase < S , Ix2 > {
93
+ match self . uplo {
94
+ UPLO :: Lower => self . factor . reversed_axes ( ) . mapv_into ( |elem| elem. conj ( ) ) ,
95
+ UPLO :: Upper => self . factor ,
96
+ }
97
+ }
98
+
99
+ /// Computes the inverse of the Cholesky-factored matrix.
100
+ ///
101
+ /// **Warning: The inverse is stored only in the triangular portion of the
102
+ /// result matrix corresponding to `self.uplo`!** If you want the other
103
+ /// triangular portion to be correct, you must fill it in yourself.
104
+ pub fn into_inverse ( mut self ) -> Result < ArrayBase < S , Ix2 > > {
105
+ unsafe {
106
+ A :: inv_cholesky (
107
+ self . factor . square_layout ( ) ?,
108
+ self . uplo ,
109
+ self . factor . as_allocated_mut ( ) ?,
110
+ ) ?
111
+ } ;
112
+ Ok ( self . factor )
113
+ }
114
+ }
115
+
116
+ impl < A , S > FactorizedCholesky < S >
117
+ where
118
+ A : Absolute ,
119
+ S : Data < Elem = A > ,
120
+ {
121
+ /// Computes the natural log of the determinant of the Cholesky-factored
122
+ /// matrix.
123
+ pub fn ln_det ( & self ) -> <A as AssociatedReal >:: Real {
124
+ self . factor
125
+ . diag ( )
126
+ . iter ( )
127
+ . map ( |elem| elem. abs_sqr ( ) . ln ( ) )
128
+ . sum ( )
129
+ }
130
+
131
+ /// Computes the determinant of the Cholesky-factored matrix.
132
+ pub fn det ( & self ) -> <A as AssociatedReal >:: Real {
133
+ self . ln_det ( ) . exp ( )
134
+ }
135
+ }
136
+
137
+ impl < A , S > FactorizedCholesky < S >
138
+ where
139
+ A : Scalar ,
140
+ S : Data < Elem = A > ,
141
+ {
142
+ /// Solves a system of linear equations `A * x = b`, where `self` is the
143
+ /// Cholesky factorization of `A`, `b` is the argument, and `x` is the
144
+ /// successful result.
145
+ pub fn solve < Sb > ( & self , b : & ArrayBase < Sb , Ix1 > ) -> Result < Array1 < A > >
146
+ where
147
+ Sb : Data < Elem = A > ,
148
+ {
149
+ let mut b = replicate ( b) ;
150
+ self . solve_mut ( & mut b) ?;
151
+ Ok ( b)
152
+ }
153
+
154
+ /// Solves a system of linear equations `A * x = b`, where `self` is the
155
+ /// Cholesky factorization `A`, `b` is the argument, and `x` is the
156
+ /// successful result.
157
+ pub fn solve_into < Sb > ( & self , mut b : ArrayBase < Sb , Ix1 > ) -> Result < ArrayBase < Sb , Ix1 > >
158
+ where
159
+ Sb : DataMut < Elem = A > ,
160
+ {
161
+ self . solve_mut ( & mut b) ?;
162
+ Ok ( b)
163
+ }
164
+
165
+ /// Solves a system of linear equations `A * x = b`, where `self` is the
166
+ /// Cholesky factorization of `A`, `b` is the argument, and `x` is the
167
+ /// successful result. The value of `x` is also assigned to the argument.
168
+ pub fn solve_mut < ' a , Sb > ( & self , b : & ' a mut ArrayBase < Sb , Ix1 > ) -> Result < & ' a mut ArrayBase < Sb , Ix1 > >
169
+ where
170
+ Sb : DataMut < Elem = A > ,
171
+ {
172
+ unsafe {
173
+ A :: solve_cholesky (
174
+ self . factor . square_layout ( ) ?,
175
+ self . uplo ,
176
+ self . factor . as_allocated ( ) ?,
177
+ b. as_slice_mut ( ) . unwrap ( ) ,
178
+ ) ?
179
+ } ;
180
+ Ok ( b)
181
+ }
182
+ }
183
+
45
184
/// Cholesky decomposition of Hermitian (or real symmetric) positive definite matrix reference
46
- pub trait Cholesky {
47
- type Output ;
185
+ pub trait Cholesky < S : Data > {
48
186
/// Computes the Cholesky decomposition of the Hermitian (or real
49
187
/// symmetric) positive definite matrix.
50
188
///
51
189
/// If the argument is `UPLO::Upper`, then computes the decomposition `A =
52
- /// U^H * U` using the upper triangular portion of `A` and returns `U`.
53
- /// Otherwise, if the argument is `UPLO::Lower`, computes the decomposition
54
- /// `A = L * L^H` using the lower triangular portion of `A` and returns
55
- /// `L`.
56
- fn cholesky ( & self , UPLO ) -> Result < Self :: Output > ;
190
+ /// U^H * U` using the upper triangular portion of `A` and returns the
191
+ /// factorization containing `U`. Otherwise, if the argument is
192
+ /// `UPLO::Lower`, computes the decomposition ` A = L * L^H` using the lower
193
+ /// triangular portion of `A` and returns the factorization containing `L`.
194
+ fn cholesky ( & self , UPLO ) -> Result < FactorizedCholesky < S > > ;
57
195
}
58
196
59
197
/// Cholesky decomposition of Hermitian (or real symmetric) positive definite matrix
60
- pub trait CholeskyInto : Sized {
198
+ pub trait CholeskyInto < S : Data > {
61
199
/// Computes the Cholesky decomposition of the Hermitian (or real
62
200
/// symmetric) positive definite matrix.
63
201
///
64
202
/// If the argument is `UPLO::Upper`, then computes the decomposition `A =
65
- /// U^H * U` using the upper triangular portion of `A` and returns `U`.
66
- /// Otherwise, if the argument is `UPLO::Lower`, computes the decomposition
67
- /// `A = L * L^H` using the lower triangular portion of `A` and returns
68
- /// `L`.
69
- fn cholesky_into ( self , UPLO ) -> Result < Self > ;
203
+ /// U^H * U` using the upper triangular portion of `A` and returns the
204
+ /// factorization containing `U`. Otherwise, if the argument is
205
+ /// `UPLO::Lower`, computes the decomposition ` A = L * L^H` using the lower
206
+ /// triangular portion of `A` and returns the factorization containing `L`.
207
+ fn cholesky_into ( self , UPLO ) -> Result < FactorizedCholesky < S > > ;
70
208
}
71
209
72
210
/// Cholesky decomposition of Hermitian (or real symmetric) positive definite mutable reference of matrix
73
- pub trait CholeskyMut {
211
+ pub trait CholeskyMut < ' a , S : Data > {
74
212
/// Computes the Cholesky decomposition of the Hermitian (or real
75
- /// symmetric) positive definite matrix, storing the result in `self` and
76
- /// returning it .
213
+ /// symmetric) positive definite matrix, storing the result (`L` or `U`
214
+ /// according to the argument) in `self` and returning the factorization .
77
215
///
78
216
/// If the argument is `UPLO::Upper`, then computes the decomposition `A =
79
- /// U^H * U` using the upper triangular portion of `A` and returns `U`.
80
- /// Otherwise, if the argument is `UPLO::Lower`, computes the decomposition
81
- /// `A = L * L^H` using the lower triangular portion of `A` and returns
82
- /// `L`.
83
- fn cholesky_mut ( & mut self , UPLO ) -> Result < & mut Self > ;
217
+ /// U^H * U` using the upper triangular portion of `A` and returns the
218
+ /// factorization containing `U`. Otherwise, if the argument is
219
+ /// `UPLO::Lower`, computes the decomposition ` A = L * L^H` using the lower
220
+ /// triangular portion of `A` and returns the factorization containing `L`.
221
+ fn cholesky_mut ( & ' a mut self , UPLO ) -> Result < FactorizedCholesky < S > > ;
84
222
}
85
223
86
- impl < A , S > CholeskyInto for ArrayBase < S , Ix2 >
224
+ impl < A , S > CholeskyInto < S > for ArrayBase < S , Ix2 >
87
225
where
88
226
A : Scalar ,
89
227
S : DataMut < Elem = A > ,
90
228
{
91
- fn cholesky_into ( mut self , uplo : UPLO ) -> Result < Self > {
229
+ fn cholesky_into ( mut self , uplo : UPLO ) -> Result < FactorizedCholesky < S > > {
92
230
unsafe { A :: cholesky ( self . square_layout ( ) ?, uplo, self . as_allocated_mut ( ) ?) ? } ;
93
- Ok ( self . into_triangular ( uplo) )
231
+ Ok ( FactorizedCholesky {
232
+ factor : self . into_triangular ( uplo) ,
233
+ uplo : uplo,
234
+ } )
94
235
}
95
236
}
96
237
97
- impl < A , S > CholeskyMut for ArrayBase < S , Ix2 >
238
+ impl < ' a , A , Si > CholeskyMut < ' a , ViewRepr < & ' a mut A > > for ArrayBase < Si , Ix2 >
98
239
where
99
240
A : Scalar ,
100
- S : DataMut < Elem = A > ,
241
+ Si : DataMut < Elem = A > ,
101
242
{
102
- fn cholesky_mut ( & mut self , uplo : UPLO ) -> Result < & mut Self > {
243
+ fn cholesky_mut ( & ' a mut self , uplo : UPLO ) -> Result < FactorizedCholesky < ViewRepr < & ' a mut A > > > {
103
244
unsafe { A :: cholesky ( self . square_layout ( ) ?, uplo, self . as_allocated_mut ( ) ?) ? } ;
104
- Ok ( self . into_triangular ( uplo) )
245
+ Ok ( FactorizedCholesky {
246
+ factor : self . into_triangular ( uplo) . view_mut ( ) ,
247
+ uplo : uplo,
248
+ } )
105
249
}
106
250
}
107
251
108
- impl < A , S > Cholesky for ArrayBase < S , Ix2 >
252
+ impl < A , Si > Cholesky < OwnedRepr < A > > for ArrayBase < Si , Ix2 >
109
253
where
110
254
A : Scalar ,
111
- S : Data < Elem = A > ,
255
+ Si : Data < Elem = A > ,
112
256
{
113
- type Output = Array2 < A > ;
114
-
115
- fn cholesky ( & self , uplo : UPLO ) -> Result < Self :: Output > {
257
+ fn cholesky ( & self , uplo : UPLO ) -> Result < FactorizedCholesky < OwnedRepr < A > > > {
116
258
let mut a = replicate ( self ) ;
117
259
unsafe { A :: cholesky ( a. square_layout ( ) ?, uplo, a. as_allocated_mut ( ) ?) ? } ;
118
- Ok ( a. into_triangular ( uplo) )
260
+ Ok ( FactorizedCholesky {
261
+ factor : a. into_triangular ( uplo) ,
262
+ uplo : uplo,
263
+ } )
119
264
}
120
265
}
0 commit comments