@@ -420,97 +420,158 @@ void DiagoPPCG<T, Device>::solve_hermitian_gevp(
420420 std::vector<T>& k_herm, std::vector<T>& m_herm,
421421 int n, std::vector<Real>& eval, std::vector<T>& evec) const
422422{
423- const int n2 = 2 * n;
424- std::vector<Real> k_blk (n2 * n2, static_cast <Real>(0 ));
425- std::vector<Real> m_blk (n2 * n2, static_cast <Real>(0 ));
423+ // -----------------------------------------------------------------------
424+ // Determine whether the matrices carry a significant imaginary part.
425+ // When wavefunctions are real (or the operator is real and the basis
426+ // has been kept real) the Hermitian Gram matrices are strictly real,
427+ // and we can use the fast real-symmetric dsygvd path directly.
428+ // -----------------------------------------------------------------------
429+ const int n2 = n * n;
430+ Real max_imag = 0 , max_abs = 0 ;
431+ for (int i = 0 ; i < n2; ++i)
432+ {
433+ max_imag = std::max (max_imag, std::abs (std::imag (k_herm[i])));
434+ max_imag = std::max (max_imag, std::abs (std::imag (m_herm[i])));
435+ max_abs = std::max (max_abs, std::abs (std::real (k_herm[i])));
436+ max_abs = std::max (max_abs, std::abs (std::real (m_herm[i])));
437+ }
438+ const bool is_real = (max_imag <= max_abs * static_cast <Real>(1e-14 ));
426439
427- // Build the 2n×2n real block matrices.
428- for (int j = 0 ; j < n; ++j)
440+ if (is_real)
429441 {
430- for (int i = 0 ; i < n; ++i)
442+ // ===== Fast path: real-symmetric matrices =====
443+ std::vector<Real> k_real (n2), m_real (n2);
444+ for (int i = 0 ; i < n2; ++i)
431445 {
432- const T kij = k_herm[i + j * n];
433- const T mij = m_herm[i + j * n];
434- const Real kr = std::real (kij);
435- const Real ki = std::imag (kij);
436- const Real mr = std::real (mij);
437- const Real mi = std::imag (mij);
438-
439- // Upper-left n×n block: Re(K), Re(M)
440- k_blk[i + j * n2] = kr;
441- m_blk[i + j * n2] = mr;
442- // Upper-right n×n block: -Im(K), -Im(M)
443- k_blk[i + (j+n) * n2] = -ki;
444- m_blk[i + (j+n) * n2] = -mi;
445- // Lower-left n×n block: Im(K), Im(M)
446- k_blk[i + n + j * n2] = ki;
447- m_blk[i + n + j * n2] = mi;
448- // Lower-right n×n block: Re(K), Re(M)
449- k_blk[i + n + (j+n) * n2] = kr;
450- m_blk[i + n + (j+n) * n2] = mr;
446+ k_real[i] = std::real (k_herm[i]);
447+ m_real[i] = std::real (m_herm[i]);
451448 }
452- }
453449
454- // Symmetrise explicitly.
455- for (int j = 0 ; j < n2; ++j)
456- for (int i = 0 ; i < j; ++i)
450+ const std::vector<Real> m0 = m_real;
451+ const Real shifts[] = {static_cast <Real>(1e-10 ),
452+ static_cast <Real>(1e-8 ),
453+ static_cast <Real>(1e-6 )};
454+ bool solved = false ;
455+ for (int attempt = 0 ; attempt < 3 ; ++attempt)
457456 {
458- k_blk[j + i * n2] = k_blk[i + j * n2];
459- m_blk[j + i * n2] = m_blk[i + j * n2];
457+ try
458+ {
459+ Lapack<Real>::sygvd (n, k_real.data (), m_real.data (),
460+ eval.data ());
461+ solved = true ;
462+ break ;
463+ }
464+ catch (const std::runtime_error&)
465+ {
466+ m_real = m0;
467+ for (int i = 0 ; i < n; ++i)
468+ m_real[i + i * n] += shifts[attempt];
469+ }
460470 }
461471
462- // M-save/restore for retry (same pattern as solve_small_generalized).
463- const std::vector<Real> m0 = m_blk;
464- std::vector<Real> eval2 (n2, static_cast <Real>(0 ));
465- const Real shifts[] = {static_cast <Real>(1e-10 ),
466- static_cast <Real>(1e-8 ),
467- static_cast <Real>(1e-6 )};
468- bool solved = false ;
469- for (int attempt = 0 ; attempt < 3 ; ++attempt)
470- {
471- try
472+ if (solved)
472473 {
473- Lapack<Real>::sygvd (n2, k_blk.data (), m_blk.data (), eval2.data ());
474- solved = true ;
475- break ;
474+ // k_real now holds the real eigenvectors column-by-column.
475+ evec.assign (n2, T (0 ));
476+ for (int j = 0 ; j < n; ++j)
477+ for (int i = 0 ; i < n; ++i)
478+ evec[i + j * n] = T (k_real[i + j * n], 0 );
476479 }
477- catch ( const std::runtime_error&)
480+ else
478481 {
479- m_blk = m0;
480- for (int i = 0 ; i < n2; ++i)
481- m_blk[i + i * n2] += shifts[attempt];
482+ evec.assign (n2, T (0 ));
483+ for (int i = 0 ; i < n; ++i)
484+ evec[i + i * n] = T (1 , 0 );
485+ eval.assign (n, static_cast <Real>(0 ));
482486 }
487+ return ;
483488 }
484489
485- if (solved)
490+ // ===== General path: complex-Hermitian matrices =====
491+ // Convert to a 2n×2n real block system and solve with dsygvd.
486492 {
487- // Extract the n lowest eigenvalues (each pair gives two nearly-equal
488- // eigenvalues; we take the first of each pair).
489- eval.assign (n, static_cast <Real>(0 ));
490- for (int i = 0 ; i < n; ++i)
491- eval[i] = eval2[2 * i];
493+ const int n2blk = 2 * n;
494+ std::vector<Real> k_blk (n2blk * n2blk, static_cast <Real>(0 ));
495+ std::vector<Real> m_blk (n2blk * n2blk, static_cast <Real>(0 ));
492496
493- // Extract complex eigenvectors from the block eigenvectors.
494- // k_blk now holds the real eigenvectors column-by-column (n2×n2).
495- evec.assign (n * n, T (0 ));
496497 for (int j = 0 ; j < n; ++j)
497498 {
498- const int jj = 2 * j; // use the first of each degenerate pair
499499 for (int i = 0 ; i < n; ++i)
500500 {
501- const Real vr = k_blk[i + jj * n2];
502- const Real vi = k_blk[i + n + jj * n2];
503- evec[i + j * n] = T (vr, vi);
501+ const T kij = k_herm[i + j * n];
502+ const T mij = m_herm[i + j * n];
503+ const Real kr = std::real (kij);
504+ const Real ki = std::imag (kij);
505+ const Real mr = std::real (mij);
506+ const Real mi = std::imag (mij);
507+
508+ k_blk[i + j * n2blk] = kr;
509+ m_blk[i + j * n2blk] = mr;
510+ k_blk[i + (j+n) * n2blk] = -ki;
511+ m_blk[i + (j+n) * n2blk] = -mi;
512+ k_blk[i + n + j * n2blk] = ki;
513+ m_blk[i + n + j * n2blk] = mi;
514+ k_blk[i + n + (j+n) * n2blk] = kr;
515+ m_blk[i + n + (j+n) * n2blk] = mr;
504516 }
505517 }
506- }
507- else
508- {
509- // All attempts failed — set eigenvectors to identity (no update).
510- std::fill (evec.begin (), evec.end (), T (0 ));
511- for (int i = 0 ; i < n; ++i)
512- evec[i + i * n] = T (1 , 0 );
513- std::fill (eval.begin (), eval.end (), static_cast <Real>(0 ));
518+
519+ // Symmetrise.
520+ for (int j = 0 ; j < n2blk; ++j)
521+ for (int i = 0 ; i < j; ++i)
522+ {
523+ k_blk[j + i * n2blk] = k_blk[i + j * n2blk];
524+ m_blk[j + i * n2blk] = m_blk[i + j * n2blk];
525+ }
526+
527+ const std::vector<Real> m0 = m_blk;
528+ std::vector<Real> eval2 (n2blk, static_cast <Real>(0 ));
529+ const Real shifts[] = {static_cast <Real>(1e-10 ),
530+ static_cast <Real>(1e-8 ),
531+ static_cast <Real>(1e-6 )};
532+ bool solved = false ;
533+ for (int attempt = 0 ; attempt < 3 ; ++attempt)
534+ {
535+ try
536+ {
537+ Lapack<Real>::sygvd (n2blk, k_blk.data (), m_blk.data (),
538+ eval2.data ());
539+ solved = true ;
540+ break ;
541+ }
542+ catch (const std::runtime_error&)
543+ {
544+ m_blk = m0;
545+ for (int i = 0 ; i < n2blk; ++i)
546+ m_blk[i + i * n2blk] += shifts[attempt];
547+ }
548+ }
549+
550+ if (solved)
551+ {
552+ eval.assign (n, static_cast <Real>(0 ));
553+ for (int i = 0 ; i < n; ++i)
554+ eval[i] = eval2[2 * i];
555+
556+ evec.assign (n2, T (0 ));
557+ for (int j = 0 ; j < n; ++j)
558+ {
559+ const int jj = 2 * j;
560+ for (int i = 0 ; i < n; ++i)
561+ {
562+ const Real vr = k_blk[i + jj * n2blk];
563+ const Real vi = k_blk[i + n + jj * n2blk];
564+ evec[i + j * n] = T (vr, vi);
565+ }
566+ }
567+ }
568+ else
569+ {
570+ evec.assign (n2, T (0 ));
571+ for (int i = 0 ; i < n; ++i)
572+ evec[i + i * n] = T (1 , 0 );
573+ eval.assign (n, static_cast <Real>(0 ));
574+ }
514575 }
515576}
516577
0 commit comments