1use matrix::{Matrix, MatrixSliceMut, BaseMatrix, BaseMatrixMut};
2use norm::Euclidean;
3use error::{Error, ErrorKind};
4
5use std::cmp;
6use std::any::Any;
7
8use libnum::{Float, Signed};
9use libnum::{cast, abs};
10
11impl<T: Any + Float + Signed> Matrix<T> {
12 fn balance_matrix(&mut self) {
13 let n = self.rows();
14 let radix = T::one() + T::one();
15
16 debug_assert!(n == self.cols(),
17 "Matrix must be square to produce balance matrix.");
18
19 let mut d = Matrix::<T>::identity(n);
20 let mut converged = false;
21
22 while !converged {
23 converged = true;
24
25 for i in 0..n {
26 let mut c = self.select_cols(&[i]).norm(Euclidean);
27 let mut r = self.select_rows(&[i]).norm(Euclidean);
28
29 let s = c * c + r * r;
30 let mut f = T::one();
31
32 while c < r / radix {
33 c = c * radix;
34 r = r / radix;
35 f = f * radix;
36 }
37
38 while c >= r * radix {
39 c = c / radix;
40 r = r * radix;
41 f = f / radix;
42 }
43
44 if (c * c + r * r) < cast::<f64, T>(0.95).unwrap() * s {
45 converged = false;
46 d.data[i * (self.cols + 1)] = f * d.data[i * (self.cols + 1)];
47
48 for j in 0..n {
49 self.data[j * self.cols + i] = f * self.data[j * self.cols + i];
50 self.data[i * self.cols + j] = self.data[i * self.cols + j] / f;
51 }
52 }
53 }
54 }
55 }
56
57 fn direct_2_by_2_eigenvalues(&self) -> Result<Vec<T>, Error> {
58 let tr = self.data[0] + self.data[3];
62 let det = self.data[0] * self.data[3] - self.data[1] * self.data[2];
63
64 let two = T::one() + T::one();
65 let four = two + two;
66
67 let discr = tr * tr - four * det;
68
69 if discr < T::zero() {
70 Err(Error::new(ErrorKind::DecompFailure,
71 "Matrix has complex eigenvalues. Currently unsupported, sorry!"))
72 } else {
73 let discr_root = discr.sqrt();
74 Ok(vec![(tr - discr_root) / two, (tr + discr_root) / two])
75 }
76
77 }
78
79 fn francis_shift_eigenvalues(self) -> Result<Vec<T>, Error> {
80 let n = self.rows();
81 debug_assert!(n > 2,
82 "Francis shift only works on matrices greater than 2x2.");
83 debug_assert!(n == self.cols, "Matrix must be square for Francis shift.");
84
85 let mut h = try!(self
86 .upper_hessenberg()
87 .map_err(|_| Error::new(ErrorKind::DecompFailure, "Could not compute eigenvalues.")));
88 h.balance_matrix();
89
90 let mut p = n - 1;
92
93 let eps = cast::<f64, T>(1e-20).expect("Failed to cast value for convergence check.");
94
95 while p > 1 {
96 let q = p - 1;
97 let s = h[[q, q]] + h[[p, p]];
98 let t = h[[q, q]] * h[[p, p]] - h[[q, p]] * h[[p, q]];
99
100 let mut x = h[[0, 0]] * h[[0, 0]] + h[[0, 1]] * h[[1, 0]] - h[[0, 0]] * s + t;
101 let mut y = h[[1, 0]] * (h[[0, 0]] + h[[1, 1]] - s);
102 let mut z = h[[1, 0]] * h[[2, 1]];
103
104 for k in 0..p - 1 {
105 let r = cmp::max(1, k) - 1;
106
107 let householder = try!(Matrix::make_householder(&[x, y, z]).map_err(|_| {
108 Error::new(ErrorKind::DecompFailure, "Could not compute eigenvalues.")
109 }));
110
111 {
112 let h_block = MatrixSliceMut::from_matrix(&mut h, [k, r], 3, n - r);
114 let transformed = &householder * &h_block;
115 h_block.set_to(transformed.as_slice());
116 }
117
118 let r = cmp::min(k + 4, p + 1);
119
120 {
121 let h_block = MatrixSliceMut::from_matrix(&mut h, [0, k], r, 3);
123 let transformed = &h_block * householder.transpose();
124 h_block.set_to(transformed.as_slice());
125 }
126
127 x = h[[k + 1, k]];
128 y = h[[k + 2, k]];
129
130 if k < p - 2 {
131 z = h[[k + 3, k]];
132 }
133 }
134
135 let (c, s) = Matrix::givens_rot(x, y);
136 let givens_mat = Matrix::new(2, 2, vec![c, -s, s, c]);
137
138 {
139 let h_block = MatrixSliceMut::from_matrix(&mut h, [q, p - 2], 2, n - p + 2);
141 let transformed = &givens_mat * &h_block;
142 h_block.set_to(transformed.as_slice());
143 }
144
145 {
146 let h_block = MatrixSliceMut::from_matrix(&mut h, [0, q], p + 1, 2);
148 let transformed = &h_block * givens_mat.transpose();
149 h_block.set_to(transformed.as_slice());
150 }
151
152 if abs(h[[p, q]]) < eps * (abs(h[[q, q]]) + abs(h[[p, p]])) {
154 h.data[p * h.cols + q] = T::zero();
155 p -= 1;
156 } else if abs(h[[p - 1, q - 1]]) < eps * (abs(h[[q - 1, q - 1]]) + abs(h[[q, q]])) {
157 h.data[(p - 1) * h.cols + q - 1] = T::zero();
158 p -= 2;
159 }
160 }
161
162 Ok(h.diag().cloned().collect::<Vec<_>>())
163 }
164
165 pub fn eigenvalues(self) -> Result<Vec<T>, Error> {
187 let n = self.rows();
188 assert!(n == self.cols,
189 "Matrix must be square for eigenvalue computation.");
190
191 match n {
192 1 => Ok(vec![self.data[0]]),
193 2 => self.direct_2_by_2_eigenvalues(),
194 _ => self.francis_shift_eigenvalues(),
195 }
196 }
197
198 fn direct_2_by_2_eigendecomp(&self) -> Result<(Vec<T>, Matrix<T>), Error> {
199 let eigenvalues = try!(self.direct_2_by_2_eigenvalues());
200 if self.data[2] != T::zero() {
204 let decomp_data = vec![eigenvalues[0] - self.data[3],
205 eigenvalues[1] - self.data[3],
206 self.data[2],
207 self.data[2]];
208 Ok((eigenvalues, Matrix::new(2, 2, decomp_data)))
209 } else if self.data[1] != T::zero() {
210 let decomp_data = vec![self.data[1],
211 self.data[1],
212 eigenvalues[0] - self.data[0],
213 eigenvalues[1] - self.data[0]];
214 Ok((eigenvalues, Matrix::new(2, 2, decomp_data)))
215 } else {
216 Ok((eigenvalues, Matrix::new(2, 2, vec![T::one(), T::zero(), T::zero(), T::one()])))
217 }
218 }
219
220 fn francis_shift_eigendecomp(self) -> Result<(Vec<T>, Matrix<T>), Error> {
221 let n = self.rows();
222 debug_assert!(n > 2,
223 "Francis shift only works on matrices greater than 2x2.");
224 debug_assert!(n == self.cols, "Matrix must be square for Francis shift.");
225
226 let (u, mut h) = try!(self.upper_hess_decomp().map_err(|_| {
227 Error::new(ErrorKind::DecompFailure,
228 "Could not compute eigen decomposition.")
229 }));
230 h.balance_matrix();
231 let mut transformation = Matrix::identity(n);
232
233 let mut p = n - 1;
235
236 let eps = cast::<f64, T>(1e-20).expect("Failed to cast value for convergence check.");
237
238 while p > 1 {
239 let q = p - 1;
240 let s = h[[q, q]] + h[[p, p]];
241 let t = h[[q, q]] * h[[p, p]] - h[[q, p]] * h[[p, q]];
242
243 let mut x = h[[0, 0]] * h[[0, 0]] + h[[0, 1]] * h[[1, 0]] - h[[0, 0]] * s + t;
244 let mut y = h[[1, 0]] * (h[[0, 0]] + h[[1, 1]] - s);
245 let mut z = h[[1, 0]] * h[[2, 1]];
246
247 for k in 0..p - 1 {
248 let r = cmp::max(1, k) - 1;
249
250 let householder = try!(Matrix::make_householder(&[x, y, z]).map_err(|_| {
251 Error::new(ErrorKind::DecompFailure,
252 "Could not compute eigen decomposition.")
253 }));
254
255 {
256 let h_block = MatrixSliceMut::from_matrix(&mut h, [k, r], 3, n - r);
258 let transformed = &householder * &h_block;
259 h_block.set_to(transformed.as_slice());
260 }
261
262 let r = cmp::min(k + 4, p + 1);
263
264 {
265 let h_block = MatrixSliceMut::from_matrix(&mut h, [0, k], r, 3);
267 let transformed = &h_block * householder.transpose();
268 h_block.set_to(transformed.as_slice());
269 }
270
271 {
272 let trans_block =
274 MatrixSliceMut::from_matrix(&mut transformation, [0, k], n, 3);
275 let transformed = &trans_block * householder.transpose();
276 trans_block.set_to(transformed.as_slice());
277 }
278
279 x = h[[k + 1, k]];
280 y = h[[k + 2, k]];
281
282 if k < p - 2 {
283 z = h[[k + 3, k]];
284 }
285 }
286
287 let (c, s) = Matrix::givens_rot(x, y);
288 let givens_mat = Matrix::new(2, 2, vec![c, -s, s, c]);
289
290 {
291 let h_block = MatrixSliceMut::from_matrix(&mut h, [q, p - 2], 2, n - p + 2);
293 let transformed = &givens_mat * &h_block;
294 h_block.set_to(transformed.as_slice());
295 }
296
297 {
298 let h_block = MatrixSliceMut::from_matrix(&mut h, [0, q], p + 1, 2);
300 let transformed = &h_block * givens_mat.transpose();
301 h_block.set_to(transformed.as_slice());
302 }
303
304 {
305 let trans_block = MatrixSliceMut::from_matrix(&mut transformation, [0, q], n, 2);
307 let transformed = &trans_block * givens_mat.transpose();
308 trans_block.set_to(transformed.as_slice());
309 }
310
311 if abs(h[[p, q]]) < eps * (abs(h[[q, q]]) + abs(h[[p, p]])) {
313 h.data[p * h.cols + q] = T::zero();
314 p -= 1;
315 } else if abs(h[[p - 1, q - 1]]) < eps * (abs(h[[q - 1, q - 1]]) + abs(h[[q, q]])) {
316 h.data[(p - 1) * h.cols + q - 1] = T::zero();
317 p -= 2;
318 }
319 }
320
321 Ok((h.diag().cloned().collect::<Vec<_>>(), u * transformation))
322 }
323
324 pub fn eigendecomp(self) -> Result<(Vec<T>, Matrix<T>), Error> {
354 let n = self.rows();
355 assert!(n == self.cols, "Matrix must be square for eigendecomp.");
356
357 match n {
358 1 => Ok((vec![self.data[0]], Matrix::ones(1, 1))),
359 2 => self.direct_2_by_2_eigendecomp(),
360 _ => self.francis_shift_eigendecomp(),
361 }
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use matrix::Matrix;
368
369 #[test]
370 fn test_1_by_1_matrix_eigenvalues() {
371 let a = Matrix::ones(1, 1) * 3.;
372 assert_eq!(vec![3.], a.eigenvalues().unwrap());
373 }
374
375 #[test]
376 fn test_2_by_2_matrix_eigenvalues() {
377 let a = matrix![1., 2.; 3., 4.];
378 assert_eq!(vec![(5. - (33.0f32).sqrt()) / 2., (5. + (33.0f32).sqrt()) / 2.],
380 a.eigenvalues().unwrap());
381 }
382
383 #[test]
384 fn test_2_by_2_matrix_zeros_eigenvalues() {
385 let a = Matrix::zeros(2, 2);
386 assert_eq!(vec![0.0, 0.0], a.eigenvalues().unwrap());
388 }
389
390 #[test]
391 fn test_2_by_2_matrix_complex_eigenvalues() {
392 let a = matrix![1., -3.; 1., 1.];
394 assert!(a.eigenvalues().is_err());
398 }
399
400 #[test]
401 fn test_2_by_2_matrix_eigendecomp() {
402 let a = matrix![20., 4.; 20., 16.];
403 let (eigenvals, eigenvecs) = a.clone().eigendecomp().unwrap();
404
405 let lambda_1 = eigenvals[0];
406 let lambda_2 = eigenvals[1];
407
408 let v1 = vector![eigenvecs[[0, 0]], eigenvecs[[1, 0]]];
409 let v2 = vector![eigenvecs[[0, 1]], eigenvecs[[1, 1]]];
410
411 let epsilon = 0.00001;
412 assert!((&a * &v1 - &v1 * lambda_1).into_vec().iter().all(|&c| c < epsilon));
413 assert!((&a * &v2 - &v2 * lambda_2).into_vec().iter().all(|&c| c < epsilon));
414 }
415
416 #[test]
417 fn test_3_by_3_eigenvals() {
418 let a = matrix![17f64, 22., 27.;
419 22., 29., 36.;
420 27., 36., 45.];
421
422 let eigs = a.clone().eigenvalues().unwrap();
423
424 let eig_1 = 90.4026;
425 let eig_2 = 0.5973;
426 let eig_3 = 0.0;
427
428 assert!(eigs.iter().any(|x| (x - eig_1).abs() < 1e-4));
429 assert!(eigs.iter().any(|x| (x - eig_2).abs() < 1e-4));
430 assert!(eigs.iter().any(|x| (x - eig_3).abs() < 1e-4));
431 }
432
433 #[test]
434 fn test_5_by_5_eigenvals() {
435 let a = matrix![1f64, 2.0, 3.0, 4.0, 5.0;
436 2.0, 4.0, 1.0, 2.0, 1.0;
437 3.0, 1.0, 7.0, 1.0, 1.0;
438 4.0, 2.0, 1.0, -1.0, 3.0;
439 5.0, 1.0, 1.0, 3.0, 2.0];
440
441 let eigs = a.eigenvalues().unwrap();
442
443 let eig_1 = 12.174;
444 let eig_2 = 5.2681;
445 let eig_3 = -4.4942;
446 let eig_4 = 2.9279;
447 let eig_5 = -2.8758;
448
449 assert!(eigs.iter().any(|x| (x - eig_1).abs() < 1e-4));
450 assert!(eigs.iter().any(|x| (x - eig_2).abs() < 1e-4));
451 assert!(eigs.iter().any(|x| (x - eig_3).abs() < 1e-4));
452 assert!(eigs.iter().any(|x| (x - eig_4).abs() < 1e-4));
453 assert!(eigs.iter().any(|x| (x - eig_5).abs() < 1e-4));
454 }
455
456 #[test]
457 #[should_panic]
458 fn test_non_square_eigenvalues() {
459 let a: Matrix<f64> = Matrix::ones(2, 3);
460
461 let _ = a.eigenvalues();
462 }
463
464 #[test]
465 #[should_panic]
466 fn test_non_square_eigendecomp() {
467 let a: Matrix<f64> = Matrix::ones(2, 3);
468
469 let _ = a.eigendecomp();
470 }
471}