gtsam
Loading...
Searching...
No Matches
BatchJacobianFactor.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
18
19#pragma once
20
23#include <gtsam/base/timing.h>
24#include <gtsam/dllexport.h>
30
31#include <Eigen/StdVector>
32#include <algorithm>
33#include <array>
34#include <cassert>
35#include <map>
36#include <stdexcept>
37#include <string>
38#include <tuple>
39#include <type_traits>
40#include <utility>
41#include <vector>
42
43namespace gtsam {
44
45namespace internal {
46class BatchJacobianFactorElimination;
47
50 public:
51 virtual ~SparseNormalAccumulator() = default;
52
54 virtual void addHessianBlock(DenseIndex rowOffset, DenseIndex columnOffset,
55 DenseIndex rows, DenseIndex columns,
56 const double* values) = 0;
57
59 virtual void addRhsBlock(DenseIndex offset, DenseIndex dimension,
60 const double* values) = 0;
61};
62} // namespace internal
63
77class GTSAM_EXPORT BatchJacobianFactorBase : public GaussianFactor,
78 public FlatGaussianFactor {
79 public:
82
84 virtual size_t rows() const = 0;
85
87 virtual const SharedDiagonal& get_model() const = 0;
88
90 virtual JacobianFactor toJacobianFactor() const = 0;
91
92 private:
93 friend class internal::BatchJacobianFactorElimination;
94
96 virtual size_t scatterInto(
97 VerticalBlockMatrix& target, size_t rowOffset,
98 const std::vector<DenseIndex>& targetBlockIndices) const = 0;
99
101 virtual void updateSparseNormal(
102 const std::vector<DenseIndex>& scalarOffsets,
103 internal::SparseNormalAccumulator* accumulator) const = 0;
104
105 public:
107 void print(
108 const std::string& s = "",
109 const KeyFormatter& formatter = DefaultKeyFormatter) const override {
110 toJacobianFactor().print(s, formatter);
111 }
112
114 bool equals(const GaussianFactor& factor, double tol = 1e-9) const override {
115 if (const auto* batch =
116 dynamic_cast<const BatchJacobianFactorBase*>(&factor)) {
117 return toJacobianFactor().equals(batch->toJacobianFactor(), tol);
118 }
119 return toJacobianFactor().equals(factor, tol);
120 }
121
123 double error(const VectorValues& values) const override {
124 return toJacobianFactor().error(values);
125 }
126
128 double deltaError(const VectorValues& values, double* oldError = nullptr,
129 double* newError = nullptr) const override {
130 return toJacobianFactor().deltaError(values, oldError, newError);
131 }
132
134 Matrix augmentedJacobian() const override {
135 return toJacobianFactor().augmentedJacobian();
136 }
137
139 std::pair<Matrix, Vector> jacobian() const override {
140 return toJacobianFactor().jacobian();
141 }
142
144 Matrix augmentedInformation() const override {
145 return toJacobianFactor().augmentedInformation();
146 }
147
149 Matrix information() const override {
150 return toJacobianFactor().information();
151 }
152
154 void hessianDiagonalAdd(VectorValues& diagonal) const override {
155 toJacobianFactor().hessianDiagonalAdd(diagonal);
156 }
157
159 void hessianDiagonal(double* diagonal) const override {
160 toJacobianFactor().hessianDiagonal(diagonal);
161 }
162
164 std::map<Key, Matrix> hessianBlockDiagonal() const override {
165 return toJacobianFactor().hessianBlockDiagonal();
166 }
167
170 return toJacobianFactor().negate();
171 }
172
175 SymmetricBlockMatrix* info) const override {
176 toJacobianFactor().updateHessian(keys, info);
177 }
178
179 private:
181 virtual void updateHessian(const std::vector<DenseIndex>& slotIndices,
182 SymmetricBlockMatrix* info) const = 0;
183
185 virtual void updateHessian(const std::vector<DenseIndex>& slotIndices,
186 SymmetricBlockMatrix* info, DenseIndex beginCol,
187 DenseIndex endCol) const = 0;
188
190 virtual void buildMappedSlots(const std::vector<DenseIndex>& slotIndices,
191 std::vector<DenseIndex>& mappedSlots) const = 0;
192
194 virtual void updateHessianWithMappedSlots(
195 const std::vector<DenseIndex>& mappedSlots,
196 SymmetricBlockMatrix* info) const = 0;
197
199 virtual void updateHessianWithMappedSlots(
200 const std::vector<DenseIndex>& mappedSlots,
201 const std::vector<DenseIndex>& mappedScalarOffsets,
202 SymmetricBlockMatrix* info) const {
203 (void)mappedScalarOffsets;
204 updateHessianWithMappedSlots(mappedSlots, info);
205 }
206
208 virtual void updateFrontalHessianWithMappedSlots(
209 const std::vector<DenseIndex>& mappedSlots, DenseIndex numFrontalBlocks,
210 VerticalBlockMatrix* frontalRows) const = 0;
211
212 public:
223 virtual void multiplyHessianAdd(double alpha,
224 const std::vector<size_t>& scalarOffsets,
225 const double* x, double* y) const override {
226 if (scalarOffsets.size() != size()) {
227 throw std::invalid_argument(
228 "BatchJacobianFactorBase::multiplyHessianAdd: offset count "
229 "mismatch.");
230 }
231 VectorValues valuesX, valuesY;
232 for (size_t position = 0; position < size(); ++position) {
233 const Key key = keys()[position];
234 const DenseIndex dimension =
235 getDim(begin() + static_cast<DenseIndex>(position));
236 valuesX.emplace(key, Eigen::Map<const Vector>(x + scalarOffsets[position],
237 dimension));
238 valuesY.emplace(key, Vector::Zero(dimension));
239 }
240 toJacobianFactor().multiplyHessianAdd(alpha, valuesX, valuesY);
241 for (size_t position = 0; position < size(); ++position) {
242 const Key key = keys()[position];
243 Eigen::Map<Vector> output(
244 y + scalarOffsets[position],
245 getDim(begin() + static_cast<DenseIndex>(position)));
246 output += valuesY.at(key);
247 }
248 }
249
257 virtual void gradientAtZeroAdd(const std::vector<size_t>& scalarOffsets,
258 double* gradient) const override {
259 if (scalarOffsets.size() != size()) {
260 throw std::invalid_argument(
261 "BatchJacobianFactorBase::gradientAtZeroAdd: offset count "
262 "mismatch.");
263 }
264 const VectorValues factorGradient = toJacobianFactor().gradientAtZero();
265 for (size_t position = 0; position < size(); ++position) {
266 Eigen::Map<Vector> output(
267 gradient + scalarOffsets[position],
268 getDim(begin() + static_cast<DenseIndex>(position)));
269 output += factorGradient.at(keys()[position]);
270 }
271 }
272
281 const std::vector<size_t>& blockSlots,
282 std::vector<Matrix>* diagonalBlocks) const override {
283 if (blockSlots.size() != size()) {
284 throw std::invalid_argument(
285 "BatchJacobianFactorBase::hessianBlockDiagonalAdd: slot count "
286 "mismatch.");
287 }
288 const std::map<Key, Matrix> factorDiagonal =
289 toJacobianFactor().hessianBlockDiagonal();
290 for (size_t position = 0; position < size(); ++position) {
291 diagonalBlocks->at(blockSlots[position]) +=
292 factorDiagonal.at(keys()[position]);
293 }
294 }
295
298 DenseIndex beginCol, DenseIndex endCol) const override {
299 toJacobianFactor().updateHessian(keys, info, beginCol, endCol);
300 }
301
303 void multiplyHessianAdd(double alpha, const VectorValues& x,
304 VectorValues& y) const override {
305 toJacobianFactor().multiplyHessianAdd(alpha, x, y);
306 }
307
309 VectorValues gradientAtZero() const override {
310 return toJacobianFactor().gradientAtZero();
311 }
312
314 void gradientAtZero(double* d) const override {
315 toJacobianFactor().gradientAtZero(d);
316 }
317
319 Vector gradient(Key key, const VectorValues& x) const override {
320 return toJacobianFactor().gradient(key, x);
321 }
322};
323
350template <int ErrorDim, int... BlockDims>
352 public:
353 static_assert(ErrorDim != Eigen::Dynamic,
354 "BatchJacobianFactor requires a fixed error dimension.");
355 static_assert(((BlockDims != Eigen::Dynamic) && ...),
356 "BatchJacobianFactor requires fixed block dimensions.");
357
358 using Base = BatchJacobianFactorBase;
359 using This = BatchJacobianFactor<ErrorDim, BlockDims...>;
360 using shared_ptr = std::shared_ptr<This>;
361 static constexpr size_t NumSlots = sizeof...(BlockDims);
362 using SlotIndices = std::array<DenseIndex, NumSlots>;
363 using HessianSlots = std::array<DenseIndex, NumSlots + 1>;
364 using RhsVector = Eigen::Matrix<double, ErrorDim, 1>;
365 template <int BlockDim>
366 using BlockMatrix = Eigen::Matrix<double, ErrorDim, BlockDim>;
367 template <int BlockDim>
368 using BlockVector =
369 std::vector<BlockMatrix<BlockDim>,
370 Eigen::aligned_allocator<BlockMatrix<BlockDim>>>;
371 using Blocks = std::tuple<BlockVector<BlockDims>...>;
372
373 private:
374 std::vector<size_t> keyDims_;
375 std::vector<SlotIndices> rowSlots_;
376 Blocks blocks_;
377 std::vector<RhsVector, Eigen::aligned_allocator<RhsVector>> rhs_;
378 SharedDiagonal model_;
379
381 template <size_t... Indices>
382 void reserveBlocks(size_t rowCount, std::index_sequence<Indices...>) {
383 (std::get<Indices>(blocks_).reserve(rowCount), ...);
384 }
385
387 template <size_t Slot>
388 void addBlock(const Matrix& block) {
389 using BlockVectorType = typename std::tuple_element<Slot, Blocks>::type;
390 using BlockType = typename BlockVectorType::value_type;
391 constexpr int BlockDim = BlockType::ColsAtCompileTime;
392 if (block.rows() != ErrorDim || block.cols() != BlockDim) {
393 throw std::invalid_argument(
394 "BatchJacobianFactor::addRow: incompatible block dimension.");
395 }
396 std::get<Slot>(blocks_).push_back(block);
397 }
398
400 template <size_t... Indices>
401 void addBlocks(const std::vector<Matrix>& blocks,
402 std::index_sequence<Indices...>) {
403 (addBlock<Indices>(blocks[Indices]), ...);
404 }
405
407 template <size_t Slot>
408 void copyBlockToDense(size_t rowIndex, VerticalBlockMatrix* dense) const {
409 using BlockVectorType = typename std::tuple_element<Slot, Blocks>::type;
410 using BlockType = typename BlockVectorType::value_type;
411 constexpr int BlockDim = BlockType::ColsAtCompileTime;
412 const DenseIndex keySlot = rowSlots_[rowIndex][Slot];
413 (*dense)(keySlot).block(static_cast<DenseIndex>(rowIndex * ErrorDim), 0,
414 ErrorDim, BlockDim) =
415 std::get<Slot>(blocks_)[rowIndex];
416 }
417
419 template <size_t... Indices>
420 void copyBlocksToDense(size_t rowIndex, VerticalBlockMatrix* dense,
421 std::index_sequence<Indices...>) const {
422 (copyBlockToDense<Indices>(rowIndex, dense), ...);
423 }
424
426 template <size_t Slot>
427 void scatterBlock(size_t rowIndex, size_t rowOffset,
428 VerticalBlockMatrix* target,
429 const std::vector<DenseIndex>& targetBlockIndices) const {
430 using BlockVectorType = typename std::tuple_element<Slot, Blocks>::type;
431 using BlockType = typename BlockVectorType::value_type;
432 constexpr int BlockDim = BlockType::ColsAtCompileTime;
433 const DenseIndex keySlot = rowSlots_[rowIndex][Slot];
434 const DenseIndex targetBlock = targetBlockIndices[keySlot];
435 if (targetBlock < 0) return;
436 (*target)(targetBlock)
437 .block(static_cast<DenseIndex>(rowOffset + rowIndex * ErrorDim), 0,
438 ErrorDim, BlockDim) = std::get<Slot>(blocks_)[rowIndex];
439 }
440
442 template <size_t... Indices>
443 void scatterBlocks(size_t rowIndex, size_t rowOffset,
444 VerticalBlockMatrix* target,
445 const std::vector<DenseIndex>& targetBlockIndices,
446 std::index_sequence<Indices...>) const {
447 (scatterBlock<Indices>(rowIndex, rowOffset, target, targetBlockIndices),
448 ...);
449 }
450
452 static bool slotInRange(DenseIndex slot, DenseIndex beginCol,
453 DenseIndex endCol) {
454 return internal::BlockColumnRange{beginCol, endCol}(slot);
455 }
456
464 template <size_t Slot>
465 void updateAugmentedDiagonal(size_t rowIndex, DenseIndex targetSlot,
466 DenseIndex targetScalarOffset,
467 const RhsVector* weights,
468 SymmetricBlockMatrix* info) const {
469 if (targetSlot < 0) return;
470 if constexpr (Slot == NumSlots) {
471 const RhsVector& b = rhs_[rowIndex];
472 Eigen::Matrix<double, 1, 1> contribution;
473 contribution(0, 0) = weights
474 ? (weights->array() * b.array().square()).sum()
475 : b.squaredNorm();
476 if (targetScalarOffset >= 0) {
477 info->updateFixedDiagonalBlockAt<1>(targetScalarOffset, contribution);
478 } else {
479 info->updateDiagonalBlock(targetSlot, contribution);
480 }
481 } else {
482 using BlockType =
483 typename std::tuple_element<Slot, Blocks>::type::value_type;
484 constexpr int BlockDim = BlockType::ColsAtCompileTime;
485 const auto& A = std::get<Slot>(blocks_)[rowIndex];
486 Eigen::Matrix<double, BlockDim, BlockDim> contribution;
487 if (weights) {
488 contribution.noalias() = A.transpose() * weights->asDiagonal() * A;
489 } else {
490 contribution.noalias() = A.transpose() * A;
491 }
492 if (targetScalarOffset >= 0) {
493 info->updateFixedDiagonalBlockAt<BlockDim>(targetScalarOffset,
494 contribution);
495 } else {
496 info->updateDiagonalBlock(targetSlot, contribution);
497 }
498 }
499 }
500
502 template <typename MatrixType>
503 void updateOffDiagonalNormalized(DenseIndex targetI, DenseIndex targetJ,
504 DenseIndex scalarOffsetI,
505 DenseIndex scalarOffsetJ,
506 const MatrixType& block,
507 SymmetricBlockMatrix* info) const {
508 assert((targetI != targetJ) &&
509 "BatchJacobianFactor: duplicate mapped Hessian slots are not "
510 "supported.");
511 constexpr int Rows = MatrixType::RowsAtCompileTime;
512 constexpr int Cols = MatrixType::ColsAtCompileTime;
513 static_assert(Rows != Eigen::Dynamic && Cols != Eigen::Dynamic);
514 if (scalarOffsetI >= 0 && scalarOffsetJ >= 0) {
515 if (targetI < targetJ) {
516 info->updateFixedOffDiagonalBlockAt<Rows, Cols>(scalarOffsetI,
517 scalarOffsetJ, block);
518 } else {
519 info->updateFixedOffDiagonalBlockAt<Cols, Rows>(
520 scalarOffsetJ, scalarOffsetI, block.transpose());
521 }
522 return;
523 }
524 if (targetI < targetJ) {
525 info->updateOffDiagonalBlock(targetI, targetJ, block);
526 return;
527 }
528 info->updateOffDiagonalBlock(targetJ, targetI, block.transpose());
529 }
530
538 template <size_t I, size_t J>
539 void updateAugmentedOffDiagonal(size_t rowIndex, DenseIndex targetI,
540 DenseIndex targetJ, DenseIndex scalarOffsetI,
541 DenseIndex scalarOffsetJ,
542 const RhsVector* weights,
543 SymmetricBlockMatrix* info) const {
544 static_assert(I < J, "BatchJacobianFactor expects upper-triangular order.");
545 if constexpr (J == NumSlots) {
546 using BlockType =
547 typename std::tuple_element<I, Blocks>::type::value_type;
548 constexpr int BlockDim = BlockType::ColsAtCompileTime;
549 const auto& A = std::get<I>(blocks_)[rowIndex];
550 const RhsVector& b = rhs_[rowIndex];
551 Eigen::Matrix<double, BlockDim, 1> contribution;
552 if (weights) {
553 const RhsVector weightedRhs = weights->asDiagonal() * b;
554 contribution.noalias() = A.transpose() * weightedRhs;
555 } else {
556 contribution.noalias() = A.transpose() * b;
557 }
558 updateOffDiagonalNormalized(targetI, targetJ, scalarOffsetI,
559 scalarOffsetJ, contribution, info);
560 } else {
561 using BlockTypeI =
562 typename std::tuple_element<I, Blocks>::type::value_type;
563 using BlockTypeJ =
564 typename std::tuple_element<J, Blocks>::type::value_type;
565 constexpr int BlockDimI = BlockTypeI::ColsAtCompileTime;
566 constexpr int BlockDimJ = BlockTypeJ::ColsAtCompileTime;
567 const auto& Ai = std::get<I>(blocks_)[rowIndex];
568 const auto& Aj = std::get<J>(blocks_)[rowIndex];
569 Eigen::Matrix<double, BlockDimI, BlockDimJ> contribution;
570 if (weights) {
571 contribution.noalias() = Ai.transpose() * weights->asDiagonal() * Aj;
572 } else {
573 contribution.noalias() = Ai.transpose() * Aj;
574 }
575 updateOffDiagonalNormalized(targetI, targetJ, scalarOffsetI,
576 scalarOffsetJ, contribution, info);
577 }
578 }
579
581 template <size_t J, size_t... Is>
582 void updateMappedPreviousAugmentedSlots(
583 size_t rowIndex, const DenseIndex* mappedSlots,
584 const DenseIndex* mappedScalarOffsets, const RhsVector* weights,
585 SymmetricBlockMatrix* info, DenseIndex beginCol, DenseIndex endCol,
586 std::index_sequence<Is...>) const {
587 (void)rowIndex;
588 (void)weights;
589 (void)info;
590 (void)beginCol;
591 (void)endCol;
592 const DenseIndex targetSlot = mappedSlots[J];
593 ((mappedSlots[Is] >= 0 && targetSlot >= 0 &&
594 slotInRange(std::max(mappedSlots[Is], targetSlot), beginCol,
595 endCol)
596 ? updateAugmentedOffDiagonal<Is, J>(
597 rowIndex, mappedSlots[Is], targetSlot,
598 mappedScalarOffsets ? mappedScalarOffsets[Is] : -1,
599 mappedScalarOffsets ? mappedScalarOffsets[J] : -1, weights,
600 info)
601 : void()),
602 ...);
603 }
604
606 template <size_t J>
607 void updateMappedAugmentedColumn(size_t rowIndex,
608 const DenseIndex* mappedSlots,
609 const DenseIndex* mappedScalarOffsets,
610 const RhsVector* weights,
612 DenseIndex beginCol,
613 DenseIndex endCol) const {
614 const DenseIndex targetSlot = mappedSlots[J];
615 if (slotInRange(targetSlot, beginCol, endCol)) {
616 updateAugmentedDiagonal<J>(
617 rowIndex, targetSlot,
618 mappedScalarOffsets ? mappedScalarOffsets[J] : -1, weights, info);
619 }
620 updateMappedPreviousAugmentedSlots<J>(
621 rowIndex, mappedSlots, mappedScalarOffsets, weights, info, beginCol,
622 endCol, std::make_index_sequence<J>{});
623 }
624
626 template <size_t... Js>
627 void updateMappedAugmentedColumns(size_t rowIndex,
628 const DenseIndex* mappedSlots,
629 const DenseIndex* mappedScalarOffsets,
630 const RhsVector* weights,
632 DenseIndex beginCol, DenseIndex endCol,
633 std::index_sequence<Js...>) const {
634 (updateMappedAugmentedColumn<Js>(rowIndex, mappedSlots, mappedScalarOffsets,
635 weights, info, beginCol, endCol),
636 ...);
637 }
638
640 void updateMappedHessianRow(size_t rowIndex, const DenseIndex* mappedSlots,
641 const DenseIndex* mappedScalarOffsets,
642 const RhsVector* weights,
643 SymmetricBlockMatrix* info, DenseIndex beginCol,
644 DenseIndex endCol) const {
645 updateMappedAugmentedColumns(rowIndex, mappedSlots, mappedScalarOffsets,
646 weights, info, beginCol, endCol,
647 std::make_index_sequence<NumSlots + 1>{});
648 }
649
651 template <typename DestinationType, typename XprType>
652 static void addFrontalBlock(DestinationType* destination,
653 const XprType& xpr) {
654 if constexpr (XprType::SizeAtCompileTime == 1) {
655 assert(destination->rows() == 1 && destination->cols() == 1);
656 (*destination)(0, 0) += xpr.coeff(0, 0);
657 } else {
658 *destination += xpr.eval();
659 }
660 }
661
663 template <size_t I, size_t J>
664 void updateFrontalHessianBlock(size_t rowIndex, const DenseIndex* mappedSlots,
665 const RhsVector* weights,
666 DenseIndex numFrontalBlocks,
667 VerticalBlockMatrix* frontalRows) const {
668 static_assert(I < NumSlots);
669 static_assert(J <= NumSlots);
670 const DenseIndex targetI = mappedSlots[I];
671 const DenseIndex targetJ = mappedSlots[J];
672 if (targetI < 0 || targetI >= numFrontalBlocks || targetJ < 0) return;
673
674 const auto& Ai = std::get<I>(blocks_)[rowIndex];
675 const DenseIndex targetRow = frontalRows->offset(targetI);
676 auto destination = (*frontalRows)(targetJ).middleRows(
677 targetRow, static_cast<DenseIndex>(Ai.cols()));
678 if constexpr (J == NumSlots) {
679 const RhsVector& b = rhs_[rowIndex];
680 if (weights) {
681 addFrontalBlock(&destination,
682 Ai.transpose() * weights->asDiagonal() * b);
683 } else {
684 addFrontalBlock(&destination, Ai.transpose() * b);
685 }
686 } else {
687 const auto& Aj = std::get<J>(blocks_)[rowIndex];
688 if (weights) {
689 addFrontalBlock(&destination,
690 Ai.transpose() * weights->asDiagonal() * Aj);
691 } else {
692 addFrontalBlock(&destination, Ai.transpose() * Aj);
693 }
694 }
695 }
696
698 template <size_t I, size_t... Js>
699 void updateFrontalHessianRowBlock(size_t rowIndex,
700 const DenseIndex* mappedSlots,
701 const RhsVector* weights,
702 DenseIndex numFrontalBlocks,
703 VerticalBlockMatrix* frontalRows,
704 std::index_sequence<Js...>) const {
705 (updateFrontalHessianBlock<I, Js>(rowIndex, mappedSlots, weights,
706 numFrontalBlocks, frontalRows),
707 ...);
708 }
709
711 template <size_t... Is>
712 void updateFrontalHessianRowGroup(size_t rowIndex,
713 const DenseIndex* mappedSlots,
714 const RhsVector* weights,
715 DenseIndex numFrontalBlocks,
716 VerticalBlockMatrix* frontalRows,
717 std::index_sequence<Is...>) const {
718 (updateFrontalHessianRowBlock<Is>(rowIndex, mappedSlots, weights,
719 numFrontalBlocks, frontalRows,
720 std::make_index_sequence<NumSlots + 1>{}),
721 ...);
722 }
723
725 RhsVector rowWeights(size_t rowIndex) const {
726 RhsVector weights = RhsVector::Ones();
727 if (!model_ || model_->isUnit()) return weights;
728
729 const auto constrained =
730 model_->isConstrained()
731 ? std::dynamic_pointer_cast<noiseModel::Constrained>(model_)
732 : nullptr;
733 const size_t rowOffset = rowIndex * ErrorDim;
734 for (size_t row = 0; row < ErrorDim; ++row) {
735 const size_t modelRow = rowOffset + row;
736 if (!constrained || !constrained->constrained(modelRow)) {
737 weights(static_cast<DenseIndex>(row)) = model_->precision(modelRow);
738 }
739 }
740 return weights;
741 }
742
744 template <size_t Slot>
745 void multiplyRowBlock(size_t rowIndex,
746 const std::vector<size_t>& scalarOffsets,
747 const double* x, RhsVector* residual) const {
748 using BlockType =
749 typename std::tuple_element<Slot, Blocks>::type::value_type;
750 constexpr int BlockDim = BlockType::ColsAtCompileTime;
751 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
752 const Eigen::Map<const Eigen::Matrix<double, BlockDim, 1>> xBlock(
753 x + scalarOffsets[keySlot]);
754 residual->noalias() += std::get<Slot>(blocks_)[rowIndex] * xBlock;
755 }
756
758 template <size_t... Slots>
759 void multiplyRowBlocks(size_t rowIndex,
760 const std::vector<size_t>& scalarOffsets,
761 const double* x, RhsVector* residual,
762 std::index_sequence<Slots...>) const {
763 (multiplyRowBlock<Slots>(rowIndex, scalarOffsets, x, residual), ...);
764 }
765
767 template <size_t Slot>
768 void addVectorValuesRowBlock(size_t rowIndex, const VectorValues& values,
769 RhsVector* residual) const {
770 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
771 residual->noalias() +=
772 std::get<Slot>(blocks_)[rowIndex] * values.at(keys_[keySlot]);
773 }
774
776 template <size_t... Slots>
777 void addVectorValuesRowBlocks(size_t rowIndex, const VectorValues& values,
778 RhsVector* residual,
779 std::index_sequence<Slots...>) const {
780 (addVectorValuesRowBlock<Slots>(rowIndex, values, residual), ...);
781 }
782
784 template <size_t Slot>
785 void hessianDiagonalVectorRowAdd(size_t rowIndex,
786 VectorValues* diagonal) const {
787 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
788 const auto& block = std::get<Slot>(blocks_)[rowIndex];
789 diagonal->at(keys_[keySlot]).array() +=
790 block.array().square().colwise().sum().transpose();
791 }
792
794 template <size_t... Slots>
795 void hessianDiagonalVectorRowAdds(size_t rowIndex, VectorValues* diagonal,
796 std::index_sequence<Slots...>) const {
797 (hessianDiagonalVectorRowAdd<Slots>(rowIndex, diagonal), ...);
798 }
799
801 template <size_t Slot>
802 void transposeRowBlockAdd(size_t rowIndex,
803 const std::vector<size_t>& scalarOffsets,
804 double alpha, const RhsVector& residual,
805 double* y) const {
806 using BlockType =
807 typename std::tuple_element<Slot, Blocks>::type::value_type;
808 constexpr int BlockDim = BlockType::ColsAtCompileTime;
809 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
810 Eigen::Map<Eigen::Matrix<double, BlockDim, 1>> yBlock(
811 y + scalarOffsets[keySlot]);
812 yBlock.noalias() +=
813 alpha * std::get<Slot>(blocks_)[rowIndex].transpose() * residual;
814 }
815
817 template <size_t... Slots>
818 void transposeRowBlocksAdd(size_t rowIndex,
819 const std::vector<size_t>& scalarOffsets,
820 double alpha, const RhsVector& residual, double* y,
821 std::index_sequence<Slots...>) const {
822 (transposeRowBlockAdd<Slots>(rowIndex, scalarOffsets, alpha, residual, y),
823 ...);
824 }
825
827 template <size_t Slot>
828 void hessianBlockDiagonalRowAdd(size_t rowIndex,
829 const std::vector<size_t>& blockSlots,
830 const RhsVector& weights,
831 std::vector<Matrix>* diagonalBlocks) const {
832 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
833 const auto& block = std::get<Slot>(blocks_)[rowIndex];
834 (*diagonalBlocks)[blockSlots[keySlot]].noalias() +=
835 block.transpose() * weights.asDiagonal() * block;
836 }
837
839 template <size_t... Slots>
840 void hessianBlockDiagonalRowAdds(size_t rowIndex,
841 const std::vector<size_t>& blockSlots,
842 const RhsVector& weights,
843 std::vector<Matrix>* diagonalBlocks,
844 std::index_sequence<Slots...>) const {
845 (hessianBlockDiagonalRowAdd<Slots>(rowIndex, blockSlots, weights,
846 diagonalBlocks),
847 ...);
848 }
849
851 template <size_t I, size_t J>
852 void updateSparseNormalBlock(
853 size_t rowIndex, const std::vector<DenseIndex>& scalarOffsets,
854 const RhsVector& weights,
855 internal::SparseNormalAccumulator* accumulator) const {
856 using BlockTypeI = typename std::tuple_element<I, Blocks>::type::value_type;
857 using BlockTypeJ = typename std::tuple_element<J, Blocks>::type::value_type;
858 constexpr int BlockDimI = BlockTypeI::ColsAtCompileTime;
859 constexpr int BlockDimJ = BlockTypeJ::ColsAtCompileTime;
860 const auto& blockI = std::get<I>(blocks_)[rowIndex];
861 const auto& blockJ = std::get<J>(blocks_)[rowIndex];
862 Eigen::Matrix<double, BlockDimI, BlockDimJ> contribution;
863 contribution.noalias() = blockI.transpose() * weights.asDiagonal() * blockJ;
864 const size_t keySlotI = static_cast<size_t>(rowSlots_[rowIndex][I]);
865 const size_t keySlotJ = static_cast<size_t>(rowSlots_[rowIndex][J]);
866 accumulator->addHessianBlock(scalarOffsets[keySlotI],
867 scalarOffsets[keySlotJ], BlockDimI, BlockDimJ,
868 contribution.data());
869 }
870
872 template <size_t J, size_t... Is>
873 void updateSparseNormalColumn(size_t rowIndex,
874 const std::vector<DenseIndex>& scalarOffsets,
875 const RhsVector& weights,
877 std::index_sequence<Is...>) const {
878 (updateSparseNormalBlock<Is, J>(rowIndex, scalarOffsets, weights,
879 accumulator),
880 ...);
881 }
882
884 template <size_t... Js>
885 void updateSparseNormalBlocks(size_t rowIndex,
886 const std::vector<DenseIndex>& scalarOffsets,
887 const RhsVector& weights,
889 std::index_sequence<Js...>) const {
890 (updateSparseNormalColumn<Js>(rowIndex, scalarOffsets, weights, accumulator,
891 std::make_index_sequence<Js + 1>{}),
892 ...);
893 }
894
896 template <size_t Slot>
897 void updateSparseNormalRhs(
898 size_t rowIndex, const std::vector<DenseIndex>& scalarOffsets,
899 const RhsVector& weightedRhs,
900 internal::SparseNormalAccumulator* accumulator) const {
901 using BlockType =
902 typename std::tuple_element<Slot, Blocks>::type::value_type;
903 constexpr int BlockDim = BlockType::ColsAtCompileTime;
904 Eigen::Matrix<double, BlockDim, 1> contribution;
905 contribution.noalias() =
906 std::get<Slot>(blocks_)[rowIndex].transpose() * weightedRhs;
907 const size_t keySlot = static_cast<size_t>(rowSlots_[rowIndex][Slot]);
908 accumulator->addRhsBlock(scalarOffsets[keySlot], BlockDim,
909 contribution.data());
910 }
911
913 template <size_t... Slots>
914 void updateSparseNormalRhsBlocks(
915 size_t rowIndex, const std::vector<DenseIndex>& scalarOffsets,
916 const RhsVector& weightedRhs,
918 std::index_sequence<Slots...>) const {
919 (updateSparseNormalRhs<Slots>(rowIndex, scalarOffsets, weightedRhs,
920 accumulator),
921 ...);
922 }
923
924 public:
932 BatchJacobianFactor(const KeyVector& keys, std::vector<size_t> keyDims,
933 const SharedDiagonal& model = SharedDiagonal())
934 : Base(keys), keyDims_(std::move(keyDims)), model_(model) {
935 if (keyDims_.size() != keys_.size()) {
936 throw std::invalid_argument(
937 "BatchJacobianFactor: key dimension count must match keys.");
938 }
939 }
940
943 return std::static_pointer_cast<GaussianFactor>(
944 std::make_shared<This>(*this));
945 }
946
948 void reserve(size_t rowCount) {
949 rowSlots_.reserve(rowCount);
950 rhs_.reserve(rowCount);
951 reserveBlocks(rowCount, std::make_index_sequence<NumSlots>{});
952 }
953
962 void addRow(const SlotIndices& slots, const std::vector<Matrix>& blocks,
963 const Vector& rhs) {
964 if (blocks.size() != NumSlots || rhs.size() != ErrorDim) {
965 throw std::invalid_argument(
966 "BatchJacobianFactor::addRow: incompatible row dimensions.");
967 }
968 rowSlots_.push_back(slots);
969 addBlocks(blocks, std::make_index_sequence<NumSlots>{});
970 RhsVector fixedRhs = rhs;
971 rhs_.push_back(fixedRhs);
972 }
973
976 DenseIndex keySlot,
977 const typename std::tuple_element<0, Blocks>::type::value_type& block,
978 const RhsVector& rhs) {
979 static_assert(NumSlots == 1,
980 "BatchJacobianFactor::addUnaryRow requires one slot.");
981 rowSlots_.push_back(SlotIndices{keySlot});
982 std::get<0>(blocks_).push_back(block);
983 rhs_.push_back(rhs);
984 }
985
987 size_t rows() const override { return rhs_.size() * ErrorDim; }
988
990 const SharedDiagonal& get_model() const override { return model_; }
991
993 DenseIndex getDim(const_iterator variable) const override {
994 return static_cast<DenseIndex>(keyDims_.at(variable - begin()));
995 }
996
998 const std::vector<SlotIndices>& rowSlots() const { return rowSlots_; }
999
1001 template <size_t Slot>
1002 const typename std::tuple_element<Slot, Blocks>::type::value_type& block(
1003 size_t rowIndex) const {
1004 static_assert(Slot < NumSlots,
1005 "BatchJacobianFactor block slot is invalid.");
1006 return std::get<Slot>(blocks_).at(rowIndex);
1007 }
1008
1010 const RhsVector& rowRhs(size_t rowIndex) const { return rhs_.at(rowIndex); }
1011
1013 double deltaError(const VectorValues& values, double* oldError = nullptr,
1014 double* newError = nullptr) const override {
1015 if (model_ && !model_->isUnit()) {
1016 return Base::deltaError(values, oldError, newError);
1017 }
1018
1019 double oldValue = 0.0;
1020 double newValue = 0.0;
1021 for (size_t row = 0; row < rowSlots_.size(); ++row) {
1022 const RhsVector& rhs = rhs_[row];
1023 RhsVector residual = -rhs;
1024 addVectorValuesRowBlocks(row, values, &residual,
1025 std::make_index_sequence<NumSlots>{});
1026 oldValue += 0.5 * rhs.squaredNorm();
1027 newValue += 0.5 * residual.squaredNorm();
1028 }
1029 if (oldError) *oldError = oldValue;
1030 if (newError) *newError = newValue;
1031 return oldValue - newValue;
1032 }
1033
1035 void hessianDiagonalAdd(VectorValues& diagonal) const override {
1036 if (model_ && !model_->isUnit()) {
1037 Base::hessianDiagonalAdd(diagonal);
1038 return;
1039 }
1040 for (size_t position = 0; position < keys_.size(); ++position) {
1041 auto [entry, inserted] =
1042 diagonal.emplace(keys_[position], keyDims_[position]);
1043 if (inserted) entry->second.setZero();
1044 }
1045 for (size_t row = 0; row < rowSlots_.size(); ++row) {
1046 hessianDiagonalVectorRowAdds(row, &diagonal,
1047 std::make_index_sequence<NumSlots>{});
1048 }
1049 }
1050
1052 void multiplyHessianAdd(double alpha,
1053 const std::vector<size_t>& scalarOffsets,
1054 const double* x, double* y) const override {
1055 if (scalarOffsets.size() != keys_.size()) {
1056 throw std::invalid_argument(
1057 "BatchJacobianFactor::multiplyHessianAdd: offset count mismatch.");
1058 }
1059 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1060 RhsVector residual = RhsVector::Zero();
1061 multiplyRowBlocks(rowIndex, scalarOffsets, x, &residual,
1062 std::make_index_sequence<NumSlots>{});
1063 residual.array() *= rowWeights(rowIndex).array();
1064 transposeRowBlocksAdd(rowIndex, scalarOffsets, alpha, residual, y,
1065 std::make_index_sequence<NumSlots>{});
1066 }
1067 }
1068
1070 void gradientAtZeroAdd(const std::vector<size_t>& scalarOffsets,
1071 double* gradient) const override {
1072 if (scalarOffsets.size() != keys_.size()) {
1073 throw std::invalid_argument(
1074 "BatchJacobianFactor::gradientAtZeroAdd: offset count mismatch.");
1075 }
1076 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1077 RhsVector weightedRhs = rhs_[rowIndex];
1078 weightedRhs.array() *= rowWeights(rowIndex).array();
1079 transposeRowBlocksAdd(rowIndex, scalarOffsets, -1.0, weightedRhs,
1080 gradient, std::make_index_sequence<NumSlots>{});
1081 }
1082 }
1083
1086 const std::vector<size_t>& blockSlots,
1087 std::vector<Matrix>* diagonalBlocks) const override {
1088 if (blockSlots.size() != keys_.size()) {
1089 throw std::invalid_argument(
1090 "BatchJacobianFactor::hessianBlockDiagonalAdd: slot count "
1091 "mismatch.");
1092 }
1093 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1094 hessianBlockDiagonalRowAdds(rowIndex, blockSlots, rowWeights(rowIndex),
1095 diagonalBlocks,
1096 std::make_index_sequence<NumSlots>{});
1097 }
1098 }
1099
1108 if (rowSlots_.empty()) return JacobianFactor();
1109 VerticalBlockMatrix dense(keyDims_, static_cast<DenseIndex>(rows()), true);
1110 dense.matrix().setZero();
1111 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1112 copyBlocksToDense(rowIndex, &dense, std::make_index_sequence<NumSlots>{});
1113 dense(keys_.size())
1114 .block(static_cast<DenseIndex>(rowIndex * ErrorDim), 0, ErrorDim, 1) =
1115 rhs_[rowIndex];
1116 }
1117 return JacobianFactor(keys_, std::move(dense), model_);
1118 }
1119
1120 private:
1126 size_t scatterInto(
1127 VerticalBlockMatrix& target, size_t rowOffset,
1128 const std::vector<DenseIndex>& targetBlockIndices) const override {
1129 if (targetBlockIndices.size() != keys_.size()) {
1130 throw std::invalid_argument(
1131 "BatchJacobianFactor::scatterInto: target index count mismatch.");
1132 }
1133 const size_t rhsBlock = target.nBlocks() - 1;
1134 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1135 scatterBlocks(rowIndex, rowOffset, &target, targetBlockIndices,
1136 std::make_index_sequence<NumSlots>{});
1137 target(rhsBlock).block(
1138 static_cast<DenseIndex>(rowOffset + rowIndex * ErrorDim), 0, ErrorDim,
1139 1) = rhs_[rowIndex];
1140 }
1141 return rows();
1142 }
1143
1144 void updateSparseNormal(
1145 const std::vector<DenseIndex>& scalarOffsets,
1146 internal::SparseNormalAccumulator* accumulator) const override {
1147 if (!accumulator) {
1148 throw std::invalid_argument(
1149 "BatchJacobianFactor::updateSparseNormal: null accumulator.");
1150 }
1151 if (scalarOffsets.size() != keys_.size()) {
1152 throw std::invalid_argument(
1153 "BatchJacobianFactor::updateSparseNormal: offset count mismatch.");
1154 }
1155 if (model_ && !model_->isUnit() && model_->isConstrained()) {
1156 throw std::invalid_argument(
1157 "BatchJacobianFactor::updateSparseNormal: constrained noise model "
1158 "is not supported.");
1159 }
1160 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1161 const RhsVector weights = rowWeights(rowIndex);
1162 updateSparseNormalBlocks(rowIndex, scalarOffsets, weights, accumulator,
1163 std::make_index_sequence<NumSlots>{});
1164 const RhsVector weightedRhs = weights.asDiagonal() * rhs_[rowIndex];
1165 updateSparseNormalRhsBlocks(rowIndex, scalarOffsets, weightedRhs,
1166 accumulator,
1167 std::make_index_sequence<NumSlots>{});
1168 }
1169 }
1170
1177 void updateHessian(const std::vector<DenseIndex>& slotIndices,
1178 SymmetricBlockMatrix* info) const override {
1179 if (rows() == 0) return;
1180 const DenseIndex rhsSlot = static_cast<DenseIndex>(info->nBlocks() - 1);
1181 std::vector<DenseIndex> mappedSlots;
1182 if (slotIndices.size() == keys_.size() + 1 && !slotIndices.empty() &&
1183 slotIndices.back() == rhsSlot) {
1184 buildMappedSlots(slotIndices, mappedSlots);
1185 updateHessianWithMappedSlots(mappedSlots, nullptr, info, 0,
1186 info->nBlocks());
1187 return;
1188 }
1189 if (slotIndices.size() != keys_.size()) {
1190 throw std::invalid_argument(
1191 "BatchJacobianFactor::updateHessian: slot index count mismatch.");
1192 }
1193
1194 std::vector<DenseIndex> slots = slotIndices;
1195 slots.push_back(rhsSlot);
1196 buildMappedSlots(slots, mappedSlots);
1197 updateHessianWithMappedSlots(mappedSlots, nullptr, info, 0,
1198 info->nBlocks());
1199 }
1200
1207 void updateHessian(const std::vector<DenseIndex>& slotIndices,
1208 SymmetricBlockMatrix* info, DenseIndex beginCol,
1209 DenseIndex endCol) const override {
1210 if (rows() == 0) return;
1211 if (slotIndices.size() != keys_.size()) {
1212 throw std::invalid_argument(
1213 "BatchJacobianFactor::updateHessian: slot index count mismatch.");
1214 }
1215
1216 std::vector<DenseIndex> slots;
1217 slots.reserve(slotIndices.size() + 1);
1218 bool foundCol = false;
1219 for (const DenseIndex slot : slotIndices) {
1220 slots.push_back(slot);
1221 if (slotInRange(slot, beginCol, endCol)) foundCol = true;
1222 }
1223 slots.push_back(info->nBlocks() - 1);
1224 if (slotInRange(slots.back(), beginCol, endCol)) foundCol = true;
1225 if (!foundCol) return;
1226
1227 std::vector<DenseIndex> mappedSlots;
1228 buildMappedSlots(slots, mappedSlots);
1229 updateHessianWithMappedSlots(mappedSlots, nullptr, info, beginCol, endCol);
1230 }
1231
1233 void buildMappedSlots(const std::vector<DenseIndex>& slotIndices,
1234 std::vector<DenseIndex>& mappedSlots) const override {
1235 const size_t stride = NumSlots + 1;
1236 if (slotIndices.size() != keys_.size() + 1) {
1237 throw std::invalid_argument(
1238 "BatchJacobianFactor::buildMappedSlots: slot index count mismatch.");
1239 }
1240 mappedSlots.resize(rowSlots_.size() * stride);
1241 auto* out = mappedSlots.data();
1242 for (const auto& rowSlot : rowSlots_) {
1243 for (size_t j = 0; j < NumSlots; ++j) {
1244 *out++ = slotIndices[rowSlot[j]];
1245 }
1246 *out++ = slotIndices.back();
1247 }
1248 }
1249
1251 void updateHessianWithMappedSlots(const std::vector<DenseIndex>& mappedSlots,
1252 SymmetricBlockMatrix* info) const override {
1253 updateHessianWithMappedSlots(mappedSlots, nullptr, info, 0,
1254 info->nBlocks());
1255 }
1256
1257 void updateHessianWithMappedSlots(
1258 const std::vector<DenseIndex>& mappedSlots,
1259 const std::vector<DenseIndex>& mappedScalarOffsets,
1260 SymmetricBlockMatrix* info) const override {
1261 updateHessianWithMappedSlots(mappedSlots, &mappedScalarOffsets, info, 0,
1262 info->nBlocks());
1263 }
1264
1265 void updateFrontalHessianWithMappedSlots(
1266 const std::vector<DenseIndex>& mappedSlots, DenseIndex numFrontalBlocks,
1267 VerticalBlockMatrix* frontalRows) const override {
1268 if (rows() == 0) return;
1269 const size_t stride = NumSlots + 1;
1270 if (mappedSlots.size() != rowSlots_.size() * stride) {
1271 throw std::invalid_argument(
1272 "BatchJacobianFactor::updateFrontalHessianWithMappedSlots: mapped "
1273 "slot count mismatch.");
1274 }
1275 RhsVector weights;
1276 const RhsVector* weightsPtr = nullptr;
1277 const DenseIndex* rowSlotsPtr = mappedSlots.data();
1278 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1279 if (model_ && !model_->isUnit()) {
1280 weights = rowWeights(rowIndex);
1281 weightsPtr = &weights;
1282 }
1283 updateFrontalHessianRowGroup(rowIndex, rowSlotsPtr + rowIndex * stride,
1284 weightsPtr, numFrontalBlocks, frontalRows,
1285 std::make_index_sequence<NumSlots>{});
1286 }
1287 }
1288
1289 private:
1291 void updateHessianWithMappedSlots(
1292 const std::vector<DenseIndex>& mappedSlots,
1293 const std::vector<DenseIndex>* mappedScalarOffsets,
1294 SymmetricBlockMatrix* info, DenseIndex beginCol,
1295 DenseIndex endCol) const {
1296 gttic(updateHessian_BatchJacobianFactor);
1297 if (rows() == 0) return;
1298 if (model_ && !model_->isUnit() && model_->isConstrained()) {
1299 throw std::invalid_argument(
1300 "BatchJacobianFactor::updateHessian: cannot update information with "
1301 "constrained noise model");
1302 }
1303 const size_t stride = NumSlots + 1;
1304 if (mappedSlots.size() != rowSlots_.size() * stride) {
1305 throw std::invalid_argument(
1306 "BatchJacobianFactor::updateHessianWithMappedSlots: mapped slot "
1307 "count mismatch.");
1308 }
1309 if (mappedScalarOffsets &&
1310 mappedScalarOffsets->size() != mappedSlots.size()) {
1311 throw std::invalid_argument(
1312 "BatchJacobianFactor::updateHessianWithMappedSlots: mapped scalar "
1313 "offset count mismatch.");
1314 }
1315
1316 const DenseIndex rhsSlot = static_cast<DenseIndex>(info->nBlocks() - 1);
1317 for (const DenseIndex slot : mappedSlots) {
1318 if (slot > rhsSlot) {
1319 throw std::invalid_argument(
1320 "BatchJacobianFactor::updateHessianWithMappedSlots: invalid "
1321 "mapped slot index.");
1322 }
1323 }
1324#ifndef NDEBUG
1325 if (mappedScalarOffsets) {
1326 for (size_t i = 0; i < mappedSlots.size(); ++i) {
1327 const DenseIndex slot = mappedSlots[i];
1328 const DenseIndex scalarOffset = (*mappedScalarOffsets)[i];
1329 assert((slot < 0 && scalarOffset < 0) ||
1330 (slot >= 0 && scalarOffset == info->blockScalarOffset(slot)));
1331 }
1332 }
1333#endif
1334
1335 RhsVector weights;
1336 const RhsVector* weightsPtr = nullptr;
1337 if (model_ && !model_->isUnit()) {
1338 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1339 weights = model_->invsigmas()
1340 .template segment<ErrorDim>(
1341 static_cast<DenseIndex>(rowIndex * ErrorDim))
1342 .array()
1343 .square();
1344 weightsPtr = &weights;
1345 updateMappedHessianRow(
1346 rowIndex, mappedSlots.data() + rowIndex * stride,
1347 mappedScalarOffsets
1348 ? mappedScalarOffsets->data() + rowIndex * stride
1349 : nullptr,
1350 weightsPtr, info, beginCol, endCol);
1351 }
1352 return;
1353 }
1354
1355 const DenseIndex* rowSlotsPtr = mappedSlots.data();
1356 const DenseIndex* rowOffsetsPtr =
1357 mappedScalarOffsets ? mappedScalarOffsets->data() : nullptr;
1358 for (size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1359 updateMappedHessianRow(
1360 rowIndex, rowSlotsPtr + rowIndex * stride,
1361 rowOffsetsPtr ? rowOffsetsPtr + rowIndex * stride : nullptr, nullptr,
1362 info, beginCol, endCol);
1363 }
1364 }
1365
1366 public:
1374 void updateHessian(const KeyVector& infoKeys,
1375 SymmetricBlockMatrix* info) const override {
1376 std::vector<DenseIndex> slots;
1377 slots.reserve(keys_.size() + 1);
1378 for (Key key : keys_) {
1379 slots.push_back(Slot(infoKeys, key));
1380 }
1381 slots.push_back(info->nBlocks() - 1);
1382 updateHessian(slots, info);
1383 }
1384
1393 void updateHessian(const KeyVector& infoKeys, SymmetricBlockMatrix* info,
1394 DenseIndex beginCol, DenseIndex endCol) const override {
1395 std::vector<DenseIndex> slots;
1396 slots.reserve(keys_.size());
1397 for (Key key : keys_) {
1398 slots.push_back(Slot(infoKeys, key));
1399 }
1400 updateHessian(slots, info, beginCol, endCol);
1401 }
1402};
1403
1404} // namespace gtsam
Timing utilities.
A matrix with column blocks of pre-defined sizes.
Access to matrices via blocks of pre-defined sizes.
Optional preindexed kernels for Gaussian factors.
A factor with a quadratic error function - a Gaussian.
Factor Graph Values.
STL namespace.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
KeyFormatter DefaultKeyFormatter
Assign default key formatter.
Definition Key.cpp:30
ptrdiff_t DenseIndex
The index type for Eigen objects.
Definition types.h:49
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition Key.h:91
std::function< std::string(Key)> KeyFormatter
Typedef for a function to format a key, i.e. to convert it to a string.
Definition Key.h:35
std::uint64_t Key
Integer nonlinear key type.
Definition types.h:43
Half-open range of physical upper-triangular block columns.
Definition SymmetricBlockMatrix.h:48
This class stores a dense matrix and allows it to be accessed as a collection of blocks.
Definition SymmetricBlockMatrix.h:80
void updateFixedOffDiagonalBlockAt(DenseIndex rowOffset, DenseIndex colOffset, const XprType &xpr)
Increment a fixed-size upper block using cached scalar offsets.
Definition SymmetricBlockMatrix.h:360
void updateFixedDiagonalBlockAt(DenseIndex scalarOffset, const XprType &xpr)
Increment a fixed-size diagonal block using its cached scalar offset.
Definition SymmetricBlockMatrix.h:299
void updateOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType &xpr)
Update an off diagonal block.
Definition SymmetricBlockMatrix.h:325
void updateDiagonalBlock(DenseIndex I, const XprType &xpr)
Increment the diagonal block by the values in xpr.
Definition SymmetricBlockMatrix.h:271
DenseIndex nBlocks() const
Block count.
Definition SymmetricBlockMatrix.h:162
This class stores a dense matrix and allows it to be accessed as a collection of vertical blocks.
Definition VerticalBlockMatrix.h:47
const Matrix & matrix() const
Access to full matrix (including any portions excluded by rowStart(), rowEnd(), and firstBlock()).
Definition VerticalBlockMatrix.h:278
DenseIndex nBlocks() const
Block count.
Definition VerticalBlockMatrix.h:181
const KeyVector & keys() const
Access the factor's involved variable keys.
Definition Factor.h:143
KeyVector keys_
The keys involved in this factor.
Definition Factor.h:88
const_iterator begin() const
Iterator at beginning of involved variable keys.
Definition Factor.h:146
KeyVector::const_iterator const_iterator
Const iterator over keys.
Definition Factor.h:83
size_t size() const
Definition Factor.h:160
Receives sparse normal-equation blocks from compact batch factors.
Definition BatchJacobianFactor.h:49
virtual void addHessianBlock(DenseIndex rowOffset, DenseIndex columnOffset, DenseIndex rows, DenseIndex columns, const double *values)=0
Add a dense Hessian block at the given scalar offsets.
virtual void addRhsBlock(DenseIndex offset, DenseIndex dimension, const double *values)=0
Add a dense right-hand-side block at the given scalar offset.
Common interface for compact batch Jacobian factors.
Definition BatchJacobianFactor.h:78
void gradientAtZero(double *d) const override
Add the gradient evaluated at zero into a raw scalar buffer.
Definition BatchJacobianFactor.h:314
double deltaError(const VectorValues &values, double *oldError=nullptr, double *newError=nullptr) const override
Compute the error change by delegating to the dense compatibility factor.
Definition BatchJacobianFactor.h:128
Matrix augmentedJacobian() const override
Return the dense augmented Jacobian matrix for compatibility callers.
Definition BatchJacobianFactor.h:134
void updateHessian(const KeyVector &keys, SymmetricBlockMatrix *info) const override
Update the augmented Hessian using key lookup through the dense fallback.
Definition BatchJacobianFactor.h:174
double error(const VectorValues &values) const override
Compute the factor error by delegating to the dense compatibility factor.
Definition BatchJacobianFactor.h:123
GaussianFactor::shared_ptr negate() const override
Return a factor representing the negated linear system.
Definition BatchJacobianFactor.h:169
Matrix augmentedInformation() const override
Return the dense augmented information matrix for compatibility callers.
Definition BatchJacobianFactor.h:144
Vector gradient(Key key, const VectorValues &x) const override
Return the gradient block for one key at the given linearization point.
Definition BatchJacobianFactor.h:319
virtual const SharedDiagonal & get_model() const =0
Optional model on the compact factor. The intended fast path is whitened.
void print(const std::string &s="", const KeyFormatter &formatter=DefaultKeyFormatter) const override
Print this compact factor by converting to a JacobianFactor.
Definition BatchJacobianFactor.h:107
std::map< Key, Matrix > hessianBlockDiagonal() const override
Return the Hessian diagonal blocks keyed by variable.
Definition BatchJacobianFactor.h:164
void hessianDiagonalAdd(VectorValues &diagonal) const override
Add this factor's Hessian diagonal into the given VectorValues.
Definition BatchJacobianFactor.h:154
std::pair< Matrix, Vector > jacobian() const override
Return the dense Jacobian matrix and right-hand side vector.
Definition BatchJacobianFactor.h:139
VectorValues gradientAtZero() const override
Return the full gradient evaluated at zero.
Definition BatchJacobianFactor.h:309
virtual void gradientAtZeroAdd(const std::vector< size_t > &scalarOffsets, double *gradient) const override
Add this factor's gradient at zero to a flat vector.
Definition BatchJacobianFactor.h:257
GaussianFactor()
Inherit GaussianFactor constructors.
Definition GaussianFactor.h:49
virtual JacobianFactor toJacobianFactor() const =0
Convert compact storage to a dense-block JacobianFactor.
void multiplyHessianAdd(double alpha, const VectorValues &x, VectorValues &y) const override
Add the Hessian-vector product into y using the dense fallback.
Definition BatchJacobianFactor.h:303
void hessianDiagonal(double *diagonal) const override
Add this factor's Hessian diagonal into a raw scalar buffer.
Definition BatchJacobianFactor.h:159
virtual void multiplyHessianAdd(double alpha, const std::vector< size_t > &scalarOffsets, const double *x, double *y) const override
Add this factor's Hessian-vector product using scalar offsets for keys().
Definition BatchJacobianFactor.h:223
virtual size_t rows() const =0
Number of scalar rows represented by this factor.
Matrix information() const override
Return the dense information matrix for compatibility callers.
Definition BatchJacobianFactor.h:149
virtual void hessianBlockDiagonalAdd(const std::vector< size_t > &blockSlots, std::vector< Matrix > *diagonalBlocks) const override
Add this factor's Hessian diagonal to ordered blocks.
Definition BatchJacobianFactor.h:280
void updateHessian(const KeyVector &keys, SymmetricBlockMatrix *info, DenseIndex beginCol, DenseIndex endCol) const override
Update a column range of the augmented Hessian using the dense fallback.
Definition BatchJacobianFactor.h:297
bool equals(const GaussianFactor &factor, double tol=1e-9) const override
Test equality by comparing dense JacobianFactor representations.
Definition BatchJacobianFactor.h:114
const RhsVector & rowRhs(size_t rowIndex) const
Return the right-hand side for one compact row group.
Definition BatchJacobianFactor.h:1010
void updateHessian(const KeyVector &infoKeys, SymmetricBlockMatrix *info) const override
Add this factor's augmented information matrix to info.
Definition BatchJacobianFactor.h:1374
const SharedDiagonal & get_model() const override
Return the optional diagonal model on the stored rows.
Definition BatchJacobianFactor.h:990
void gradientAtZeroAdd(const std::vector< size_t > &scalarOffsets, double *gradient) const override
Add -A.transpose()*W*b directly to a flat gradient vector.
Definition BatchJacobianFactor.h:1070
size_t rows() const override
Return the number of scalar rows represented by all row groups.
Definition BatchJacobianFactor.h:987
BatchJacobianFactor(const KeyVector &keys, std::vector< size_t > keyDims, const SharedDiagonal &model=SharedDiagonal())
Construct an empty compact batch factor with known key dimensions.
Definition BatchJacobianFactor.h:932
const std::tuple_element< Slot, Blocks >::type::value_type & block(size_t rowIndex) const
Return one fixed-size Jacobian block from a compact row group.
Definition BatchJacobianFactor.h:1002
void hessianBlockDiagonalAdd(const std::vector< size_t > &blockSlots, std::vector< Matrix > *diagonalBlocks) const override
Add compact A.transpose()*W*A diagonal blocks without keyed maps.
Definition BatchJacobianFactor.h:1085
DenseIndex getDim(const_iterator variable) const override
Return the dimension of the variable at the given key iterator.
Definition BatchJacobianFactor.h:993
void reserve(size_t rowCount)
Reserve storage for row groups before repeated addRow() calls.
Definition BatchJacobianFactor.h:948
void addRow(const SlotIndices &slots, const std::vector< Matrix > &blocks, const Vector &rhs)
Add one row group corresponding to one original nonlinear factor.
Definition BatchJacobianFactor.h:962
JacobianFactor toJacobianFactor() const override
Convert compact row-block storage into a conventional JacobianFactor.
Definition BatchJacobianFactor.h:1107
void addUnaryRow(DenseIndex keySlot, const typename std::tuple_element< 0, Blocks >::type::value_type &block, const RhsVector &rhs)
Add one row to a unary compact batch without dynamic block containers.
Definition BatchJacobianFactor.h:975
void multiplyHessianAdd(double alpha, const std::vector< size_t > &scalarOffsets, const double *x, double *y) const override
Add alpha*A.transpose()*W*A*x directly to a flat output vector.
Definition BatchJacobianFactor.h:1052
GaussianFactor::shared_ptr clone() const override
Return a deep copy as a GaussianFactor.
Definition BatchJacobianFactor.h:942
void updateHessian(const KeyVector &infoKeys, SymmetricBlockMatrix *info, DenseIndex beginCol, DenseIndex endCol) const override
Add this factor's augmented information matrix over a block-column range.
Definition BatchJacobianFactor.h:1393
const std::vector< SlotIndices > & rowSlots() const
Return the compact key slot used by each row group and factor slot.
Definition BatchJacobianFactor.h:998
double deltaError(const VectorValues &values, double *oldError=nullptr, double *newError=nullptr) const override
Evaluate the linear-model error change directly from compact rows.
Definition BatchJacobianFactor.h:1013
void hessianDiagonalAdd(VectorValues &diagonal) const override
Accumulate the Hessian scalar diagonal directly from compact rows.
Definition BatchJacobianFactor.h:1035
Optional preindexed kernels for matrix-free Gaussian factors.
Definition FlatGaussianFactor.h:35
virtual DenseIndex getDim(const_iterator variable) const =0
Return the dimension of the variable pointed to by the given key iterator.
std::shared_ptr< This > shared_ptr
shared_ptr to this class
Definition GaussianFactor.h:42
GaussianFactor()
Default constructor creates empty factor.
Definition GaussianFactor.h:49
A Gaussian factor in the squared-error form.
Definition JacobianFactor.h:92
VectorValues represents a collection of vector-valued variables associated each with a unique integer...
Definition VectorValues.h:73
std::pair< VectorValues::iterator, bool > emplace(Key j, Args &&... args)
Emplace a vector value with key j.
Definition VectorValues.h:187
Vector & at(Key j)
Read/write access to the vector value with key j, throws std::out_of_range if j does not exist,...
Definition VectorValues.h:141