gtsam
Loading...
Searching...
No Matches
FixedJacobianFactor.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010-2026, 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
16
17#pragma once
18
19#include <gtsam/base/Manifold.h>
20#include <gtsam/base/timing.h>
22#include <gtsam/linear/internal/FixedJacobianFactorOps.h>
23
24#include <algorithm>
25#include <array>
26#include <memory>
27#include <type_traits>
28#include <utility>
29#include <vector>
30
31namespace gtsam {
32namespace internal {
33
35template <size_t I, int Default, int... Dimensions>
36struct DimensionAt : std::integral_constant<int, Default> {};
37
38template <size_t I, int Default, int First, int... Rest>
39struct DimensionAt<I, Default, First, Rest...>
40 : DimensionAt<I - 1, Default, Rest...> {};
41
42template <int Default, int First, int... Rest>
43struct DimensionAt<0, Default, First, Rest...>
44 : std::integral_constant<int, First> {};
45
47template <typename OutputVec, typename... ValueTypes>
49 inline constexpr static size_t arity = sizeof...(ValueTypes);
50 inline constexpr static int M = traits<OutputVec>::dimension;
51 inline constexpr static std::array<int, arity + 1> dimensions{
53 template <size_t I>
54 inline constexpr static int N = dimensions.at(I + 1);
55 inline constexpr static bool allFixed =
56 M != Eigen::Dynamic &&
57 ((traits<ValueTypes>::dimension != Eigen::Dynamic) && ...);
58};
59
61template <typename Dimensions, size_t... Is>
62bool dimensionsMatch(const std::vector<Matrix>& jacobians,
63 std::index_sequence<Is...>) {
64 return ((jacobians[Is].rows() == Dimensions::M &&
65 jacobians[Is].cols() == Dimensions::template N<Is>) &&
66 ...);
67}
68
69} // namespace internal
70
75template <int M, int... Ns>
78 inline constexpr static size_t arity = sizeof...(Ns);
79
81 inline constexpr static std::array<int, arity> dimensions{Ns...};
82
83 private:
84 template <size_t I>
85 inline constexpr static int N = internal::DimensionAt<I, 1, Ns...>::value;
86
87 inline constexpr static std::array<DenseIndex, arity + 1> offsets = [] {
88 std::array<DenseIndex, arity + 1> result{};
89 for (size_t i = 0; i < arity; ++i) {
90 result[i + 1] = result[i] + dimensions[i];
91 }
92 return result;
93 }();
94
95 static VerticalBlockMatrix augmentedMatrix(
96 const std::vector<Matrix>& jacobians, const Vector& b) {
97 if (jacobians.size() != arity || b.size() != M) {
98 throw std::invalid_argument(
99 "FixedJacobianFactor: error or Jacobian count mismatch");
100 }
101
102 const std::array<size_t, arity> blockDimensions{static_cast<size_t>(Ns)...};
103 VerticalBlockMatrix result(blockDimensions, M, true);
104 for (size_t i = 0; i < arity; ++i) {
105 if (jacobians[i].rows() != M || jacobians[i].cols() != dimensions[i]) {
106 throw std::invalid_argument(
107 "FixedJacobianFactor: Jacobian dimension mismatch");
108 }
109 result(i) = jacobians[i];
110 }
111 result(arity) = b;
112 return result;
113 }
114
115 template <size_t I>
116 internal::FixedJacobianBlock<M, N<I>> block() const {
117 return internal::FixedJacobianBlock<M, N<I>>(Ab_.matrix(), 0, offsets[I]);
118 }
119
120 template <size_t... Is>
121 void hessianDiagonalAdd(VectorValues& diagonal,
122 std::index_sequence<Is...>) const {
123 (internal::accumulateHessianDiagonal<M, N<Is>>(keys_[Is], block<Is>(),
124 diagonal),
125 ...);
126 }
127
128 template <size_t... Is>
129 void accumulateResidual(const VectorValues& values,
130 Eigen::Matrix<double, M, 1>& residual,
131 std::index_sequence<Is...>) const {
132 (internal::accumulateResidual<M, N<Is>>(block<Is>(), values.at(keys_[Is]),
133 residual),
134 ...);
135 }
136
137 template <size_t... Is>
138 std::array<DenseIndex, arity> slots(const KeyVector& infoKeys,
139 std::index_sequence<Is...>) const {
140 return {{Slot(infoKeys, keys_[Is])...}};
141 }
142
143 template <size_t I, size_t... Js>
144 void updateCrossHessiansFor(const std::array<DenseIndex, arity>& factorSlots,
145 SymmetricBlockMatrix* info,
146 std::index_sequence<Js...>) const {
147 (internal::updateCrossHessian<M, N<I>, N<I + Js + 1>>(
148 factorSlots[I], block<I>(), factorSlots[I + Js + 1],
149 block<I + Js + 1>(), info),
150 ...);
151 }
152
153 template <size_t... Is>
154 void updateCrossHessians(const std::array<DenseIndex, arity>& factorSlots,
155 SymmetricBlockMatrix* info,
156 std::index_sequence<Is...>) const {
157 (updateCrossHessiansFor<Is>(factorSlots, info,
158 std::make_index_sequence<arity - Is - 1>{}),
159 ...);
160 }
161
162 template <size_t... Is>
163 void updateJacobianHessians(const std::array<DenseIndex, arity>& factorSlots,
164 DenseIndex slotB,
165 const internal::FixedJacobianBlock<M, 1>& b,
166 SymmetricBlockMatrix* info,
167 std::index_sequence<Is...>) const {
168 (internal::updateJacobianHessian<M, N<Is>>(factorSlots[Is], block<Is>(),
169 slotB, b, info),
170 ...);
171 }
172
173 template <size_t I, typename OwnsColumn, size_t... Js>
174 void updateOwnedCrossHessiansFor(
175 const std::array<DenseIndex, arity>& factorSlots,
176 const OwnsColumn& ownsColumn, SymmetricBlockMatrix* info,
177 std::index_sequence<Js...>) const {
178 ((ownsColumn(std::max(factorSlots[I], factorSlots[I + Js + 1]))
179 ? static_cast<void>(
180 internal::updateCrossHessian<M, N<I>, N<I + Js + 1>>(
181 factorSlots[I], block<I>(), factorSlots[I + Js + 1],
182 block<I + Js + 1>(), info))
183 : static_cast<void>(0)),
184 ...);
185 }
186
187 template <typename OwnsColumn, size_t... Is>
188 void updateOwnedCrossHessians(
189 const std::array<DenseIndex, arity>& factorSlots,
190 const OwnsColumn& ownsColumn, SymmetricBlockMatrix* info,
191 std::index_sequence<Is...>) const {
192 (updateOwnedCrossHessiansFor<Is>(
193 factorSlots, ownsColumn, info,
194 std::make_index_sequence<arity - Is - 1>{}),
195 ...);
196 }
197
198 template <typename OwnsColumn, size_t... Is>
199 void updateOwnedSelfAndRhsHessians(
200 const std::array<DenseIndex, arity>& factorSlots, DenseIndex slotB,
201 const internal::FixedJacobianBlock<M, 1>& b, const OwnsColumn& ownsColumn,
202 SymmetricBlockMatrix* info, std::index_sequence<Is...>) const {
203 ((ownsColumn(factorSlots[Is])
204 ? static_cast<void>(internal::updateSelfHessian<M, N<Is>>(
205 factorSlots[Is], block<Is>(), info))
206 : static_cast<void>(0)),
207 ...);
208 ((ownsColumn(std::max(factorSlots[Is], slotB))
209 ? static_cast<void>(internal::updateCrossHessian<M, N<Is>, 1>(
210 factorSlots[Is], block<Is>(), slotB, b, info))
211 : static_cast<void>(0)),
212 ...);
213 }
214
215 public:
216 static_assert(arity > 0 && M > 0 && ((Ns > 0) && ...),
217 "Fixed factor arity and dimensions must be positive");
218
226 const std::vector<Matrix>& jacobians, const Vector& b,
227 const SharedDiagonal& model = SharedDiagonal())
228 : JacobianFactor(keys, augmentedMatrix(jacobians, b), model) {}
229
231 void hessianDiagonalAdd(VectorValues& diagonal) const override {
232 const SharedDiagonal& model = get_model();
233 if (model && !model->isUnit()) {
235 return;
236 }
237 hessianDiagonalAdd(diagonal, std::make_index_sequence<arity>{});
238 }
239
241 double deltaError(const VectorValues& values, double* oldError = nullptr,
242 double* newError = nullptr) const override {
243 const SharedDiagonal& model = get_model();
244 if (model && !model->isUnit()) {
245 return JacobianFactor::deltaError(values, oldError, newError);
246 }
247
248 const internal::FixedJacobianBlock<M, 1> b(Ab_.matrix(), 0, offsets[arity]);
249 Eigen::Matrix<double, M, 1> error = -b;
250 accumulateResidual(values, error, std::make_index_sequence<arity>{});
251
252 const double oldValue = 0.5 * b.squaredNorm();
253 const double newValue = 0.5 * error.squaredNorm();
254 if (oldError) *oldError = oldValue;
255 if (newError) *newError = newValue;
256 return oldValue - newValue;
257 }
258
260 void updateHessian(const KeyVector& infoKeys,
261 SymmetricBlockMatrix* info) const override {
262 gttic(updateHessian_FixedJacobianFactor);
263 const SharedDiagonal& model = get_model();
264 if (model && !model->isUnit()) {
265 if (model->isConstrained()) {
266 throw std::invalid_argument(
267 "FixedJacobianFactor::updateHessian: cannot update information "
268 "with constrained noise model");
269 }
270 std::vector<Matrix> whitenedJacobians;
271 whitenedJacobians.reserve(arity);
272 for (auto variable = begin(); variable != end(); ++variable) {
273 whitenedJacobians.push_back(model->Whiten(getA(variable)));
274 }
275 const FixedJacobianFactor whitenedFactor(keys_, whitenedJacobians,
276 model->whiten(getb()));
277 whitenedFactor.updateHessian(infoKeys, info);
278 return;
279 }
280
281 const auto factorSlots = slots(infoKeys, std::make_index_sequence<arity>{});
282 const DenseIndex slotB = info->nBlocks() - 1;
283 const internal::FixedJacobianBlock<M, 1> b(Ab_.matrix(), 0, offsets[arity]);
284 updateJacobianHessians(factorSlots, slotB, b, info,
285 std::make_index_sequence<arity>{});
286 if constexpr (arity > 1) {
287 updateCrossHessians(factorSlots, info,
288 std::make_index_sequence<arity - 1>{});
289 }
290 internal::updateSelfHessian<M, 1>(slotB, b, info);
291 }
292
294 void updateHessian(const KeyVector& infoKeys, SymmetricBlockMatrix* info,
295 DenseIndex beginCol, DenseIndex endCol) const override {
296 gttic(updateHessianRange_FixedJacobianFactor);
297 const SharedDiagonal& model = get_model();
298 if (model && !model->isUnit()) {
299 JacobianFactor::updateHessian(infoKeys, info, beginCol, endCol);
300 return;
301 }
302
303 const auto factorSlots = slots(infoKeys, std::make_index_sequence<arity>{});
304 const DenseIndex slotB = info->nBlocks() - 1;
305 const internal::BlockColumnRange ownsColumn{beginCol, endCol};
306 const internal::FixedJacobianBlock<M, 1> b(Ab_.matrix(), 0, offsets[arity]);
307 updateOwnedSelfAndRhsHessians(factorSlots, slotB, b, ownsColumn, info,
308 std::make_index_sequence<arity>{});
309 if constexpr (arity > 1) {
310 updateOwnedCrossHessians(factorSlots, ownsColumn, info,
311 std::make_index_sequence<arity - 1>{});
312 }
313 if (ownsColumn(slotB)) {
314 internal::updateSelfHessian<M, 1>(slotB, b, info);
315 }
316 }
317};
318
319template <int M, int... Ns>
321 : Testable<FixedJacobianFactor<M, Ns...>> {};
322
323namespace internal {
324
326template <int M, int... Ns>
329 inline constexpr static bool available =
330 M != Eigen::Dynamic && ((Ns != Eigen::Dynamic) && ...);
331
333 static std::shared_ptr<GaussianFactor> create(
334 const KeyVector& keys, const std::vector<Matrix>& jacobians,
335 const Vector& b, const SharedDiagonal& model) {
336 return std::make_shared<FixedJacobianFactor<M, Ns...>>(keys, jacobians, b,
337 model);
338 }
339};
340
341} // namespace internal
342} // namespace gtsam
Timing utilities.
Base class and basic functions for Manifold types.
bool dimensionsMatch(const std::vector< Matrix > &jacobians, std::index_sequence< Is... >)
Check dynamic Jacobian blocks against their compile-time dimensions.
Definition FixedJacobianFactor.h:62
Global functions in a separate testing namespace.
Definition chartTesting.h:28
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
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
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
DenseIndex nBlocks() const
Block count.
Definition SymmetricBlockMatrix.h:162
A helper that implements the traits interface for GTSAM types.
Definition Testable.h:152
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
const_iterator end() const
Iterator at end of involved variable keys.
Definition Factor.h:149
Return the element at I in a compile-time integer pack, or Default.
Definition FixedJacobianFactor.h:36
Compile-time residual and variable dimensions for a factor of any arity.
Definition FixedJacobianFactor.h:48
A JacobianFactor whose residual and variable dimensions are known at compile time.
Definition FixedJacobianFactor.h:76
FixedJacobianFactor(const KeyVector &keys, const std::vector< Matrix > &jacobians, const Vector &b, const SharedDiagonal &model=SharedDiagonal())
Construct from keys, Jacobian blocks, and a right-hand side.
Definition FixedJacobianFactor.h:225
static constexpr std::array< int, arity > dimensions
Compile-time dimension of each variable block.
Definition FixedJacobianFactor.h:81
void updateHessian(const KeyVector &infoKeys, SymmetricBlockMatrix *info) const override
Update the complete augmented Hessian using fixed-size products.
Definition FixedJacobianFactor.h:260
void hessianDiagonalAdd(VectorValues &diagonal) const override
Add the Hessian diagonal using fixed-size matrix operations.
Definition FixedJacobianFactor.h:231
void updateHessian(const KeyVector &infoKeys, SymmetricBlockMatrix *info, DenseIndex beginCol, DenseIndex endCol) const override
Update selected augmented-Hessian block columns.
Definition FixedJacobianFactor.h:294
double deltaError(const VectorValues &values, double *oldError=nullptr, double *newError=nullptr) const override
Compute the error change using a fixed-size residual.
Definition FixedJacobianFactor.h:241
static constexpr size_t arity
Number of variable blocks in the factor.
Definition FixedJacobianFactor.h:78
Construct the fixed-size Jacobian factor for a dimension pack.
Definition FixedJacobianFactor.h:327
static constexpr bool available
Whether every factor dimension is fixed at compile time.
Definition FixedJacobianFactor.h:329
static std::shared_ptr< GaussianFactor > create(const KeyVector &keys, const std::vector< Matrix > &jacobians, const Vector &b, const SharedDiagonal &model)
Construct a fixed-size factor behind the GaussianFactor interface.
Definition FixedJacobianFactor.h:333
const constBVector getb() const
Get a view of the r.h.s.
Definition JacobianFactor.h:344
const SharedDiagonal & get_model() const
get a copy of model
Definition JacobianFactor.h:338
JacobianFactor(const GaussianFactor &gf)
Convert from other GaussianFactor.
Definition JacobianFactor.cpp:51
void hessianDiagonalAdd(VectorValues &d) const override
Add the current diagonal to a VectorValues instance.
Definition JacobianFactor.cpp:625
double deltaError(const VectorValues &c, double *oldError=nullptr, double *newError=nullptr) const override
Compute the change in error from zero to c, optionally returning the old and new errors.
Definition JacobianFactor.cpp:579
double error(const VectorValues &c) const override
0.5*(A*x-b)'D(A*x-b).
Definition JacobianFactor.cpp:570
size_t rows() const
return the number of rows in the corresponding linear system
Definition JacobianFactor.h:330
size_t cols() const
return the number of columns in the corresponding linear system
Definition JacobianFactor.h:335
void updateHessian(const KeyVector &keys, SymmetricBlockMatrix *info) const override
Update an information matrix by adding the information corresponding to this factor (used internally ...
Definition JacobianFactor.cpp:673
constABlock getA(const_iterator variable) const
Get a view of the A matrix for the variable pointed to by the given key iterator.
Definition JacobianFactor.h:347
VectorValues represents a collection of vector-valued variables associated each with a unique integer...
Definition VectorValues.h:73