PersistentLaplacians
up_algorithms.hpp
Go to the documentation of this file.
1 #ifndef up_algs_H
2 #define up_algs_H
3 
4 #include "../typedefs.hpp"
6 // #include <Eigen/IterativeLinearSolvers>
7 // #include <unsupported/Eigen/SparseExtra>
8 #include <Eigen/Cholesky>
9 #include <Eigen/QR>
10 #include <chrono>
11 // #include "Eigen/Eigenvalues"
12 // #include "Eigen/SVD"
13 // #include "../PersistentLaplacians.hpp"
14 namespace PersistentLaplacians{
15 
16  // wrapper classes for algorithms to compute up Laplacian.
17  // just need to implement the following signature:
18  // void operator()(FilteredBoundaryMatrix<int>* fbm, filtration_type a, filtration_type b, DenseMatrix_PL &L_up);
19 
20 
21  // Wrapper for Schur complement algorithm
22  class schur{
23  public:
25 
26  int a_row_index = fbm->index_of_filtration(false,a);
27  int b_row_index = fbm->index_of_filtration(false,b);
28  int b_col_index = fbm->index_of_filtration(true,b);
29 
30  if (a_row_index == b_row_index){
31  SparseMatrix_storage B_pers(b_row_index, b_col_index);
32  fbm->submatrix_at_filtration(b, B_pers);
33  L_up = (B_pers*B_pers.transpose()).cast<coefficient_type>();
34  return;
35  } else if (a_row_index == -1){ // no rows, return 0x0 empty matrix
36  L_up.setZero(0,0);
37  return;
38 
39  } else if (b_col_index == -1){ // no columns, return nxn 0-matrix for n = number of cells in C_{up_dim}^a
40  L_up.setZero(a_row_index+1,a_row_index+1);
41  return;
42 
43  }
44  // NOTE: doing the self adjoint lower triangular trick here is slower
45  // possibly because of the conversions between dense and sparse
46 
47  SparseMatrix_storage B_pers_int(b_row_index, b_col_index);
48  fbm->submatrix_at_filtration(b, B_pers_int);
49 
50 
51  SparseMatrix_storage L_up_b_int(B_pers_int.rows(), B_pers_int.rows());
52  // L_up_b_int = B_pers_int*B_pers_int.transpose();
53  L_up_b_int.selfadjointView<Eigen::Lower>().rankUpdate(B_pers_int);
54  SparseMatrix_PL L_up_b(L_up_b_int.rows(), L_up_b_int.cols());
55  L_up_b = L_up_b_int.cast<coefficient_type>();
56 
57  int a_rows = a_row_index + 1;
58  int b_rows = b_row_index + 1;
59 
60 
61 
62  // auto start_solve = std::chrono::high_resolution_clock::now();
63  // split L_up_b into 4 along those borders
64  // A B
65  // C D
66  // and compute A - B*D^{-1}*C
67  // by the structure of L, we know B = C^T
68  // and we want A - B * (things)
69  // Also compute D^{-1}*C by solving the sparse lower-triangular linear system
70 
71  SparseMatrix_PL A = L_up_b.topLeftCorner(a_rows, a_rows);
72  SparseMatrix_PL C = L_up_b.bottomLeftCorner(b_rows-a_rows, a_rows);
73  SparseMatrix_PL B = C.transpose();
74  SparseMatrix_PL D = L_up_b.bottomRightCorner(b_rows-a_rows, b_rows-a_rows);
75  // D.makeCompressed();
76  // Eigen::LeastSquaresConjugateGradient<SparseMatrix_PL> solver(D);
77  // Eigen::BiCGSTAB<SparseMatrix_PL> solver(D);
78  // solver.setMaxIterations(1000);
79 
80  // Eigen::SparseQR<SparseMatrix_PL, Eigen::COLAMDOrdering<int>> solver(D);
81  // Eigen::ConjugateGradient<SparseMatrix_PL, Eigen::Lower> solver(D);
82  // solver.setTolerance(1e-4)
83  // L_up = A - B * solver.solve(C);
84 
85  L_up = A - B * DenseMatrix_PL(D).ldlt().solve(DenseMatrix_PL(C));
86 
87  // The computed matrix is only correct in the lower triangular portion.
88  // Eigen::SelfAdjointEigensolver is okay with this, but to avoid confusion and issues with other eigensolvers,
89  // We symmetrize the matrix:
90  L_up = DenseMatrix_PL(L_up.selfadjointView<Eigen::Lower>());
91 
92 
93  // auto end_solve = std::chrono::high_resolution_clock::now();
94  // auto duration_solve = std::chrono::duration_cast<std::chrono::milliseconds>(end_solve - start_solve);
95  // std::cout << "duration solve (ms):" << duration_solve.count() << std::endl;
96 
97  return;
98  }
99  };
100 
101  class ortho{
102  public:
104  auto start_pre_ortho = std::chrono::high_resolution_clock::now();
105 
106  int a_row_index = fbm->index_of_filtration(false,a);
107  int b_row_index = fbm->index_of_filtration(false,b);
108  int b_col_index = fbm->index_of_filtration(true,b);
109 
110  if (a_row_index == b_row_index){
111  SparseMatrix_storage B_pers;
112  fbm->submatrix_at_filtration(b, B_pers);
113 
114  L_up = (B_pers*B_pers.transpose()).cast<coefficient_type>();
115  return;
116  } else if (a_row_index == -1){ // no rows, return 0x0 empty matrix
117  L_up.setZero(0,0);
118  return;
119  } else if (b_col_index == -1){ // no columns, return nxn 0-matrix for n = number of cells in C_{up_dim}^a
120  L_up.setZero(a_row_index+1,a_row_index+1);
121 
122  return;
123  }
124 
125  std::tuple<SparseMatrix_PL,SparseMatrix_PL,std::vector<int>, int> reduction = fbm->reduce(a_row_index,b_row_index,b_col_index);
126 
127  SparseMatrix_PL B = std::get<0>(reduction);
128  DenseMatrix_PL Y = DenseMatrix_PL(std::get<1>(reduction));
129 
130  std::vector<int> I = std::get<2>(reduction);
131  int num_rows_a = std::get<3>(reduction)+1;
132 
133  DenseMatrix_PL Z = Y(Eigen::indexing::all,I); //https://eigen.tuxfamily.org/dox-devel/group__TutorialSlicingIndexing.html
134  // Note: slicing introduced in Eigen 3.4, which is not the version of libeigen3-dev on all platforms
135  // You must install Eigen 3.4
136 
137  if (Z.size() == 0){ // no zero-columns means empty basis of C_{p+1}^{a,b}, return empty matrix
138  L_up.setZero(num_rows_a,num_rows_a);//TODO can we make this sparse?
139  return;
140  }
141 
142  DenseMatrix_PL Z_ortho(Z.rows() ,Z.cols());
143 
144  Eigen::ColPivHouseholderQR<DenseMatrix_PL> qr3(Z);
145 
146  DenseMatrix_PL Q3 = qr3.householderQ();
147 
148  Z_ortho = Q3.leftCols(Z.cols()-1);
149 
150  DenseMatrix_PL B_pers_temp = B*Z_ortho;
151 
152  DenseMatrix_PL B_pers = B_pers_temp.topRows(num_rows_a);
153 
154  L_up = B_pers*B_pers.transpose();
155  return; // TODO: convert this to lower triangular product and test it!
156 
157  }
158  };
159 }
160 
161 #endif
Definition: FilteredBoundaryMatrix.hpp:16
void submatrix_at_filtration(filtration_type a, Eigen::SparseMatrix< FBMcoeff, Eigen::ColMajor > &M)
Definition: FilteredBoundaryMatrix.hpp:113
int index_of_filtration(bool use_domain_filtrations, filtration_type a)
Definition: FilteredBoundaryMatrix.hpp:79
std::tuple< SparseMatrix_PL, SparseMatrix_PL, std::vector< int >, int > reduce(int a_row_index, int b_row_index, int b_col_index)
Definition: FilteredBoundaryMatrix.hpp:120
Definition: up_algorithms.hpp:101
void operator()(FilteredBoundaryMatrix< storage > *fbm, filtration_type a, filtration_type b, DenseMatrix_PL &L_up)
Definition: up_algorithms.hpp:103
Definition: up_algorithms.hpp:22
void operator()(FilteredBoundaryMatrix< storage > *fbm, filtration_type a, filtration_type b, DenseMatrix_PL &L_up)
Definition: up_algorithms.hpp:24
Definition: PersistentLaplacians.cpp:7
float coefficient_type
Definition: typedefs.hpp:13
Eigen::Matrix< coefficient_type, Eigen::Dynamic, Eigen::Dynamic > DenseMatrix_PL
Definition: typedefs.hpp:24
Eigen::SparseMatrix< storage, Eigen::ColMajor > SparseMatrix_storage
Definition: typedefs.hpp:21
Eigen::SparseMatrix< coefficient_type, Eigen::ColMajor > SparseMatrix_PL
Definition: typedefs.hpp:23
double filtration_type
Definition: typedefs.hpp:34