1use super::Matrix;
2use crate::{dgeev_data, dgeev_data_lr, to_i32, CcBool, StrError, Vector, C_FALSE, C_TRUE};
3
4extern "C" {
5 fn c_dgeev(
8 calc_vl: CcBool,
9 calc_vr: CcBool,
10 n: *const i32,
11 a: *mut f64,
12 lda: *const i32,
13 wr: *mut f64,
14 wi: *mut f64,
15 vl: *mut f64,
16 ldvl: *const i32,
17 vr: *mut f64,
18 ldvr: *const i32,
19 work: *mut f64,
20 lwork: *const i32,
21 info: *mut i32,
22 );
23}
24
25pub fn mat_eigen(
126 l_real: &mut Vector,
127 l_imag: &mut Vector,
128 v_real: &mut Matrix,
129 v_imag: &mut Matrix,
130 a: &mut Matrix,
131) -> Result<(), StrError> {
132 let (m, n) = a.dims();
133 if m != n {
134 return Err("matrix must be square");
135 }
136 if l_real.dim() != m || l_imag.dim() != m {
137 return Err("vectors are incompatible");
138 }
139 if v_real.nrow() != m || v_real.ncol() != m || v_imag.nrow() != m || v_imag.ncol() != m {
140 return Err("matrices are incompatible");
141 }
142 let m_i32 = to_i32(m);
143 let lda = m_i32;
144 let ldu = 1;
145 let ldv = m_i32;
146 const EXTRA: i32 = 1;
147 let lwork = 4 * m_i32 + EXTRA;
148 let mut u = vec![0.0; ldu as usize];
149 let mut v = vec![0.0; m * m];
150 let mut work = vec![0.0; lwork as usize];
151 let mut info = 0;
152 unsafe {
153 c_dgeev(
154 C_FALSE,
155 C_TRUE,
156 &m_i32,
157 a.as_mut_data().as_mut_ptr(),
158 &lda,
159 l_real.as_mut_data().as_mut_ptr(),
160 l_imag.as_mut_data().as_mut_ptr(),
161 u.as_mut_ptr(),
162 &ldu,
163 v.as_mut_ptr(),
164 &ldv,
165 work.as_mut_ptr(),
166 &lwork,
167 &mut info,
168 );
169 }
170 if info < 0 {
171 println!("LAPACK ERROR (dgeev): Argument #{} had an illegal value", -info);
172 return Err("LAPACK ERROR (dgeev): An argument had an illegal value");
173 } else if info > 0 {
174 println!("LAPACK ERROR (dgeev): The QR algorithm failed. Elements {}+1:N of l_real and l_imag contain eigenvalues which have converged", info-1);
175 return Err("LAPACK ERROR (dgeev): The QR algorithm failed to compute all the eigenvalues, and no eigenvectors have been computed");
176 }
177 dgeev_data(v_real.as_mut_data(), v_imag.as_mut_data(), l_imag.as_data(), &v)
178}
179
180pub fn mat_eigen_lr(
291 l_real: &mut Vector,
292 l_imag: &mut Vector,
293 u_real: &mut Matrix,
294 u_imag: &mut Matrix,
295 v_real: &mut Matrix,
296 v_imag: &mut Matrix,
297 a: &mut Matrix,
298) -> Result<(), StrError> {
299 let (m, n) = a.dims();
300 if m != n {
301 return Err("matrix must be square");
302 }
303 if l_real.dim() != m || l_imag.dim() != m {
304 return Err("vectors are incompatible");
305 }
306 if u_real.nrow() != m
307 || u_real.ncol() != m
308 || u_imag.nrow() != m
309 || u_imag.ncol() != m
310 || v_real.nrow() != m
311 || v_real.ncol() != m
312 || v_imag.nrow() != m
313 || v_imag.ncol() != m
314 {
315 return Err("matrices are incompatible");
316 }
317 let m_i32 = to_i32(m);
318 let lda = m_i32;
319 let ldu = m_i32;
320 let ldv = m_i32;
321 const EXTRA: i32 = 1;
322 let lwork = 4 * m_i32 + EXTRA;
323 let mut u = vec![0.0; m * m];
324 let mut v = vec![0.0; m * m];
325 let mut work = vec![0.0; lwork as usize];
326 let mut info = 0;
327 unsafe {
328 c_dgeev(
329 C_TRUE,
330 C_TRUE,
331 &m_i32,
332 a.as_mut_data().as_mut_ptr(),
333 &lda,
334 l_real.as_mut_data().as_mut_ptr(),
335 l_imag.as_mut_data().as_mut_ptr(),
336 u.as_mut_ptr(),
337 &ldu,
338 v.as_mut_ptr(),
339 &ldv,
340 work.as_mut_ptr(),
341 &lwork,
342 &mut info,
343 );
344 }
345 if info < 0 {
346 println!("LAPACK ERROR (dgeev): Argument #{} had an illegal value", -info);
347 return Err("LAPACK ERROR (dgeev): An argument had an illegal value");
348 } else if info > 0 {
349 println!("LAPACK ERROR (dgeev): The QR algorithm failed. Elements {}+1:N of l_real and l_imag contain eigenvalues which have converged", info-1);
350 return Err("LAPACK ERROR (dgeev): The QR algorithm failed to compute all the eigenvalues, and no eigenvectors have been computed");
351 }
352 dgeev_data_lr(
353 u_real.as_mut_data(),
354 u_imag.as_mut_data(),
355 v_real.as_mut_data(),
356 v_imag.as_mut_data(),
357 l_imag.as_data(),
358 &u,
359 &v,
360 )?;
361 Ok(())
362}
363
364#[cfg(test)]
367mod tests {
368 use super::{mat_eigen, mat_eigen_lr};
369 use crate::mat_approx_eq;
370 use crate::matrix::testing::{check_eigen, check_eigen_sym};
371 use crate::{vec_approx_eq, Matrix, Vector};
372
373 #[test]
374 fn mat_eigen_fails_on_non_square() {
375 let mut a = Matrix::new(3, 4);
376 let m = a.nrow();
377 let mut l_real = Vector::new(m);
378 let mut l_imag = Vector::new(m);
379 let mut v_real = Matrix::new(m, m);
380 let mut v_imag = Matrix::new(m, m);
381 assert_eq!(
382 mat_eigen(&mut l_real, &mut l_imag, &mut v_real, &mut v_imag, &mut a),
383 Err("matrix must be square")
384 );
385 }
386
387 #[test]
388 fn mat_eigen_fails_on_wrong_dims() {
389 let mut a = Matrix::new(2, 2);
390 let m = a.nrow();
391 let mut l_real = Vector::new(m);
392 let mut l_imag = Vector::new(m);
393 let mut v_real = Matrix::new(m, m);
394 let mut v_imag = Matrix::new(m, m);
395 let mut l_real_wrong = Vector::new(m + 1);
396 let mut l_imag_wrong = Vector::new(m + 1);
397 let mut v_real_wrong = Matrix::new(m + 1, m);
398 let mut v_imag_wrong = Matrix::new(m, m + 1);
399 assert_eq!(
400 mat_eigen(&mut l_real_wrong, &mut l_imag, &mut v_real, &mut v_imag, &mut a),
401 Err("vectors are incompatible")
402 );
403 assert_eq!(
404 mat_eigen(&mut l_real, &mut l_imag_wrong, &mut v_real, &mut v_imag, &mut a),
405 Err("vectors are incompatible")
406 );
407 assert_eq!(
408 mat_eigen(&mut l_real, &mut l_imag, &mut v_real_wrong, &mut v_imag, &mut a),
409 Err("matrices are incompatible")
410 );
411 assert_eq!(
412 mat_eigen(&mut l_real, &mut l_imag, &mut v_real, &mut v_imag_wrong, &mut a),
413 Err("matrices are incompatible")
414 );
415 }
416
417 #[test]
418 fn mat_eigen_lr_fails_on_non_square() {
419 let mut a = Matrix::new(3, 4);
420 let m = a.nrow();
421 let mut l_real = Vector::new(m);
422 let mut l_imag = Vector::new(m);
423 let mut u_real = Matrix::new(m, m);
424 let mut u_imag = Matrix::new(m, m);
425 let mut v_real = Matrix::new(m, m);
426 let mut v_imag = Matrix::new(m, m);
427 assert_eq!(
428 mat_eigen_lr(
429 &mut l_real,
430 &mut l_imag,
431 &mut u_real,
432 &mut u_imag,
433 &mut v_real,
434 &mut v_imag,
435 &mut a,
436 ),
437 Err("matrix must be square"),
438 );
439 }
440
441 #[test]
442 fn mat_eigen_lr_fails_on_wrong_dims() {
443 let mut a = Matrix::new(2, 2);
444 let m = a.nrow();
445 let mut l_real = Vector::new(m);
446 let mut l_imag = Vector::new(m);
447 let mut u_real = Matrix::new(m, m);
448 let mut u_imag = Matrix::new(m, m);
449 let mut v_real = Matrix::new(m, m);
450 let mut v_imag = Matrix::new(m, m);
451 let mut l_real_wrong = Vector::new(m + 1);
452 let mut l_imag_wrong = Vector::new(m + 1);
453 let mut u_real_wrong = Matrix::new(m + 1, m);
454 let mut u_imag_wrong = Matrix::new(m, m + 1);
455 let mut v_real_wrong = Matrix::new(m + 1, m);
456 let mut v_imag_wrong = Matrix::new(m, m + 1);
457 assert_eq!(
458 mat_eigen_lr(
459 &mut l_real_wrong,
460 &mut l_imag,
461 &mut u_real,
462 &mut u_imag,
463 &mut v_real,
464 &mut v_imag,
465 &mut a,
466 ),
467 Err("vectors are incompatible"),
468 );
469 assert_eq!(
470 mat_eigen_lr(
471 &mut l_real,
472 &mut l_imag_wrong,
473 &mut u_real,
474 &mut u_imag,
475 &mut v_real,
476 &mut v_imag,
477 &mut a,
478 ),
479 Err("vectors are incompatible"),
480 );
481 assert_eq!(
482 mat_eigen_lr(
483 &mut l_real,
484 &mut l_imag,
485 &mut u_real_wrong,
486 &mut u_imag,
487 &mut v_real,
488 &mut v_imag,
489 &mut a,
490 ),
491 Err("matrices are incompatible"),
492 );
493 assert_eq!(
494 mat_eigen_lr(
495 &mut l_real,
496 &mut l_imag,
497 &mut u_real,
498 &mut u_imag_wrong,
499 &mut v_real,
500 &mut v_imag,
501 &mut a,
502 ),
503 Err("matrices are incompatible"),
504 );
505 assert_eq!(
506 mat_eigen_lr(
507 &mut l_real,
508 &mut l_imag,
509 &mut u_real,
510 &mut u_imag,
511 &mut v_real_wrong,
512 &mut v_imag,
513 &mut a,
514 ),
515 Err("matrices are incompatible"),
516 );
517 assert_eq!(
518 mat_eigen_lr(
519 &mut l_real,
520 &mut l_imag,
521 &mut u_real,
522 &mut u_imag,
523 &mut v_real,
524 &mut v_imag_wrong,
525 &mut a,
526 ),
527 Err("matrices are incompatible"),
528 );
529 }
530
531 #[test]
532 fn mat_eigen_works() {
533 #[rustfmt::skip]
534 let data = [
535 [0.0, 1.0, 0.0],
536 [0.0, 0.0, 1.0],
537 [1.0, 0.0, 0.0],
538 ];
539 let mut a = Matrix::from(&data);
540 let m = a.nrow();
541 let mut l_real = Vector::new(m);
542 let mut l_imag = Vector::new(m);
543 let mut v_real = Matrix::new(m, m);
544 let mut v_imag = Matrix::new(m, m);
545 mat_eigen(&mut l_real, &mut l_imag, &mut v_real, &mut v_imag, &mut a).unwrap();
546 let s3 = f64::sqrt(3.0);
547 let l_real_correct = &[-0.5, -0.5, 1.0];
548 let l_imag_correct = &[s3 / 2.0, -s3 / 2.0, 0.0];
549 vec_approx_eq(&l_real, l_real_correct, 1e-15);
550 vec_approx_eq(&l_imag, l_imag_correct, 1e-15);
551 check_eigen(&data, &v_real, &l_real, &v_imag, &l_imag, 1e-15);
552 }
553
554 #[test]
555 fn mat_eigen_repeated_eval_works() {
556 #[rustfmt::skip]
558 let data = [
559 [2.0, 0.0, 0.0, 0.0],
560 [1.0, 2.0, 0.0, 0.0],
561 [0.0, 1.0, 3.0, 0.0],
562 [0.0, 0.0, 1.0, 3.0],
563 ];
564 let mut a = Matrix::from(&data);
565 let m = a.nrow();
566 let mut l_real = Vector::new(m);
567 let mut l_imag = Vector::new(m);
568 let mut v_real = Matrix::new(m, m);
569 let mut v_imag = Matrix::new(m, m);
570 mat_eigen(&mut l_real, &mut l_imag, &mut v_real, &mut v_imag, &mut a).unwrap();
571 let l_real_correct = &[3.0, 3.0, 2.0, 2.0];
572 let l_imag_correct = &[0.0, 0.0, 0.0, 0.0];
573 let os3 = 1.0 / f64::sqrt(3.0);
574 #[rustfmt::skip]
575 let v_real_correct = &[
576 [0.0, 0.0, 0.0, 0.0],
577 [0.0, 0.0, os3, -os3],
578 [0.0, 0.0, -os3, os3],
579 [1.0, -1.0, os3, -os3],
580 ];
581 let v_imag_correct = Matrix::new(4, 4);
582 vec_approx_eq(&l_real, l_real_correct, 1e-15);
583 vec_approx_eq(&l_imag, l_imag_correct, 1e-15);
584 mat_approx_eq(&v_real, v_real_correct, 1e-15);
585 mat_approx_eq(&v_imag, &v_imag_correct, 1e-15);
586 check_eigen_sym(&data, &v_real, &l_real, 1e-15);
587 }
588
589 #[test]
590 fn mat_eigen_lr_works() {
591 #[rustfmt::skip]
592 let data = [
593 [0.0, 1.0, 0.0],
594 [0.0, 0.0, 1.0],
595 [1.0, 0.0, 0.0],
596 ];
597 let mut a = Matrix::from(&data);
598 let m = a.nrow();
599 let mut l_real = Vector::new(m);
600 let mut l_imag = Vector::new(m);
601 let mut u_real = Matrix::new(m, m);
602 let mut u_imag = Matrix::new(m, m);
603 let mut v_real = Matrix::new(m, m);
604 let mut v_imag = Matrix::new(m, m);
605 mat_eigen_lr(
606 &mut l_real,
607 &mut l_imag,
608 &mut u_real,
609 &mut u_imag,
610 &mut v_real,
611 &mut v_imag,
612 &mut a,
613 )
614 .unwrap();
615 let s3 = f64::sqrt(3.0);
616 let l_real_correct = &[-0.5, -0.5, 1.0];
617 let l_imag_correct = &[s3 / 2.0, -s3 / 2.0, 0.0];
618 vec_approx_eq(&l_real, l_real_correct, 1e-15);
619 vec_approx_eq(&l_imag, l_imag_correct, 1e-15);
620 check_eigen(&data, &v_real, &l_real, &v_imag, &l_imag, 1e-15);
621 }
622}