CppADCodeGen  2.3.0
A C++ Algorithmic Differentiation Package with Source Code Generation
sparse_forjac_hessian.hpp
1 #ifndef CPPAD_CG_SPARSE_FORJAC_HESSIAN_INCLUDED
2 #define CPPAD_CG_SPARSE_FORJAC_HESSIAN_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2013 Ciengis
6  *
7  * CppADCodeGen is distributed under multiple licenses:
8  *
9  * - Eclipse Public License Version 1.0 (EPL1), and
10  * - GNU General Public License Version 3 (GPL3).
11  *
12  * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
13  * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
14  * ----------------------------------------------------------------------------
15  * Author: Joao Leal
16  */
20 namespace CppAD {
21 namespace cg {
22 
28 public:
30  std::vector<size_t> user_row;
32  std::vector<size_t> user_col;
35  std::vector<size_t> sort_row;
38  std::vector<size_t> sort_col;
40  size_t K;
41 
42  template<class Base, class VectorSize>
43  inline void prepare(const ADFun<Base>& fun,
44  const VectorSize& row,
45  const VectorSize& col) {
49  K = row.size();
50  size_t n = fun.Domain();
51  size_t m = fun.Range();
52 
53  if (user_row.size() == 0) {
54  // create version of (row, col, k) sorted by column value
55  user_col.resize(K + 1);
56  user_row.resize(K + 1);
57  sort_col.resize(K + 1);
58 
59  // put sorted indices in user_row and user_col
60  for (size_t k = 0; k < K; k++) {
61  user_row[k] = row[k];
62  user_col[k] = col[k];
63  }
64  user_row[K] = m;
65  user_col[K] = n;
66 
67  // put sorting indices in sort_col
68  index_sort(user_col, sort_col);
69  }
70 
71 #ifndef NDEBUG
72  CPPAD_ASSERT_KNOWN(size_t(row.size()) == K && size_t(col.size()) == K,
73  "sparseForJacHessian: either r or c does not have "
74  "the same size as jac.");
75  CPPAD_ASSERT_KNOWN(user_row.size() == K + 1 &&
76  user_col.size() == K + 1 &&
77  sort_col.size() == K + 1,
78  "sparseForJacHessian: invalid value in work.");
79  for (size_t k = 0; k < K; k++) {
80  CPPAD_ASSERT_KNOWN(row[k] < m,
81  "sparseForJacHessian: invalid value in r.");
82  CPPAD_ASSERT_KNOWN(col[k] < n,
83  "sparseForJacHessian: invalid value in c.");
84  CPPAD_ASSERT_KNOWN(sort_col[k] < K,
85  "sparseForJacHessian: invalid value in work.");
86  CPPAD_ASSERT_KNOWN(user_row[k] == row[k],
87  "sparseForJacHessian: invalid value in work.");
88  CPPAD_ASSERT_KNOWN(user_col[k] == col[k],
89  "sparseForJacHessian: invalid value in work.");
90  }
91 #endif
92  }
94 
95  inline void clear(void) {
96  user_row.clear();
97  user_col.clear();
98  sort_row.clear();
99  sort_col.clear();
100  }
101 };
102 
108 public:
110  std::vector<size_t> r_sort;
112  std::vector<size_t> c_sort;
114  std::vector<size_t> k_sort;
116  size_t K;
117 
118  template<class Base, class VectorSize>
119  inline void prepare(const ADFun<Base>& fun,
120  const VectorSize& row,
121  const VectorSize& col) {
125  size_t n = fun.Domain();
126  K = row.size();
127 
128  if (r_sort.size() == 0) {
129  // create version of (row, col, k) sorted by row value
130  c_sort.resize(K);
131  r_sort.resize(K + 1);
132  k_sort.resize(K);
133 
134  // put sorting indices in k_sort
135  index_sort(row, k_sort);
136 
137  for (size_t k = 0; k < K; k++) {
138  r_sort[k] = row[ k_sort[k] ];
139  c_sort[k] = col[ k_sort[k] ];
140  }
141  r_sort[K] = n;
142  }
143 #ifndef NDEBUG
144  CPPAD_ASSERT_KNOWN(size_t(row.size()) == K && size_t(col.size()) == K,
145  "sparseForJacHessian: either r or c does not have the same size as ehs.");
146  CPPAD_ASSERT_KNOWN(r_sort.size() == K + 1 &&
147  c_sort.size() == K &&
148  k_sort.size() == K,
149  "sparseForJacHessian: invalid value in work.");
150  for (size_t k = 0; k < K; k++) {
151  CPPAD_ASSERT_KNOWN(row[k] < n,
152  "sparseForJacHessian: invalid value in r.");
153  CPPAD_ASSERT_KNOWN(col[k] < n,
154  "sparseForJacHessian: invalid value in c.");
155  CPPAD_ASSERT_KNOWN(k_sort[k] < K,
156  "sparseForJacHessian: invalid value in work.");
157  CPPAD_ASSERT_KNOWN(r_sort[k] == row[ k_sort[k] ],
158  "sparseForJacHessian: invalid value in work.");
159  CPPAD_ASSERT_KNOWN(c_sort[k] == col[ k_sort[k] ],
160  "sparseForJacHessian: invalid value in work.");
161  }
162 #endif
163  }
165 
166  inline void clear(void) {
167  r_sort.clear();
168  c_sort.clear();
169  k_sort.clear();
170  }
171 };
172 
178 public:
182  std::vector<size_t> color;
183 
184  template<class Base, class VectorSize>
185  inline void prepare(const ADFun<Base>& fun,
186  const VectorSize& jacRow,
187  const VectorSize& jacCol,
188  const VectorSize& hesRow,
189  const VectorSize& hesCol) {
190  size_t n = fun.Domain();
191 
192  CPPAD_ASSERT_KNOWN(color.size() == 0 || color.size() == n,
193  "sparseForJacHessian: invalid value in work.");
194  if (color.size() != 0) {
195  for (size_t j = 0; j < n; j++) {
196  CPPAD_ASSERT_KNOWN(color[j] < n,
197  "sparseForJacHessian: invalid value in work.");
198  }
199  }
200 
201  jac.prepare(fun, jacRow, jacCol);
202  hes.prepare(fun, hesRow, hesCol);
203  }
205 
206  inline void clear(void) {
207  jac.clear();
208  hes.clear();
209  color.clear();
210  }
211 };
212 
213 template<class SparsityPattern>
214 inline void computeNotUsed(SparsityPattern& not_used,
215  const SparsityPattern& sparsity,
216  const SparsityPattern& r_used,
217  size_t m,
218  size_t n) {
219  using SparIter = typename SparsityPattern::const_iterator;
220 
221  assert(not_used.n_set() == 0);
222  not_used.resize(m, n);
223 
224  for (size_t i = 0; i < n; i++) {
225  SparIter j_itr(sparsity, i);
226  size_t j = *j_itr;
227  while (j != sparsity.end()) {
228  if (!r_used.is_element(j, i))
229  not_used.add_element(j, i);
230  j = *(++j_itr);
231  }
232  }
233 }
234 
235 template<class Base, class VectorSet>
236 inline size_t colorForwardJacobianHessian(const ADFun<Base>& fun,
237  const VectorSet& jac_p,
238  const VectorSet& hes_p,
239  SparseForjacHessianWork& work) {
244  size_t i, j1, j11, j2, c, k;
245 
246  using Set_type = typename VectorSet::value_type;
247  using SparsityPattern = typename local::internal_sparsity<Set_type>::pattern_type;
248  using SparIter = typename SparsityPattern::const_iterator;
249 
250  size_t n = fun.Domain();
251  size_t m = fun.Range();
252 
253  std::vector<size_t>& color = work.color;
254 
255  if (color.size() == 0) {
256 
257  color.resize(n);
258 
259  CPPAD_ASSERT_KNOWN(jac_p.size() == m,
260  "sparseForJacHessian: invalid jacobian sparsity pattern dimension.");
261  CPPAD_ASSERT_KNOWN(hes_p.size() == n,
262  "sparseForJacHessian: invalid hessian sparsity pattern dimension.");
263 
267  // transpose sparsity pattern
268  SparsityPattern p_transpose;
269  bool transpose = true;
270  sparsity_user2internal(p_transpose, jac_p, n, m, transpose, "Invalid sparsity pattern");
271 
272 
273  size_t jac_K = work.jac.K;
274  std::vector<size_t>& jac_row = work.jac.user_row;
275  std::vector<size_t>& jac_col = work.jac.user_col;
276  std::vector<size_t>& sort_col = work.jac.sort_col;
277 
278  CPPAD_ASSERT_UNKNOWN(p_transpose.n_set() == n);
279  CPPAD_ASSERT_UNKNOWN(p_transpose.end() == m);
280 
281  // rows and columns that are in the returned jacobian
282  SparsityPattern jac_r_used, jac_c_used;
283  jac_r_used.resize(n, m);
284  jac_c_used.resize(m, n);
285 
286  for (k = 0; k < jac_K; k++) {
287  CPPAD_ASSERT_UNKNOWN(jac_row[sort_col[k]] < m && jac_col[sort_col[k]] < n);
288  CPPAD_ASSERT_UNKNOWN(k == 0 || jac_col[sort_col[k - 1]] <= jac_col[sort_col[k]]);
289  CPPAD_ASSERT_KNOWN(p_transpose.is_element(jac_col[sort_col[k]], jac_row[sort_col[k]]),
290  "sparseForJacHessian: "
291  "a (row, col) pair is not in sparsity pattern.");
292  jac_r_used.add_element(jac_col[sort_col[k]], jac_row[sort_col[k]]);
293  jac_c_used.add_element(jac_row[sort_col[k]], jac_col[sort_col[k]]);
294  }
295 
296  // given a row index, which columns are non-zero and not used
297  SparsityPattern jac_not_used;
298  computeNotUsed(jac_not_used, p_transpose, jac_c_used, m, n);
299 
303  SparsityPattern hes_sparsity;
304  transpose = false;
305  sparsity_user2internal(hes_sparsity, hes_p, n, n, transpose, "Invalid sparsity pattern");
306 
307  size_t hes_K = work.hes.K;
308  std::vector<size_t>& hes_row(work.hes.r_sort);
309  std::vector<size_t>& hes_col(work.hes.c_sort);
310 
311  CPPAD_ASSERT_UNKNOWN(hes_sparsity.n_set() == n);
312  CPPAD_ASSERT_UNKNOWN(hes_sparsity.end() == n);
313 
314  // rows and columns that are in the returned hessian
315  SparsityPattern hes_r_used, hes_c_used;
316  hes_r_used.resize(n, n);
317  hes_c_used.resize(n, n);
318 
319  for (k = 0; k < hes_K; k++) {
320  CPPAD_ASSERT_UNKNOWN(hes_row[k] < n && hes_col[k] < n);
321  CPPAD_ASSERT_UNKNOWN(k == 0 || hes_row[k - 1] <= hes_row[k]);
322  CPPAD_ASSERT_KNOWN(hes_sparsity.is_element(hes_row[k], hes_col[k]),
323  "sparseForJacHessian: a (row, col) pair is not in sparsity pattern.");
324  hes_r_used.add_element(hes_col[k], hes_row[k]);
325  hes_c_used.add_element(hes_row[k], hes_col[k]);
326  }
327 
328  // given a column index, which rows are non-zero and not used
329  SparsityPattern hes_not_used;
330  computeNotUsed(hes_not_used, hes_sparsity, hes_r_used, n, n);
331 
332  // initial coloring
333  for (j1 = 0; j1 < n; j1++) {
334  color[j1] = j1;
335  }
336 
337  // See GreedyPartialD2Coloring Algorithm Section 3.6.2 of
338  // Graph Coloring in Optimization Revisited by
339  // Assefaw Gebremedhin, Fredrik Maane, Alex Pothen
340  vectorBool forbidden(n);
341  for (j1 = 1; j1 < n; j1++) {
342  // initialize all colors as ok for this column
343  // (value of forbidden for c > j does not matter)
344  for (c = 0; c <= j1; c++)
345  forbidden[c] = false;
346 
350  // for each row that is non-zero for this column
351  SparIter p_itr(p_transpose, j1);
352  i = *p_itr;
353  while (i != p_transpose.end()) {
354  // for each column that this row uses
355  SparIter jac_c_used_itr(jac_c_used, i);
356  j11 = *jac_c_used_itr;
357  while (j11 != jac_c_used.end()) {
358  // if this is not the same column, forbid its color
359  if (j11 < j1)
360  forbidden[ color[j11] ] = true;
361  j11 = *(++jac_c_used_itr);
362  }
363  i = *(++p_itr);
364  }
365 
366  // for each row that this column uses
367  SparIter jac_r_used_itr(jac_r_used, j1);
368  i = *jac_r_used_itr;
369 
370  while (i != jac_r_used.end()) {
371  // For each column that is non-zero for this row
372  // (the used columns have already been checked above).
373  SparIter jac_not_used_itr(jac_not_used, i);
374  j11 = *jac_not_used_itr;
375  while (j11 != jac_not_used.end()) {
376  // if this is not the same column, forbid its color
377  if (j11 < j1)
378  forbidden[ color[j11] ] = true;
379  j11 = *(++jac_not_used_itr);
380  }
381  i = *(++jac_r_used_itr);
382  }
383 
387  // -----------------------------------------------------
388  // Forbid colors that this row would destroy results for.
389  // for each column that is non-zero for this row
390  SparIter hes_itr(hes_sparsity, j1);
391  j2 = *hes_itr;
392  while (j2 != hes_sparsity.end()) {
393  // for each row that this column uses
394  SparIter hes_r_used_itr(hes_r_used, j2);
395  j11 = *hes_r_used_itr;
396  while (j11 != hes_r_used.end()) {
397  // if this is not the same row, forbid its color
398  if (j11 < j1)
399  forbidden[ color[j11] ] = true;
400  j11 = *(++hes_r_used_itr);
401  }
402  j2 = *(++hes_itr);
403  }
404 
405  // -------------------------------------------------------
406  // Forbid colors that would destroy the results for this row.
407  // for each column that this row used
408  SparIter hes_c_used_itr(hes_c_used, j1);
409  j2 = *hes_c_used_itr;
410  while (j2 != hes_c_used.end()) {
411  // For each row that is non-zero for this column
412  // (the used rows have already been checked above).
413  SparIter hes_not_used_itr(hes_not_used, j2);
414  j11 = *hes_not_used_itr;
415  while (j11 != hes_not_used.end()) {
416  // if this is not the same row, forbid its color
417  if (j11 < j1)
418  forbidden[ color[j11] ] = true;
419  j11 = *(++hes_not_used_itr);
420  }
421  j2 = *(++hes_c_used_itr);
422  }
423 
424 
425  // pick the color with smallest index
426  c = 0;
427  while (forbidden[c]) {
428  c++;
429  CPPAD_ASSERT_UNKNOWN(c <= j1);
430  }
431  color[j1] = c;
432  }
433  }
434 
435 
436  size_t n_color = 1;
437  for (j1 = 0; j1 < n; j1++)
438  n_color = std::max(n_color, color[j1] + 1);
439 
440  return n_color;
441 }
442 
489 template<class Base, class VectorBase, class VectorSet, class VectorSize>
490 size_t sparseForJacHessian(ADFun<Base>& fun,
491  const VectorBase& x,
492  const VectorBase& w,
493  VectorBase& y,
494  const VectorSet& jac_p,
495  const VectorSize& jac_row,
496  const VectorSize& jac_col,
497  VectorBase& jac,
498  const VectorSet& hes_p,
499  const VectorSize& hes_row,
500  const VectorSize& hes_col,
501  VectorBase& hes,
502  SparseForjacHessianWork& work) {
503  std::vector<VectorBase> vw(1);
504  std::vector<VectorBase> vhes(1);
505  vw[0] = w;
506  vhes[0] = hes;
507 
508  size_t n_sweep = sparseForJacHessian(fun,
509  x, vw,
510  y,
511  jac_p, jac_row, jac_col, jac,
512  hes_p, hes_row, hes_col, vhes,
513  work);
514 
515  hes = vhes[0];
516 
517  return n_sweep;
518 }
519 
520 template<class Base, class VectorBase, class VectorVectorBase, class VectorSet, class VectorSize>
521 size_t sparseForJacHessian(ADFun<Base>& fun,
522  const VectorBase& x,
523  const VectorVectorBase& w,
524  VectorBase& y,
525  const VectorSet& jac_p,
526  const VectorSize& jac_row,
527  const VectorSize& jac_col,
528  VectorBase& jac,
529  const VectorSet& hes_p,
530  const VectorSize& hes_row,
531  const VectorSize& hes_col,
532  VectorVectorBase& hes,
533  SparseForjacHessianWork& work) {
534  using CppAD::vectorBool;
535  size_t j1, k, c;
536 
537  size_t n = fun.Domain();
538  size_t m = fun.Range();
539 
540  size_t nH = size_t(hes.size());
541  size_t jac_K = size_t(jac_row.size());
542  size_t hes_K = size_t(hes_row.size());
543 
544  CPPADCG_ASSERT_KNOWN(size_t(x.size()) == n,
545  "sparseForJacHessian: size of x not equal domain dimension for f.");
546 
547  CPPADCG_ASSERT_KNOWN(size_t(w.size()) == nH,
548  "sparseForJacHessian: size of w not equal to the size of hes.");
549 
550  const std::vector<size_t>& jac_scol = work.jac.sort_col;
551  const std::vector<size_t>& hes_srow = work.hes.r_sort;
552  const std::vector<size_t>& hes_scol = work.hes.c_sort;
553  const std::vector<size_t>& hes_user_k = work.hes.k_sort;
554  const std::vector<size_t>& color = work.color;
555 
556  // some values
557  const Base zero(0);
558  const Base one(1);
559 
560  // check VectorBase is Simple Vector class with Base type elements
561  CheckSimpleVector<Base, VectorBase>();
562 
563  CPPAD_ASSERT_UNKNOWN(size_t(x.size()) == n);
564 
565  work.prepare(fun, jac_row, jac_col, hes_row, hes_col);
566 
570  size_t n_color = colorForwardJacobianHessian(fun, jac_p, hes_p, work);
571 
572 
573  // Point at which we are evaluating the Hessian
574  y = fun.Forward(0, x);
575 
576  // direction vector for calls to forward (columns of jacobian and rows of the Hessian)
577  VectorBase u(n);
578 
579  // location for return values from forward
580  VectorBase dy(m);
581 
582  // location for return values from reverse (columns of the Hessian)
583  VectorBase ddw(2 * n);
584 
585  // initialize the return value
586  for (k = 0; k < jac_K; k++)
587  jac[k] = zero;
588  for (size_t h = 0; h < nH; h++) {
589  VectorBase& hesh = hes[h];
590  for (k = 0; k < hes_K; k++)
591  hesh[k] = zero;
592  }
593 
594  // loop over colors
595  size_t n_sweep = 0;
596  for (c = 0; c < n_color; c++) {
597 
598  bool anyJac = false;
599  size_t kJac = 0;
600  for (j1 = 0; j1 < n; j1++) {
601  if (color[j1] == c) {
602  // find first k such that col[sort_col[k]] has color c
603  while (work.jac.user_col[jac_scol[kJac]] < j1)
604  kJac++;
605  anyJac = work.jac.user_col[jac_scol[kJac]] == j1;
606  if (anyJac)
607  break;
608  }
609  }
610 
611  bool anyHes = false;
612  size_t kHessStart = 0;
613  for (j1 = 0; j1 < n; j1++) {
614  if (color[j1] == c) {
615  // find first k such that row[k] has color c
616  while (hes_srow[kHessStart] < j1)
617  kHessStart++;
618  anyHes = hes_srow[kHessStart] == j1;
619  if (anyHes)
620  break;
621  }
622  }
623 
624  if (anyJac || anyHes) {
625  n_sweep++;
626  // combine all rows with this color
627  for (j1 = 0; j1 < n; j1++) {
628  u[j1] = zero;
629  if (color[j1] == c)
630  u[j1] = one;
631  }
632  // call forward mode for all these rows at once
633  dy = fun.Forward(1, u);
634 
635  if (anyJac) {
636  // set the corresponding components of the result
637  for (j1 = 0; j1 < n; j1++) {
638  if (color[j1] == c) {
639  // find first index in c for this jacobian column
640  while (work.jac.user_col[jac_scol[kJac]] < j1)
641  kJac++;
642  // extract the row results for this column
643  while (work.jac.user_col[jac_scol[kJac]] == j1) {
644  jac[ jac_scol[kJac] ] = dy[ work.jac.user_row[jac_scol[kJac]] ];
645  kJac++;
646  }
647  }
648  }
649  }
650 
651  if (anyHes) {
652  n_sweep++;
653 
654  for (size_t h = 0; h < nH; h++) {
655  // evaluate derivative of w^T * F'(x) * u
656  ddw = fun.Reverse(2, w[h]);
657 
658  VectorBase& hesh = hes[h];
659 
660  // set the corresponding components of the result
661  size_t kHess = kHessStart;
662  for (j1 = 0; j1 < n; j1++) {
663  if (color[j1] == c) {
664  // find first index in c for this column
665  while (hes_srow[kHess] < j1)
666  kHess++;
667  // extract the results for this row
668  while (hes_srow[kHess] == j1) {
669  size_t j2 = hes_scol[kHess];
670  hesh[ hes_user_k[kHess] ] = ddw[ j2 * 2 + 1 ];
671  kHess++;
672  }
673  }
674  }
675  }
676  }
677  }
678  }
679  return n_sweep;
680 }
681 
682 }
683 }
684 
685 #endif
std::vector< size_t > user_col
version of user col array with the extra value n at end
std::vector< size_t > color
results of the coloring algorithm
std::vector< size_t > user_row
version of user row array with the extra value m at end
void clear(void)
inform CppAD that this information needs to be recomputed
void clear(void)
inform CppAD that this information needs to be recomputed
std::vector< size_t > sort_row
indices that sort the user arrays by row with the extra value K at the end
std::vector< size_t > sort_col
indices that sort the user arrays by column with the extra value K at the end
std::vector< size_t > r_sort
version of user r array sorted by row or column
size_t K
number elements in the user sparse Jacobian
void prepare(const ADFun< Base > &fun, const VectorSize &row, const VectorSize &col)
size_t K
number elements in the user sparse Hessian
void clear(void)
inform CppAD that this information needs to be recomputed
void prepare(const ADFun< Base > &fun, const VectorSize &row, const VectorSize &col)
std::vector< size_t > k_sort
mapping from sorted array indices to user array indices
std::vector< size_t > c_sort
version of user c array sorted by row or column