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.");
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>
369 std::vector<BlockMatrix<BlockDim>,
370 Eigen::aligned_allocator<BlockMatrix<BlockDim>>>;
371 using Blocks = std::tuple<BlockVector<BlockDims>...>;
374 std::vector<size_t> keyDims_;
375 std::vector<SlotIndices> rowSlots_;
377 std::vector<RhsVector, Eigen::aligned_allocator<RhsVector>> rhs_;
378 SharedDiagonal model_;
381 template <
size_t... Indices>
382 void reserveBlocks(
size_t rowCount, std::index_sequence<Indices...>) {
383 (std::get<Indices>(blocks_).reserve(rowCount), ...);
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.");
396 std::get<Slot>(blocks_).push_back(
block);
400 template <
size_t... Indices>
401 void addBlocks(
const std::vector<Matrix>& blocks,
402 std::index_sequence<Indices...>) {
403 (addBlock<Indices>(blocks[Indices]), ...);
407 template <
size_t Slot>
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];
419 template <
size_t... Indices>
421 std::index_sequence<Indices...>)
const {
422 (copyBlockToDense<Indices>(rowIndex, dense), ...);
426 template <
size_t Slot>
427 void scatterBlock(
size_t rowIndex,
size_t rowOffset,
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)
438 ErrorDim, BlockDim) = std::get<Slot>(blocks_)[rowIndex];
442 template <
size_t... Indices>
443 void scatterBlocks(
size_t rowIndex,
size_t rowOffset,
445 const std::vector<DenseIndex>& targetBlockIndices,
446 std::index_sequence<Indices...>)
const {
447 (scatterBlock<Indices>(rowIndex, rowOffset, target, targetBlockIndices),
464 template <
size_t Slot>
465 void updateAugmentedDiagonal(
size_t rowIndex,
DenseIndex targetSlot,
467 const RhsVector* weights,
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()
476 if (targetScalarOffset >= 0) {
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;
488 contribution.noalias() = A.transpose() * weights->asDiagonal() * A;
490 contribution.noalias() = A.transpose() * A;
492 if (targetScalarOffset >= 0) {
502 template <
typename MatrixType>
506 const MatrixType&
block,
508 assert((targetI != targetJ) &&
509 "BatchJacobianFactor: duplicate mapped Hessian slots are not "
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) {
517 scalarOffsetJ,
block);
520 scalarOffsetJ, scalarOffsetI,
block.transpose());
524 if (targetI < targetJ) {
538 template <
size_t I,
size_t J>
539 void updateAugmentedOffDiagonal(
size_t rowIndex,
DenseIndex targetI,
542 const RhsVector* weights,
544 static_assert(I < J,
"BatchJacobianFactor expects upper-triangular order.");
545 if constexpr (J == NumSlots) {
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;
553 const RhsVector weightedRhs = weights->asDiagonal() * b;
554 contribution.noalias() = A.transpose() * weightedRhs;
556 contribution.noalias() = A.transpose() * b;
558 updateOffDiagonalNormalized(targetI, targetJ, scalarOffsetI,
559 scalarOffsetJ, contribution, info);
562 typename std::tuple_element<I, Blocks>::type::value_type;
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;
571 contribution.noalias() = Ai.transpose() * weights->asDiagonal() * Aj;
573 contribution.noalias() = Ai.transpose() * Aj;
575 updateOffDiagonalNormalized(targetI, targetJ, scalarOffsetI,
576 scalarOffsetJ, contribution, info);
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,
586 std::index_sequence<Is...>)
const {
593 ((mappedSlots[Is] >= 0 && targetSlot >= 0 &&
594 slotInRange(std::max(mappedSlots[Is], targetSlot), beginCol,
596 ? updateAugmentedOffDiagonal<Is, J>(
597 rowIndex, mappedSlots[Is], targetSlot,
598 mappedScalarOffsets ? mappedScalarOffsets[Is] : -1,
599 mappedScalarOffsets ? mappedScalarOffsets[J] : -1, weights,
607 void updateMappedAugmentedColumn(
size_t rowIndex,
610 const RhsVector* weights,
615 if (slotInRange(targetSlot, beginCol, endCol)) {
616 updateAugmentedDiagonal<J>(
617 rowIndex, targetSlot,
618 mappedScalarOffsets ? mappedScalarOffsets[J] : -1, weights, info);
620 updateMappedPreviousAugmentedSlots<J>(
621 rowIndex, mappedSlots, mappedScalarOffsets, weights, info, beginCol,
622 endCol, std::make_index_sequence<J>{});
626 template <
size_t... Js>
627 void updateMappedAugmentedColumns(
size_t rowIndex,
630 const RhsVector* weights,
633 std::index_sequence<Js...>)
const {
634 (updateMappedAugmentedColumn<Js>(rowIndex, mappedSlots, mappedScalarOffsets,
635 weights, info, beginCol, endCol),
640 void updateMappedHessianRow(
size_t rowIndex,
const DenseIndex* mappedSlots,
642 const RhsVector* weights,
645 updateMappedAugmentedColumns(rowIndex, mappedSlots, mappedScalarOffsets,
646 weights, info, beginCol, endCol,
647 std::make_index_sequence<NumSlots + 1>{});
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);
658 *destination += xpr.eval();
663 template <
size_t I,
size_t J>
664 void updateFrontalHessianBlock(
size_t rowIndex,
const DenseIndex* mappedSlots,
665 const RhsVector* weights,
668 static_assert(I < NumSlots);
669 static_assert(J <= NumSlots);
672 if (targetI < 0 || targetI >= numFrontalBlocks || targetJ < 0)
return;
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];
681 addFrontalBlock(&destination,
682 Ai.transpose() * weights->asDiagonal() * b);
684 addFrontalBlock(&destination, Ai.transpose() * b);
687 const auto& Aj = std::get<J>(blocks_)[rowIndex];
689 addFrontalBlock(&destination,
690 Ai.transpose() * weights->asDiagonal() * Aj);
692 addFrontalBlock(&destination, Ai.transpose() * Aj);
698 template <
size_t I,
size_t... Js>
699 void updateFrontalHessianRowBlock(
size_t rowIndex,
701 const RhsVector* weights,
704 std::index_sequence<Js...>)
const {
705 (updateFrontalHessianBlock<I, Js>(rowIndex, mappedSlots, weights,
706 numFrontalBlocks, frontalRows),
711 template <
size_t... Is>
712 void updateFrontalHessianRowGroup(
size_t rowIndex,
714 const RhsVector* weights,
717 std::index_sequence<Is...>)
const {
718 (updateFrontalHessianRowBlock<Is>(rowIndex, mappedSlots, weights,
719 numFrontalBlocks, frontalRows,
720 std::make_index_sequence<NumSlots + 1>{}),
725 RhsVector rowWeights(
size_t rowIndex)
const {
726 RhsVector weights = RhsVector::Ones();
727 if (!model_ || model_->isUnit())
return weights;
729 const auto constrained =
730 model_->isConstrained()
731 ? std::dynamic_pointer_cast<noiseModel::Constrained>(model_)
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);
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 {
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;
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), ...);
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]);
776 template <
size_t... Slots>
777 void addVectorValuesRowBlocks(
size_t rowIndex,
const VectorValues& values,
779 std::index_sequence<Slots...>)
const {
780 (addVectorValuesRowBlock<Slots>(rowIndex, values, residual), ...);
784 template <
size_t Slot>
785 void hessianDiagonalVectorRowAdd(
size_t rowIndex,
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();
794 template <
size_t... Slots>
795 void hessianDiagonalVectorRowAdds(
size_t rowIndex,
VectorValues* diagonal,
796 std::index_sequence<Slots...>)
const {
797 (hessianDiagonalVectorRowAdd<Slots>(rowIndex, diagonal), ...);
801 template <
size_t Slot>
802 void transposeRowBlockAdd(
size_t rowIndex,
803 const std::vector<size_t>& scalarOffsets,
804 double alpha,
const RhsVector& residual,
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]);
813 alpha * std::get<Slot>(blocks_)[rowIndex].transpose() * residual;
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),
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;
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,
851 template <
size_t I,
size_t J>
852 void updateSparseNormalBlock(
853 size_t rowIndex,
const std::vector<DenseIndex>& scalarOffsets,
854 const RhsVector& weights,
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]);
867 scalarOffsets[keySlotJ], BlockDimI, BlockDimJ,
868 contribution.data());
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,
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>{}),
896 template <
size_t Slot>
897 void updateSparseNormalRhs(
898 size_t rowIndex,
const std::vector<DenseIndex>& scalarOffsets,
899 const RhsVector& weightedRhs,
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());
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,
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.");
943 return std::static_pointer_cast<GaussianFactor>(
944 std::make_shared<This>(*
this));
949 rowSlots_.reserve(rowCount);
950 rhs_.reserve(rowCount);
951 reserveBlocks(rowCount, std::make_index_sequence<NumSlots>{});
962 void addRow(
const SlotIndices& slots,
const std::vector<Matrix>& blocks,
964 if (blocks.size() != NumSlots || rhs.size() != ErrorDim) {
965 throw std::invalid_argument(
966 "BatchJacobianFactor::addRow: incompatible row dimensions.");
968 rowSlots_.push_back(slots);
969 addBlocks(blocks, std::make_index_sequence<NumSlots>{});
970 RhsVector fixedRhs = rhs;
971 rhs_.push_back(fixedRhs);
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);
987 size_t rows()
const override {
return rhs_.size() * ErrorDim; }
990 const SharedDiagonal&
get_model()
const override {
return model_; }
998 const std::vector<SlotIndices>&
rowSlots()
const {
return rowSlots_; }
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);
1010 const RhsVector&
rowRhs(
size_t rowIndex)
const {
return rhs_.at(rowIndex); }
1014 double* newError =
nullptr)
const override {
1015 if (model_ && !model_->isUnit()) {
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();
1029 if (oldError) *oldError = oldValue;
1030 if (newError) *newError = newValue;
1031 return oldValue - newValue;
1036 if (model_ && !model_->isUnit()) {
1040 for (
size_t position = 0; position <
keys_.size(); ++position) {
1041 auto [entry, inserted] =
1043 if (inserted) entry->second.setZero();
1045 for (
size_t row = 0; row < rowSlots_.size(); ++row) {
1046 hessianDiagonalVectorRowAdds(row, &diagonal,
1047 std::make_index_sequence<NumSlots>{});
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.");
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>{});
1072 if (scalarOffsets.size() !=
keys_.size()) {
1073 throw std::invalid_argument(
1074 "BatchJacobianFactor::gradientAtZeroAdd: offset count mismatch.");
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>{});
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 "
1093 for (
size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1094 hessianBlockDiagonalRowAdds(rowIndex, blockSlots, rowWeights(rowIndex),
1096 std::make_index_sequence<NumSlots>{});
1110 dense.
matrix().setZero();
1111 for (
size_t rowIndex = 0; rowIndex < rowSlots_.size(); ++rowIndex) {
1112 copyBlocksToDense(rowIndex, &dense, std::make_index_sequence<NumSlots>{});
1114 .block(
static_cast<DenseIndex>(rowIndex * ErrorDim), 0, ErrorDim, 1) =
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.");
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];
1144 void updateSparseNormal(
1145 const std::vector<DenseIndex>& scalarOffsets,
1146 internal::SparseNormalAccumulator* accumulator)
const override {
1148 throw std::invalid_argument(
1149 "BatchJacobianFactor::updateSparseNormal: null accumulator.");
1151 if (scalarOffsets.size() !=
keys_.size()) {
1152 throw std::invalid_argument(
1153 "BatchJacobianFactor::updateSparseNormal: offset count mismatch.");
1155 if (model_ && !model_->isUnit() && model_->isConstrained()) {
1156 throw std::invalid_argument(
1157 "BatchJacobianFactor::updateSparseNormal: constrained noise model "
1158 "is not supported.");
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,
1167 std::make_index_sequence<NumSlots>{});
1177 void updateHessian(
const std::vector<DenseIndex>& slotIndices,
1178 SymmetricBlockMatrix* info)
const override {
1179 if (
rows() == 0)
return;
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,
1189 if (slotIndices.size() !=
keys_.size()) {
1190 throw std::invalid_argument(
1191 "BatchJacobianFactor::updateHessian: slot index count mismatch.");
1194 std::vector<DenseIndex> slots = slotIndices;
1195 slots.push_back(rhsSlot);
1196 buildMappedSlots(slots, mappedSlots);
1197 updateHessianWithMappedSlots(mappedSlots,
nullptr, info, 0,
1207 void updateHessian(
const std::vector<DenseIndex>& slotIndices,
1208 SymmetricBlockMatrix* info,
DenseIndex beginCol,
1210 if (
rows() == 0)
return;
1211 if (slotIndices.size() !=
keys_.size()) {
1212 throw std::invalid_argument(
1213 "BatchJacobianFactor::updateHessian: slot index count mismatch.");
1216 std::vector<DenseIndex> slots;
1217 slots.reserve(slotIndices.size() + 1);
1218 bool foundCol =
false;
1220 slots.push_back(slot);
1221 if (slotInRange(slot, beginCol, endCol)) foundCol =
true;
1223 slots.push_back(info->nBlocks() - 1);
1224 if (slotInRange(slots.back(), beginCol, endCol)) foundCol =
true;
1225 if (!foundCol)
return;
1227 std::vector<DenseIndex> mappedSlots;
1228 buildMappedSlots(slots, mappedSlots);
1229 updateHessianWithMappedSlots(mappedSlots,
nullptr, info, beginCol, endCol);
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.");
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]];
1246 *out++ = slotIndices.back();
1251 void updateHessianWithMappedSlots(
const std::vector<DenseIndex>& mappedSlots,
1252 SymmetricBlockMatrix* info)
const override {
1253 updateHessianWithMappedSlots(mappedSlots,
nullptr, info, 0,
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,
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.");
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;
1283 updateFrontalHessianRowGroup(rowIndex, rowSlotsPtr + rowIndex * stride,
1284 weightsPtr, numFrontalBlocks, frontalRows,
1285 std::make_index_sequence<NumSlots>{});
1291 void updateHessianWithMappedSlots(
1292 const std::vector<DenseIndex>& mappedSlots,
1293 const std::vector<DenseIndex>* mappedScalarOffsets,
1294 SymmetricBlockMatrix* info,
DenseIndex beginCol,
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");
1303 const size_t stride = NumSlots + 1;
1304 if (mappedSlots.size() != rowSlots_.size() * stride) {
1305 throw std::invalid_argument(
1306 "BatchJacobianFactor::updateHessianWithMappedSlots: mapped slot "
1309 if (mappedScalarOffsets &&
1310 mappedScalarOffsets->size() != mappedSlots.size()) {
1311 throw std::invalid_argument(
1312 "BatchJacobianFactor::updateHessianWithMappedSlots: mapped scalar "
1313 "offset count mismatch.");
1318 if (slot > rhsSlot) {
1319 throw std::invalid_argument(
1320 "BatchJacobianFactor::updateHessianWithMappedSlots: invalid "
1321 "mapped slot index.");
1325 if (mappedScalarOffsets) {
1326 for (
size_t i = 0; i < mappedSlots.size(); ++i) {
1328 const DenseIndex scalarOffset = (*mappedScalarOffsets)[i];
1329 assert((slot < 0 && scalarOffset < 0) ||
1330 (slot >= 0 && scalarOffset == info->blockScalarOffset(slot)));
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))
1344 weightsPtr = &weights;
1345 updateMappedHessianRow(
1346 rowIndex, mappedSlots.data() + rowIndex * stride,
1348 ? mappedScalarOffsets->data() + rowIndex * stride
1350 weightsPtr, info, beginCol, endCol);
1355 const DenseIndex* rowSlotsPtr = mappedSlots.data();
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);
1376 std::vector<DenseIndex> slots;
1377 slots.reserve(
keys_.size() + 1);
1379 slots.push_back(Slot(infoKeys, key));
1381 slots.push_back(info->
nBlocks() - 1);
1382 updateHessian(slots, info);
1395 std::vector<DenseIndex> slots;
1396 slots.reserve(
keys_.size());
1398 slots.push_back(Slot(infoKeys, key));
1400 updateHessian(slots, info, beginCol, endCol);