14
14
15
15
16
16
namespace py = pybind11;
17
+ #include < pybind11/eigen.h>
17
18
18
19
19
20
std::pair<std::vector<double >, std::vector<std::vector<double >> >
@@ -585,6 +586,183 @@ secular_solver(
585
586
}
586
587
587
588
589
+ #include < Eigen/Dense>
590
+ #include < Eigen/Sparse>
591
+ #include < unordered_set>
592
+
593
+ /* *
594
+ * Applies the deflation step in a divide-and-conquer eigenvalue algorithm.
595
+ *
596
+ * @param D Diagonal entries of the matrix (as Eigen::VectorXd).
597
+ * @param v Rank-one update vector (modified in-place).
598
+ * @param beta Scalar multiplier for the rank-one update.
599
+ * @param tol_factor Factor to scale the deflation tolerance (default 1e-12).
600
+ * @return A tuple containing:
601
+ * - deflated_eigvals: Vector of trivial eigenvalues (Eigen::VectorXd).
602
+ * - deflated_eigvecs: Matrix whose columns are the trivial eigenvectors (Eigen::MatrixXd).
603
+ * - D_keep: Remaining diagonal entries after deflation (Eigen::VectorXd).
604
+ * - v_keep: Remaining rank-one vector entries after deflation (Eigen::VectorXd).
605
+ * - P_final: Combined permutation & Givens rotation as an Eigen::SparseMatrix<double>.
606
+ */
607
+
608
+
609
+ /* *
610
+ * Applies the deflation step in a divide-and-conquer eigenvalue algorithm.
611
+ *
612
+ * @param D Diagonal entries of the matrix (as Eigen::VectorXd).
613
+ * @param v Rank-one update vector (modified in-place).
614
+ * @param beta Scalar multiplier for the rank-one update.
615
+ * @param tol_factor Factor to scale the deflation tolerance (default 1e-12).
616
+ * @return A tuple containing:
617
+ * - deflated_eigvals: Vector of trivial eigenvalues (Eigen::VectorXd).
618
+ * - deflated_eigvecs: Matrix whose columns are the trivial eigenvectors (Eigen::MatrixXd).
619
+ * - D_keep: Remaining diagonal entries after deflation (Eigen::VectorXd).
620
+ * - v_keep: Remaining rank-one vector entries after deflation (Eigen::VectorXd).
621
+ * - P_final: Combined permutation & rotation as an Eigen::SparseMatrix<double>.
622
+ */
623
+ std::tuple<
624
+ Eigen::VectorXd,
625
+ Eigen::MatrixXd,
626
+ Eigen::VectorXd,
627
+ Eigen::VectorXd,
628
+ Eigen::SparseMatrix<double >>
629
+ deflateEigenpairs (
630
+ const Eigen::VectorXd& D,
631
+ Eigen::VectorXd v,
632
+ double beta,
633
+ double tol_factor = 1e-12
634
+ ) {
635
+ int n = D.size ();
636
+ // 1) Build full matrix M and compute norm for tolerance
637
+ Eigen::MatrixXd M = D.asDiagonal ();
638
+ M += beta * v * v.transpose ();
639
+ double norm_T = M.norm ();
640
+ double tol = tol_factor * norm_T;
641
+
642
+ // 2) Prepare containers for deflation
643
+ std::vector<int > keep_indices, deflated_indices;
644
+ Eigen::VectorXd deflated_eigvals = Eigen::VectorXd::Zero (n);
645
+ std::vector<Eigen::VectorXd> deflated_eigvecs_list;
646
+ int j = 0 ;
647
+
648
+ // 3) Zero-component deflation
649
+ for (int i = 0 ; i < n; ++i) {
650
+ if (std::abs (v (i)) < tol) {
651
+ deflated_eigvals (j) = D (i);
652
+ Eigen::VectorXd e_vec = Eigen::VectorXd::Zero (n);
653
+ e_vec (i) = 1.0 ;
654
+ deflated_eigvecs_list.push_back (e_vec);
655
+ deflated_indices.push_back (i);
656
+ ++j;
657
+ } else {
658
+ keep_indices.push_back (i);
659
+ }
660
+ }
661
+
662
+ // 4) Build permutation P: [keep_indices, deflated_indices]
663
+ std::vector<int > new_order;
664
+ new_order.reserve (n);
665
+ new_order.insert (new_order.end (), keep_indices.begin (), keep_indices.end ());
666
+ new_order.insert (new_order.end (), deflated_indices.begin (), deflated_indices.end ());
667
+ Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P (n);
668
+ P.indices () = Eigen::VectorXi::Map (new_order.data (), n);
669
+
670
+ // 5) Extract subproblem D_keep and v_keep
671
+ Eigen::VectorXd D_keep (static_cast <int >(keep_indices.size ()));
672
+ Eigen::VectorXd v_keep (static_cast <int >(keep_indices.size ()));
673
+ for (int idx = 0 ; idx < static_cast <int >(keep_indices.size ()); ++idx) {
674
+ D_keep (idx) = D (keep_indices[idx]);
675
+ v_keep (idx) = v (keep_indices[idx]);
676
+ }
677
+
678
+ // 6) Givens rotations for near-duplicate entries
679
+ std::unordered_set<int > to_check;
680
+ to_check.reserve (keep_indices.size ());
681
+ for (int i = 0 ; i < static_cast <int >(keep_indices.size ()); ++i)
682
+ to_check.insert (i);
683
+ std::vector<std::tuple<int ,int ,double ,double >> rotations;
684
+ std::vector<int > vec_idx_list;
685
+ std::vector<int > to_check_copy (to_check.begin (), to_check.end ());
686
+
687
+ for (size_t idx_i = 0 ; idx_i + 1 < to_check_copy.size (); ++idx_i) {
688
+ int i = to_check_copy[idx_i];
689
+ if (to_check.find (i) == to_check.end ()) continue ;
690
+ for (int k = i + 1 ; k < static_cast <int >(D_keep.size ()); ++k) {
691
+ if (std::abs (D_keep (k) - D_keep (i)) < tol) {
692
+ to_check.erase (k);
693
+ double r = std::hypot (v_keep (i), v_keep (k));
694
+ double c = v_keep (i) / r;
695
+ double s = -v_keep (k) / r;
696
+ v_keep (i) = r;
697
+ v_keep (k) = 0.0 ;
698
+ rotations.emplace_back (i, k, c, s);
699
+ deflated_eigvals (j) = D_keep (i);
700
+ ++j;
701
+ // local eigenvector in full basis
702
+ Eigen::VectorXd tmp = Eigen::VectorXd::Zero (n);
703
+ tmp (k) = c;
704
+ tmp (i) = s;
705
+ deflated_eigvecs_list.push_back (P.transpose () * tmp);
706
+ vec_idx_list.push_back (k);
707
+ }
708
+ }
709
+ }
710
+
711
+ // 7) Final ordering after rotations
712
+ std::vector<int > final_order (to_check.begin (), to_check.end ());
713
+ final_order.insert (final_order.end (), vec_idx_list.begin (), vec_idx_list.end ());
714
+
715
+ // 8) Resize deflated_eigvals to actual number found
716
+ deflated_eigvals.conservativeResize (j);
717
+
718
+ // 9) Build P2: accumulate sparse Givens rotations
719
+ Eigen::SparseMatrix<double > P2 (n,n);
720
+ P2.setIdentity ();
721
+ for (auto &rot: rotations) {
722
+ int i,k; double c,s; std::tie (i,k,c,s) = rot;
723
+ Eigen::SparseMatrix<double > G (n,n);
724
+ G.setIdentity ();
725
+ G.coeffRef (i,i) = c;
726
+ G.coeffRef (k,k) = c;
727
+ G.coeffRef (i,k) = -s;
728
+ G.coeffRef (k,i) = s;
729
+ P2 = P2 * G;
730
+ }
731
+
732
+ // 10) Build permutation matrix P3 from final_order
733
+ Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P3 (n);
734
+ P3.indices () = Eigen::VectorXi::Map (final_order.data (), n);
735
+
736
+ // 11) Convert P and P3 to sparse<double> and combine all transforms
737
+ Eigen::SparseMatrix<double > P_sparse = P.toDenseMatrix ().cast <double >().sparseView ();
738
+ Eigen::SparseMatrix<double > P3_sparse = P3.toDenseMatrix ().cast <double >().sparseView ();
739
+ Eigen::SparseMatrix<double > P_final = P3_sparse * P2 * P_sparse;
740
+
741
+ // 12) Extract final D_keep and v_keep for reduced problem
742
+ std::vector<int > final_keep (to_check.begin (), to_check.end ());
743
+ Eigen::VectorXd D_keep_final (static_cast <int >(final_keep.size ()));
744
+ Eigen::VectorXd v_keep_final (static_cast <int >(final_keep.size ()));
745
+ for (int idx = 0 ; idx < static_cast <int >(final_keep.size ()); ++idx) {
746
+ D_keep_final (idx) = D_keep (final_keep[idx]);
747
+ v_keep_final (idx) = v_keep (final_keep[idx]);
748
+ }
749
+
750
+ // 13) Assemble deflated eigenvectors matrix
751
+ Eigen::MatrixXd deflated_eigvecs (n, static_cast <int >(deflated_eigvecs_list.size ()));
752
+ for (int col = 0 ; col < static_cast <int >(deflated_eigvecs_list.size ()); ++col) {
753
+ deflated_eigvecs.col (col) = deflated_eigvecs_list[col];
754
+ }
755
+
756
+ return std::make_tuple (
757
+ deflated_eigvals,
758
+ deflated_eigvecs.transpose (),
759
+ D_keep_final,
760
+ v_keep_final,
761
+ P_final
762
+ );
763
+ }
764
+
765
+
588
766
// PYTHON BINDINGS USING PYBIND11
589
767
590
768
PYBIND11_MODULE (cxx_utils, m) {
@@ -593,4 +771,5 @@ PYBIND11_MODULE(cxx_utils, m) {
593
771
m.def (" QR_algorithm" , &QR_algorithm, py::arg (" diag" ), py::arg (" off_diag" ), py::arg (" tol" )=1e-8 , py::arg (" max_iter" )=5000 );
594
772
m.def (" Eigen_value_calculator" , &Eigen_value_calculator, py::arg (" diag" ), py::arg (" off_diag" ), py::arg (" tol" )=1e-8 , py::arg (" max_iter" )=5000 );
595
773
m.def (" secular_solver_cxx" , &secular_solver, py::arg (" rho" ), py::arg (" d" ), py::arg (" v" ), py::arg (" indices" ));
774
+ m.def (" deflate_eigenpairs_cxx" , &deflateEigenpairs, py::arg (" D" ), py::arg (" v" ), py::arg (" beta" ), py::arg (" tol_factor" ) = 1e-12 );
596
775
}
0 commit comments