TMB Documentation  v1.9.11
supernodal_inverse_subset.hpp
1 #include <Eigen/CholmodSupport>
2 #include <unsupported/Eigen/AutoDiff>
3 
4 namespace Eigen {
5 
8  Eigen::CholmodSupernodalLLT<Eigen::SparseMatrix<double> > {
9  typedef Eigen::CholmodSupernodalLLT<Eigen::SparseMatrix<double> > Base;
10  // Inherit all CTORs
11  using Base::Base;
12  cholmod_factor* get_factor() {
13  return m_cholmodFactor;
14  }
15  StorageIndex* get_super() {
16  return static_cast<StorageIndex*>(get_factor()->super);
17  }
18  StorageIndex* get_pi() {
19  return static_cast<StorageIndex*>(get_factor()->pi);
20  }
21  StorageIndex* get_s() {
22  return static_cast<StorageIndex*>(get_factor()->s);
23  }
24  StorageIndex* get_Perm() {
25  return static_cast<StorageIndex*>(get_factor()->Perm);
26  }
27  StorageIndex* get_px() {
28  return static_cast<StorageIndex*>(get_factor()->px);
29  }
30  size_t get_n() {
31  return get_factor()->n;
32  }
33  size_t get_nsuper() {
34  return get_factor()->nsuper;
35  }
36  size_t get_xsize() {
37  return get_factor()->xsize;
38  }
39  double* get_x() {
40  return static_cast<double*>(get_factor()->x);
41  }
42 };
43 template<class T>
44 struct SupernodalInverseSubset {
45  // typedefs
47  typedef Base::StorageIndex StorageIndex;
48  typedef SparseMatrix<T> SpMat;
49  typedef Matrix<T, Dynamic, Dynamic> Mat;
50  typedef Map<Array<StorageIndex, Dynamic, 1> > Rows_ref;
51  enum Operation {
52  Get,
53  Set,
54  Update
55  };
56  // workspaces
57  std::shared_ptr<Base> chm;
58  std::vector<StorageIndex> col2super;
59  std::vector<StorageIndex> col2offset;
60  std::vector<StorageIndex> idg;
61  std::vector<StorageIndex> iwrk;
62  std::vector<T> ans;
63  // Helper functions
64  void init_col2super() {
65  if (col2super.size() > 0) return;
66  col2super.resize( chm->get_n() );
67  StorageIndex *super = chm->get_super();
68  Index nsuper = chm->get_nsuper();
69  for (Index k=0, l=0; k < nsuper; k++) {
70  StorageIndex ncols = super[k + 1] - super[k];
71  while (ncols--) col2super[l++] = k;
72  }
73  }
74  void init_col2offset() {
75  if (col2offset.size() > 0) return;
76  col2offset.resize( chm->get_n(), 0 );
77  StorageIndex *pi = chm->get_pi();
78  for (size_t i = 1; i < col2offset.size(); i++) {
79  StorageIndex k = col2super[i - 1];
80  StorageIndex nrows = pi[k + 1] - pi[k];
81  col2offset[i] = col2offset[i - 1] + nrows;
82  }
83  }
84  void chm_index_scatter(StorageIndex col, StorageIndex* wrk) {
85  init_col2super();
86  init_col2offset();
87  StorageIndex k = col2super[col];
88  StorageIndex *pi = chm->get_pi();
89  StorageIndex *s = chm->get_s();
90  for (StorageIndex i = pi[k], j = col2offset[col] ; i < pi[k + 1] ; i++) {
91  wrk[s[i]] = j++;
92  }
93  }
94  std::vector<StorageIndex> index_gather(const SpMat &mat) {
95  std::vector<StorageIndex> ans;
96  StorageIndex *Perm = chm->get_Perm();
97  std::vector<StorageIndex> iPerm(mat.rows());
98  for (size_t i=0; i<iPerm.size(); i++)
99  iPerm[Perm[i]] = i;
100  std::vector<StorageIndex> wrk(mat.rows());
101  for (StorageIndex k=0; k<mat.outerSize(); ++k) {
102  chm_index_scatter(iPerm[k], wrk.data());
103  for (typename SpMat::InnerIterator it(mat, k); it; ++it) {
104  if (iPerm[it.row()] < iPerm[it.col()]) ans.push_back(-1); // Skip upper triangle
105  else ans.push_back( wrk[iPerm[it.row()]] );
106  }
107  }
108  return ans;
109  }
110  template<Operation Op>
111  void values(SpMat &mat) {
112  if (idg.size() == 0)
113  idg = index_gather(mat);
114  T* vptr = mat.valuePtr();
115  for (size_t i=0; i<idg.size(); i++) {
116  if (idg[i] != -1) {
117  if (Op == Set)
118  ans[idg[i]] = vptr[i];
119  if (Op == Get)
120  vptr[i] = ans[idg[i]];
121  }
122  }
123  }
124  Rows_ref rws(StorageIndex k) {
125  StorageIndex *pi = chm->get_pi();
126  StorageIndex *s = chm->get_s();
127  StorageIndex nrows = pi[k + 1] - pi[k];
128  return Rows_ref(s + pi[k], nrows);
129  }
130  StorageIndex nrow(StorageIndex k) {
131  StorageIndex *pi = chm->get_pi();
132  StorageIndex nrows = pi[k + 1] - pi[k];
133  return nrows;
134  }
135  StorageIndex ncol(StorageIndex k) {
136  StorageIndex *super = chm->get_super();
137  StorageIndex ncols = super[k + 1] - super[k];
138  return ncols;
139  }
140  template<Operation Op>
141  void denseBlock(Mat &ans,
142  T* x,
143  StorageIndex* r,
144  StorageIndex* c,
145  StorageIndex nr,
146  StorageIndex nc) {
147  init_col2super();
148  if (iwrk.size() != chm->get_n())
149  iwrk.resize(chm->get_n());
150  // Super node specific information
151  std::vector<StorageIndex> rel_rows; // rows relative to supernode
152  StorageIndex i_start = 0;
153  for (StorageIndex j = 0; j < nc; j++) {
154  StorageIndex k = col2super[c[j]];
155  auto X = getSuperNode(x, k);
156  StorageIndex *super = chm->get_super();
157  StorageIndex rel_col = c[j] - super[k]; // column relative to this supernode
158  if (j == 0 || k != col2super[c[j-1]]) {
159  // New supernode: Must update relative rows
160  // Scatter relative supernode rows
161  Rows_ref rows = rws(k);
162  for (StorageIndex i=0; i<rows.size(); i++)
163  iwrk[rows[i]] = i;
164  // Gather relative supernode rows
165  rel_rows.resize(nr);
166  for (StorageIndex i=0; i<nr; i++)
167  rel_rows[i] = iwrk[r[i]];
168  // Find i_start
169  i_start = 0;
170  while (i_start < nr && r[i_start] < c[j]) i_start++;
171  }
172  for (StorageIndex i = i_start; i < nr; i++) {
173  if (Op == Get)
174  ans(i, j) = X(rel_rows[i], rel_col);
175  if (Op == Set)
176  X(rel_rows[i], rel_col) = ans(i, j);
177  if (Op == Update)
178  X(rel_rows[i], rel_col) += ans(i, j);
179  }
180  }
181  }
182  Map<Mat> getSuperNode(T* x,
183  StorageIndex k) {
184  StorageIndex *px = chm->get_px();
185  T* data = x + px[k];
186  Map<Mat> ans(data, nrow(k), ncol(k));
187  return ans;
188  }
189  /* --- chol: get result from cholmod -------------------------------------- */
190  void chol_get_cached_values() {
191  double* x = chm->get_x();
192  size_t n = chm->get_xsize();
193  ans.resize(n);
194  for (size_t i=0; i<n; i++) ans[i] = x[i];
195  }
196  /* --- chol: forward recursions ------------------------------------------- */
197  void chol(SpMat x) {
198  // Zero initialized workspace to mimic get_factor()->x
199  ans.resize(0); ans.resize(chm->get_xsize(), 0);
200  // Fill SpMat into workspace
201  values<Set>(x);
202  // Forward loop
203  StorageIndex *super = chm->get_super();
204  Index nsuper = chm->get_nsuper();
205  for (Index k=0; k < nsuper; k++) {
206  Rows_ref rows = rws(k);
207  // Notation
208  StorageIndex ns = super[k + 1] - super[k];
209  StorageIndex np = rows.size() - ns;
210  StorageIndex* p = rows.data() + ns;
211  // r:=c(s,p)
212  auto Xrs = getSuperNode(ans.data(), k); // Write access !
213  Xrs.block(0, 0, ns, ns) =
214  Xrs.block(0, 0, ns, ns).llt().matrixL();
215  if (np > 0) {
216  Mat Xpp(np, np);
217  Xpp.setZero();
218  auto L = Xrs.block(0, 0, ns, ns);
219  auto Xps = Xrs.block(ns, 0, np, ns);
220  Xrs.block(ns, 0, np, ns) =
221  L.template triangularView<Lower>().
222  solve( Xps.transpose() ).
223  transpose();
224  Xpp.template selfadjointView<Lower>().
225  rankUpdate(Xrs.block(ns, 0, np, ns), -1.);
226  denseBlock<Update>(Xpp, ans.data(), p, p, np, np);
227  }
228  }
229  }
230  /* --- chol2inv: reverse recursions --------------------------------------- */
231  void chol2inv() {
232  // Reverse loop
233  StorageIndex *super = chm->get_super();
234  Index nsuper = chm->get_nsuper();
235  for (Index k=nsuper; k > 0; ) {
236  k--;
237  Rows_ref rows = rws(k);
238  // Notation
239  StorageIndex ns = super[k + 1] - super[k];
240  StorageIndex np = rows.size() - ns;
241  StorageIndex* p = rows.data() + ns;
242  // r:=c(s,p)
243  auto Xrs = getSuperNode(ans.data(), k); // Write access !
244  auto Lss = Xrs.block(0, 0, ns, ns);
245  // Xss = chol2inv(Lss)
246  Mat Lss_inv(ns, ns);
247  Lss_inv.setIdentity();
248  Lss.template triangularView<Lower>().
249  solveInPlace(Lss_inv);
250  Xrs.block(0, 0, ns, ns).setZero();
251  Xrs.block(0, 0, ns, ns).
252  template selfadjointView<Lower>().
253  rankUpdate(Lss_inv.transpose(), 1.);
254  if (np > 0) {
255  Mat Xpp(np, np);
256  denseBlock<Get>(Xpp, ans.data(), p, p, np, np);
257  Mat Lps = Xrs.block(ns, 0, np, ns);
258  Mat tmp =
259  Lss_inv.template triangularView<Lower>().
260  transpose() * Lps.transpose();
261  Xrs.block(ns, 0, np, ns) =
262  (-tmp * Xpp.template selfadjointView<Lower>()).transpose();
263  Xrs.block(0, 0, ns, ns) -= tmp * Xrs.block(ns, 0, np, ns);
264  }
265  }
266  }
267  SpMat operator()(SpMat x) {
268  // forward recursions (use cached values if T=double)
269  if (isDouble<T>::value) {
270  chol_get_cached_values();
271  } else {
272  chol(x);
273  }
274  // Reverse
275  chol2inv();
276  // Get result
277  x = x * 0;
278  values<Get> (x);
279  return x;
280  }
281  SupernodalInverseSubset(std::shared_ptr<Base> chm) :
282  chm(chm) { }
283  void print_common() {
284  cholmod_print_common("C", &(chm->cholmod()));
285  }
286 };
287 } // End namespace Eigen
Supernodal Cholesky factor with access to protected members.
License: GPL v2