hyper_graph_optimization_problem_edge_based.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
26 
27 namespace corbo {
28 
29 // TODO(roesmann): reduce amount of code by introducing more general functions working on all edge containers!
30 
32 {
33  assert(_graph.hasEdgeSet());
35  assert(gradient.size() == getParameterDimension());
36 
37  // we need to set to zero everything first
38  gradient.setZero();
39 
41 
42  // Iterate plain objective edges
43  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
44  {
45  // if (edge->providesJacobian())
46  for (int i = 0; i < edge->getNumVertices(); ++i)
47  {
48  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
49  if (vert_dim_unfixed == 0) continue;
50 
51  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
52  edge->computeJacobian(i, block_jacobian, nullptr);
53  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
54  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
55  }
56  }
57  // Iterate lsq objective edges
58  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
59  {
60  for (int i = 0; i < edge->getNumVertices(); ++i)
61  {
62  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
63  if (vert_dim_unfixed == 0) continue;
64 
65  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
66  edge->computeJacobian(i, block_jacobian, nullptr);
67 
68  // apply chain rule // TODO(roesmann): in case of forward differences we could use this for computing the jacobian
69  Eigen::VectorXd values(edge->getDimension());
70  edge->computeValues(values);
71 
72  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) += 2.0 * values.transpose() * block_jacobian;
73  }
74  }
75  // Iterate mixed edges
76  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
77  {
78  if (edge->getObjectiveDimension() == 0) continue;
79 
80  for (int i = 0; i < edge->getNumVertices(); ++i)
81  {
82  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
83  if (vert_dim_unfixed == 0) continue;
84 
85  Eigen::MatrixXd block_jacobian(edge->getObjectiveDimension(), vert_dim_unfixed);
86  edge->computeObjectiveJacobian(i, block_jacobian, nullptr);
87 
88  if (edge->isObjectiveLeastSquaresForm())
89  {
90  // apply chain rule // TODO(roesmann): in case of forward differences we could use this for computing the jacobian
91  Eigen::VectorXd values(edge->getObjectiveDimension());
92  edge->computeObjectiveValues(values);
93  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) += 2.0 * values.transpose() * block_jacobian;
94  }
95  else
96  {
97  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
98  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
99  }
100  }
101  }
102 }
103 
105 {
106  assert(_graph.hasEdgeSet());
108  assert(gradient.size() == getParameterDimension());
109 
110  // we need to set to zero everything first
111  gradient.setZero();
112 
114 
115  // Iterate plain objective edges
116  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
117  {
118  // if (edge->providesJacobian())
119  for (int i = 0; i < edge->getNumVertices(); ++i)
120  {
121  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
122  if (vert_dim_unfixed == 0) continue;
123 
124  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
125  edge->computeJacobian(i, block_jacobian, nullptr);
126  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
127  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
128  }
129  }
130 
131  // Iterate mixed edges
132  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
133  {
134  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLeastSquaresForm()) continue;
135 
136  for (int i = 0; i < edge->getNumVertices(); ++i)
137  {
138  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
139  if (vert_dim_unfixed == 0) continue;
140 
141  Eigen::MatrixXd block_jacobian(edge->getObjectiveDimension(), vert_dim_unfixed);
142  edge->computeObjectiveJacobian(i, block_jacobian, nullptr);
143 
144  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
145  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
146  }
147  }
148 }
149 
151 {
152  assert(_graph.hasEdgeSet());
154  assert(jacobian.rows() == getLsqObjectiveDimension());
155  assert(jacobian.cols() == getParameterDimension());
156 
157  // we need to set to zero everything first
158  jacobian.setZero();
159 
161 
162  // Iterate lsq objective edges
163  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
164  {
165  for (int i = 0; i < edge->getNumVertices(); ++i)
166  {
167  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
168  if (vert_dim_unfixed == 0) continue;
169 
170  edge->computeJacobian(i,
171  jacobian.block(edge->getEdgeIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
172  multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
173  }
174  }
175  // Iterate mixed edges
176  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
177  {
178  if (edge->getObjectiveDimension() == 0 || !edge->isObjectiveLeastSquaresForm()) continue;
179 
180  for (int i = 0; i < edge->getNumVertices(); ++i)
181  {
182  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
183  if (vert_dim_unfixed == 0) continue;
184 
185  edge->computeObjectiveJacobian(
186  i,
187  jacobian.block(edge->getEdgeObjectiveIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getObjectiveDimension(), vert_dim_unfixed),
188  multipliers ? multipliers + edge->getEdgeObjectiveIdx() : nullptr);
189  }
190  }
191 }
192 
194 {
195  int nnz = 0;
196 
197  assert(_graph.hasEdgeSet());
199 
201 
202  // Iterate lsq objective edges
203  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
204  {
205  for (int i = 0; i < edge->getNumVertices(); ++i)
206  {
207  nnz += edge->getDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
208  }
209  }
210  // Iterate mixed edges
211  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
212  {
213  if (edge->getObjectiveDimension() == 0 || !edge->isObjectiveLeastSquaresForm()) continue;
214 
215  for (int i = 0; i < edge->getNumVertices(); ++i)
216  {
217  nnz += edge->getObjectiveDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
218  }
219  }
220  return nnz;
221 }
222 
225 {
226  assert(i_row.size() == computeSparseJacobianLsqObjectiveNNZ());
227  assert(j_col.size() == i_row.size());
228 
230 
231  int nnz_idx = 0;
232 
233  // Iterate lsq objective edges
234  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
235  {
236  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
237  {
238  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
239  // Iterate all free variables
240  int idx_free = 0;
241  for (int i = 0; i < vertex->getDimension(); ++i)
242  {
243  if (!vertex->isFixedComponent(i))
244  {
245  // Iterate inner edge dimension
246  for (int j = 0; j < edge->getDimension(); ++j)
247  {
248  i_row[nnz_idx] = edge->getEdgeIdx() + j;
249  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
250  ++nnz_idx;
251  }
252  ++idx_free;
253  }
254  }
255  }
256  }
257  // Iterate mixed edges
258  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
259  {
260  if (edge->getObjectiveDimension() == 0 || !edge->isObjectiveLeastSquaresForm()) continue;
261 
262  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
263  {
264  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
265  // Iterate all free variables
266  int idx_free = 0;
267  for (int i = 0; i < vertex->getDimension(); ++i)
268  {
269  if (!vertex->isFixedComponent(i))
270  {
271  // Iterate inner edge dimension
272  for (int j = 0; j < edge->getObjectiveDimension(); ++j)
273  {
274  i_row[nnz_idx] = edge->getEdgeObjectiveIdx() + j;
275  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
276  ++nnz_idx;
277  }
278  ++idx_free;
279  }
280  }
281  }
282  }
283 }
284 
286 {
287  assert(values.size() == computeSparseJacobianLsqObjectiveNNZ());
288 
289  int nnz_idx = 0;
290 
292 
293  // Iterate lsq objective edges
294  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
295  {
296  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
297  {
298  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
299  if (vert_dim_unfixed == 0) continue;
300 
301  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
302  edge->computeJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
303  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
304  }
305  }
306 
307  // Iterate mixed edges
308  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
309  {
310  if (edge->getObjectiveDimension() == 0 || !edge->isObjectiveLeastSquaresForm()) continue;
311 
312  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
313  {
314  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
315  if (vert_dim_unfixed == 0) continue;
316 
317  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getObjectiveDimension(), vert_dim_unfixed);
318  edge->computeObjectiveJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeObjectiveIdx() : nullptr);
319  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
320  }
321  }
322 }
323 
325 {
326  assert(_graph.hasEdgeSet());
328  assert(jacobian.rows() == getEqualityDimension());
329  assert(jacobian.cols() == getParameterDimension());
330 
331  // we need to set to zero everything first
332  jacobian.setZero();
333 
335 
336  // Iterate equality edges
337  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
338  {
339  for (int i = 0; i < edge->getNumVertices(); ++i)
340  {
341  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
342  if (vert_dim_unfixed == 0) continue;
343 
344  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
345  edge->computeJacobian(i,
346  jacobian.block(edge->getEdgeIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
347  multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
348  }
349  }
350  // Iterate mixed edges
351  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
352  {
353  if (edge->getEqualityDimension() == 0) continue;
354 
355  for (int i = 0; i < edge->getNumVertices(); ++i)
356  {
357  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
358  if (vert_dim_unfixed == 0) continue;
359 
360  Eigen::MatrixXd block_jacobian(edge->getEqualityDimension(), vert_dim_unfixed);
361  edge->computeEqualityJacobian(
362  i, jacobian.block(edge->getEdgeEqualityIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getEqualityDimension(), vert_dim_unfixed),
363  multipliers ? multipliers + edge->getEdgeEqualityIdx() : nullptr);
364  }
365  }
366 }
367 
369 {
370  int nnz = 0;
371 
372  assert(_graph.hasEdgeSet());
374 
376 
377  // Iterate equality edges
378  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
379  {
380  for (int i = 0; i < edge->getNumVertices(); ++i)
381  {
382  nnz += edge->getDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
383  }
384  }
385  // Iterate mixed edges
386  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
387  {
388  if (edge->getEqualityDimension() == 0) continue;
389 
390  for (int i = 0; i < edge->getNumVertices(); ++i)
391  {
392  nnz += edge->getEqualityDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
393  }
394  }
395  return nnz;
396 }
397 
400 {
401  assert(i_row.size() == computeSparseJacobianEqualitiesNNZ());
402  assert(j_col.size() == i_row.size());
403 
405 
406  int nnz_idx = 0;
407 
408  // Iterate equality edges
409  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
410  {
411  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
412  {
413  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
414  // Iterate all free variables
415  int idx_free = 0;
416  for (int i = 0; i < vertex->getDimension(); ++i)
417  {
418  if (!vertex->isFixedComponent(i))
419  {
420  // Iterate inner edge dimension
421  for (int j = 0; j < edge->getDimension(); ++j)
422  {
423  i_row[nnz_idx] = edge->getEdgeIdx() + j;
424  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
425  ++nnz_idx;
426  }
427  ++idx_free;
428  }
429  }
430  }
431  }
432  // Iterate mixed edges
433  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
434  {
435  if (edge->getEqualityDimension() == 0) continue;
436 
437  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
438  {
439  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
440  // Iterate all free variables
441  int idx_free = 0;
442  for (int i = 0; i < vertex->getDimension(); ++i)
443  {
444  if (!vertex->isFixedComponent(i))
445  {
446  // Iterate inner edge dimension
447  for (int j = 0; j < edge->getEqualityDimension(); ++j)
448  {
449  i_row[nnz_idx] = edge->getEdgeEqualityIdx() + j;
450  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
451  ++nnz_idx;
452  }
453  ++idx_free;
454  }
455  }
456  }
457  }
458 }
459 
461 {
462  assert(values.size() == computeSparseJacobianEqualitiesNNZ());
463 
464  int nnz_idx = 0;
465 
467 
468  // Iterate equality edges
469  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
470  {
471  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
472  {
473  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
474  if (vert_dim_unfixed == 0) continue;
475 
476  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
477  edge->computeJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
478  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
479  }
480  }
481 
482  // Iterate mixed edges
483  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
484  {
485  if (edge->getEqualityDimension() == 0) continue;
486 
487  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
488  {
489  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
490  if (vert_dim_unfixed == 0) continue;
491 
492  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
493  edge->computeEqualityJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeEqualityIdx() : nullptr);
494  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
495  }
496  }
497 }
498 
500 {
501  assert(_graph.hasEdgeSet());
503  assert(jacobian.rows() == getInequalityDimension());
504  assert(jacobian.cols() == getParameterDimension());
505 
506  // we need to set to zero everything first
507  jacobian.setZero();
508 
510 
511  // Iterate inequality edges
512  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
513  {
514  for (int i = 0; i < edge->getNumVertices(); ++i)
515  {
516  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
517  if (vert_dim_unfixed == 0) continue;
518 
519  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
520  edge->computeJacobian(i,
521  jacobian.block(edge->getEdgeIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
522  multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
523  }
524  }
525  // Iterate mixed edges
526  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
527  {
528  if (edge->getInequalityDimension() == 0) continue;
529 
530  for (int i = 0; i < edge->getNumVertices(); ++i)
531  {
532  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
533  if (vert_dim_unfixed == 0) continue;
534 
535  Eigen::MatrixXd block_jacobian(edge->getInequalityDimension(), vert_dim_unfixed);
536  edge->computeInequalityJacobian(
537  i,
538  jacobian.block(edge->getEdgeInequalityIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getInequalityDimension(), vert_dim_unfixed),
539  multipliers ? multipliers + edge->getEdgeInequalityIdx() : nullptr);
540  }
541  }
542 }
543 
545 {
546  int nnz = 0;
547 
548  assert(_graph.hasEdgeSet());
550 
552 
553  // Iterate inequality edges
554  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
555  {
556  for (int i = 0; i < edge->getNumVertices(); ++i)
557  {
558  nnz += edge->getDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
559  }
560  }
561  // Iterate mixed edges
562  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
563  {
564  if (edge->getInequalityDimension() == 0) continue;
565 
566  for (int i = 0; i < edge->getNumVertices(); ++i)
567  {
568  nnz += edge->getInequalityDimension() * edge->getVertexRaw(i)->getDimensionUnfixed(); // block size
569  }
570  }
571  return nnz;
572 }
573 
576 {
577  assert(i_row.size() == computeSparseJacobianInequalitiesNNZ());
578  assert(j_col.size() == i_row.size());
579 
581 
582  int nnz_idx = 0;
583 
584  // Iterate inequality edges
585  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
586  {
587  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
588  {
589  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
590  // Iterate all free variables
591  int idx_free = 0;
592  for (int i = 0; i < vertex->getDimension(); ++i)
593  {
594  if (!vertex->isFixedComponent(i))
595  {
596  // Iterate inner edge dimension
597  for (int j = 0; j < edge->getDimension(); ++j)
598  {
599  i_row[nnz_idx] = edge->getEdgeIdx() + j;
600  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
601  ++nnz_idx;
602  }
603  ++idx_free;
604  }
605  }
606  }
607  }
608  // Iterate mixed edges
609  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
610  {
611  if (edge->getInequalityDimension() == 0) continue;
612 
613  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
614  {
615  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
616  // Iterate all free variables
617  int idx_free = 0;
618  for (int i = 0; i < vertex->getDimension(); ++i)
619  {
620  if (!vertex->isFixedComponent(i))
621  {
622  // Iterate inner edge dimension
623  for (int j = 0; j < edge->getInequalityDimension(); ++j)
624  {
625  i_row[nnz_idx] = edge->getEdgeInequalityIdx() + j;
626  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
627  ++nnz_idx;
628  }
629  ++idx_free;
630  }
631  }
632  }
633  }
634 }
635 
637 {
638  assert(values.size() == computeSparseJacobianInequalitiesNNZ());
639 
640  int nnz_idx = 0;
641 
643 
644  // Iterate inequality edges
645  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
646  {
647  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
648  {
649  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
650  if (vert_dim_unfixed == 0) continue;
651 
652  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
653  edge->computeJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeIdx() : nullptr);
654  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
655  }
656  }
657 
658  // Iterate mixed edges
659  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
660  {
661  if (edge->getInequalityDimension() == 0) continue;
662 
663  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
664  {
665  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
666  if (vert_dim_unfixed == 0) continue;
667 
668  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
669  edge->computeInequalityJacobian(vert_idx, block_jacobian, multipliers ? multipliers + edge->getEdgeInequalityIdx() : nullptr);
670  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
671  }
672  }
673 }
674 
676 {
677  assert(_graph.hasEdgeSet());
679  assert(jacobian.rows() == getInequalityDimension());
680  assert(jacobian.cols() == getParameterDimension());
681 
682  // we need to set to zero everything first
683  jacobian.setZero();
684 
686 
687  // Iterate inequality edges
688  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
689  {
690  for (int i = 0; i < edge->getNumVertices(); ++i)
691  {
692  const VertexInterface* vertex = edge->getVertexRaw(i);
693  int vert_dim_unfixed = vertex->getDimensionUnfixed();
694  if (vert_dim_unfixed == 0) continue;
695 
696  // compute values and check which values are active
697  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
698  Eigen::VectorXd values(edge->getDimension());
699  edge->computeValues(values);
700  Eigen::Array<bool, -1, 1> active = values.array() > 0.0;
701 
702  if (!active.any()) continue;
703 
704  // TODO(roesmann): we need to compute the complete block jacobian here (some rows are already zero according to array "active"
705  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
706  edge->computeJacobian(i, jacobian.block(edge->getEdgeIdx(), vertex->getVertexIdx(), edge->getDimension(), vert_dim_unfixed), nullptr);
707 
708  // now set values to zero if inactive or multiply with weight otherwise
709  for (int j = 0; j < edge->getDimension(); ++j)
710  {
711  if (active[j] && weight != 1.0)
712  jacobian.block(edge->getEdgeIdx() + j, edge->getVertexRaw(i)->getVertexIdx(), 1, vert_dim_unfixed) *= weight;
713  else if (!active[j])
714  jacobian.block(edge->getEdgeIdx() + j, edge->getVertexRaw(i)->getVertexIdx(), 1, vert_dim_unfixed).setZero(); // should be zero
715  // already
716  }
717  }
718  }
719  // Iterate mixed edges
720  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
721  {
722  if (edge->getInequalityDimension() == 0) continue;
723 
724  for (int i = 0; i < edge->getNumVertices(); ++i)
725  {
726  const VertexInterface* vertex = edge->getVertexRaw(i);
727  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
728  if (vert_dim_unfixed == 0) continue;
729 
730  // compute values and check which values are active
731  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
732  Eigen::VectorXd values(edge->getInequalityDimension());
733  edge->precompute();
734  edge->computeInequalityValues(values);
735  Eigen::Array<bool, -1, 1> active = values.array() > 0.0;
736 
737  if (!active.any()) continue;
738 
739  Eigen::MatrixXd block_jacobian(edge->getInequalityDimension(), vert_dim_unfixed);
740  edge->computeInequalityJacobian(
741  i, jacobian.block(edge->getEdgeInequalityIdx(), vertex->getVertexIdx(), edge->getInequalityDimension(), vert_dim_unfixed), nullptr);
742 
743  // now set values to zero if inactive or multiply with weight otherwise
744  for (int j = 0; j < edge->getInequalityDimension(); ++j)
745  {
746  if (active[j] && weight != 1.0)
747  jacobian.block(edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed) *= weight;
748  else if (!active[j])
749  jacobian.block(edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed).setZero(); // should be zero
750  }
751  }
752  }
753 }
755 {
756  assert(values.size() == computeSparseJacobianInequalitiesNNZ());
757 
758  int jac_row_idx = 0;
759  int nnz_idx = 0;
760 
762 
763  // Iterate inequality edges
764  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
765  {
766  // compute values and check which values are active
767  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
768  Eigen::VectorXd edge_values(edge->getDimension());
769  edge->computeValues(edge_values);
770  Eigen::Array<bool, -1, 1> active = edge_values.array() > 0.0;
771 
772  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
773  {
774  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
775  if (vert_dim_unfixed == 0) continue;
776 
777  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
778  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
779 
780  if (!active.any())
781  {
782  block_jacobian.setZero();
783  continue;
784  }
785 
786  edge->computeJacobian(vert_idx, block_jacobian, nullptr);
787 
788  // now set values to zero if inactive or multiply with weight otherwise
789  for (int j = 0; j < edge->getDimension(); ++j)
790  {
791  if (active[j] && weight != 1.0)
792  block_jacobian.block(j, 0, 1, vert_dim_unfixed) *= weight;
793  else if (!active[j])
794  block_jacobian.block(j, 0, 1, vert_dim_unfixed).setZero();
795  }
796  }
797  jac_row_idx += edge->getDimension();
798  }
799 
800  // Iterate mixed edges
801  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
802  {
803  if (edge->getInequalityDimension() == 0) continue;
804 
805  // compute values and check which values are active
806  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
807  Eigen::VectorXd edge_values(edge->getInequalityDimension());
808  edge->precompute();
809  edge->computeInequalityValues(edge_values);
810  Eigen::Array<bool, -1, 1> active = edge_values.array() > 0.0;
811 
812  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
813  {
814  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
815  if (vert_dim_unfixed == 0) continue;
816 
817  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
818  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
819 
820  if (!active.any())
821  {
822  block_jacobian.setZero();
823  continue;
824  }
825 
826  edge->computeInequalityJacobian(vert_idx, block_jacobian, nullptr);
827 
828  // now set values to zero if inactive or multiply with weight otherwise
829  for (int j = 0; j < edge->getInequalityDimension(); ++j)
830  {
831  if (active[j] && weight != 1.0)
832  block_jacobian.block(j, 0, 1, vert_dim_unfixed) *= weight;
833  else if (!active[j])
834  block_jacobian.block(j, 0, 1, vert_dim_unfixed).setZero();
835  }
836  }
837  jac_row_idx += edge->getInequalityDimension();
838  }
839 }
840 
842  Eigen::Ref<Eigen::MatrixXd> jacobian_lsq_obj,
843  Eigen::Ref<Eigen::MatrixXd> jacobian_eq, Eigen::Ref<Eigen::MatrixXd> jacobian_ineq,
844  const double* multipliers_lsq_obj, const double* multipliers_eq,
845  const double* multipliers_ineq, bool active_ineq, double active_ineq_weight)
846 {
847  assert(gradient_non_lsq_obj.size() == getParameterDimension());
848  assert(jacobian_lsq_obj.rows() == getLsqObjectiveDimension());
849  assert(jacobian_lsq_obj.cols() == getParameterDimension());
850  assert(jacobian_eq.rows() == getEqualityDimension());
851  assert(jacobian_eq.cols() == getParameterDimension());
852  assert(jacobian_ineq.rows() == getInequalityDimension());
853  assert(jacobian_ineq.cols() == getParameterDimension());
854 
855  PRINT_DEBUG_COND_ONCE(active_ineq && multipliers_ineq,
856  "HyperGraphOptimizationProblemEdgeBased::computeDenseJacobians(): multiplier_ineq is ignored if active_ineq is enabled");
857 
858  assert(_graph.hasEdgeSet());
860 
861  // int jac_row_idx = 0;
862  // int nnz_idx = 0;
863 
864  // set matrices to zero
865  gradient_non_lsq_obj.setZero();
866  jacobian_lsq_obj.setZero();
867  jacobian_eq.setZero();
868  jacobian_ineq.setZero();
869 
871 
872  // just copy and paste, the important thing is for MixedEdges -> precompute is called just once
873 
874  // Iterate plain objective edges (for gradient)
875  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
876  {
877  for (int i = 0; i < edge->getNumVertices(); ++i)
878  {
879  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
880  if (vert_dim_unfixed == 0) continue;
881 
882  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
883  edge->computeJacobian(i, block_jacobian, nullptr);
884  gradient_non_lsq_obj.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
885  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
886  }
887  }
888 
889  // Iterate lsq objective edges
890  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
891  {
892  for (int i = 0; i < edge->getNumVertices(); ++i)
893  {
894  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
895  if (vert_dim_unfixed == 0) continue;
896 
897  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
898  edge->computeJacobian(
899  i, jacobian_lsq_obj.block(edge->getEdgeIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
900  multipliers_lsq_obj ? multipliers_lsq_obj + edge->getEdgeIdx() : nullptr);
901  }
902  }
903 
904  // Iterate equality edges
905  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
906  {
907  for (int i = 0; i < edge->getNumVertices(); ++i)
908  {
909  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
910  if (vert_dim_unfixed == 0) continue;
911 
912  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
913  edge->computeJacobian(
914  i, jacobian_eq.block(edge->getEdgeIdx(), edge->getVertexRaw(i)->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
915  multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr);
916  }
917  }
918 
919  // Iterate inequality edges
920  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
921  {
922  for (int i = 0; i < edge->getNumVertices(); ++i)
923  {
924  const VertexInterface* vertex = edge->getVertexRaw(i);
925  int vert_dim_unfixed = vertex->getDimensionUnfixed();
926  if (vert_dim_unfixed == 0) continue;
927 
928  if (active_ineq)
929  {
930  // compute values and check which values are active
931  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
932  Eigen::VectorXd edge_values(edge->getDimension());
933  edge->computeValues(edge_values);
934  Eigen::Array<bool, -1, 1> active = edge_values.array() > 0.0;
935 
936  if (!active.any()) continue;
937 
938  // TODO(roesmann): we need to compute the complete block jacobian here (some rows are already zero according to array "active"
939  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
940  edge->computeJacobian(i, jacobian_ineq.block(edge->getEdgeIdx(), vertex->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
941  nullptr);
942 
943  // now set values to zero if inactive or multiply with weight otherwise
944  for (int j = 0; j < edge->getDimension(); ++j)
945  {
946  if (active[j] && active_ineq_weight != 1.0)
947  jacobian_ineq.block(edge->getEdgeIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed) *= active_ineq_weight;
948  else if (!active[j])
949  jacobian_ineq.block(edge->getEdgeIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed).setZero();
950  }
951  }
952  else
953  {
954  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
955  edge->computeJacobian(i, jacobian_ineq.block(edge->getEdgeIdx(), vertex->getVertexIdx(), edge->getDimension(), vert_dim_unfixed),
956  multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr);
957  }
958  }
959  }
960 
961  // Iterate mixed edges
962  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
963  {
964  const double* mult_ineq_part = (multipliers_ineq && !active_ineq) ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
965  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
966  const double* mult_obj_lsq_part = multipliers_lsq_obj ? multipliers_lsq_obj + edge->getEdgeObjectiveIdx() : nullptr;
967 
968  for (int i = 0; i < edge->getNumVertices(); ++i)
969  {
970  const VertexInterface* vertex = edge->getVertexRaw(i);
971  int vert_dim_unfixed = vertex->getDimensionUnfixed();
972  if (vert_dim_unfixed == 0) continue;
973 
974  Eigen::Ref<Eigen::MatrixXd> block_eq =
975  jacobian_eq.block(edge->getEdgeEqualityIdx(), vertex->getVertexIdx(), edge->getEqualityDimension(), vert_dim_unfixed);
976 
977  Eigen::Ref<Eigen::MatrixXd> block_ineq =
978  jacobian_ineq.block(edge->getEdgeInequalityIdx(), vertex->getVertexIdx(), edge->getInequalityDimension(), vert_dim_unfixed);
979 
980  if (edge->isObjectiveLeastSquaresForm())
981  {
982  Eigen::Ref<Eigen::MatrixXd> block_lsq_obj =
983  jacobian_lsq_obj.block(edge->getEdgeObjectiveIdx(), vertex->getVertexIdx(), edge->getObjectiveDimension(), vert_dim_unfixed);
984  edge->computeJacobians(i, block_lsq_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
985  }
986  else
987  {
988  Eigen::MatrixXd block_lsq_obj(edge->getObjectiveDimension(), vert_dim_unfixed);
989  edge->computeJacobians(i, block_lsq_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
990 
991  gradient_non_lsq_obj.segment(vertex->getVertexIdx(), vert_dim_unfixed) +=
992  block_lsq_obj.colwise().sum(); // sum up all values, we need a dimension of one
993  }
994 
995  if (active_ineq)
996  {
997  // compute values and check which values are active
998  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
999  Eigen::VectorXd ineq_values(edge->getInequalityDimension());
1000  edge->precompute();
1001  edge->computeInequalityValues(ineq_values);
1002  Eigen::Array<bool, -1, 1> active = ineq_values.array() > 0.0;
1003 
1004  // now set values to zero if inactive or multiply with weight otherwise
1005  for (int j = 0; j < edge->getInequalityDimension(); ++j)
1006  {
1007  if (active[j] && active_ineq_weight != 1.0)
1008  jacobian_ineq.block(edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed) *= active_ineq_weight;
1009  else if (!active[j])
1010  jacobian_ineq.block(edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx(), 1, vert_dim_unfixed).setZero();
1011  }
1012  }
1013  }
1014  }
1015 }
1016 
1018  Eigen::Ref<Eigen::VectorXd> values_eq,
1019  Eigen::Ref<Eigen::VectorXd> values_ineq, const double* multipliers_obj,
1020  const double* multipliers_eq, const double* multipliers_ineq,
1021  bool active_ineq, double active_ineq_weight)
1022 {
1023  assert(values_obj.size() == computeSparseJacobianLsqObjectiveNNZ());
1024  assert(values_eq.size() == computeSparseJacobianEqualitiesNNZ());
1025  assert(values_ineq.size() == computeSparseJacobianInequalitiesNNZ());
1026 
1028  active_ineq && multipliers_ineq,
1029  "HyperGraphOptimizationProblemEdgeBased::computeSparseJacobiansValues(): multiplier_ineq is ignored if active_ineq is enabled");
1030 
1032 
1033  int obj_nnz_idx = 0;
1034  int eq_nnz_idx = 0;
1035  int ineq_nnz_idx = 0;
1036 
1037  // Iterate lsq objective edges
1038  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1039  {
1040  const double* mult_obj_lsq_part = multipliers_obj ? multipliers_obj + edge->getEdgeIdx() : nullptr;
1041 
1042  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1043  {
1044  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1045  if (vert_dim_unfixed == 0) continue;
1046 
1047  Eigen::Map<Eigen::MatrixXd> block_jacobian(values_obj.data() + obj_nnz_idx, edge->getDimension(), vert_dim_unfixed);
1048  edge->computeJacobian(vert_idx, block_jacobian, mult_obj_lsq_part);
1049  obj_nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1050  }
1051  }
1052 
1053  // Iterate equality edges
1054  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
1055  {
1056  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
1057 
1058  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1059  {
1060  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1061  if (vert_dim_unfixed == 0) continue;
1062 
1063  Eigen::Map<Eigen::MatrixXd> block_jacobian(values_eq.data() + eq_nnz_idx, edge->getDimension(), vert_dim_unfixed);
1064  edge->computeJacobian(vert_idx, block_jacobian, mult_eq_part);
1065  eq_nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1066  }
1067  }
1068 
1069  // Iterate inequality edges
1070  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
1071  {
1072  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
1073 
1074  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1075  {
1076  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1077  if (vert_dim_unfixed == 0) continue;
1078 
1079  if (active_ineq)
1080  {
1081  // compute values and check which values are active
1082  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
1083  Eigen::VectorXd edge_values(edge->getDimension());
1084  edge->computeValues(edge_values);
1085  Eigen::Array<bool, -1, 1> active = edge_values.array() > 0.0;
1086 
1087  Eigen::Map<Eigen::MatrixXd> block_jacobian(values_ineq.data() + ineq_nnz_idx, edge->getDimension(), vert_dim_unfixed);
1088  ineq_nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1089 
1090  if (!active.any())
1091  {
1092  block_jacobian.setZero();
1093  continue;
1094  }
1095 
1096  edge->computeJacobian(vert_idx, block_jacobian, nullptr);
1097 
1098  // now set values to zero if inactive or multiply with weight otherwise
1099  for (int j = 0; j < edge->getDimension(); ++j)
1100  {
1101  if (active[j] && active_ineq_weight != 1.0)
1102  block_jacobian.block(j, 0, 1, vert_dim_unfixed) *= active_ineq_weight;
1103  else if (!active[j])
1104  block_jacobian.block(j, 0, 1, vert_dim_unfixed).setZero();
1105  }
1106  }
1107  else
1108  {
1109  Eigen::Map<Eigen::MatrixXd> block_jacobian(values_ineq.data() + ineq_nnz_idx, edge->getDimension(), vert_dim_unfixed);
1110  edge->computeJacobian(vert_idx, block_jacobian, mult_ineq_part);
1111  ineq_nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1112  }
1113  }
1114  }
1115 
1116  // Iterate mixed edges
1117  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
1118  {
1119  // we currently need no gradient of the non-lsq terms
1120  if (!edge->isObjectiveLeastSquaresForm() && edge->getEqualityDimension() == 0 && edge->getInequalityDimension() == 0) continue;
1121 
1122  const double* mult_obj_lsq_part = multipliers_obj ? multipliers_obj + edge->getEdgeObjectiveIdx() : nullptr;
1123  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
1124  const double* mult_ineq_part = (multipliers_ineq && !active_ineq) ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
1125 
1126  for (int i = 0; i < edge->getNumVertices(); ++i)
1127  {
1128  const VertexInterface* vertex = edge->getVertexRaw(i);
1129  int vert_dim_unfixed = vertex->getDimensionUnfixed();
1130  if (vert_dim_unfixed == 0) continue;
1131 
1132  Eigen::Map<Eigen::MatrixXd> block_eq(values_eq.data() + eq_nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1133  eq_nnz_idx += block_eq.rows() * block_eq.cols();
1134 
1135  Eigen::Map<Eigen::MatrixXd> block_ineq(values_ineq.data() + ineq_nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1136  ineq_nnz_idx += block_ineq.rows() * block_ineq.cols();
1137 
1138  if (edge->isObjectiveLeastSquaresForm())
1139  {
1140  Eigen::Map<Eigen::MatrixXd> block_lsq_obj(values_obj.data() + obj_nnz_idx, edge->getObjectiveDimension(), vert_dim_unfixed);
1141  obj_nnz_idx += block_lsq_obj.rows() * block_lsq_obj.cols();
1142 
1143  edge->computeJacobians(i, block_lsq_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
1144  }
1145  else
1146  {
1147  // Do nothing, the sparse version currently computes no gradient of the non-lsq parts
1148  // TODO(roesmann): the following matrix is useless and might be better removed (or the method should also compute the gradient)
1149  Eigen::MatrixXd block_lsq_obj(edge->getObjectiveDimension(), vert_dim_unfixed);
1150  edge->computeJacobians(i, block_lsq_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
1151 
1152  // Eigen::MatrixXd block_lsq_obj(edge->getObjectiveDimension(), vert_dim_unfixed);
1153  // edge->computeJacobians(i, block_lsq_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
1154 
1155  // gradient_non_lsq_obj.segment(vertex->getVertexIdx(), vert_dim_unfixed) +=
1156  // block_lsq_obj.colwise().sum(); // sum up all values, we need a dimension of one
1157  }
1158 
1159  if (active_ineq)
1160  {
1161  // compute values and check which values are active
1162  // TODO(roesmann): we could obtain values computed before calling computeDenseJacobianActiveInequalities();
1163  Eigen::VectorXd ineq_values(edge->getInequalityDimension());
1164  edge->precompute();
1165  edge->computeInequalityValues(ineq_values);
1166  Eigen::Array<bool, -1, 1> active = ineq_values.array() > 0.0;
1167 
1168  // now set values to zero if inactive or multiply with weight otherwise
1169  for (int j = 0; j < edge->getInequalityDimension(); ++j)
1170  {
1171  if (active[j] && active_ineq_weight != 1.0)
1172  block_ineq.row(j) *= active_ineq_weight;
1173  else if (!active[j])
1174  block_ineq.row(j).setZero();
1175  }
1176  }
1177  }
1178  }
1179 }
1180 
1181 int HyperGraphOptimizationProblemEdgeBased::computeCombinedSparseJacobiansNNZ(bool objective_lsq, bool equality, bool inequality)
1182 {
1183  int nnz = 0;
1184  if (objective_lsq) nnz += computeSparseJacobianLsqObjectiveNNZ();
1185  if (equality) nnz += computeSparseJacobianEqualitiesNNZ();
1186  if (inequality) nnz += computeSparseJacobianInequalitiesNNZ();
1187  return nnz;
1188 }
1190  Eigen::Ref<Eigen::VectorXi> j_col, bool objective_lsq,
1191  bool equality, bool inequality)
1192 {
1193  assert(i_row.size() == computeCombinedSparseJacobiansNNZ(objective_lsq, equality, inequality));
1194  assert(j_col.size() == i_row.size());
1195 
1197 
1198  int nnz_idx = 0;
1199 
1200  int equality_row_start = objective_lsq ? getLsqObjectiveDimension() : 0;
1201  int inequality_row_start = equality ? equality_row_start + getEqualityDimension() : equality_row_start;
1202 
1203  if (objective_lsq)
1204  {
1205  // Iterate lsq objective edges
1206  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1207  {
1208  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1209  {
1210  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1211  // Iterate all free variables
1212  int idx_free = 0;
1213  for (int i = 0; i < vertex->getDimension(); ++i)
1214  {
1215  if (!vertex->isFixedComponent(i))
1216  {
1217  // Iterate inner edge dimension
1218  for (int j = 0; j < edge->getDimension(); ++j)
1219  {
1220  i_row[nnz_idx] = edge->getEdgeIdx() + j;
1221  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1222  ++nnz_idx;
1223  }
1224  ++idx_free;
1225  }
1226  }
1227  }
1228  }
1229  }
1230  if (equality)
1231  {
1232  // Iterate equality edges
1233  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
1234  {
1235  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1236  {
1237  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1238  // Iterate all free variables
1239  int idx_free = 0;
1240  for (int i = 0; i < vertex->getDimension(); ++i)
1241  {
1242  if (!vertex->isFixedComponent(i))
1243  {
1244  // Iterate inner edge dimension
1245  for (int j = 0; j < edge->getDimension(); ++j)
1246  {
1247  i_row[nnz_idx] = equality_row_start + edge->getEdgeIdx() + j;
1248  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1249  ++nnz_idx;
1250  }
1251  ++idx_free;
1252  }
1253  }
1254  }
1255  }
1256  }
1257  if (inequality)
1258  {
1259  // Iterate equality edges
1260  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
1261  {
1262  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1263  {
1264  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1265  // Iterate all free variables
1266  int idx_free = 0;
1267  for (int i = 0; i < vertex->getDimension(); ++i)
1268  {
1269  if (!vertex->isFixedComponent(i))
1270  {
1271  // Iterate inner edge dimension
1272  for (int j = 0; j < edge->getDimension(); ++j)
1273  {
1274  i_row[nnz_idx] = inequality_row_start + edge->getEdgeIdx() + j;
1275  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1276  ++nnz_idx;
1277  }
1278  ++idx_free;
1279  }
1280  }
1281  }
1282  }
1283  }
1284 
1285  // Iterate mixed edges
1286  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
1287  {
1288  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1289  {
1290  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1291 
1292  // we need to do the following in the same order as in computeCombinedSparseJacobiansValues()
1293  if (objective_lsq)
1294  {
1295  // Iterate all free variables
1296  int idx_free = 0;
1297  for (int i = 0; i < vertex->getDimension(); ++i)
1298  {
1299  if (!vertex->isFixedComponent(i))
1300  {
1301  for (int j = 0; j < edge->getObjectiveDimension(); ++j)
1302  {
1303  i_row[nnz_idx] = edge->getEdgeObjectiveIdx() + j;
1304  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1305  ++nnz_idx;
1306  }
1307  ++idx_free;
1308  }
1309  }
1310  }
1311 
1312  if (equality)
1313  {
1314  // Iterate all free variables
1315  int idx_free = 0;
1316  for (int i = 0; i < vertex->getDimension(); ++i)
1317  {
1318  if (!vertex->isFixedComponent(i))
1319  {
1320  for (int j = 0; j < edge->getEqualityDimension(); ++j)
1321  {
1322  i_row[nnz_idx] = equality_row_start + edge->getEdgeEqualityIdx() + j;
1323  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1324  ++nnz_idx;
1325  }
1326  ++idx_free;
1327  }
1328  }
1329  }
1330 
1331  if (inequality)
1332  {
1333  // Iterate all free variables
1334  int idx_free = 0;
1335  for (int i = 0; i < vertex->getDimension(); ++i)
1336  {
1337  if (!vertex->isFixedComponent(i))
1338  {
1339  for (int j = 0; j < edge->getInequalityDimension(); ++j)
1340  {
1341  i_row[nnz_idx] = inequality_row_start + edge->getEdgeInequalityIdx() + j;
1342  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
1343  ++nnz_idx;
1344  }
1345  ++idx_free;
1346  }
1347  }
1348  }
1349  }
1350  }
1351 }
1353  bool equality, bool inequality, const double* multipliers_obj,
1354  const double* multipliers_eq, const double* multipliers_ineq)
1355 {
1356  assert(values.size() == computeCombinedSparseJacobiansNNZ(objective_lsq, equality, inequality));
1357 
1358  int nnz_idx = 0;
1359 
1361 
1362  if (objective_lsq)
1363  {
1364  // Iterate lsq objective edges
1365  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1366  {
1367  const double* mult_obj_lsq_part = multipliers_obj ? multipliers_obj + edge->getEdgeIdx() : nullptr;
1368 
1369  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1370  {
1371  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1372  if (vert_dim_unfixed == 0) continue;
1373 
1374  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
1375  edge->computeJacobian(vert_idx, block_jacobian, mult_obj_lsq_part);
1376  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1377  }
1378  }
1379  }
1380 
1381  if (equality)
1382  {
1383  // Iterate lsq objective edges
1384  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
1385  {
1386  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
1387 
1388  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1389  {
1390  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1391  if (vert_dim_unfixed == 0) continue;
1392 
1393  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
1394  edge->computeJacobian(vert_idx, block_jacobian, mult_eq_part);
1395  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1396  }
1397  }
1398  }
1399 
1400  if (inequality)
1401  {
1402  // Iterate lsq objective edges
1403  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
1404  {
1405  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
1406 
1407  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1408  {
1409  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1410  if (vert_dim_unfixed == 0) continue;
1411 
1412  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
1413  edge->computeJacobian(vert_idx, block_jacobian, mult_ineq_part);
1414  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1415  }
1416  }
1417  }
1418 
1419  // Iterate mixed edges
1420  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
1421  {
1422  if (!objective_lsq && edge->getEqualityDimension() == 0 && edge->getInequalityDimension() == 0) continue;
1423  // TODO(roesmann) implement the other cases as well
1424 
1425  const double* mult_obj_lsq_part = multipliers_obj ? multipliers_obj + edge->getEdgeObjectiveIdx() : nullptr;
1426  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
1427  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
1428 
1429  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1430  {
1431  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1432  if (vert_dim_unfixed == 0) continue;
1433 
1434  if (objective_lsq && equality && inequality)
1435  {
1436  Eigen::Map<Eigen::MatrixXd> block_obj(values.data() + nnz_idx, edge->getObjectiveDimension(), vert_dim_unfixed);
1437  nnz_idx += block_obj.rows() * block_obj.cols();
1438  Eigen::Map<Eigen::MatrixXd> block_eq(values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1439  nnz_idx += block_eq.rows() * block_eq.cols();
1440  Eigen::Map<Eigen::MatrixXd> block_ineq(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1441  nnz_idx += block_ineq.rows() * block_ineq.cols();
1442 
1443  edge->computeJacobians(vert_idx, block_obj, block_eq, block_ineq, mult_obj_lsq_part, mult_eq_part, mult_ineq_part);
1444  }
1445  else if (equality && inequality)
1446  {
1447  Eigen::Map<Eigen::MatrixXd> block_eq(values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1448  nnz_idx += block_eq.rows() * block_eq.cols();
1449  Eigen::Map<Eigen::MatrixXd> block_ineq(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1450  nnz_idx += block_ineq.rows() * block_ineq.cols();
1451 
1452  edge->computeConstraintJacobians(vert_idx, block_eq, block_ineq, mult_eq_part, mult_ineq_part);
1453  }
1454  else
1455  {
1456  // TODO(roesmann) this is not really efficient yet
1457  if (objective_lsq)
1458  {
1459  Eigen::Map<Eigen::MatrixXd> block_obj(values.data() + nnz_idx, edge->getObjectiveDimension(), vert_dim_unfixed);
1460  nnz_idx += block_obj.rows() * block_obj.cols();
1461  edge->computeObjectiveJacobian(vert_idx, block_obj, mult_obj_lsq_part);
1462  }
1463  if (equality)
1464  {
1465  Eigen::Map<Eigen::MatrixXd> block_eq(values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1466  nnz_idx += block_eq.rows() * block_eq.cols();
1467  edge->computeEqualityJacobian(vert_idx, block_eq, mult_eq_part);
1468  }
1469  if (inequality)
1470  {
1471  Eigen::Map<Eigen::MatrixXd> block_ineq(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1472  nnz_idx += block_ineq.rows() * block_ineq.cols();
1473  edge->computeInequalityJacobian(vert_idx, block_ineq, mult_ineq_part);
1474  }
1475  }
1476  }
1477  }
1478 }
1479 
1481  bool inequality, bool finite_combined_bounds, bool active_ineq,
1482  double weight_eq, double weight_ineq, double weight_bounds,
1483  const Eigen::VectorXd* values, const Eigen::VectorXi* col_nnz)
1484 {
1485  jacobian.setZero(); // this might be obsolete as we explicitly set numeric zeros below.
1486  // TODO(roesmann): what is most efficient, if we just want to udpate the Jacobian?
1487  if (col_nnz) jacobian.reserve(*col_nnz);
1488 
1490 
1491  int equality_row_start = objective_lsq ? getLsqObjectiveDimension() : 0;
1492  int inequality_row_start = equality ? equality_row_start + getEqualityDimension() : equality_row_start;
1493  int bounds_start = inequality ? inequality_row_start + getInequalityDimension() : inequality_row_start;
1494 
1495  if (objective_lsq && getLsqObjectiveDimension() > 0)
1496  {
1497  // Iterate lsq objective edges
1498  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1499  {
1500  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1501  {
1502  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1503 
1504  int vert_dim_unfixed = vertex->getDimensionUnfixed();
1505  if (vert_dim_unfixed == 0) continue;
1506 
1507  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
1508  edge->computeJacobian(vert_idx, block_jacobian);
1509 
1510  // Iterate all free variables
1511  int idx_free = 0;
1512  for (int i = 0; i < vertex->getDimension(); ++i)
1513  {
1514  if (!vertex->isFixedComponent(i))
1515  {
1516  // Iterate inner edge dimension
1517  for (int j = 0; j < edge->getDimension(); ++j)
1518  {
1519  jacobian.coeffRef(edge->getEdgeIdx() + j, vertex->getVertexIdx() + idx_free) = block_jacobian(j, idx_free);
1520  }
1521  ++idx_free;
1522  }
1523  }
1524  }
1525  }
1526  }
1527 
1528  if (equality && getEqualityDimension() > 0)
1529  {
1530  // Iterate equality edges
1531  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
1532  {
1533  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1534  {
1535  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1536 
1537  int vert_dim_unfixed = vertex->getDimensionUnfixed();
1538  if (vert_dim_unfixed == 0) continue;
1539 
1540  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
1541  edge->computeJacobian(vert_idx, block_jacobian);
1542 
1543  // Iterate all free variables
1544  int idx_free = 0;
1545  for (int i = 0; i < vertex->getDimension(); ++i)
1546  {
1547  if (!vertex->isFixedComponent(i))
1548  {
1549  // Iterate inner edge dimension
1550  for (int j = 0; j < edge->getDimension(); ++j)
1551  {
1552  jacobian.coeffRef(equality_row_start + edge->getEdgeIdx() + j, vertex->getVertexIdx() + idx_free) =
1553  block_jacobian(j, idx_free) * weight_eq;
1554  }
1555  ++idx_free;
1556  }
1557  }
1558  }
1559  }
1560  }
1561 
1562  if (inequality && getInequalityDimension() > 0)
1563  {
1564  // Iterate inequality edges
1565  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
1566  {
1567  // check if active
1568  Eigen::Array<bool, -1, 1> active(edge->getDimension());
1569  if (active_ineq)
1570  {
1571  if (values)
1572  active = values->segment(inequality_row_start + edge->getEdgeIdx(), edge->getDimension()).array() > 0.0;
1573  else
1574  {
1575  Eigen::VectorXd values_tmp(edge->getDimension());
1576  edge->computeValues(values_tmp);
1577  active = values_tmp.array() > 0.0;
1578  }
1579  }
1580  else
1581  active.setConstant(true);
1582 
1583  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1584  {
1585  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1586 
1587  int vert_dim_unfixed = vertex->getDimensionUnfixed();
1588  if (vert_dim_unfixed == 0) continue;
1589 
1590  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
1591  edge->computeJacobian(vert_idx, block_jacobian);
1592 
1593  // Iterate all free variables
1594  int idx_free = 0;
1595  for (int i = 0; i < vertex->getDimension(); ++i)
1596  {
1597  if (!vertex->isFixedComponent(i))
1598  {
1599  // Iterate inner edge dimension
1600  for (int j = 0; j < edge->getDimension(); ++j)
1601  {
1602  if (active[j])
1603  {
1604  jacobian.coeffRef(inequality_row_start + edge->getEdgeIdx() + j, vertex->getVertexIdx() + idx_free) =
1605  block_jacobian(j, idx_free) * weight_ineq;
1606  }
1607  else
1608  {
1609  jacobian.coeffRef(inequality_row_start + edge->getEdgeIdx() + j, vertex->getVertexIdx() + idx_free) = 0.0;
1610  }
1611  }
1612  ++idx_free;
1613  }
1614  }
1615  }
1616  }
1617  }
1618 
1619  // Iterate mixed edges
1620  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
1621  {
1622  if (!objective_lsq && edge->getEqualityDimension() == 0 && edge->getInequalityDimension() == 0) continue;
1623  // TODO(roesmann) implement the other cases as well
1624 
1625  // check if active
1626  Eigen::Array<bool, -1, 1> active(edge->getInequalityDimension());
1627  if (active_ineq && inequality)
1628  {
1629  if (values)
1630  active = values->segment(inequality_row_start + edge->getEdgeInequalityIdx(), edge->getInequalityDimension()).array() > 0.0;
1631  else
1632  {
1633  Eigen::VectorXd values_tmp(edge->getInequalityDimension());
1634  edge->computeInequalityValues(values_tmp);
1635  active = values_tmp.array() > 0.0;
1636  }
1637  }
1638  else if (inequality)
1639  active.setConstant(true);
1640 
1641  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1642  {
1643  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
1644 
1645  int vert_dim_unfixed = vertex->getDimensionUnfixed();
1646  if (vert_dim_unfixed == 0) continue;
1647 
1648  Eigen::MatrixXd block_obj(edge->getObjectiveDimension(), vert_dim_unfixed);
1649  Eigen::MatrixXd block_eq(edge->getEqualityDimension(), vert_dim_unfixed);
1650  Eigen::MatrixXd block_ineq(edge->getInequalityDimension(), vert_dim_unfixed);
1651 
1652  if (objective_lsq && equality && inequality)
1653  {
1654  edge->computeJacobians(vert_idx, block_obj, block_eq, block_ineq);
1655  }
1656  else if (equality && inequality)
1657  {
1658  edge->computeConstraintJacobians(vert_idx, block_eq, block_ineq);
1659  }
1660  else
1661  {
1662  // TODO(roesmann) this is not really efficient yet
1663  if (objective_lsq)
1664  {
1665  edge->computeObjectiveJacobian(vert_idx, block_obj);
1666  }
1667  if (equality)
1668  {
1669  edge->computeEqualityJacobian(vert_idx, block_eq);
1670  }
1671  if (inequality)
1672  {
1673  edge->computeInequalityJacobian(vert_idx, block_ineq);
1674  }
1675  }
1676 
1677  // Iterate all free variables
1678  int idx_free = 0;
1679 
1680  for (int i = 0; i < vertex->getDimension(); ++i)
1681  {
1682  if (!vertex->isFixedComponent(i))
1683  {
1684  // Iterate inner edge dimension
1685  if (objective_lsq)
1686  {
1687  for (int j = 0; j < edge->getObjectiveDimension(); ++j)
1688  {
1689  jacobian.coeffRef(edge->getEdgeObjectiveIdx() + j, vertex->getVertexIdx() + idx_free) = block_obj(j, idx_free);
1690  }
1691  }
1692  if (equality)
1693  {
1694  for (int j = 0; j < edge->getEqualityDimension(); ++j)
1695  {
1696  jacobian.coeffRef(equality_row_start + edge->getEdgeEqualityIdx() + j, vertex->getVertexIdx() + idx_free) =
1697  block_eq(j, idx_free) * weight_eq;
1698  }
1699  }
1700  if (inequality)
1701  {
1702  for (int j = 0; j < edge->getInequalityDimension(); ++j)
1703  {
1704  if (active[j])
1705  {
1706  jacobian.coeffRef(inequality_row_start + edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx() + idx_free) =
1707  block_ineq(j, idx_free) * weight_ineq;
1708  }
1709  else
1710  {
1711  jacobian.coeffRef(inequality_row_start + edge->getEdgeInequalityIdx() + j, vertex->getVertexIdx() + idx_free) = 0.0;
1712  }
1713  }
1714  }
1715  ++idx_free;
1716  }
1717  }
1718  }
1719  }
1720 
1721  if (finite_combined_bounds && finiteCombinedBoundsDimension() > 0)
1722  {
1723  // we have a single value per row
1724  int jac_row_idx = bounds_start;
1725  // Iterate vertices
1726  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
1727  {
1728  int vert_idx = vertex->getVertexIdx();
1729  int free_idx = 0;
1730  for (int i = 0; i < vertex->getDimension(); ++i)
1731  {
1732  if (vertex->isFixedComponent(i)) continue;
1733 
1734  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
1735  {
1736  if (vertex->getData()[i] < vertex->getLowerBounds()[i])
1737  {
1738  jacobian.coeffRef(jac_row_idx, vert_idx + free_idx) = -weight_bounds;
1739  }
1740  else if (vertex->getData()[i] > vertex->getUpperBounds()[i])
1741  {
1742  jacobian.coeffRef(jac_row_idx, vert_idx + free_idx) = weight_bounds;
1743  }
1744  else
1745  jacobian.coeffRef(jac_row_idx, vert_idx + free_idx) = 0.0; // preserve structure
1746 
1747  ++jac_row_idx;
1748  }
1749  ++free_idx;
1750  }
1751  }
1752  }
1753 }
1754 
1755 // useful for IPOPT (w/ hessian-approx)
1757  Eigen::Ref<Eigen::VectorXd> jac_values,
1758  bool equality, bool inequality,
1759  const double* multipliers_eq,
1760  const double* multipliers_ineq)
1761 {
1762  assert(jac_values.size() == computeCombinedSparseJacobiansNNZ(false, equality, inequality));
1763  assert(gradient.size() == getParameterDimension());
1764  assert(equality == true); // TODO(roesmann)
1765  assert(inequality == true); // TODO(roesmann)
1766 
1767  // we need to set the gradient to zero first
1768  gradient.setZero();
1769 
1771 
1772  // Iterate plain objective edges
1773  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
1774  {
1775  // if (edge->providesJacobian())
1776  for (int i = 0; i < edge->getNumVertices(); ++i)
1777  {
1778  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
1779  if (vert_dim_unfixed == 0) continue;
1780 
1781  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
1782  edge->computeJacobian(i, block_jacobian, nullptr);
1783  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
1784  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
1785  }
1786  }
1787  // Iterate lsq objective edges
1788  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1789  {
1790  for (int i = 0; i < edge->getNumVertices(); ++i)
1791  {
1792  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
1793  if (vert_dim_unfixed == 0) continue;
1794 
1795  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
1796  edge->computeJacobian(i, block_jacobian, nullptr);
1797 
1798  // apply chain rule // TODO(roesmann): in case of forward differences we could use this for computing the jacobian
1799  Eigen::VectorXd values(edge->getDimension());
1800  edge->computeValues(values);
1801 
1802  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) += 2.0 * values.transpose() * block_jacobian;
1803  }
1804  }
1805 
1806  int nnz_idx = 0; // jacobian
1807 
1808  if (equality)
1809  {
1810  // Iterate lsq objective edges
1811  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
1812  {
1813  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
1814 
1815  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1816  {
1817  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1818  if (vert_dim_unfixed == 0) continue;
1819 
1820  Eigen::Map<Eigen::MatrixXd> block_jacobian(jac_values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
1821  edge->computeJacobian(vert_idx, block_jacobian, mult_eq_part);
1822  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1823  }
1824  }
1825  }
1826 
1827  if (inequality)
1828  {
1829  // Iterate lsq objective edges
1830  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
1831  {
1832  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
1833 
1834  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1835  {
1836  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1837  if (vert_dim_unfixed == 0) continue;
1838 
1839  Eigen::Map<Eigen::MatrixXd> block_jacobian(jac_values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
1840  edge->computeJacobian(vert_idx, block_jacobian, mult_ineq_part);
1841  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
1842  }
1843  }
1844  }
1845 
1846  // Iterate mixed edges
1847  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
1848  {
1849  // bool has_obj = edge->getObjectiveDimension() > 0;
1850  // bool has_eq = equality && edge->getEqualityDimension();
1851  // bool has_ineq = inequality && edge->getInequalityDimension();
1852 
1853  // ###############
1854  for (int i = 0; i < edge->getNumVertices(); ++i)
1855  {
1856  int vert_dim_unfixed = edge->getVertexRaw(i)->getDimensionUnfixed();
1857  if (vert_dim_unfixed == 0) continue;
1858 
1859  Eigen::MatrixXd block_jacobian(edge->getObjectiveDimension(), vert_dim_unfixed);
1860  edge->computeObjectiveJacobian(i, block_jacobian, nullptr);
1861 
1862  if (edge->isObjectiveLeastSquaresForm())
1863  {
1864  // apply chain rule // TODO(roesmann): in case of forward differences we could use this for computing the jacobian
1865  Eigen::VectorXd values(edge->getObjectiveDimension());
1866  edge->computeObjectiveValues(values);
1867  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) += 2.0 * values.transpose() * block_jacobian;
1868  }
1869  else
1870  {
1871  gradient.segment(edge->getVertexRaw(i)->getVertexIdx(), vert_dim_unfixed) +=
1872  block_jacobian.colwise().sum(); // sum up all values, we need a dimension of one
1873  }
1874  }
1875  // #########
1876 
1877  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
1878  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
1879 
1880  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
1881  {
1882  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
1883  if (vert_dim_unfixed == 0) continue;
1884 
1885  Eigen::MatrixXd block_obj(edge->getObjectiveDimension(), vert_dim_unfixed);
1886  Eigen::Map<Eigen::MatrixXd> block_eq(jac_values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1887  nnz_idx += block_eq.rows() * block_eq.cols();
1888  Eigen::Map<Eigen::MatrixXd> block_ineq(jac_values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1889  nnz_idx += block_ineq.rows() * block_ineq.cols();
1890  edge->computeJacobians(vert_idx, block_obj, block_eq, block_ineq, nullptr, mult_eq_part, mult_ineq_part);
1891 
1892  if (edge->isObjectiveLeastSquaresForm())
1893  {
1894  // apply chain rule // TODO(roesmann): in case of forward differences we could use this for computing the jacobian
1895  Eigen::VectorXd values(edge->getObjectiveDimension());
1896  edge->computeObjectiveValues(values);
1897  gradient.segment(edge->getVertexRaw(vert_idx)->getVertexIdx(), vert_dim_unfixed) += 2.0 * values.transpose() * block_obj;
1898  }
1899  else
1900  {
1901  gradient.segment(edge->getVertexRaw(vert_idx)->getVertexIdx(), vert_dim_unfixed) +=
1902  block_obj.colwise().sum(); // sum up all values, we need a dimension of one
1903  }
1904 
1905  // if (has_obj && has_eq && has_ineq)
1906  // {
1907  // }
1908 
1909  // if (equality && inequality)
1910  // {
1911  // Eigen::Map<Eigen::MatrixXd> block_eq(jac_values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1912  // nnz_idx += block_eq.rows() * block_eq.cols();
1913  // Eigen::Map<Eigen::MatrixXd> block_ineq(jac_values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
1914  // nnz_idx += block_ineq.rows() * block_ineq.cols();
1915 
1916  // edge->computeConstraintJacobians(vert_idx, block_eq, block_ineq, mult_eq_part, mult_ineq_part);
1917  // }
1918  // else
1919  // {
1920  // if (equality)
1921  // {
1922  // Eigen::Map<Eigen::MatrixXd> block_eq(jac_values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
1923  // nnz_idx += block_eq.rows() * block_eq.cols();
1924  // edge->computeEqualityJacobian(vert_idx, block_eq, mult_eq_part);
1925  // }
1926  // if (inequality)
1927  // {
1928  // Eigen::Map<Eigen::MatrixXd> block_ineq(jac_values.data() + nnz_idx, edge->getInequalityDimension(),
1929  // vert_dim_unfixed);
1930  // nnz_idx += block_ineq.rows() * block_ineq.cols();
1931  // edge->computeEqualityJacobian(vert_idx, block_ineq, mult_ineq_part);
1932  // }
1933  // }
1934  }
1935  }
1936 }
1937 
1939 {
1940  assert(_graph.hasEdgeSet());
1942  assert(hessian.rows() == getParameterDimension());
1943  assert(hessian.rows() == hessian.cols());
1944 
1945  // we need to set to zero everything first
1946  // but we only compute the upper hessian via edges
1947  hessian.triangularView<Eigen::Upper>().setZero();
1948 
1949  if (getObjectiveDimension() == 0) return;
1950 
1952 
1953  // Iterate plain objective edges
1954  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
1955  {
1956  if (edge->isLinear()) continue;
1957 
1958  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
1959  {
1960  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
1961 
1962  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
1963  if (vert_i_dim_unfixed == 0) continue;
1964 
1965  // compute jacobian to speed up hessian computation
1966  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
1967  edge->computeJacobian(row_i, block_jacobian1);
1968 
1969  for (int col_j = row_i; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
1970  {
1971  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
1972 
1973  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
1974  if (vert_j_dim_unfixed == 0) continue;
1975 
1976  // warning: we require the hessian to be initialized with zeros
1977  // and that edge->computeHessian only adds (+=) values!!
1978  edge->computeHessianInc(row_i, col_j, block_jacobian1,
1979  hessian.block(vertex1->getVertexIdx(), vertex2->getVertexIdx(), vert_i_dim_unfixed, vert_j_dim_unfixed),
1980  nullptr, multiplier);
1981  }
1982  }
1983  }
1984 
1985  // Iterate lsq objective edges
1986  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
1987  {
1988  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
1989 
1990  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
1991  {
1992  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
1993 
1994  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
1995  if (vert_i_dim_unfixed == 0) continue;
1996 
1997  // compute jacobian to speed up hessian computation
1998  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
1999  edge->computeJacobian(row_i, block_jacobian1);
2000 
2001  for (int col_j = row_i; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2002  {
2003  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2004 
2005  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2006  if (vert_j_dim_unfixed == 0) continue;
2007 
2008  // we need the second jacobian for applying the chain rule
2009  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
2010  edge->computeJacobian(col_j, block_jacobian2);
2011 
2012  // approximate H = 2*J^T*J
2013  // if (_hessian_lsq_obj_approx)
2014  //{
2015  hessian.block(vertex1->getVertexIdx(), vertex2->getVertexIdx(), vert_i_dim_unfixed, vert_j_dim_unfixed) +=
2016  2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2017  //}
2018  // else
2019  // {
2020  // Eigen::MatrixXd block_hessian(vertex1->getDimensionUnfixed(), vertex2->getDimensionUnfixed());
2021  // edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, nullptr, 1.0); // weight is already in
2022  // the next equation
2023 
2024  // // Eigen::VectorXd edge_values(edge->getDimension());
2025  // // edge->computeValues(edge_values);
2026 
2027  // hessian.block(vertex1->getVertexIdx(), vertex2->getVertexIdx(), vert_i_dim_unfixed, vert_j_dim_unfixed) +=
2028  // 2.0 * (multiplier * block_jacobian1.transpose() * block_jacobian2 + block_hessian); // the second part is
2029  // not
2030  // correct yet
2031  // }
2032  }
2033  }
2034  }
2035 
2036  // Iterate mixed edges
2037  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2038  {
2039  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2040 
2041  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2042  {
2043  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2044 
2045  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2046  if (vert_i_dim_unfixed == 0) continue;
2047 
2048  // compute jacobian to speed up hessian computation
2049  Eigen::MatrixXd block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
2050  edge->computeObjectiveJacobian(row_i, block_jacobian1);
2051 
2052  for (int col_j = row_i; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2053  {
2054  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2055 
2056  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2057  if (vert_j_dim_unfixed == 0) continue;
2058 
2059  if (edge->isObjectiveLeastSquaresForm())
2060  {
2061  // we need the second jacobian for applying the chain rule
2062  Eigen::MatrixXd block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
2063  edge->computeObjectiveJacobian(col_j, block_jacobian2);
2064 
2065  // approximate H = 2*J^T*J
2066  // if (_hessian_lsq_obj_approx)
2067  //{
2068  hessian.block(vertex1->getVertexIdx(), vertex2->getVertexIdx(), vert_i_dim_unfixed, vert_j_dim_unfixed) +=
2069  2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2070  }
2071  else
2072  {
2073  // warning: we require the hessian to be initialized with zeros
2074  // and that edge->computeHessian only adds (+=) values!!
2075  edge->computeObjectiveHessianInc(
2076  row_i, col_j, block_jacobian1,
2077  hessian.block(vertex1->getVertexIdx(), vertex2->getVertexIdx(), vert_i_dim_unfixed, vert_j_dim_unfixed), nullptr, multiplier);
2078  }
2079  }
2080  }
2081  }
2082 
2083  // TODO(roesmann) at only_lower/only_upper flag to the function call in order to avoid the following copy
2084  hessian.triangularView<Eigen::Lower>() = hessian.adjoint().triangularView<Eigen::Lower>();
2085 }
2086 
2088 {
2089  int nnz = 0;
2090 
2091  assert(_graph.hasEdgeSet());
2093 
2095 
2096  // Iterate plain objective edges
2097  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
2098  {
2099  if (edge->isLinear()) continue;
2100 
2101  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2102  {
2103  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2104 
2105  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2106  if (vert_i_dim_unfixed == 0) continue;
2107 
2108  int col_start = lower_part_only ? row_i : 0;
2109  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2110  {
2111  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2112 
2113  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2114  if (vert_j_dim_unfixed == 0) continue;
2115 
2116  if (lower_part_only && vertex1 == vertex2)
2117  {
2118  int n = vertex1->getDimensionUnfixed();
2119  nnz += n * (n + 1) / 2; // lower triangular including diagonal
2120  }
2121  else
2122  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
2123  }
2124  }
2125  }
2126 
2127  // Iterate lsq objective edges
2128  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
2129  {
2130  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
2131 
2132  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2133  {
2134  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2135 
2136  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2137  if (vert_i_dim_unfixed == 0) continue;
2138 
2139  int col_start = lower_part_only ? row_i : 0;
2140  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2141  {
2142  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2143 
2144  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2145  if (vert_j_dim_unfixed == 0) continue;
2146 
2147  if (lower_part_only && vertex1 == vertex2)
2148  {
2149  int n = vertex1->getDimensionUnfixed();
2150  nnz += n * (n + 1) / 2; // lower triangular including diagonal
2151  }
2152  else
2153  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
2154  }
2155  }
2156  }
2157 
2158  // Iterate mixed edges
2159  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2160  {
2161  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2162 
2163  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2164  {
2165  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2166 
2167  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2168  if (vert_i_dim_unfixed == 0) continue;
2169 
2170  int col_start = lower_part_only ? row_i : 0;
2171  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2172  {
2173  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2174 
2175  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2176  if (vert_j_dim_unfixed == 0) continue;
2177 
2178  if (lower_part_only && vertex1 == vertex2)
2179  {
2180  int n = vertex1->getDimensionUnfixed();
2181  nnz += n * (n + 1) / 2; // lower triangular including diagonal
2182  }
2183  else
2184  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
2185  }
2186  }
2187  }
2188 
2189  return nnz;
2190 }
2191 
2193  Eigen::Ref<Eigen::VectorXi> j_col, bool lower_part_only)
2194 {
2195  assert(_graph.hasEdgeSet());
2197 
2198  assert(i_row.size() == computeSparseHessianObjectiveNNZ(lower_part_only));
2199  assert(i_row.size() == j_col.size());
2200 
2202 
2203  int nnz_idx = 0;
2204 
2205  // Iterate plain objective edges
2206  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
2207  {
2208  if (edge->isLinear()) continue;
2209 
2210  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2211  {
2212  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2213 
2214  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2215  if (vert_i_dim_unfixed == 0) continue;
2216 
2217  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2218  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2219  {
2220  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2221 
2222  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2223  if (vert_j_dim_unfixed == 0) continue;
2224 
2225  if (lower_part_only && vertex1 == vertex2)
2226  {
2227  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2228  {
2229  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
2230  {
2231  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2232  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2233  ++nnz_idx;
2234  }
2235  }
2236  }
2237  else
2238  {
2239  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2240  {
2241  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
2242  {
2243  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2244  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2245  ++nnz_idx;
2246  }
2247  }
2248  }
2249  }
2250  }
2251  }
2252 
2253  // Iterate lsq objective edges
2254  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
2255  {
2256  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
2257 
2258  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2259  {
2260  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2261 
2262  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2263  if (vert_i_dim_unfixed == 0) continue;
2264 
2265  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2266  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2267  {
2268  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2269 
2270  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2271  if (vert_j_dim_unfixed == 0) continue;
2272 
2273  if (lower_part_only && vertex1 == vertex2)
2274  {
2275  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2276  {
2277  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
2278  {
2279  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2280  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2281  ++nnz_idx;
2282  }
2283  }
2284  }
2285  else
2286  {
2287  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2288  {
2289  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
2290  {
2291  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2292  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2293  ++nnz_idx;
2294  }
2295  }
2296  }
2297  }
2298  }
2299  }
2300 
2301  // Iterate mixed edges
2302  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2303  {
2304  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2305 
2306  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2307  {
2308  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2309 
2310  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2311  if (vert_i_dim_unfixed == 0) continue;
2312 
2313  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2314  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2315  {
2316  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2317 
2318  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2319  if (vert_j_dim_unfixed == 0) continue;
2320 
2321  if (lower_part_only && vertex1 == vertex2)
2322  {
2323  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2324  {
2325  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
2326  {
2327  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2328  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2329  ++nnz_idx;
2330  }
2331  }
2332  }
2333  else
2334  {
2335  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2336  {
2337  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
2338  {
2339  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2340  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2341  ++nnz_idx;
2342  }
2343  }
2344  }
2345  }
2346  }
2347  }
2348 }
2349 
2351  bool lower_part_only)
2352 {
2353  assert(values.size() == computeSparseHessianObjectiveNNZ(lower_part_only));
2354  values.setZero();
2355 
2356  if (getObjectiveDimension() == 0) return;
2357 
2358  int nnz_idx = 0;
2359 
2361 
2362  // Iterate plain objective edges
2363  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
2364  {
2365  if (edge->isLinear()) continue;
2366 
2367  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2368  {
2369  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2370 
2371  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2372  if (vert_i_dim_unfixed == 0) continue;
2373 
2374  // compute jacobian to speed up hessian computation
2375  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
2376  edge->computeJacobian(row_i, block_jacobian1);
2377 
2378  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2379  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2380  {
2381  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2382 
2383  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2384  if (vert_j_dim_unfixed == 0) continue;
2385 
2386  // warning: we require the hessian to be initialized with zeros
2387  // and that edge->computeHessian only adds (+=) values!!
2388  if (lower_part_only && vertex1 == vertex2)
2389  {
2390  // we only need the lower triangular part, so cache Hessian
2391  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
2392  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, nullptr, multiplier);
2393  for (int i = 0; i < vert_i_dim_unfixed; ++i)
2394  {
2395  for (int j = 0; j < i + 1; ++j)
2396  {
2397  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
2398  ++nnz_idx;
2399  }
2400  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
2401  }
2402  }
2403  else
2404  {
2405  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
2406  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, nullptr, multiplier);
2407  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
2408  }
2409  }
2410  }
2411  }
2412 
2413  // Iterate lsq objective edges
2414  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
2415  {
2416  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
2417 
2418  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2419  {
2420  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2421 
2422  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2423  if (vert_i_dim_unfixed == 0) continue;
2424 
2425  // compute jacobian to speed up hessian computation
2426  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
2427  edge->computeJacobian(row_i, block_jacobian1);
2428 
2429  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2430  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2431  {
2432  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2433 
2434  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2435  if (vert_j_dim_unfixed == 0) continue;
2436 
2437  // we need the second jacobian for applying the chain rule
2438  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
2439  edge->computeJacobian(col_j, block_jacobian2);
2440 
2441  // approximate H = 2*J^T*J
2442  if (lower_part_only && vertex1 == vertex2)
2443  {
2444  // we only need the lower triangular part, so cache Hessian
2445  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
2446  block_hessian_ii.noalias() = 2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2447  for (int i = 0; i < vert_i_dim_unfixed; ++i)
2448  {
2449  for (int j = 0; j < i + 1; ++j)
2450  {
2451  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
2452  ++nnz_idx;
2453  }
2454  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
2455  }
2456  }
2457  else
2458  {
2459  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
2460  block_hessian += 2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2461  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
2462  }
2463  }
2464  }
2465  }
2466 
2467  // Iterate mixed edges
2468  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2469  {
2470  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2471 
2472  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2473  {
2474  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2475 
2476  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2477  if (vert_i_dim_unfixed == 0) continue;
2478 
2479  // compute jacobian to speed up hessian computation
2480  Eigen::MatrixXd block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
2481  edge->computeObjectiveJacobian(row_i, block_jacobian1);
2482 
2483  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2484  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2485  {
2486  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2487 
2488  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2489  if (vert_j_dim_unfixed == 0) continue;
2490 
2491  if (edge->isObjectiveLeastSquaresForm())
2492  {
2493  // we need the second jacobian for applying the chain rule
2494  Eigen::MatrixXd block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
2495  edge->computeObjectiveJacobian(col_j, block_jacobian2);
2496 
2497  // approximate H = 2*J^T*J
2498  if (lower_part_only && vertex1 == vertex2)
2499  {
2500  // we only need the lower triangular part, so cache Hessian
2501  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
2502  block_hessian_ii.noalias() = 2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2503  for (int i = 0; i < vert_i_dim_unfixed; ++i)
2504  {
2505  for (int j = 0; j < i + 1; ++j)
2506  {
2507  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
2508  ++nnz_idx;
2509  }
2510  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
2511  }
2512  }
2513  else
2514  {
2515  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
2516  block_hessian += 2.0 * multiplier * block_jacobian1.transpose() * block_jacobian2;
2517  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
2518  }
2519  }
2520  else
2521  {
2522  // warning: we require the hessian to be initialized with zeros
2523  // and that edge->computeHessian only adds (+=) values!!
2524  if (lower_part_only && vertex1 == vertex2)
2525  {
2526  // we only need the lower triangular part, so cache Hessian
2527  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
2528  edge->computeObjectiveHessian(row_i, col_j, block_jacobian1, block_hessian_ii, nullptr, multiplier);
2529  for (int i = 0; i < vert_i_dim_unfixed; ++i)
2530  {
2531  for (int j = 0; j < i + 1; ++j)
2532  {
2533  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
2534  ++nnz_idx;
2535  }
2536  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
2537  }
2538  }
2539  else
2540  {
2541  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
2542  edge->computeObjectiveHessianInc(row_i, col_j, block_jacobian1, block_hessian, nullptr, multiplier);
2543  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
2544  }
2545  }
2546  }
2547  }
2548  }
2549 }
2550 
2552  const Eigen::VectorXi* col_nnz, bool upper_part_only)
2553 {
2554  H.setZero();
2555  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
2556  if (col_nnz) H.reserve(*col_nnz);
2557 
2559 
2560  // Iterate plain objective edges
2561  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
2562  {
2563  if (edge->isLinear()) continue;
2564 
2565  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2566  {
2567  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2568 
2569  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2570  if (vert_i_dim_unfixed == 0) continue;
2571 
2572  // compute jacobian to speed up hessian computation
2573  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
2574  edge->computeJacobian(row_i, block_jacobian1);
2575 
2576  int col_start = upper_part_only ? row_i : 0;
2577  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2578  {
2579  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2580 
2581  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2582  if (vert_j_dim_unfixed == 0) continue;
2583 
2584  // warning: we require the hessian to be initialized with zeros
2585  // and that edge->computeHessian only adds (+=) values!!
2586  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
2587  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian);
2588 
2589  // insert values
2590  if (upper_part_only && vertex1 == vertex2)
2591  {
2592  // block on the main diagonal, so use upper part only
2593  for (int i = 0; i < block_hessian.rows(); ++i)
2594  {
2595  for (int j = i; j < block_hessian.cols(); ++j)
2596  {
2597  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2598  }
2599  }
2600  }
2601  else
2602  {
2603  for (int i = 0; i < block_hessian.rows(); ++i)
2604  {
2605  for (int j = 0; j < block_hessian.cols(); ++j)
2606  {
2607  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2608  }
2609  }
2610  }
2611  }
2612  }
2613  }
2614 
2615  // Iterate lsq objective edges
2616  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
2617  {
2618  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
2619 
2620  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2621  {
2622  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2623 
2624  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2625  if (vert_i_dim_unfixed == 0) continue;
2626 
2627  // compute jacobian to speed up hessian computation
2628  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
2629  edge->computeJacobian(row_i, block_jacobian1);
2630 
2631  int col_start = upper_part_only ? row_i : 0;
2632  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2633  {
2634  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2635 
2636  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2637  if (vert_j_dim_unfixed == 0) continue;
2638 
2639  // we need the second jacobian for applying the chain rule
2640  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
2641  edge->computeJacobian(col_j, block_jacobian2);
2642 
2643  // approximate H = 2*J^T*J
2644  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
2645  block_hessian = 2.0 * block_jacobian1.transpose() * block_jacobian2;
2646 
2647  // insert values
2648  if (upper_part_only && vertex1 == vertex2)
2649  {
2650  // block on the main diagonal, so use upper part only
2651  for (int i = 0; i < block_hessian.rows(); ++i)
2652  {
2653  for (int j = i; j < block_hessian.cols(); ++j)
2654  {
2655  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2656  }
2657  }
2658  }
2659  else
2660  {
2661  for (int i = 0; i < block_hessian.rows(); ++i)
2662  {
2663  for (int j = 0; j < block_hessian.cols(); ++j)
2664  {
2665  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2666  }
2667  }
2668  }
2669  }
2670  }
2671  }
2672 
2673  // Iterate mixed edges
2674  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2675  {
2676  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2677 
2678  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2679  {
2680  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2681 
2682  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2683  if (vert_i_dim_unfixed == 0) continue;
2684 
2685  // compute jacobian to speed up hessian computation
2686  Eigen::MatrixXd obj_block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
2687  Eigen::MatrixXd eq_block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
2688  Eigen::MatrixXd ineq_block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
2689  edge->computeJacobians(row_i, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1);
2690 
2691  int col_start = upper_part_only ? row_i : 0;
2692  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2693  {
2694  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2695 
2696  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2697  if (vert_j_dim_unfixed == 0) continue;
2698 
2699  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
2700  block_hessian.setZero();
2701 
2702  if (edge->isObjectiveLeastSquaresForm())
2703  {
2704  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
2705  {
2706  // we need the second jacobian for applying the chain rule
2707  Eigen::MatrixXd obj_block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
2708  edge->computeObjectiveJacobian(col_j, obj_block_jacobian2);
2709 
2710  // approximate H = 2*J^T*J
2711  block_hessian += 2.0 * obj_block_jacobian1.transpose() * obj_block_jacobian2;
2712  }
2713  }
2714  else // TODO(roesmann): check all reasonable cases:
2715  {
2716  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
2717  {
2718  edge->computeObjectiveHessianInc(row_i, col_j, obj_block_jacobian1, block_hessian, nullptr, 1.0);
2719  }
2720  }
2721  // insert values
2722  if (upper_part_only && vertex1 == vertex2)
2723  {
2724  // block on the main diagonal, so use upper part only
2725  for (int i = 0; i < block_hessian.rows(); ++i)
2726  {
2727  for (int j = i; j < block_hessian.cols(); ++j)
2728  {
2729  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2730  }
2731  }
2732  }
2733  else
2734  {
2735  for (int i = 0; i < block_hessian.rows(); ++i)
2736  {
2737  for (int j = 0; j < block_hessian.cols(); ++j)
2738  {
2739  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
2740  }
2741  }
2742  }
2743  }
2744  }
2745  }
2746 }
2747 
2749 {
2750  assert(col_nnz.size() == getParameterDimension());
2751 
2752  col_nnz.setZero();
2753  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
2754 
2755  // TODO(roesmann): with the following strategy, we overestimate the number of nnz, since vertices are shared among multiple edges...
2756  // however, we do not want to create the complete sparsity pattern here by creating a matrix of zeros and ones (tagging).
2757 
2759 
2760  // Iterate plain objective edges
2761  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
2762  {
2763  if (edge->isLinear()) continue;
2764 
2765  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2766  {
2767  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2768 
2769  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2770  if (vert_i_dim_unfixed == 0) continue;
2771 
2772  int col_start = upper_part_only ? row_i : 0;
2773  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2774  {
2775  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2776 
2777  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2778  if (vert_j_dim_unfixed == 0) continue;
2779 
2780  if (upper_part_only && vertex1 == vertex2)
2781  {
2782  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
2783  for (int l = 0; l < vert_j_dim_unfixed; ++l)
2784  {
2785  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
2786  }
2787  }
2788  else
2789  {
2790  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
2791  }
2792  }
2793  }
2794  }
2795 
2796  // Iterate lsq objective edges
2797  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
2798  {
2799  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
2800 
2801  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2802  {
2803  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2804 
2805  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2806  if (vert_i_dim_unfixed == 0) continue;
2807 
2808  int col_start = upper_part_only ? row_i : 0;
2809  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2810  {
2811  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2812 
2813  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2814  if (vert_j_dim_unfixed == 0) continue;
2815 
2816  if (upper_part_only && vertex1 == vertex2)
2817  {
2818  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
2819  for (int l = 0; l < vert_j_dim_unfixed; ++l)
2820  {
2821  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
2822  }
2823  }
2824  else
2825  {
2826  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
2827  }
2828  }
2829  }
2830  }
2831 
2832  // Iterate mixed edges
2833  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2834  {
2835  if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
2836 
2837  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2838  {
2839  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2840 
2841  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2842  if (vert_i_dim_unfixed == 0) continue;
2843 
2844  int col_start = upper_part_only ? row_i : 0;
2845  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
2846  {
2847  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2848 
2849  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2850  if (vert_j_dim_unfixed == 0) continue;
2851 
2852  if (upper_part_only && vertex1 == vertex2)
2853  {
2854  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
2855  for (int l = 0; l < vert_j_dim_unfixed; ++l)
2856  {
2857  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
2858  }
2859  }
2860  else
2861  {
2862  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
2863  }
2864  }
2865  }
2866  }
2867 }
2868 
2870 {
2871  int nnz = 0;
2872 
2873  assert(_graph.hasEdgeSet());
2875 
2876  if (getEqualityDimension() == 0) return nnz;
2877 
2879 
2880  // Iterate equality edges
2881  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
2882  {
2883  if (edge->isLinear()) continue;
2884 
2885  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2886  {
2887  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2888 
2889  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2890  if (vert_i_dim_unfixed == 0) continue;
2892  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2893  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2894  {
2895  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2896 
2897  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2898  if (vert_j_dim_unfixed == 0) continue;
2899 
2900  if (lower_part_only && vertex1 == vertex2)
2901  {
2902  int n = vertex1->getDimensionUnfixed();
2903  nnz += n * (n + 1) / 2; // lower triangular including diagonal
2904  }
2905  else
2906  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
2907  }
2908  }
2909  }
2910  // Iterate mixed edges
2911  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
2912  {
2913  if (getEqualityDimension() == 0 || edge->isEqualityLinear()) continue;
2914 
2915  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2916  {
2917  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2918 
2919  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2920  if (vert_i_dim_unfixed == 0) continue;
2921 
2922  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2923  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2924  {
2925  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2926 
2927  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2928  if (vert_j_dim_unfixed == 0) continue;
2929 
2930  if (lower_part_only && vertex1 == vertex2)
2931  {
2932  int n = vertex1->getDimensionUnfixed();
2933  nnz += n * (n + 1) / 2; // lower triangular including diagonal
2934  }
2935  else
2936  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
2937  }
2938  }
2939  }
2940 
2941  return nnz;
2942 }
2944  Eigen::Ref<Eigen::VectorXi> j_col, bool lower_part_only)
2945 {
2946  assert(_graph.hasEdgeSet());
2948 
2949  assert(i_row.size() == computeSparseHessianEqualitiesNNZ(lower_part_only));
2950  assert(i_row.size() == j_col.size());
2951 
2953 
2954  int nnz_idx = 0;
2955 
2956  // Iterate equality edges
2957  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
2958  {
2959  if (edge->isLinear()) continue;
2960 
2961  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
2962  {
2963  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
2964 
2965  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
2966  if (vert_i_dim_unfixed == 0) continue;
2967 
2968  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
2969  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
2970  {
2971  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
2972 
2973  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
2974  if (vert_j_dim_unfixed == 0) continue;
2975 
2976  if (lower_part_only && vertex1 == vertex2)
2977  {
2978  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2979  {
2980  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
2981  {
2982  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2983  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2984  ++nnz_idx;
2985  }
2986  }
2987  }
2988  else
2989  {
2990  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
2991  {
2992  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
2993  {
2994  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
2995  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
2996  ++nnz_idx;
2997  }
2998  }
2999  }
3000  }
3001  }
3002  }
3003 
3004  // Iterate mixed edges
3005  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3006  {
3007  if (getEqualityDimension() == 0 || edge->isEqualityLinear()) continue;
3008 
3009  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3010  {
3011  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3012 
3013  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3014  if (vert_i_dim_unfixed == 0) continue;
3015 
3016  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3017  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3018  {
3019  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3020 
3021  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3022  if (vert_j_dim_unfixed == 0) continue;
3023 
3024  if (lower_part_only && vertex1 == vertex2)
3025  {
3026  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3027  {
3028  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
3029  {
3030  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3031  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3032  ++nnz_idx;
3033  }
3034  }
3035  }
3036  else
3037  {
3038  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3039  {
3040  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
3041  {
3042  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3043  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3044  ++nnz_idx;
3045  }
3046  }
3047  }
3048  }
3049  }
3050  }
3051 }
3053  bool lower_part_only)
3054 {
3055  assert(values.size() == computeSparseHessianEqualitiesNNZ(lower_part_only));
3056 
3057  values.setZero();
3058 
3059  if (getEqualityDimension() == 0) return;
3060 
3061  int nnz_idx = 0;
3062 
3064 
3065  // Iterate equality edges
3066  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
3067  {
3068  if (edge->isLinear()) continue;
3069 
3070  const double* mult_eq_part = multipliers ? multipliers + edge->getEdgeIdx() : nullptr;
3071 
3072  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3073  {
3074  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3075 
3076  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3077  if (vert_i_dim_unfixed == 0) continue;
3078 
3079  // compute jacobian to speed up hessian computation
3080  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3081  edge->computeJacobian(row_i, block_jacobian1);
3082 
3083  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3084  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3085  {
3086  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3087 
3088  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3089  if (vert_j_dim_unfixed == 0) continue;
3090 
3091  // warning: we require the hessian to be initialized with zeros
3092  // and that edge->computeHessian only adds (+=) values!!
3093  if (lower_part_only && vertex1 == vertex2)
3094  {
3095  // we only need the lower triangular part, so cache Hessian
3096  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3097  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_eq_part);
3098  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3099  {
3100  for (int j = 0; j < i + 1; ++j)
3101  {
3102  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
3103  ++nnz_idx;
3104  }
3105  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3106  }
3107  }
3108  else
3109  {
3110  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3111  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_eq_part);
3112  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3113  }
3114  }
3115  }
3116  }
3117 
3118  // Iterate mixed edges
3119  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3120  {
3121  if (edge->getEqualityDimension() == 0 || edge->isEqualityLinear()) continue;
3122 
3123  const double* mult_eq_part = multipliers ? multipliers + edge->getEdgeEqualityIdx() : nullptr;
3124 
3125  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3126  {
3127  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3128 
3129  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3130  if (vert_i_dim_unfixed == 0) continue;
3131 
3132  // compute jacobian to speed up hessian computation
3133  Eigen::MatrixXd block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
3134  edge->computeEqualityJacobian(row_i, block_jacobian1);
3135 
3136  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3137  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3138  {
3139  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3140 
3141  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3142  if (vert_j_dim_unfixed == 0) continue;
3143 
3144  // warning: we require the hessian to be initialized with zeros
3145  // and that edge->computeHessian only adds (+=) values!!
3146  if (lower_part_only && vertex1 == vertex2)
3147  {
3148  // we only need the lower triangular part, so cache Hessian
3149  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3150  edge->computeEqualityHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_eq_part);
3151  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3152  {
3153  for (int j = 0; j < i + 1; ++j)
3154  {
3155  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
3156  ++nnz_idx;
3157  }
3158  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3159  }
3160  }
3161  else
3162  {
3163  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3164  edge->computeEqualityHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_eq_part);
3165  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3166  }
3167  }
3168  }
3169  }
3170 }
3171 
3173 {
3174  int nnz = 0;
3175 
3176  assert(_graph.hasEdgeSet());
3178 
3179  if (getInequalityDimension() == 0) return nnz;
3180 
3182 
3183  // Iterate inequality edges
3184  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
3185  {
3186  if (edge->isLinear()) continue;
3187 
3188  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3189  {
3190  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3191 
3192  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3193  if (vert_i_dim_unfixed == 0) continue;
3195  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3196  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3197  {
3198  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3199 
3200  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3201  if (vert_j_dim_unfixed == 0) continue;
3202 
3203  if (lower_part_only && vertex1 == vertex2)
3204  {
3205  int n = vertex1->getDimensionUnfixed();
3206  nnz += n * (n + 1) / 2; // lower triangular including diagonal
3207  }
3208  else
3209  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
3210  }
3211  }
3212  }
3213  // Iterate mixed edges
3214  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3215  {
3216  if (getInequalityDimension() == 0 || edge->isInequalityLinear()) continue;
3217 
3218  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3219  {
3220  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3221 
3222  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3223  if (vert_i_dim_unfixed == 0) continue;
3224 
3225  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3226  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3227  {
3228  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3229 
3230  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3231  if (vert_j_dim_unfixed == 0) continue;
3232 
3233  if (lower_part_only && vertex1 == vertex2)
3234  {
3235  int n = vertex1->getDimensionUnfixed();
3236  nnz += n * (n + 1) / 2; // lower triangular including diagonal
3237  }
3238  else
3239  nnz += vertex1->getDimensionUnfixed() * vertex2->getDimensionUnfixed(); // block size
3240  }
3241  }
3242  }
3243 
3244  return nnz;
3245 }
3247  Eigen::Ref<Eigen::VectorXi> j_col, bool lower_part_only)
3248 {
3249  assert(_graph.hasEdgeSet());
3251 
3252  assert(i_row.size() == computeSparseHessianInequalitiesNNZ(lower_part_only));
3253  assert(i_row.size() == j_col.size());
3254 
3256 
3257  int nnz_idx = 0;
3258 
3259  // Iterate inequality edges
3260  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
3261  {
3262  if (edge->isLinear()) continue;
3263 
3264  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3265  {
3266  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3267 
3268  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3269  if (vert_i_dim_unfixed == 0) continue;
3270 
3271  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3272  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3273  {
3274  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3275 
3276  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3277  if (vert_j_dim_unfixed == 0) continue;
3278 
3279  if (lower_part_only && vertex1 == vertex2)
3280  {
3281  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3282  {
3283  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
3284  {
3285  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3286  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3287  ++nnz_idx;
3288  }
3289  }
3290  }
3291  else
3292  {
3293  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3294  {
3295  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
3296  {
3297  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3298  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3299  ++nnz_idx;
3300  }
3301  }
3302  }
3303  }
3304  }
3305  }
3306 
3307  // Iterate mixed edges
3308  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3309  {
3310  if (getInequalityDimension() == 0 || edge->isInequalityLinear()) continue;
3311 
3312  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3313  {
3314  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3315 
3316  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3317  if (vert_i_dim_unfixed == 0) continue;
3318 
3319  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3320  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3321  {
3322  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3323 
3324  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3325  if (vert_j_dim_unfixed == 0) continue;
3326 
3327  if (lower_part_only && vertex1 == vertex2)
3328  {
3329  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3330  {
3331  for (int v2_idx = 0; v2_idx < v1_idx + 1; ++v2_idx)
3332  {
3333  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3334  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3335  ++nnz_idx;
3336  }
3337  }
3338  }
3339  else
3340  {
3341  for (int v1_idx = 0; v1_idx < vert_i_dim_unfixed; ++v1_idx)
3342  {
3343  for (int v2_idx = 0; v2_idx < vert_j_dim_unfixed; ++v2_idx)
3344  {
3345  i_row[nnz_idx] = vertex1->getVertexIdx() + v1_idx;
3346  j_col[nnz_idx] = vertex2->getVertexIdx() + v2_idx;
3347  ++nnz_idx;
3348  }
3349  }
3350  }
3351  }
3352  }
3353  }
3354 }
3356  bool lower_part_only)
3357 {
3358  assert(values.size() == computeSparseHessianInequalitiesNNZ(lower_part_only));
3359 
3360  values.setZero();
3361 
3362  if (getInequalityDimension() == 0) return;
3363 
3364  int nnz_idx = 0;
3365 
3367 
3368  // Iterate inequality edges
3369  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
3370  {
3371  if (edge->isLinear()) continue;
3372 
3373  const double* mult_ineq_part = multipliers ? multipliers + edge->getEdgeIdx() : nullptr;
3374 
3375  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3376  {
3377  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3378 
3379  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3380  if (vert_i_dim_unfixed == 0) continue;
3381 
3382  // compute jacobian to speed up hessian computation
3383  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3384  edge->computeJacobian(row_i, block_jacobian1);
3385 
3386  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3387  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3388  {
3389  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3390 
3391  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3392  if (vert_j_dim_unfixed == 0) continue;
3393 
3394  // warning: we require the hessian to be initialized with zeros
3395  // and that edge->computeHessian only adds (+=) values!!
3396  if (lower_part_only && vertex1 == vertex2)
3397  {
3398  // we only need the lower triangular part, so cache Hessian
3399  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3400  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_ineq_part);
3401  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3402  {
3403  for (int j = 0; j < i + 1; ++j)
3404  {
3405  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
3406  ++nnz_idx;
3407  }
3408  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3409  }
3410  }
3411  else
3412  {
3413  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3414  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_ineq_part);
3415  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3416  }
3417  }
3418  }
3419  }
3420 
3421  // Iterate mixed edges
3422  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3423  {
3424  if (edge->getInequalityDimension() == 0 || edge->isInequalityLinear()) continue;
3425 
3426  const double* mult_ineq_part = multipliers ? multipliers + edge->getEdgeInequalityIdx() : nullptr;
3427 
3428  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3429  {
3430  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3431 
3432  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3433  if (vert_i_dim_unfixed == 0) continue;
3434 
3435  // compute jacobian to speed up hessian computation
3436  Eigen::MatrixXd block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
3437  edge->computeInequalityJacobian(row_i, block_jacobian1);
3438 
3439  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3440  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3441  {
3442  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3443 
3444  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3445  if (vert_j_dim_unfixed == 0) continue;
3446 
3447  // warning: we require the hessian to be initialized with zeros
3448  // and that edge->computeHessian only adds (+=) values!!
3449  if (lower_part_only && vertex1 == vertex2)
3450  {
3451  // we only need the lower triangular part, so cache Hessian
3452  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3453  edge->computeInequalityHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_ineq_part);
3454  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3455  {
3456  for (int j = 0; j < i + 1; ++j)
3457  {
3458  values[nnz_idx] += block_hessian_ii.coeffRef(i, j);
3459  ++nnz_idx;
3460  }
3461  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3462  }
3463  }
3464  else
3465  {
3466  Eigen::Map<Eigen::MatrixXd> block_hessian(values.data() + nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3467  edge->computeInequalityHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_ineq_part);
3468  nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3469  }
3470  }
3471  }
3472  }
3473 }
3474 
3475 void HyperGraphOptimizationProblemEdgeBased::computeSparseHessiansNNZ(int& nnz_obj, int& nnz_eq, int& nnz_ineq, bool lower_part_only)
3476 {
3477  // we should not forget to check for isObjectiveLinear/isEqualityLinear/isInequalityLinear to match number of NNZ
3478  nnz_obj = computeSparseHessianObjectiveNNZ(lower_part_only);
3479  nnz_eq = computeSparseHessianEqualitiesNNZ(lower_part_only);
3480  nnz_ineq = computeSparseHessianInequalitiesNNZ(lower_part_only);
3481 }
3482 
3485  Eigen::Ref<Eigen::VectorXi> j_col_eq, Eigen::Ref<Eigen::VectorXi> i_row_ineq, Eigen::Ref<Eigen::VectorXi> j_col_ineq, bool lower_part_only)
3486 {
3487  computeSparseHessianObjectiveStructure(i_row_obj, j_col_obj, lower_part_only);
3488  computeSparseHessianEqualitiesStructure(i_row_eq, j_col_eq, lower_part_only);
3489  computeSparseHessianInequalitiesStructure(i_row_ineq, j_col_ineq, lower_part_only);
3490 }
3492  Eigen::Ref<Eigen::VectorXd> values_eq,
3493  Eigen::Ref<Eigen::VectorXd> values_ineq, double multiplier_obj,
3494  const double* multipliers_eq, const double* multipliers_ineq,
3495  bool lower_part_only)
3496 {
3497  assert(values_obj.size() == computeSparseHessianObjectiveNNZ(lower_part_only));
3498  assert(values_eq.size() == computeSparseHessianEqualitiesNNZ(lower_part_only));
3499  assert(values_ineq.size() == computeSparseHessianInequalitiesNNZ(lower_part_only));
3500 
3501  values_obj.setZero();
3502  values_eq.setZero();
3503  values_ineq.setZero();
3504 
3505  int obj_nnz_idx = 0;
3506  int eq_nnz_idx = 0;
3507  int ineq_nnz_idx = 0;
3508 
3510 
3511  // Iterate plain objective edges
3512  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
3513  {
3514  if (edge->isLinear()) continue;
3515 
3516  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3517  {
3518  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3519 
3520  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3521  if (vert_i_dim_unfixed == 0) continue;
3522 
3523  // compute jacobian to speed up hessian computation
3524  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3525  edge->computeJacobian(row_i, block_jacobian1);
3526 
3527  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3528  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3529  {
3530  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3531 
3532  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3533  if (vert_j_dim_unfixed == 0) continue;
3534 
3535  // warning: we require the hessian to be initialized with zeros
3536  // and that edge->computeHessian only adds (+=) values!!
3537  if (lower_part_only && vertex1 == vertex2)
3538  {
3539  // we only need the lower triangular part, so cache Hessian
3540  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3541  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, nullptr, multiplier_obj);
3542  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3543  {
3544  for (int j = 0; j < i + 1; ++j)
3545  {
3546  values_obj[obj_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3547  ++obj_nnz_idx;
3548  }
3549  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3550  }
3551  }
3552  else
3553  {
3554  Eigen::Map<Eigen::MatrixXd> block_hessian(values_obj.data() + obj_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3555  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, nullptr, multiplier_obj);
3556  obj_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3557  }
3558  }
3559  }
3560  }
3561 
3562  // Iterate lsq objective edges
3563  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
3564  {
3565  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
3566 
3567  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3568  {
3569  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3570 
3571  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3572  if (vert_i_dim_unfixed == 0) continue;
3573 
3574  // compute jacobian to speed up hessian computation
3575  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3576  edge->computeJacobian(row_i, block_jacobian1);
3577 
3578  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3579  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3580  {
3581  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3582 
3583  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3584  if (vert_j_dim_unfixed == 0) continue;
3585 
3586  // we need the second jacobian for applying the chain rule
3587  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
3588  edge->computeJacobian(col_j, block_jacobian2);
3589 
3590  // approximate H = 2*J^T*J
3591  if (lower_part_only && vertex1 == vertex2)
3592  {
3593  // we only need the lower triangular part, so cache Hessian
3594  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3595  block_hessian_ii.noalias() = 2.0 * multiplier_obj * block_jacobian1.transpose() * block_jacobian2;
3596  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3597  {
3598  for (int j = 0; j < i + 1; ++j)
3599  {
3600  values_obj[obj_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3601  ++obj_nnz_idx;
3602  }
3603  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3604  }
3605  }
3606  else
3607  {
3608  Eigen::Map<Eigen::MatrixXd> block_hessian(values_obj.data() + obj_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3609  block_hessian += 2.0 * multiplier_obj * block_jacobian1.transpose() * block_jacobian2;
3610  obj_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3611  }
3612  }
3613  }
3614  }
3615 
3616  // Iterate equality edges
3617  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
3618  {
3619  if (edge->isLinear()) continue;
3620 
3621  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
3622 
3623  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3624  {
3625  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3626 
3627  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3628  if (vert_i_dim_unfixed == 0) continue;
3629 
3630  // compute jacobian to speed up hessian computation
3631  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3632  edge->computeJacobian(row_i, block_jacobian1);
3633 
3634  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3635  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3636  {
3637  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3638 
3639  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3640  if (vert_j_dim_unfixed == 0) continue;
3641 
3642  // warning: we require the hessian to be initialized with zeros
3643  // and that edge->computeHessian only adds (+=) values!!
3644  if (lower_part_only && vertex1 == vertex2)
3645  {
3646  // we only need the lower triangular part, so cache Hessian
3647  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3648  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_eq_part);
3649  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3650  {
3651  for (int j = 0; j < i + 1; ++j)
3652  {
3653  values_eq[eq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3654  ++eq_nnz_idx;
3655  }
3656  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3657  }
3658  }
3659  else
3660  {
3661  Eigen::Map<Eigen::MatrixXd> block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3662  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_eq_part);
3663  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3664  }
3665  }
3666  }
3667  }
3668 
3669  // Iterate inequality edges
3670  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
3671  {
3672  if (edge->isLinear()) continue;
3673 
3674  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
3675 
3676  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3677  {
3678  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3679 
3680  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3681  if (vert_i_dim_unfixed == 0) continue;
3682 
3683  // compute jacobian to speed up hessian computation
3684  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
3685  edge->computeJacobian(row_i, block_jacobian1);
3686 
3687  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3688  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3689  {
3690  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3691 
3692  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3693  if (vert_j_dim_unfixed == 0) continue;
3694 
3695  // warning: we require the hessian to be initialized with zeros
3696  // and that edge->computeHessian only adds (+=) values!!
3697  if (lower_part_only && vertex1 == vertex2)
3698  {
3699  // we only need the lower triangular part, so cache Hessian
3700  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3701  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian_ii, mult_ineq_part);
3702  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3703  {
3704  for (int j = 0; j < i + 1; ++j)
3705  {
3706  values_ineq[ineq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3707  ++ineq_nnz_idx;
3708  }
3709  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3710  }
3711  }
3712  else
3713  {
3714  Eigen::Map<Eigen::MatrixXd> block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3715  edge->computeHessianInc(row_i, col_j, block_jacobian1, block_hessian, mult_ineq_part);
3716  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3717  }
3718  }
3719  }
3720  }
3721 
3722  // Iterate mixed edges
3723  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
3724  {
3725  // if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
3726 
3727  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
3728  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
3729 
3730  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
3731  {
3732  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
3733 
3734  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
3735  if (vert_i_dim_unfixed == 0) continue;
3736 
3737  // compute jacobian to speed up hessian computation
3738  Eigen::MatrixXd obj_block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
3739  Eigen::MatrixXd eq_block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
3740  Eigen::MatrixXd ineq_block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
3741  edge->computeJacobians(row_i, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1);
3742 
3743  int col_end = lower_part_only ? row_i + 1 : edge->getNumVertices();
3744  for (int col_j = 0; col_j < col_end; ++col_j) // hessian is symmetric
3745  {
3746  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
3747 
3748  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
3749  if (vert_j_dim_unfixed == 0) continue;
3750 
3751  if (edge->isObjectiveLeastSquaresForm())
3752  {
3753  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
3754  {
3755  // we need the second jacobian for applying the chain rule
3756  Eigen::MatrixXd obj_block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
3757  edge->computeObjectiveJacobian(col_j, obj_block_jacobian2);
3758 
3759  // approximate H = 2*J^T*J
3760  if (lower_part_only && vertex1 == vertex2)
3761  {
3762  // we only need the lower triangular part, so cache Hessian
3763  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3764  block_hessian_ii.noalias() = 2.0 * multiplier_obj * obj_block_jacobian1.transpose() * obj_block_jacobian2;
3765  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3766  {
3767  for (int j = 0; j < i + 1; ++j)
3768  {
3769  values_obj[obj_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3770  ++obj_nnz_idx;
3771  }
3772  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3773  }
3774  }
3775  else
3776  {
3777  Eigen::Map<Eigen::MatrixXd> obj_block_hessian(values_obj.data() + obj_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3778  obj_block_hessian += 2.0 * multiplier_obj * obj_block_jacobian1.transpose() * obj_block_jacobian2;
3779  obj_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3780  }
3781  }
3782 
3783  if (!edge->isEqualityLinear() && !edge->isInequalityLinear())
3784  {
3785  if (lower_part_only && vertex1 == vertex2)
3786  {
3787  // we only need the lower triangular part, so cache Hessian
3788  Eigen::MatrixXd eq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3789  Eigen::MatrixXd ineq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3790  edge->computeConstraintHessians(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, eq_block_hessian_ii,
3791  ineq_block_hessian_ii, mult_eq_part, mult_ineq_part);
3792  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3793  {
3794  for (int j = 0; j < i + 1; ++j)
3795  {
3796  values_eq[eq_nnz_idx] += eq_block_hessian_ii.coeffRef(i, j);
3797  ++eq_nnz_idx;
3798  values_ineq[ineq_nnz_idx] += ineq_block_hessian_ii.coeffRef(i, j);
3799  ++ineq_nnz_idx;
3800  }
3801  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3802  }
3803  }
3804  else
3805  {
3806  Eigen::Map<Eigen::MatrixXd> eq_block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3807  Eigen::Map<Eigen::MatrixXd> ineq_block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3808  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, eq_block_hessian,
3809  ineq_block_hessian, mult_eq_part, mult_ineq_part);
3810  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3811  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3812  }
3813  }
3814  else
3815  {
3816  if (!edge->isEqualityLinear())
3817  {
3818  if (lower_part_only && vertex1 == vertex2)
3819  {
3820  // we only need the lower triangular part, so cache Hessian
3821  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3822  edge->computeEqualityHessian(row_i, col_j, eq_block_jacobian1, block_hessian_ii, mult_eq_part);
3823  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3824  {
3825  for (int j = 0; j < i + 1; ++j)
3826  {
3827  values_eq[eq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3828  ++eq_nnz_idx;
3829  }
3830  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3831  }
3832  }
3833  else
3834  {
3835  Eigen::Map<Eigen::MatrixXd> eq_block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3836  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, eq_block_hessian, mult_eq_part);
3837  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3838  }
3839  }
3840  else if (!edge->isInequalityLinear())
3841  {
3842  if (lower_part_only && vertex1 == vertex2)
3843  {
3844  // we only need the lower triangular part, so cache Hessian
3845  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3846  edge->computeInequalityHessian(row_i, col_j, ineq_block_jacobian1, block_hessian_ii, mult_ineq_part);
3847  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3848  {
3849  for (int j = 0; j < i + 1; ++j)
3850  {
3851  values_ineq[ineq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3852  ++ineq_nnz_idx;
3853  }
3854  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3855  }
3856  }
3857  else
3858  {
3859  Eigen::Map<Eigen::MatrixXd> ineq_block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed,
3860  vert_j_dim_unfixed);
3861  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, ineq_block_hessian, mult_ineq_part);
3862  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3863  }
3864  }
3865  }
3866  }
3867  else // TODO(roesmann): check all reasonable cases:
3868  {
3869  if (!edge->isObjectiveLinear() && !edge->isEqualityLinear() && !edge->isInequalityLinear() &&
3870  edge->getObjectiveDimension() != 0 && edge->getEqualityDimension() != 0 && edge->getInequalityDimension() != 0)
3871  {
3872  if (lower_part_only && vertex1 == vertex2)
3873  {
3874  // we only need the lower triangular part, so cache Hessian
3875  Eigen::MatrixXd obj_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3876  Eigen::MatrixXd eq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3877  Eigen::MatrixXd ineq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3878  edge->computeHessians(row_i, col_j, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1, obj_block_hessian_ii,
3879  eq_block_hessian_ii, ineq_block_hessian_ii, mult_eq_part, mult_ineq_part, multiplier_obj);
3880  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3881  {
3882  for (int j = 0; j < i + 1; ++j)
3883  {
3884  values_obj[obj_nnz_idx] += obj_block_hessian_ii.coeffRef(i, j);
3885  ++obj_nnz_idx;
3886  values_eq[eq_nnz_idx] += eq_block_hessian_ii.coeffRef(i, j);
3887  ++eq_nnz_idx;
3888  values_ineq[ineq_nnz_idx] += ineq_block_hessian_ii.coeffRef(i, j);
3889  ++ineq_nnz_idx;
3890  }
3891  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3892  }
3893  }
3894  else
3895  {
3896  Eigen::Map<Eigen::MatrixXd> obj_block_hessian(values_obj.data() + obj_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3897  Eigen::Map<Eigen::MatrixXd> eq_block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3898  Eigen::Map<Eigen::MatrixXd> ineq_block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3899  edge->computeHessiansInc(row_i, col_j, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1, obj_block_hessian,
3900  eq_block_hessian, ineq_block_hessian, mult_eq_part, mult_ineq_part, multiplier_obj);
3901  obj_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3902  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3903  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3904  }
3905  }
3906  else if (!edge->isEqualityLinear() && !edge->isInequalityLinear() && edge->getEqualityDimension() != 0 &&
3907  edge->getInequalityDimension() != 0)
3908  {
3909  if (lower_part_only && vertex1 == vertex2)
3910  {
3911  // we only need the lower triangular part, so cache Hessian
3912  Eigen::MatrixXd eq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3913  Eigen::MatrixXd ineq_block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3914  edge->computeConstraintHessians(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, eq_block_hessian_ii,
3915  ineq_block_hessian_ii, mult_eq_part, mult_ineq_part);
3916  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3917  {
3918  for (int j = 0; j < i + 1; ++j)
3919  {
3920  values_eq[eq_nnz_idx] += eq_block_hessian_ii.coeffRef(i, j);
3921  ++eq_nnz_idx;
3922  values_ineq[ineq_nnz_idx] += ineq_block_hessian_ii.coeffRef(i, j);
3923  ++ineq_nnz_idx;
3924  }
3925  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3926  }
3927  }
3928  else
3929  {
3930  Eigen::Map<Eigen::MatrixXd> eq_block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3931  Eigen::Map<Eigen::MatrixXd> ineq_block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3932  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, eq_block_hessian,
3933  ineq_block_hessian, mult_eq_part, mult_ineq_part);
3934  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3935  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3936  }
3937  }
3938  else
3939  {
3940  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
3941  {
3942  if (lower_part_only && vertex1 == vertex2)
3943  {
3944  // we only need the lower triangular part, so cache Hessian
3945  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3946  edge->computeObjectiveHessian(row_i, col_j, obj_block_jacobian1, block_hessian_ii, nullptr, multiplier_obj);
3947  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3948  {
3949  for (int j = 0; j < i + 1; ++j)
3950  {
3951  values_obj[obj_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3952  ++obj_nnz_idx;
3953  }
3954  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3955  }
3956  }
3957  else
3958  {
3959  Eigen::Map<Eigen::MatrixXd> obj_block_hessian(values_obj.data() + obj_nnz_idx, vert_i_dim_unfixed,
3960  vert_j_dim_unfixed);
3961  edge->computeObjectiveHessianInc(row_i, col_j, obj_block_jacobian1, obj_block_hessian, nullptr, multiplier_obj);
3962  obj_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3963  }
3964  }
3965 
3966  if (!edge->isEqualityLinear() && edge->getEqualityDimension() != 0)
3967  {
3968  if (lower_part_only && vertex1 == vertex2)
3969  {
3970  // we only need the lower triangular part, so cache Hessian
3971  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3972  edge->computeEqualityHessian(row_i, col_j, eq_block_jacobian1, block_hessian_ii, mult_eq_part);
3973  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3974  {
3975  for (int j = 0; j < i + 1; ++j)
3976  {
3977  values_eq[eq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
3978  ++eq_nnz_idx;
3979  }
3980  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
3981  }
3982  }
3983  else
3984  {
3985  Eigen::Map<Eigen::MatrixXd> eq_block_hessian(values_eq.data() + eq_nnz_idx, vert_i_dim_unfixed, vert_j_dim_unfixed);
3986  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, eq_block_hessian, mult_eq_part);
3987  eq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
3988  }
3989  }
3990  else if (!edge->isInequalityLinear() && edge->getInequalityDimension() != 0)
3991  {
3992  if (lower_part_only && vertex1 == vertex2)
3993  {
3994  // we only need the lower triangular part, so cache Hessian
3995  Eigen::MatrixXd block_hessian_ii(vert_i_dim_unfixed, vert_j_dim_unfixed);
3996  edge->computeInequalityHessian(row_i, col_j, ineq_block_jacobian1, block_hessian_ii, mult_ineq_part);
3997  for (int i = 0; i < vert_i_dim_unfixed; ++i)
3998  {
3999  for (int j = 0; j < i + 1; ++j)
4000  {
4001  values_ineq[ineq_nnz_idx] += block_hessian_ii.coeffRef(i, j);
4002  ++ineq_nnz_idx;
4003  }
4004  // nnz_idx += vert_i_dim_unfixed * (vert_i_dim_unfixed + 1) / 2;
4005  }
4006  }
4007  else
4008  {
4009  Eigen::Map<Eigen::MatrixXd> ineq_block_hessian(values_ineq.data() + ineq_nnz_idx, vert_i_dim_unfixed,
4010  vert_j_dim_unfixed);
4011  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, ineq_block_hessian, mult_ineq_part);
4012  ineq_nnz_idx += vert_i_dim_unfixed * vert_j_dim_unfixed;
4013  }
4014  }
4015  }
4016  }
4017  }
4018  }
4019  }
4020 }
4021 
4023  const double* multipliers_eq, const double* multipliers_ineq,
4024  const Eigen::VectorXi* col_nnz, bool upper_part_only)
4025 {
4026  H.setZero();
4027  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
4028  if (col_nnz) H.reserve(*col_nnz);
4029 
4031 
4032  // Iterate plain objective edges
4033  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
4034  {
4035  if (edge->isLinear()) continue;
4036 
4037  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4038  {
4039  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4040 
4041  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4042  if (vert_i_dim_unfixed == 0) continue;
4043 
4044  // compute jacobian to speed up hessian computation
4045  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
4046  edge->computeJacobian(row_i, block_jacobian1);
4047 
4048  int col_start = upper_part_only ? row_i : 0;
4049  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4050  {
4051  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4052 
4053  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4054  if (vert_j_dim_unfixed == 0) continue;
4055 
4056  // warning: we require the hessian to be initialized with zeros
4057  // and that edge->computeHessian only adds (+=) values!!
4058  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
4059  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian);
4060 
4061  // insert values
4062  if (upper_part_only && vertex1 == vertex2)
4063  {
4064  // block on the main diagonal, so use upper part only
4065  for (int i = 0; i < block_hessian.rows(); ++i)
4066  {
4067  for (int j = i; j < block_hessian.cols(); ++j)
4068  {
4069  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4070  }
4071  }
4072  }
4073  else
4074  {
4075  for (int i = 0; i < block_hessian.rows(); ++i)
4076  {
4077  for (int j = 0; j < block_hessian.cols(); ++j)
4078  {
4079  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4080  }
4081  }
4082  }
4083  }
4084  }
4085  }
4086 
4087  // Iterate lsq objective edges
4088  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
4089  {
4090  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
4091 
4092  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4093  {
4094  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4095 
4096  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4097  if (vert_i_dim_unfixed == 0) continue;
4098 
4099  // compute jacobian to speed up hessian computation
4100  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
4101  edge->computeJacobian(row_i, block_jacobian1);
4102 
4103  int col_start = upper_part_only ? row_i : 0;
4104  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4105  {
4106  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4107 
4108  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4109  if (vert_j_dim_unfixed == 0) continue;
4110 
4111  // we need the second jacobian for applying the chain rule
4112  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
4113  edge->computeJacobian(col_j, block_jacobian2);
4114 
4115  // approximate H = 2*J^T*J
4116  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
4117  block_hessian = 2.0 * block_jacobian1.transpose() * block_jacobian2;
4118 
4119  // insert values
4120  if (upper_part_only && vertex1 == vertex2)
4121  {
4122  // block on the main diagonal, so use upper part only
4123  for (int i = 0; i < block_hessian.rows(); ++i)
4124  {
4125  for (int j = i; j < block_hessian.cols(); ++j)
4126  {
4127  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4128  }
4129  }
4130  }
4131  else
4132  {
4133  for (int i = 0; i < block_hessian.rows(); ++i)
4134  {
4135  for (int j = 0; j < block_hessian.cols(); ++j)
4136  {
4137  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4138  }
4139  }
4140  }
4141  }
4142  }
4143  }
4144 
4145  // Iterate equality edges
4146  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4147  {
4148  if (edge->isLinear()) continue;
4149 
4150  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
4151 
4152  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4153  {
4154  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4155 
4156  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4157  if (vert_i_dim_unfixed == 0) continue;
4158 
4159  // compute jacobian to speed up hessian computation
4160  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
4161  edge->computeJacobian(row_i, block_jacobian1);
4162 
4163  int col_start = upper_part_only ? row_i : 0;
4164  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4165  {
4166  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4167 
4168  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4169  if (vert_j_dim_unfixed == 0) continue;
4170 
4171  // warning: we require the hessian to be initialized with zeros
4172  // and that edge->computeHessian only adds (+=) values!!
4173  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
4174  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian, mult_eq_part);
4175 
4176  // insert values
4177  if (upper_part_only && vertex1 == vertex2)
4178  {
4179  // block on the main diagonal, so use upper part only
4180  for (int i = 0; i < block_hessian.rows(); ++i)
4181  {
4182  for (int j = i; j < block_hessian.cols(); ++j)
4183  {
4184  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4185  }
4186  }
4187  }
4188  else
4189  {
4190  for (int i = 0; i < block_hessian.rows(); ++i)
4191  {
4192  for (int j = 0; j < block_hessian.cols(); ++j)
4193  {
4194  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4195  }
4196  }
4197  }
4198  }
4199  }
4200  }
4201 
4202  // Iterate inequality edges
4203  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4204  {
4205  if (edge->isLinear()) continue;
4206 
4207  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
4208 
4209  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4210  {
4211  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4212 
4213  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4214  if (vert_i_dim_unfixed == 0) continue;
4215 
4216  // compute jacobian to speed up hessian computation
4217  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
4218  edge->computeJacobian(row_i, block_jacobian1);
4219 
4220  int col_start = upper_part_only ? row_i : 0;
4221  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4222  {
4223  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4224 
4225  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4226  if (vert_j_dim_unfixed == 0) continue;
4227 
4228  // warning: we require the hessian to be initialized with zeros
4229  // and that edge->computeHessian only adds (+=) values!!
4230  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
4231  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian, mult_ineq_part);
4232 
4233  // insert values
4234  if (upper_part_only && vertex1 == vertex2)
4235  {
4236  // block on the main diagonal, so use upper part only
4237  for (int i = 0; i < block_hessian.rows(); ++i)
4238  {
4239  for (int j = i; j < block_hessian.cols(); ++j)
4240  {
4241  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4242  }
4243  }
4244  }
4245  else
4246  {
4247  for (int i = 0; i < block_hessian.rows(); ++i)
4248  {
4249  for (int j = 0; j < block_hessian.cols(); ++j)
4250  {
4251  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4252  }
4253  }
4254  }
4255  }
4256  }
4257  }
4258 
4259  // Iterate mixed edges
4260  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4261  {
4262  // if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
4263 
4264  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
4265  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
4266 
4267  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4268  {
4269  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4270 
4271  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4272  if (vert_i_dim_unfixed == 0) continue;
4273 
4274  // compute jacobian to speed up hessian computation
4275  Eigen::MatrixXd obj_block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
4276  Eigen::MatrixXd eq_block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
4277  Eigen::MatrixXd ineq_block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
4278  edge->computeJacobians(row_i, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1);
4279 
4280  int col_start = upper_part_only ? row_i : 0;
4281  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4282  {
4283  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4284 
4285  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4286  if (vert_j_dim_unfixed == 0) continue;
4287 
4288  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
4289  block_hessian.setZero();
4290 
4291  if (edge->isObjectiveLeastSquaresForm())
4292  {
4293  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
4294  {
4295  // we need the second jacobian for applying the chain rule
4296  Eigen::MatrixXd obj_block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
4297  edge->computeObjectiveJacobian(col_j, obj_block_jacobian2);
4298 
4299  // approximate H = 2*J^T*J
4300  block_hessian += 2.0 * obj_block_jacobian1.transpose() * obj_block_jacobian2;
4301  }
4302 
4303  if (!edge->isEqualityLinear() && !edge->isInequalityLinear())
4304  {
4305  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, block_hessian, block_hessian,
4306  mult_eq_part, mult_ineq_part);
4307  }
4308  else
4309  {
4310  if (!edge->isEqualityLinear())
4311  {
4312  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, block_hessian, mult_eq_part);
4313  }
4314  else if (!edge->isInequalityLinear())
4315  {
4316  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, block_hessian, mult_ineq_part);
4317  }
4318  }
4319  }
4320  else // TODO(roesmann): check all reasonable cases:
4321  {
4322  if (!edge->isObjectiveLinear() && !edge->isEqualityLinear() && !edge->isInequalityLinear() &&
4323  edge->getObjectiveDimension() != 0 && edge->getEqualityDimension() != 0 && edge->getInequalityDimension() != 0)
4324  {
4325  edge->computeHessiansInc(row_i, col_j, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1, block_hessian,
4326  block_hessian, block_hessian, mult_eq_part, mult_ineq_part, 1.0);
4327  }
4328  else if (!edge->isEqualityLinear() && !edge->isInequalityLinear() && edge->getEqualityDimension() != 0 &&
4329  edge->getInequalityDimension() != 0)
4330  {
4331  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, block_hessian, block_hessian,
4332  mult_eq_part, mult_ineq_part);
4333  }
4334  else
4335  {
4336  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
4337  {
4338  edge->computeObjectiveHessianInc(row_i, col_j, obj_block_jacobian1, block_hessian, nullptr, 1.0);
4339  }
4340 
4341  if (!edge->isEqualityLinear() && edge->getEqualityDimension() != 0)
4342  {
4343  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, block_hessian, mult_eq_part);
4344  }
4345  else if (!edge->isInequalityLinear() && edge->getInequalityDimension() != 0)
4346  {
4347  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, block_hessian, mult_ineq_part);
4348  }
4349  }
4350  }
4351  // insert values
4352  if (upper_part_only && vertex1 == vertex2)
4353  {
4354  // block on the main diagonal, so use upper part only
4355  for (int i = 0; i < block_hessian.rows(); ++i)
4356  {
4357  for (int j = i; j < block_hessian.cols(); ++j)
4358  {
4359  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4360  }
4361  }
4362  }
4363  else
4364  {
4365  for (int i = 0; i < block_hessian.rows(); ++i)
4366  {
4367  for (int j = 0; j < block_hessian.cols(); ++j)
4368  {
4369  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
4370  }
4371  }
4372  }
4373  }
4374  }
4375  }
4376 }
4377 
4379 {
4380  assert(col_nnz.size() == getParameterDimension());
4381 
4382  col_nnz.setZero();
4383  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
4384 
4385  // TODO(roesmann): with the following strategy, we overestimate the number of nnz, since vertices are shared among multiple edges...
4386  // however, we do not want to create the complete sparsity pattern here by creating a matrix of zeros and ones (tagging).
4387 
4389 
4390  // Iterate plain objective edges
4391  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
4392  {
4393  if (edge->isLinear()) continue;
4394 
4395  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4396  {
4397  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4398 
4399  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4400  if (vert_i_dim_unfixed == 0) continue;
4401 
4402  int col_start = upper_part_only ? row_i : 0;
4403  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4404  {
4405  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4406 
4407  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4408  if (vert_j_dim_unfixed == 0) continue;
4409 
4410  if (upper_part_only && vertex1 == vertex2)
4411  {
4412  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
4413  for (int l = 0; l < vert_j_dim_unfixed; ++l)
4414  {
4415  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
4416  }
4417  }
4418  else
4419  {
4420  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
4421  }
4422  }
4423  }
4424  }
4425 
4426  // Iterate lsq objective edges
4427  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
4428  {
4429  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
4430 
4431  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4432  {
4433  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4434 
4435  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4436  if (vert_i_dim_unfixed == 0) continue;
4437 
4438  int col_start = upper_part_only ? row_i : 0;
4439  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4440  {
4441  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4442 
4443  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4444  if (vert_j_dim_unfixed == 0) continue;
4445 
4446  if (upper_part_only && vertex1 == vertex2)
4447  {
4448  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
4449  for (int l = 0; l < vert_j_dim_unfixed; ++l)
4450  {
4451  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
4452  }
4453  }
4454  else
4455  {
4456  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
4457  }
4458  }
4459  }
4460  }
4461 
4462  // Iterate equality edges
4463  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4464  {
4465  if (edge->isLinear()) continue;
4466 
4467  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4468  {
4469  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4470 
4471  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4472  if (vert_i_dim_unfixed == 0) continue;
4473 
4474  int col_start = upper_part_only ? row_i : 0;
4475  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4476  {
4477  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4478 
4479  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4480  if (vert_j_dim_unfixed == 0) continue;
4481 
4482  if (upper_part_only && vertex1 == vertex2)
4483  {
4484  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
4485  for (int l = 0; l < vert_j_dim_unfixed; ++l)
4486  {
4487  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
4488  }
4489  }
4490  else
4491  {
4492  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
4493  }
4494  }
4495  }
4496  }
4497 
4498  // Iterate inequality edges
4499  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4500  {
4501  if (edge->isLinear()) continue;
4502 
4503  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4504  {
4505  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4506 
4507  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4508  if (vert_i_dim_unfixed == 0) continue;
4509 
4510  int col_start = upper_part_only ? row_i : 0;
4511  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4512  {
4513  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4514 
4515  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4516  if (vert_j_dim_unfixed == 0) continue;
4517 
4518  if (upper_part_only && vertex1 == vertex2)
4519  {
4520  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
4521  for (int l = 0; l < vert_j_dim_unfixed; ++l)
4522  {
4523  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
4524  }
4525  }
4526  else
4527  {
4528  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
4529  }
4530  }
4531  }
4532  }
4533 
4534  // Iterate mixed edges
4535  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4536  {
4537  // if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
4538 
4539  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4540  {
4541  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4542 
4543  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4544  if (vert_i_dim_unfixed == 0) continue;
4545 
4546  int col_start = upper_part_only ? row_i : 0;
4547  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
4548  {
4549  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
4550 
4551  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
4552  if (vert_j_dim_unfixed == 0) continue;
4553 
4554  if (upper_part_only && vertex1 == vertex2)
4555  {
4556  // just the upper triangular part of the bock matrix including diagonal, so N*(N+1)/2 elements
4557  for (int l = 0; l < vert_j_dim_unfixed; ++l)
4558  {
4559  col_nnz(vertex2->getVertexIdx() + l) += l + 1; // first column 0, second 1, ...., vert_i_dim_unfixed
4560  }
4561  }
4562  else
4563  {
4564  col_nnz.segment(vertex2->getVertexIdx(), vert_j_dim_unfixed).array() += vert_i_dim_unfixed;
4565  }
4566  }
4567  }
4568  }
4569 }
4570 
4572  bool include_finite_bounds, const Eigen::VectorXi* col_nnz)
4573 {
4575 
4576  A.setZero(); // we need to reset since we call insert below
4577  if (col_nnz) A.reserve(*col_nnz);
4578 
4579  int inequality_row_start = getEqualityDimension();
4580  int bounds_row_start = inequality_row_start + getInequalityDimension();
4581 
4582  // Iterate equality edges
4583  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4584  {
4585  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4586  {
4587  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4588 
4589  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4590  if (vert_dim_unfixed == 0) continue;
4591 
4592  // TODO(roesmann): Check if we can avoid the temporary here...
4593  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
4594  edge->computeJacobian(vert_idx, block_jacobian);
4595 
4596  // Now insert values
4597  for (int i = 0; i < block_jacobian.rows(); ++i)
4598  {
4599  for (int j = 0; j < block_jacobian.cols(); ++j)
4600  {
4601  A.insert(edge->getEdgeIdx() + i, vertex->getVertexIdx() + j) = block_jacobian.coeffRef(i, j);
4602  }
4603  }
4604  }
4605  }
4606 
4607  // Iterate inequality edges
4608  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4609  {
4610  int A_edge_idx = inequality_row_start + edge->getEdgeIdx();
4611  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4612  {
4613  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4614 
4615  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4616  if (vert_dim_unfixed == 0) continue;
4617 
4618  // TODO(roesmann): Check if we can avoid the temporary here...
4619  Eigen::MatrixXd block_jacobian(edge->getDimension(), vert_dim_unfixed);
4620  edge->computeJacobian(vert_idx, block_jacobian);
4621 
4622  // Now insert values
4623  for (int i = 0; i < block_jacobian.rows(); ++i)
4624  {
4625  for (int j = 0; j < block_jacobian.cols(); ++j)
4626  {
4627  A.insert(A_edge_idx + i, vertex->getVertexIdx() + j) = block_jacobian.coeffRef(i, j);
4628  }
4629  }
4630  }
4631  }
4632 
4633  // Iterate mixed edges
4634  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4635  {
4636  int A_edge_eq_idx = edge->getEdgeEqualityIdx();
4637  int A_edge_ineq_idx = inequality_row_start + edge->getEdgeInequalityIdx();
4638 
4639  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4640  {
4641  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4642 
4643  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4644  if (vert_dim_unfixed == 0) continue;
4645 
4646  // TODO(roesmann): Check if we can avoid the temporary here...
4647  Eigen::MatrixXd block_jacobian_eq(edge->getEqualityDimension(), vert_dim_unfixed);
4648  Eigen::MatrixXd block_jacobian_ineq(edge->getInequalityDimension(), vert_dim_unfixed);
4649  edge->computeConstraintJacobians(vert_idx, block_jacobian_eq, block_jacobian_ineq);
4650 
4651  // Now insert values
4652  for (int i = 0; i < block_jacobian_eq.rows(); ++i)
4653  {
4654  for (int j = 0; j < block_jacobian_eq.cols(); ++j)
4655  {
4656  A.insert(A_edge_eq_idx + i, vertex->getVertexIdx() + j) = block_jacobian_eq.coeffRef(i, j);
4657  }
4658  }
4659  for (int i = 0; i < block_jacobian_ineq.rows(); ++i)
4660  {
4661  for (int j = 0; j < block_jacobian_ineq.cols(); ++j)
4662  {
4663  A.insert(A_edge_ineq_idx + i, vertex->getVertexIdx() + j) = block_jacobian_ineq.coeffRef(i, j);
4664  }
4665  }
4666  }
4667  }
4668 
4669  if (include_finite_bounds)
4670  {
4671  int row_idx = bounds_row_start;
4672  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
4673  {
4674  int vert_idx = vertex->getVertexIdx();
4675  int free_idx = 0;
4676  for (int i = 0; i < vertex->getDimension(); ++i)
4677  {
4678  if (vertex->isFixedComponent(i)) continue;
4679 
4680  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
4681  {
4682  A.insert(row_idx, vert_idx + free_idx) = 1;
4683  ++row_idx;
4684  }
4685  ++free_idx;
4686  }
4687  }
4688  }
4689 }
4690 
4692  bool include_finite_bounds)
4693 {
4694  assert(col_nnz.size() == getParameterDimension());
4695 
4696  col_nnz.setZero();
4697 
4699 
4700  // Iterate equality edges
4701  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4702  {
4703  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4704  {
4705  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4706  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4707  if (vert_dim_unfixed == 0) continue;
4708 
4709  col_nnz.segment(vertex->getVertexIdx(), vert_dim_unfixed).array() += edge->getDimension();
4710  }
4711  }
4712 
4713  // Iterate equality edges
4714  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4715  {
4716  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4717  {
4718  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4719  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4720  if (vert_dim_unfixed == 0) continue;
4721 
4722  col_nnz.segment(vertex->getVertexIdx(), vert_dim_unfixed).array() += edge->getDimension();
4723  }
4724  }
4725 
4726  // Iterate mixed edges
4727  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4728  {
4729  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4730  {
4731  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4732 
4733  int vert_dim_unfixed = vertex->getDimensionUnfixed();
4734  if (vert_dim_unfixed == 0) continue;
4735 
4736  col_nnz.segment(vertex->getVertexIdx(), vert_dim_unfixed).array() += edge->getEqualityDimension() + edge->getInequalityDimension();
4737  }
4738  }
4739 
4740  if (include_finite_bounds)
4741  {
4742  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
4743  {
4744  if (!vertex->hasFixedComponents() || !vertex->hasFiniteBounds()) continue;
4745 
4746  int vert_idx = vertex->getVertexIdx();
4747  int free_idx = 0;
4748  for (int i = 0; i < vertex->getDimension(); ++i)
4749  {
4750  if (vertex->isFixedComponent(i)) continue;
4751 
4752  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
4753  {
4754  col_nnz[vert_idx + free_idx] += 1;
4755  }
4756  ++free_idx;
4757  }
4758  }
4759  }
4760 }
4761 
4763 {
4764  int nnz = 0;
4767  if (include_finite_bounds) nnz += finiteCombinedBoundsDimension();
4768  return nnz;
4769 }
4770 
4773  bool include_finite_bounds)
4774 {
4775  assert(i_row.size() == computeCombinedSparseJacobiansNNZ(include_finite_bounds));
4776  assert(j_col.size() == i_row.size());
4777 
4779 
4780  int nnz_idx = 0;
4781 
4782  int inequality_row_start = getEqualityDimension();
4783  int bounds_row_start = inequality_row_start + getInequalityDimension();
4785  // Iterate equality edges
4786  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4787  {
4788  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4789  {
4790  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4791  // Iterate all free variables
4792  int idx_free = 0;
4793  for (int i = 0; i < vertex->getDimension(); ++i)
4794  {
4795  if (!vertex->isFixedComponent(i))
4796  {
4797  // Iterate inner edge dimension
4798  for (int j = 0; j < edge->getDimension(); ++j)
4799  {
4800  i_row[nnz_idx] = edge->getEdgeIdx() + j;
4801  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
4802  ++nnz_idx;
4803  }
4804  ++idx_free;
4805  }
4806  }
4807  }
4808  }
4809 
4810  // Iterate equality edges
4811  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4812  {
4813  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4814  {
4815  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4816  // Iterate all free variables
4817  int idx_free = 0;
4818  for (int i = 0; i < vertex->getDimension(); ++i)
4819  {
4820  if (!vertex->isFixedComponent(i))
4821  {
4822  // Iterate inner edge dimension
4823  for (int j = 0; j < edge->getDimension(); ++j)
4824  {
4825  i_row[nnz_idx] = inequality_row_start + edge->getEdgeIdx() + j;
4826  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
4827  ++nnz_idx;
4828  }
4829  ++idx_free;
4830  }
4831  }
4832  }
4833  }
4834 
4835  // Iterate mixed edges
4836  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4837  {
4838  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4839  {
4840  const VertexInterface* vertex = edge->getVertexRaw(vert_idx);
4841 
4842  // we need to do the following in the same order as in computeSparseJacobianTwoSideBoundedLinearFormValues()
4843  // Iterate all free variables
4844  int idx_free = 0;
4845  for (int i = 0; i < vertex->getDimension(); ++i)
4846  {
4847  if (!vertex->isFixedComponent(i))
4848  {
4849  for (int j = 0; j < edge->getEqualityDimension(); ++j)
4850  {
4851  i_row[nnz_idx] = edge->getEdgeEqualityIdx() + j;
4852  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
4853  ++nnz_idx;
4854  }
4855  ++idx_free;
4856  }
4857  }
4858 
4859  // Iterate all free variables
4860  idx_free = 0;
4861  for (int i = 0; i < vertex->getDimension(); ++i)
4862  {
4863  if (!vertex->isFixedComponent(i))
4864  {
4865  for (int j = 0; j < edge->getInequalityDimension(); ++j)
4866  {
4867  i_row[nnz_idx] = inequality_row_start + edge->getEdgeInequalityIdx() + j;
4868  j_col[nnz_idx] = vertex->getVertexIdx() + idx_free;
4869  ++nnz_idx;
4870  }
4871  ++idx_free;
4872  }
4873  }
4874  }
4875  }
4876 
4877  if (include_finite_bounds)
4878  {
4879  int row_idx = bounds_row_start;
4880  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
4881  {
4882  int vert_idx = vertex->getVertexIdx();
4883  int free_idx = 0;
4884  for (int i = 0; i < vertex->getDimension(); ++i)
4885  {
4886  if (vertex->isFixedComponent(i)) continue;
4887 
4888  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
4889  {
4890  i_row[nnz_idx] = row_idx;
4891  j_col[nnz_idx] = vert_idx + free_idx;
4892 
4893  ++row_idx;
4894  ++nnz_idx;
4895  }
4896  ++free_idx;
4897  }
4898  }
4899  }
4900 
4901  assert(nnz_idx == i_row.size());
4902 }
4903 
4905  bool include_finite_bounds)
4906 {
4907  assert(values.size() == computeSparseJacobianTwoSideBoundedLinearFormNNZ(include_finite_bounds));
4908 
4909  int nnz_idx = 0;
4910 
4912 
4913  // Iterate equality edges
4914  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
4915  {
4916  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4917  {
4918  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
4919  if (vert_dim_unfixed == 0) continue;
4920 
4921  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
4922  edge->computeJacobian(vert_idx, block_jacobian);
4923  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
4924  }
4925  }
4927  // Iterate inequality edges
4928  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
4929  {
4930  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4931  {
4932  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
4933  if (vert_dim_unfixed == 0) continue;
4934 
4935  Eigen::Map<Eigen::MatrixXd> block_jacobian(values.data() + nnz_idx, edge->getDimension(), vert_dim_unfixed);
4936  edge->computeJacobian(vert_idx, block_jacobian);
4937  nnz_idx += block_jacobian.rows() * block_jacobian.cols();
4938  }
4939  }
4940 
4941  // Iterate mixed edges
4942  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
4943  {
4944  if (edge->getEqualityDimension() == 0 && edge->getInequalityDimension() == 0) continue;
4945  // TODO(roesmann) implement the other cases as well
4946 
4947  for (int vert_idx = 0; vert_idx < edge->getNumVertices(); ++vert_idx)
4948  {
4949  int vert_dim_unfixed = edge->getVertexRaw(vert_idx)->getDimensionUnfixed();
4950  if (vert_dim_unfixed == 0) continue;
4951 
4952  Eigen::Map<Eigen::MatrixXd> block_eq(values.data() + nnz_idx, edge->getEqualityDimension(), vert_dim_unfixed);
4953  nnz_idx += block_eq.rows() * block_eq.cols();
4954  Eigen::Map<Eigen::MatrixXd> block_ineq(values.data() + nnz_idx, edge->getInequalityDimension(), vert_dim_unfixed);
4955  nnz_idx += block_ineq.rows() * block_ineq.cols();
4956 
4957  edge->computeConstraintJacobians(vert_idx, block_eq, block_ineq);
4958  }
4959  }
4960 
4961  if (include_finite_bounds)
4962  {
4963  values.tail(finiteCombinedBoundsDimension()).setOnes();
4964  }
4965 
4966  assert(nnz_idx + finiteCombinedBoundsDimension() - 1 == values.size());
4967 }
4968 
4970  Eigen::SparseMatrix<double, Eigen::ColMajor, long long>& H, const double* multipliers_eq, const double* multipliers_ineq,
4971  Eigen::SparseMatrix<double, Eigen::ColMajor, long long>& A, bool include_finite_bounds, const Eigen::VectorXi* col_nnz_H,
4972  const Eigen::VectorXi* col_nnz_A, bool upper_part_only_H)
4973 {
4974  H.setZero();
4975  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
4976  if (col_nnz_H) H.reserve(*col_nnz_H);
4977 
4978  A.setZero(); // we need to reset since we call insert below
4979  if (col_nnz_A) A.reserve(*col_nnz_A);
4980 
4981  int inequality_row_start = getEqualityDimension();
4982  int bounds_row_start = inequality_row_start + getInequalityDimension();
4983 
4985 
4986  // Iterate plain objective edges
4987  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
4988  {
4989  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
4990  {
4991  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
4992 
4993  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
4994  if (vert_i_dim_unfixed == 0) continue;
4995 
4996  // compute jacobian to speed up hessian computation
4997  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
4998  edge->computeJacobian(row_i, block_jacobian1);
4999 
5000  if (edge->isLinear()) continue;
5001 
5002  int col_start = upper_part_only_H ? row_i : 0;
5003  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5004  {
5005  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5006 
5007  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5008  if (vert_j_dim_unfixed == 0) continue;
5009 
5010  // warning: we require the hessian to be initialized with zeros
5011  // and that edge->computeHessian only adds (+=) values!!
5012  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5013  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian);
5014 
5015  // insert values
5016  if (upper_part_only_H && vertex1 == vertex2)
5017  {
5018  // block on the main diagonal, so use upper part only
5019  for (int i = 0; i < block_hessian.rows(); ++i)
5020  {
5021  for (int j = i; j < block_hessian.cols(); ++j)
5022  {
5023  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5024  }
5025  }
5026  }
5027  else
5028  {
5029  for (int i = 0; i < block_hessian.rows(); ++i)
5030  {
5031  for (int j = 0; j < block_hessian.cols(); ++j)
5032  {
5033  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5034  }
5035  }
5036  }
5037  }
5038  }
5039  }
5040 
5041  // Iterate lsq objective edges
5042  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
5043  {
5044  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
5045 
5046  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5047  {
5048  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5049 
5050  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5051  if (vert_i_dim_unfixed == 0) continue;
5052 
5053  // compute jacobian to speed up hessian computation
5054  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5055  edge->computeJacobian(row_i, block_jacobian1);
5056 
5057  if (edge->isLinear()) continue;
5058 
5059  int col_start = upper_part_only_H ? row_i : 0;
5060  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5061  {
5062  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5063 
5064  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5065  if (vert_j_dim_unfixed == 0) continue;
5066 
5067  // we need the second jacobian for applying the chain rule
5068  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
5069  edge->computeJacobian(col_j, block_jacobian2);
5070 
5071  // approximate H = 2*J^T*J
5072  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5073  block_hessian = 2.0 * block_jacobian1.transpose() * block_jacobian2;
5074 
5075  // insert values
5076  if (upper_part_only_H && vertex1 == vertex2)
5077  {
5078  // block on the main diagonal, so use upper part only
5079  for (int i = 0; i < block_hessian.rows(); ++i)
5080  {
5081  for (int j = i; j < block_hessian.cols(); ++j)
5082  {
5083  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5084  }
5085  }
5086  }
5087  else
5088  {
5089  for (int i = 0; i < block_hessian.rows(); ++i)
5090  {
5091  for (int j = 0; j < block_hessian.cols(); ++j)
5092  {
5093  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5094  }
5095  }
5096  }
5097  }
5098  }
5099  }
5100 
5101  // Iterate equality edges
5102  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
5103  {
5104  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeIdx() : nullptr;
5105 
5106  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5107  {
5108  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5109 
5110  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5111  if (vert_i_dim_unfixed == 0) continue;
5112 
5113  // compute jacobian to speed up hessian computation
5114  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5115  edge->computeJacobian(row_i, block_jacobian1);
5116 
5117  // Now insert values to Jacobian matrix A
5118  for (int i = 0; i < block_jacobian1.rows(); ++i)
5119  {
5120  for (int j = 0; j < block_jacobian1.cols(); ++j)
5121  {
5122  A.insert(edge->getEdgeIdx() + i, vertex1->getVertexIdx() + j) = block_jacobian1.coeffRef(i, j);
5123  }
5124  }
5125 
5126  if (edge->isLinear()) continue;
5127 
5128  int col_start = upper_part_only_H ? row_i : 0;
5129  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5130  {
5131  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5132 
5133  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5134  if (vert_j_dim_unfixed == 0) continue;
5135 
5136  // warning: we require the hessian to be initialized with zeros
5137  // and that edge->computeHessian only adds (+=) values!!
5138  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5139  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian, mult_eq_part);
5140 
5141  // insert values
5142  if (upper_part_only_H && vertex1 == vertex2)
5143  {
5144  // block on the main diagonal, so use upper part only
5145  for (int i = 0; i < block_hessian.rows(); ++i)
5146  {
5147  for (int j = i; j < block_hessian.cols(); ++j)
5148  {
5149  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5150  }
5151  }
5152  }
5153  else
5154  {
5155  for (int i = 0; i < block_hessian.rows(); ++i)
5156  {
5157  for (int j = 0; j < block_hessian.cols(); ++j)
5158  {
5159  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5160  }
5161  }
5162  }
5163  }
5164  }
5165  }
5166 
5167  // Iterate inequality edges
5168  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
5169  {
5170  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeIdx() : nullptr;
5171  int A_edge_idx = inequality_row_start + edge->getEdgeIdx();
5172 
5173  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5174  {
5175  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5176 
5177  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5178  if (vert_i_dim_unfixed == 0) continue;
5179 
5180  // compute jacobian to speed up hessian computation
5181  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5182  edge->computeJacobian(row_i, block_jacobian1);
5183 
5184  // Now insert values to Jacobian A
5185  for (int i = 0; i < block_jacobian1.rows(); ++i)
5186  {
5187  for (int j = 0; j < block_jacobian1.cols(); ++j)
5188  {
5189  A.insert(A_edge_idx + i, vertex1->getVertexIdx() + j) = block_jacobian1.coeffRef(i, j);
5190  }
5191  }
5192 
5193  if (edge->isLinear()) continue;
5194 
5195  int col_start = upper_part_only_H ? row_i : 0;
5196  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5197  {
5198  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5199 
5200  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5201  if (vert_j_dim_unfixed == 0) continue;
5202 
5203  // warning: we require the hessian to be initialized with zeros
5204  // and that edge->computeHessian only adds (+=) values!!
5205  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5206  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian, mult_ineq_part);
5207 
5208  // insert values
5209  if (upper_part_only_H && vertex1 == vertex2)
5210  {
5211  // block on the main diagonal, so use upper part only
5212  for (int i = 0; i < block_hessian.rows(); ++i)
5213  {
5214  for (int j = i; j < block_hessian.cols(); ++j)
5215  {
5216  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5217  }
5218  }
5219  }
5220  else
5221  {
5222  for (int i = 0; i < block_hessian.rows(); ++i)
5223  {
5224  for (int j = 0; j < block_hessian.cols(); ++j)
5225  {
5226  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5227  }
5228  }
5229  }
5230  }
5231  }
5232  }
5233 
5234  // Iterate mixed edges
5235  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
5236  {
5237  // if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
5238 
5239  const double* mult_eq_part = multipliers_eq ? multipliers_eq + edge->getEdgeEqualityIdx() : nullptr;
5240  const double* mult_ineq_part = multipliers_ineq ? multipliers_ineq + edge->getEdgeInequalityIdx() : nullptr;
5241 
5242  int A_edge_eq_idx = edge->getEdgeEqualityIdx();
5243  int A_edge_ineq_idx = inequality_row_start + edge->getEdgeInequalityIdx();
5244 
5245  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5246  {
5247  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5248 
5249  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5250  if (vert_i_dim_unfixed == 0) continue;
5251 
5252  // compute jacobian to speed up hessian computation
5253  Eigen::MatrixXd obj_block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
5254  Eigen::MatrixXd eq_block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
5255  Eigen::MatrixXd ineq_block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
5256  edge->computeJacobians(row_i, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1);
5257 
5258  // Now insert values to Jacobian A
5259  for (int i = 0; i < eq_block_jacobian1.rows(); ++i)
5260  {
5261  for (int j = 0; j < eq_block_jacobian1.cols(); ++j)
5262  {
5263  A.insert(A_edge_eq_idx + i, vertex1->getVertexIdx() + j) = eq_block_jacobian1.coeffRef(i, j);
5264  }
5265  }
5266  for (int i = 0; i < ineq_block_jacobian1.rows(); ++i)
5267  {
5268  for (int j = 0; j < ineq_block_jacobian1.cols(); ++j)
5269  {
5270  A.insert(A_edge_ineq_idx + i, vertex1->getVertexIdx() + j) = ineq_block_jacobian1.coeffRef(i, j);
5271  }
5272  }
5273 
5274  int col_start = upper_part_only_H ? row_i : 0;
5275  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5276  {
5277  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5278 
5279  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5280  if (vert_j_dim_unfixed == 0) continue;
5281 
5282  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5283  block_hessian.setZero();
5284 
5285  if (edge->isObjectiveLeastSquaresForm())
5286  {
5287  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
5288  {
5289  // we need the second jacobian for applying the chain rule
5290  Eigen::MatrixXd obj_block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
5291  edge->computeObjectiveJacobian(col_j, obj_block_jacobian2);
5292 
5293  // approximate H = 2*J^T*J
5294  block_hessian += 2.0 * obj_block_jacobian1.transpose() * obj_block_jacobian2;
5295  }
5296 
5297  if (!edge->isEqualityLinear() && !edge->isInequalityLinear())
5298  {
5299  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, block_hessian, block_hessian,
5300  mult_eq_part, mult_ineq_part);
5301  }
5302  else
5303  {
5304  if (!edge->isEqualityLinear())
5305  {
5306  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, block_hessian, mult_eq_part);
5307  }
5308  else if (!edge->isInequalityLinear())
5309  {
5310  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, block_hessian, mult_ineq_part);
5311  }
5312  }
5313  }
5314  else // TODO(roesmann): check all reasonable cases:
5315  {
5316  if (!edge->isObjectiveLinear() && !edge->isEqualityLinear() && !edge->isInequalityLinear() &&
5317  edge->getObjectiveDimension() != 0 && edge->getEqualityDimension() != 0 && edge->getInequalityDimension() != 0)
5318  {
5319  edge->computeHessiansInc(row_i, col_j, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1, block_hessian,
5320  block_hessian, block_hessian, mult_eq_part, mult_ineq_part, 1.0);
5321  }
5322  else if (!edge->isEqualityLinear() && !edge->isInequalityLinear() && edge->getEqualityDimension() != 0 &&
5323  edge->getInequalityDimension() != 0)
5324  {
5325  edge->computeConstraintHessiansInc(row_i, col_j, eq_block_jacobian1, ineq_block_jacobian1, block_hessian, block_hessian,
5326  mult_eq_part, mult_ineq_part);
5327  }
5328  else
5329  {
5330  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
5331  {
5332  edge->computeObjectiveHessianInc(row_i, col_j, obj_block_jacobian1, block_hessian, nullptr, 1.0);
5333  }
5334 
5335  if (!edge->isEqualityLinear() && edge->getEqualityDimension() != 0)
5336  {
5337  edge->computeEqualityHessianInc(row_i, col_j, eq_block_jacobian1, block_hessian, mult_eq_part);
5338  }
5339  else if (!edge->isInequalityLinear() && edge->getInequalityDimension() != 0)
5340  {
5341  edge->computeInequalityHessianInc(row_i, col_j, ineq_block_jacobian1, block_hessian, mult_ineq_part);
5342  }
5343  }
5344  }
5345  // insert values
5346  if (upper_part_only_H && vertex1 == vertex2)
5347  {
5348  // block on the main diagonal, so use upper part only
5349  for (int i = 0; i < block_hessian.rows(); ++i)
5350  {
5351  for (int j = i; j < block_hessian.cols(); ++j)
5352  {
5353  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5354  }
5355  }
5356  }
5357  else
5358  {
5359  for (int i = 0; i < block_hessian.rows(); ++i)
5360  {
5361  for (int j = 0; j < block_hessian.cols(); ++j)
5362  {
5363  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5364  }
5365  }
5366  }
5367  }
5368  }
5369  }
5370 
5371  if (include_finite_bounds)
5372  {
5373  int row_idx = bounds_row_start;
5374  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
5375  {
5376  int vert_idx = vertex->getVertexIdx();
5377  int free_idx = 0;
5378  for (int i = 0; i < vertex->getDimension(); ++i)
5379  {
5380  if (vertex->isFixedComponent(i)) continue;
5381 
5382  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
5383  {
5384  A.insert(row_idx, vert_idx + free_idx) = 1;
5385  ++row_idx;
5386  }
5387  ++free_idx;
5388  }
5389  }
5390  }
5391 }
5392 
5395  bool include_finite_bounds, const Eigen::VectorXi* col_nnz_H, const Eigen::VectorXi* col_nnz_A, bool upper_part_only_H)
5396 {
5397  H.setZero();
5398  // TODO(roesmann): what is most efficient, if we just want to udpate the Hessian?
5399  if (col_nnz_H) H.reserve(*col_nnz_H);
5400 
5401  A.setZero(); // we need to reset since we call insert below
5402  if (col_nnz_A) A.reserve(*col_nnz_A);
5403 
5404  int inequality_row_start = getEqualityDimension();
5405  int bounds_row_start = inequality_row_start + getInequalityDimension();
5406 
5408 
5409  // Iterate plain objective edges
5410  for (BaseEdge::Ptr& edge : edges->getObjectiveEdgesRef())
5411  {
5412  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5413  {
5414  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5416  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5417  if (vert_i_dim_unfixed == 0) continue;
5418 
5419  // compute jacobian to speed up hessian computation
5420  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5421  edge->computeJacobian(row_i, block_jacobian1);
5422 
5423  if (edge->isLinear()) continue;
5424 
5425  int col_start = upper_part_only_H ? row_i : 0;
5426  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5427  {
5428  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5429 
5430  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5431  if (vert_j_dim_unfixed == 0) continue;
5432 
5433  // warning: we require the hessian to be initialized with zeros
5434  // and that edge->computeHessian only adds (+=) values!!
5435  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5436  edge->computeHessian(row_i, col_j, block_jacobian1, block_hessian);
5437 
5438  // insert values
5439  if (upper_part_only_H && vertex1 == vertex2)
5440  {
5441  // block on the main diagonal, so use upper part only
5442  for (int i = 0; i < block_hessian.rows(); ++i)
5443  {
5444  for (int j = i; j < block_hessian.cols(); ++j)
5445  {
5446  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5447  }
5448  }
5449  }
5450  else
5451  {
5452  for (int i = 0; i < block_hessian.rows(); ++i)
5453  {
5454  for (int j = 0; j < block_hessian.cols(); ++j)
5455  {
5456  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5457  }
5458  }
5459  }
5460  }
5461  }
5462  }
5463 
5464  // Iterate lsq objective edges
5465  for (BaseEdge::Ptr& edge : edges->getLsqObjectiveEdgesRef())
5466  {
5467  // if (edge->isLinear()) continue; // we must not check this one, since Linear-squared is not linear ;-)
5468 
5469  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5470  {
5471  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5472 
5473  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5474  if (vert_i_dim_unfixed == 0) continue;
5475 
5476  // compute jacobian to speed up hessian computation
5477  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5478  edge->computeJacobian(row_i, block_jacobian1);
5479 
5480  if (edge->isLinear()) continue;
5481 
5482  int col_start = upper_part_only_H ? row_i : 0;
5483  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5484  {
5485  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5486 
5487  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5488  if (vert_j_dim_unfixed == 0) continue;
5489 
5490  // we need the second jacobian for applying the chain rule
5491  Eigen::MatrixXd block_jacobian2(edge->getDimension(), vertex2->getDimensionUnfixed());
5492  edge->computeJacobian(col_j, block_jacobian2);
5493 
5494  // approximate H = 2*J^T*J
5495  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5496  block_hessian = 2.0 * block_jacobian1.transpose() * block_jacobian2;
5497 
5498  // insert values
5499  if (upper_part_only_H && vertex1 == vertex2)
5500  {
5501  // block on the main diagonal, so use upper part only
5502  for (int i = 0; i < block_hessian.rows(); ++i)
5503  {
5504  for (int j = i; j < block_hessian.cols(); ++j)
5505  {
5506  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5507  }
5508  }
5509  }
5510  else
5511  {
5512  for (int i = 0; i < block_hessian.rows(); ++i)
5513  {
5514  for (int j = 0; j < block_hessian.cols(); ++j)
5515  {
5516  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5517  }
5518  }
5519  }
5520  }
5521  }
5522  }
5523 
5524  // Iterate equality edges
5525  for (BaseEdge::Ptr& edge : edges->getEqualityEdgesRef())
5526  {
5527  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5528  {
5529  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5530 
5531  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5532  if (vert_i_dim_unfixed == 0) continue;
5533 
5534  // compute jacobian to speed up hessian computation
5535  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5536  edge->computeJacobian(row_i, block_jacobian1);
5537 
5538  // Now insert values to Jacobian matrix A
5539  for (int i = 0; i < block_jacobian1.rows(); ++i)
5540  {
5541  for (int j = 0; j < block_jacobian1.cols(); ++j)
5542  {
5543  A.insert(edge->getEdgeIdx() + i, vertex1->getVertexIdx() + j) = block_jacobian1.coeffRef(i, j);
5544  }
5545  }
5546  }
5547  }
5548 
5549  // Iterate inequality edges
5550  for (BaseEdge::Ptr& edge : edges->getInequalityEdgesRef())
5551  {
5552  int A_edge_idx = inequality_row_start + edge->getEdgeIdx();
5553 
5554  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5555  {
5556  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5557 
5558  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5559  if (vert_i_dim_unfixed == 0) continue;
5560 
5561  // compute jacobian to speed up hessian computation
5562  Eigen::MatrixXd block_jacobian1(edge->getDimension(), vertex1->getDimensionUnfixed());
5563  edge->computeJacobian(row_i, block_jacobian1);
5564 
5565  // Now insert values to Jacobian A
5566  for (int i = 0; i < block_jacobian1.rows(); ++i)
5567  {
5568  for (int j = 0; j < block_jacobian1.cols(); ++j)
5569  {
5570  A.insert(A_edge_idx + i, vertex1->getVertexIdx() + j) = block_jacobian1.coeffRef(i, j);
5571  }
5572  }
5573  }
5574  }
5575 
5576  // Iterate mixed edges
5577  for (BaseMixedEdge::Ptr& edge : edges->getMixedEdgesRef())
5578  {
5579  // if (edge->getObjectiveDimension() == 0 || edge->isObjectiveLinear()) continue;
5580  int A_edge_eq_idx = edge->getEdgeEqualityIdx();
5581  int A_edge_ineq_idx = inequality_row_start + edge->getEdgeInequalityIdx();
5582 
5583  for (int row_i = 0; row_i < edge->getNumVertices(); ++row_i)
5584  {
5585  const VertexInterface* vertex1 = edge->getVertexRaw(row_i);
5586 
5587  int vert_i_dim_unfixed = vertex1->getDimensionUnfixed();
5588  if (vert_i_dim_unfixed == 0) continue;
5589 
5590  // compute jacobian to speed up hessian computation
5591  Eigen::MatrixXd obj_block_jacobian1(edge->getObjectiveDimension(), vertex1->getDimensionUnfixed());
5592  Eigen::MatrixXd eq_block_jacobian1(edge->getEqualityDimension(), vertex1->getDimensionUnfixed());
5593  Eigen::MatrixXd ineq_block_jacobian1(edge->getInequalityDimension(), vertex1->getDimensionUnfixed());
5594  edge->computeJacobians(row_i, obj_block_jacobian1, eq_block_jacobian1, ineq_block_jacobian1);
5595 
5596  // Now insert values to Jacobian A
5597  for (int i = 0; i < eq_block_jacobian1.rows(); ++i)
5598  {
5599  for (int j = 0; j < eq_block_jacobian1.cols(); ++j)
5600  {
5601  A.insert(A_edge_eq_idx + i, vertex1->getVertexIdx() + j) = eq_block_jacobian1.coeffRef(i, j);
5602  }
5603  }
5604  for (int i = 0; i < ineq_block_jacobian1.rows(); ++i)
5605  {
5606  for (int j = 0; j < ineq_block_jacobian1.cols(); ++j)
5607  {
5608  A.insert(A_edge_ineq_idx + i, vertex1->getVertexIdx() + j) = ineq_block_jacobian1.coeffRef(i, j);
5609  }
5610  }
5611 
5612  if (edge->isObjectiveLinear() || edge->getObjectiveDimension() == 0) continue;
5613 
5614  int col_start = upper_part_only_H ? row_i : 0;
5615  for (int col_j = col_start; col_j < edge->getNumVertices(); ++col_j) // hessian is symmetric
5616  {
5617  const VertexInterface* vertex2 = edge->getVertexRaw(col_j);
5618 
5619  int vert_j_dim_unfixed = vertex2->getDimensionUnfixed();
5620  if (vert_j_dim_unfixed == 0) continue;
5621 
5622  Eigen::MatrixXd block_hessian(vert_i_dim_unfixed, vert_j_dim_unfixed);
5623  block_hessian.setZero();
5624 
5625  if (edge->isObjectiveLeastSquaresForm())
5626  {
5627  if (!edge->isObjectiveLinear() && edge->getObjectiveDimension() != 0)
5628  {
5629  // we need the second jacobian for applying the chain rule
5630  Eigen::MatrixXd obj_block_jacobian2(edge->getObjectiveDimension(), vertex2->getDimensionUnfixed());
5631  edge->computeObjectiveJacobian(col_j, obj_block_jacobian2);
5632 
5633  // approximate H = 2*J^T*J
5634  block_hessian += 2.0 * obj_block_jacobian1.transpose() * obj_block_jacobian2;
5635  }
5636  }
5637  else // TODO(roesmann): check all reasonable cases:
5638  {
5639  edge->computeObjectiveHessianInc(row_i, col_j, obj_block_jacobian1, block_hessian, nullptr, 1.0);
5640  }
5641 
5642  // insert values
5643  if (upper_part_only_H && vertex1 == vertex2)
5644  {
5645  // block on the main diagonal, so use upper part only
5646  for (int i = 0; i < block_hessian.rows(); ++i)
5647  {
5648  for (int j = i; j < block_hessian.cols(); ++j)
5649  {
5650  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5651  }
5652  }
5653  }
5654  else
5655  {
5656  for (int i = 0; i < block_hessian.rows(); ++i)
5657  {
5658  for (int j = 0; j < block_hessian.cols(); ++j)
5659  {
5660  H.coeffRef(vertex1->getVertexIdx() + i, vertex2->getVertexIdx() + j) += block_hessian.coeffRef(i, j);
5661  }
5662  }
5663  }
5664  }
5665  }
5666  }
5667 
5668  if (include_finite_bounds)
5669  {
5670  int row_idx = bounds_row_start;
5671  for (const VertexInterface* vertex : _graph.getVertexSet()->getActiveVertices())
5672  {
5673  int vert_idx = vertex->getVertexIdx();
5674  int free_idx = 0;
5675  for (int i = 0; i < vertex->getDimension(); ++i)
5676  {
5677  if (vertex->isFixedComponent(i)) continue;
5678 
5679  if (vertex->hasFiniteLowerBound(i) || vertex->hasFiniteUpperBound(i))
5680  {
5681  A.insert(row_idx, vert_idx + free_idx) = 1;
5682  ++row_idx;
5683  }
5684  ++free_idx;
5685  }
5686  }
5687  }
5688 }
5689 
5690 } // namespace corbo
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianObjectiveNNZ
int computeSparseHessianObjectiveNNZ(bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2109
corbo::VertexInterface::getDimension
virtual int getDimension() const =0
Return number of elements/values/components stored in this vertex.
corbo::OptimizationEdgeSet::Ptr
std::shared_ptr< OptimizationEdgeSet > Ptr
Definition: edge_set.h:99
Eigen::SparseMatrix< double >
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianInequalitiesStructure
void computeSparseHessianInequalitiesStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3268
corbo::HyperGraphOptimizationProblemEdgeBased::computeGradientNonLsqObjective
void computeGradientNonLsqObjective(Eigen::Ref< Eigen::VectorXd > gradient) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:126
corbo::HyperGraphOptimizationProblemEdgeBased::computeCombinedSparseJacobiansNNZ
int computeCombinedSparseJacobiansNNZ(bool objective_lsq=true, bool equality=true, bool inequality=true) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1203
corbo::VertexInterface::hasFiniteUpperBound
virtual bool hasFiniteUpperBound(int idx) const =0
Check if finite upper bound for a single component is provided.
corbo::HyperGraph::getVertexSet
VertexSetInterface::Ptr getVertexSet() const
Definition: hyper_graph.h:118
corbo::BaseHyperGraphOptimizationProblem::getObjectiveDimension
int getObjectiveDimension() override
Get dimension of the objective (should be zero or one, includes Lsq objectives if present)
Definition: hyper_graph_optimization_problem_base.h:154
corbo
Definition: communication/include/corbo-communication/utilities.h:37
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianObjectiveStructure
void computeSparseHessianObjectiveStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2214
PRINT_DEBUG_COND_ONCE
#define PRINT_DEBUG_COND_ONCE(cond, msg)
Print msg-stream only if cond == true, only once and only if project is compiled in Debug-mode.
Definition: console.h:77
corbo::HyperGraphOptimizationProblemEdgeBased::computeCombinedSparseJacobiansStructure
void computeCombinedSparseJacobiansStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col, bool objective_lsq=true, bool equality=true, bool inequality=true) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1211
corbo::BaseHyperGraphOptimizationProblem::_graph
HyperGraph _graph
Definition: hyper_graph_optimization_problem_base.h:299
corbo::BaseHyperGraphOptimizationProblem::getEqualityDimension
int getEqualityDimension() override
Total dimension of equality constraints.
Definition: hyper_graph_optimization_problem_base.h:160
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianLsqObjectiveStructure
void computeSparseJacobianLsqObjectiveStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:245
corbo::HyperGraph::hasEdgeSet
bool hasEdgeSet() const
Definition: hyper_graph.h:114
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianObjectiveNNZperCol
void computeSparseHessianObjectiveNNZperCol(Eigen::Ref< Eigen::VectorXi > col_nnz, bool upper_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2770
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearForm
void computeSparseJacobianTwoSideBoundedLinearForm(Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &A, bool include_finite_bounds, const Eigen::VectorXi *col_nnz=nullptr) override
Compute the jacobian A for the linear form lbA <= A x <= lbB.
Definition: hyper_graph_optimization_problem_edge_based.cpp:4593
Eigen::Upper
@ Upper
Definition: Constants.h:206
Eigen::Array
General-purpose arrays with easy API for coefficient-wise operations.
Definition: Array.h:45
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianEqualitiesValues
void computeSparseHessianEqualitiesValues(Eigen::Ref< Eigen::VectorXd > values, const double *multipliers=nullptr, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3074
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianLsqObjectiveNNZ
int computeSparseJacobianLsqObjectiveNNZ() override
Definition: hyper_graph_optimization_problem_edge_based.cpp:215
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormAndHessianLagrangian
void computeSparseJacobianTwoSideBoundedLinearFormAndHessianLagrangian(Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &H, const double *multipliers_eq, const double *multipliers_ineq, Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &A, bool include_finite_bounds, const Eigen::VectorXi *col_nnz_H, const Eigen::VectorXi *col_nnz_A, bool upper_part_only_H) override
Compute the Jacobian and Hessian of the lagrangian.
Definition: hyper_graph_optimization_problem_edge_based.cpp:4991
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseJacobians
void computeDenseJacobians(Eigen::Ref< Eigen::VectorXd > gradient_non_lsq_obj, Eigen::Ref< Eigen::MatrixXd > jacobian_lsq_obj, Eigen::Ref< Eigen::MatrixXd > jacobian_eq, Eigen::Ref< Eigen::MatrixXd > jacobian_ineq, const double *multipliers_lsq_obj=nullptr, const double *multipliers_eq=nullptr, const double *multipliers_ineq=nullptr, bool active_ineq=false, double active_ineq_weight=1.0) override
Compute the objective and constraint Jacobians at once.
Definition: hyper_graph_optimization_problem_edge_based.cpp:863
corbo::BaseHyperGraphOptimizationProblem::getParameterDimension
int getParameterDimension() override
Effictive dimension of the optimization parameter set (changeable, non-fixed part)
Definition: hyper_graph_optimization_problem_base.h:232
corbo::BaseHyperGraphOptimizationProblem::finiteCombinedBoundsDimension
int finiteCombinedBoundsDimension() override
Dimension of the set of finite bounds (combined such that each ub and lb component define a single di...
Definition: hyper_graph_optimization_problem_base.cpp:271
corbo::BaseHyperGraphOptimizationProblem::precomputeGraphQuantities
virtual void precomputeGraphQuantities()
Definition: hyper_graph_optimization_problem_base.cpp:79
corbo::HyperGraphOptimizationProblemEdgeBased::computeGradientObjective
void computeGradientObjective(Eigen::Ref< Eigen::VectorXd > gradient) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:53
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianEqualitiesStructure
void computeSparseJacobianEqualitiesStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:420
corbo::VertexInterface
Generic interface class for vertices.
Definition: vertex_interface.h:75
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianInequalitiesNNZ
int computeSparseJacobianInequalitiesNNZ() override
Definition: hyper_graph_optimization_problem_edge_based.cpp:566
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianObjectiveValues
void computeSparseHessianObjectiveValues(Eigen::Ref< Eigen::VectorXd > values, double multiplier=1.0, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2372
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormNNZPerColumn
void computeSparseJacobianTwoSideBoundedLinearFormNNZPerColumn(Eigen::Ref< Eigen::VectorXi > col_nnz, bool include_finite_bounds) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:4713
corbo::VertexInterface::getDimensionUnfixed
virtual int getDimensionUnfixed() const =0
Return number of unfixed elements (unfixed elements are skipped as parameters in the Hessian and Jaco...
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseJacobianActiveInequalities
void computeDenseJacobianActiveInequalities(Eigen::Ref< Eigen::MatrixXd > jacobian, double weight=1.0) override
Compute the Jacobian Jc(x) with non-zeros for active constraints c(x)>= 0 and zeros for inactive ones...
Definition: hyper_graph_optimization_problem_edge_based.cpp:697
Eigen::PlainObjectBase::setConstant
EIGEN_DEVICE_FUNC Derived & setConstant(Index size, const Scalar &val)
Definition: CwiseNullaryOp.h:341
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianInequalitiesValues
void computeSparseJacobianInequalitiesValues(Eigen::Ref< Eigen::VectorXd > values, const double *multipliers=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:658
corbo::BaseMixedEdge::Ptr
std::shared_ptr< BaseMixedEdge > Ptr
Definition: edge_interface.h:244
corbo::VertexInterface::hasFiniteBounds
virtual bool hasFiniteBounds() const =0
Check if finite bounds (lower or upper) are provided.
corbo::VertexInterface::getVertexIdx
int getVertexIdx() const
Retrieve current edge index (warning, this value is determined within the related VertexSetInterface ...
Definition: vertex_interface.h:194
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianLagrangianNNZperCol
void computeSparseHessianLagrangianNNZperCol(Eigen::Ref< Eigen::VectorXi > col_nnz, bool upper_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:4400
corbo::HyperGraph::getEdgeSet
OptimizationEdgeSet::Ptr getEdgeSet() const
Definition: hyper_graph.h:117
corbo::BaseHyperGraphOptimizationProblem::getInequalityDimension
int getInequalityDimension() override
Total dimension of general inequality constraints.
Definition: hyper_graph_optimization_problem_base.h:166
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormValues
void computeSparseJacobianTwoSideBoundedLinearFormValues(Eigen::Ref< Eigen::VectorXd > values, bool include_finite_bounds) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:4926
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianInequalitiesNNZ
int computeSparseHessianInequalitiesNNZ(bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3194
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianEqualitiesNNZ
int computeSparseJacobianEqualitiesNNZ() override
Definition: hyper_graph_optimization_problem_edge_based.cpp:390
corbo::BaseHyperGraphOptimizationProblem::getLsqObjectiveDimension
int getLsqObjectiveDimension() override
Total dimension of least-squares objective function terms.
Definition: hyper_graph_optimization_problem_base.h:149
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianLagrangian
void computeSparseHessianLagrangian(Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &H, const double *multipliers_eq, const double *multipliers_ineq, const Eigen::VectorXi *col_nnz=nullptr, bool upper_part_only=false) override
Compute the hessian of the lagrangian L(x) = f(x) + lambda1 * c(x) + lambda2 * ceq(x)
Definition: hyper_graph_optimization_problem_edge_based.cpp:4044
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianEqualitiesNNZ
int computeSparseHessianEqualitiesNNZ(bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2891
hyper_graph_optimization_problem_edge_based.h
corbo::HyperGraphOptimizationProblemEdgeBased::computeCombinedSparseJacobiansValues
void computeCombinedSparseJacobiansValues(Eigen::Ref< Eigen::VectorXd > values, bool objective_lsq=true, bool equality=true, bool inequality=true, const double *multipliers_obj=nullptr, const double *multipliers_eq=nullptr, const double *multipliers_ineq=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1374
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianLsqObjectiveValues
void computeSparseJacobianLsqObjectiveValues(Eigen::Ref< Eigen::VectorXd > values, const double *multipliers=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:307
corbo::BaseEdge::Ptr
std::shared_ptr< BaseEdge > Ptr
Definition: edge_interface.h:134
corbo::HyperGraphOptimizationProblemEdgeBased::computeCombinedSparseJacobian
void computeCombinedSparseJacobian(Eigen::SparseMatrix< double > &jacobian, bool objective_lsq, bool equality, bool inequality, bool finite_combined_bounds, bool active_ineq=false, double weight_eq=1.0, double weight_ineq=1.0, double weight_bounds=1.0, const Eigen::VectorXd *values=nullptr, const Eigen::VectorXi *col_nnz=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1502
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseJacobianEqualities
void computeDenseJacobianEqualities(Eigen::Ref< Eigen::MatrixXd > jacobian, const double *multipliers=nullptr) override
Compute the equality constraint Jacobian Jceq(x) for the current parameter set.
Definition: hyper_graph_optimization_problem_edge_based.cpp:346
corbo::HyperGraphOptimizationProblemEdgeBased::computeGradientObjectiveAndCombinedSparseJacobiansValues
void computeGradientObjectiveAndCombinedSparseJacobiansValues(Eigen::Ref< Eigen::VectorXd > gradient, Eigen::Ref< Eigen::VectorXd > jac_values, bool equality=true, bool inequality=true, const double *multipliers_eq=nullptr, const double *multipliers_ineq=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1778
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormNNZ
int computeSparseJacobianTwoSideBoundedLinearFormNNZ(bool include_finite_bounds) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:4784
Eigen::Lower
@ Lower
Definition: Constants.h:204
Eigen::Map
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:94
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormAndHessianObjective
void computeSparseJacobianTwoSideBoundedLinearFormAndHessianObjective(Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &H, Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &A, bool include_finite_bounds, const Eigen::VectorXi *col_nnz_H, const Eigen::VectorXi *col_nnz_A, bool upper_part_only_H) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:5415
Eigen::SparseMatrix::reserve
void reserve(Index reserveSize)
Definition: SparseMatrix.h:262
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessiansNNZ
void computeSparseHessiansNNZ(int &nnz_obj, int &nnz_eq, int &nnz_ineq, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3497
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianObjectiveLL
void computeSparseHessianObjectiveLL(Eigen::SparseMatrix< double, Eigen::ColMajor, long long > &H, const Eigen::VectorXi *col_nnz=nullptr, bool upper_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2573
corbo::VertexInterface::hasFiniteLowerBound
virtual bool hasFiniteLowerBound(int idx) const =0
Check if finite lower bound for a single component is provided.
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianTwoSideBoundedLinearFormStructure
void computeSparseJacobianTwoSideBoundedLinearFormStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col, bool include_finite_bounds) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:4793
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianActiveInequalitiesValues
void computeSparseJacobianActiveInequalitiesValues(Eigen::Ref< Eigen::VectorXd > values, double weight=1.0) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:776
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:192
corbo::VertexInterface::hasFixedComponents
virtual bool hasFixedComponents() const =0
Check if the vertex has fixed components.
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseJacobianInequalities
void computeDenseJacobianInequalities(Eigen::Ref< Eigen::MatrixXd > jacobian, const double *multipliers=nullptr) override
Compute the inequality constraint Jacobian Jc(x) for the current parameter set.
Definition: hyper_graph_optimization_problem_edge_based.cpp:521
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseJacobianLsqObjective
void computeDenseJacobianLsqObjective(Eigen::Ref< Eigen::MatrixXd > jacobian, const double *multipliers=nullptr) override
Compute the objective Jacobian Jf(x) for the current parameter set.
Definition: hyper_graph_optimization_problem_edge_based.cpp:172
Eigen::SparseMatrix::setZero
void setZero()
Definition: SparseMatrix.h:251
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessiansStructure
void computeSparseHessiansStructure(Eigen::Ref< Eigen::VectorXi > i_row_obj, Eigen::Ref< Eigen::VectorXi > j_col_obj, Eigen::Ref< Eigen::VectorXi > i_row_eq, Eigen::Ref< Eigen::VectorXi > j_col_eq, Eigen::Ref< Eigen::VectorXi > i_row_ineq, Eigen::Ref< Eigen::VectorXi > j_col_ineq, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3505
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianEqualitiesStructure
void computeSparseHessianEqualitiesStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:2965
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobiansValues
void computeSparseJacobiansValues(Eigen::Ref< Eigen::VectorXd > values_obj, Eigen::Ref< Eigen::VectorXd > values_eq, Eigen::Ref< Eigen::VectorXd > values_ineq, const double *multipliers_obj=nullptr, const double *multipliers_eq=nullptr, const double *multipliers_ineq=nullptr, bool active_ineq=false, double active_ineq_weight=1.0) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1039
n
PlainMatrixType mat * n
Definition: eigenvalues.cpp:41
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianEqualitiesValues
void computeSparseJacobianEqualitiesValues(Eigen::Ref< Eigen::VectorXd > values, const double *multipliers=nullptr) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:482
A
MatrixType A(a, *n, *n, *lda)
corbo::HyperGraphOptimizationProblemEdgeBased::computeDenseHessianObjective
void computeDenseHessianObjective(Eigen::Ref< Eigen::MatrixXd > hessian, double multiplier=1.0) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:1960
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessianInequalitiesValues
void computeSparseHessianInequalitiesValues(Eigen::Ref< Eigen::VectorXd > values, const double *multipliers=nullptr, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3377
Eigen::SparseMatrix::coeffRef
Scalar & coeffRef(Index row, Index col)
Definition: SparseMatrix.h:206
corbo::VertexInterface::isFixedComponent
virtual bool isFixedComponent(int idx) const =0
Check if individual components are fixed or unfixed.
corbo::BaseHyperGraphOptimizationProblem::_graph_precomputed
bool _graph_precomputed
Definition: hyper_graph_optimization_problem_base.h:300
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseJacobianInequalitiesStructure
void computeSparseJacobianInequalitiesStructure(Eigen::Ref< Eigen::VectorXi > i_row, Eigen::Ref< Eigen::VectorXi > j_col) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:596
corbo::HyperGraphOptimizationProblemEdgeBased::computeSparseHessiansValues
void computeSparseHessiansValues(Eigen::Ref< Eigen::VectorXd > values_obj, Eigen::Ref< Eigen::VectorXd > values_eq, Eigen::Ref< Eigen::VectorXd > values_ineq, double multiplier_obj=1.0, const double *multipliers_eq=nullptr, const double *multipliers_ineq=nullptr, bool lower_part_only=false) override
Definition: hyper_graph_optimization_problem_edge_based.cpp:3513


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:05:48