29#include <Eigen/StdVector>
48template <
typename FactorType,
typename K1,
typename K2,
typename Meas,
49 typename Model,
typename... Args>
50static FactorType createFactor(K1 k1, K2 k2,
const Meas& z,
const Model& model,
52 if constexpr (std::is_constructible_v<FactorType, K1, K2, Meas, Model,
54 return FactorType(k1, k2, z, model, std::forward<Args>(args)...);
55 }
else if constexpr (std::is_constructible_v<FactorType, Meas, Model, K1, K2,
57 return FactorType(z, model, k1, k2, std::forward<Args>(args)...);
58 }
else if constexpr (std::is_constructible_v<FactorType, K1, K2, Meas,
60 return FactorType(k1, k2, z, std::forward<Args>(args)..., model);
65 std::is_constructible_v<FactorType, K1, K2, Meas, Model, Args...>,
66 "BatchFactor: Could not find a matching constructor for FactorType. "
67 "Tried: (K1, K2, Z, Model, Args...), (Z, Model, K1, K2, Args...), (K1, "
68 "K2, Z, Args..., Model)");
69 return FactorType(k1, k2, z, model, std::forward<Args>(args)...);
114template <
typename FactorType,
int ErrorDim>
118 static_assert(std::is_base_of<NoiseModelFactor, FactorType>::value,
119 "FactorType must derive from NoiseModelFactor");
123 using shared_ptr = std::shared_ptr<This>;
125 std::vector<FactorType, Eigen::aligned_allocator<FactorType>>;
128 using Allocator =
typename FactorVector::allocator_type;
129 FactorVector factors_;
136 std::vector<KeyInfo> keyInfo_;
137 std::vector<std::array<DenseIndex, FactorType::N>> indices_;
138 bool useHessianFactor_{
false};
139 bool allDimensionsFixed_{
false};
140 bool hasConstrainedNoiseModel_{
false};
141 bool allNoiseModelsAreUnit_{
true};
142 std::vector<size_t> keyDimensions_;
144 template <
size_t... Is>
145 static constexpr bool hasFixedDimensions(std::index_sequence<Is...>) {
147 (
traits<
typename FactorType::template ValueType<Is + 1>>::dimension !=
152 template <
size_t... Is>
153 size_t keyDimensionFromSlot(
const Key key,
size_t slot,
const Values& values,
154 std::index_sequence<Is...>)
const {
155 const std::array<size_t, FactorType::N> dims = {
157 values.
at<
typename FactorType::template ValueType<Is + 1>>(key))...};
159 if (slot >= dims.size()) {
160 throw std::runtime_error(
161 "BatchFactor::keyDimensionFromSlot: slot out of range.");
163 const size_t dimension = dims[slot];
167 const std::vector<size_t>& keyDimensions()
const {
168 if (!allDimensionsFixed_) {
169 throw std::runtime_error(
170 "BatchFactor::keyDimensions: cached dimensions unavailable for dynamic "
173 return keyDimensions_;
176 std::vector<size_t> keyDimensions(
const Values& values)
const {
177 std::vector<size_t> dims;
178 dims.reserve(keyInfo_.size());
179 for (
const auto& info : keyInfo_) {
181 dims.push_back(
static_cast<size_t>(info.dim));
183 constexpr auto slots = std::make_index_sequence<FactorType::N>{};
184 const size_t dim = keyDimensionFromSlot(info.key, info.slot, values, slots);
186 throw std::runtime_error(
187 "BatchFactor::keyDimensions: cannot determine dynamic key "
196 std::shared_ptr<JacobianFactor> linearizeToJacobianFactor(
197 const Values& values)
const {
198 const size_t total_rows = factors_.size() * ErrorDim;
199 const std::vector<size_t>& keyDims =
200 allDimensionsFixed_ ? keyDimensions() : keyDimensions(values);
204 std::vector<Matrix> H(FactorType::N);
207 if (hasConstrainedNoiseModel_) {
208 rowSigmas = Vector::Ones(total_rows);
209 rowMus = Vector::Constant(total_rows, 1000.0);
211 for (
size_t i = 0; i < factors_.size(); ++i) {
212 const auto& factor = factors_[i];
213 const size_t row_start = i * ErrorDim;
214 Vector rhs = -factor.unwhitenedError(values, H);
216 const auto noise = factor.noiseModel();
217 if (!allNoiseModelsAreUnit_ && noise && !noise->isUnit()) {
218 factor.noiseModel()->WhitenSystem(H, rhs);
221 if (hasConstrainedNoiseModel_ && noise && noise->isConstrained()) {
222 auto constrainedModel =
223 std::dynamic_pointer_cast<noiseModel::Constrained>(noise);
224 if (!constrainedModel) {
225 throw std::runtime_error(
226 "BatchFactor::linearizeToJacobianFactor: unsupported constrained "
227 "noise model type.");
229 for (
size_t r = 0; r < ErrorDim; ++r) {
230 const size_t row = row_start + r;
231 if (constrainedModel->constrained(r)) {
232 rowMus[row] = constrainedModel->mu()[r];
233 rowSigmas[row] = 0.0;
235 rowSigmas[row] = 1.0;
240 const auto& indices_i = indices_[i];
241 for (
size_t k = 0; k < FactorType::N; ++k) {
243 Ab(index).block(row_start, 0, ErrorDim, H[k].cols()) = H[k];
245 Ab(
keys().
size()).block(row_start, 0, ErrorDim, 1) = rhs;
249 if (hasConstrainedNoiseModel_) {
250 jacobianModel = std::static_pointer_cast<noiseModel::Diagonal>(
253 return std::make_shared<JacobianFactor>(
254 keys(), std::move(Ab), jacobianModel);
257 template <
size_t... Is>
258 std::shared_ptr<GaussianFactor> linearizeToBatchJacobian(
259 const Values& values, std::index_sequence<Is...>)
const {
264 auto batch = std::make_shared<CompactFactor>(
keys(), keyDimensions());
265 batch->reserve(factors_.size());
267 std::vector<Matrix> H(FactorType::N);
268 for (
size_t i = 0; i < factors_.size(); ++i) {
269 const auto& factor = factors_[i];
270 Vector rhs = -factor.unwhitenedError(values, H);
272 if (factor.noiseModel() && !factor.noiseModel()->isUnit()) {
273 factor.noiseModel()->WhitenSystem(H, rhs);
276 batch->addRow(indices_[i], H, rhs);
289 const FactorVector&
factors()
const {
return factors_; }
308 factors_.reserve(
factors.size());
315 factors_.reserve(
factors.size());
317 factors_.push_back(std::move(f));
332 template <
typename Measurement,
typename... Args>
335 factors_.reserve(measurements.size());
336 for (
const auto& [key2, z] : measurements) {
337 factors_.push_back(detail::createFactor<FactorType>(
338 key1, key2, z, model, std::forward<Args>(args)...));
353 template <
typename Measurement,
typename... Args>
356 factors_.reserve(measurements.size());
357 for (
const auto& [key1, z] : measurements) {
358 factors_.push_back(detail::createFactor<FactorType>(
359 key1, key2, z, model, std::forward<Args>(args)...));
370 const std::string& s =
"",
373 std::cout <<
"BatchFactor with " << factors_.size()
374 <<
" factors:" << std::endl;
375 for (
const auto& f : factors_) {
376 f.print(
"", keyFormatter);
382 const This* p =
dynamic_cast<const This*
>(&f);
383 if (!p || factors_.
size() != p->factors_.size())
return false;
384 for (
size_t i = 0; i < factors_.size(); ++i) {
385 if (!factors_[i].
equals(p->factors_[i], tol))
return false;
399 double total_error = 0.0;
400 for (
const auto& f : factors_) {
401 total_error += f.error(c);
407 size_t dim()
const override {
return factors_.size() * ErrorDim; }
409 void setUseHessianFactor(
bool flag) { useHessianFactor_ = flag; }
421 const Values& values)
const override {
422 if (factors_.empty())
return std::make_shared<JacobianFactor>();
424 if (useHessianFactor_) {
425 auto jacobian = linearizeToJacobianFactor(values);
426 return std::make_shared<HessianFactor>(*jacobian);
429 constexpr auto factorSlots = std::make_index_sequence<FactorType::N>{};
430 if constexpr (hasFixedDimensions(factorSlots)) {
431 if (!hasConstrainedNoiseModel_) {
432 return linearizeToBatchJacobian(values, factorSlots);
435 return linearizeToJacobianFactor(values);
439 template <
size_t... Is>
441 (keyInfo_.push_back(KeyInfo{
450 constexpr auto factorSlots = std::make_index_sequence<FactorType::N>{};
451 allDimensionsFixed_ = hasFixedDimensions(factorSlots);
452 hasConstrainedNoiseModel_ =
false;
453 allNoiseModelsAreUnit_ =
true;
454 keyDimensions_.clear();
457 keyInfo_.reserve(factors_.size() * FactorType::N);
459 for (
const auto& f : factors_) {
460 collectKeys(f, std::make_index_sequence<FactorType::N>{});
464 std::sort(keyInfo_.begin(), keyInfo_.end(),
465 [](
const KeyInfo& a,
const KeyInfo& b) { return a.key < b.key; });
466 auto isDuplicate = [](
const KeyInfo& a,
const KeyInfo& b) {
467 if (a.key != b.key)
return false;
468 assert(a.dim == b.dim);
471 auto last = std::unique(keyInfo_.begin(), keyInfo_.end(), isDuplicate);
472 keyInfo_.erase(last, keyInfo_.end());
476 keys_.reserve(keyInfo_.size());
478 for (
auto& info : keyInfo_) {
479 info.offset = offset;
480 keys_.push_back(info.key);
484 if (allDimensionsFixed_ && info.dim >= 0) {
485 keyDimensions_.push_back(
static_cast<size_t>(info.dim));
490 for (
const auto& factor : factors_) {
491 const auto& model = factor.noiseModel();
492 if (!model)
continue;
493 hasConstrainedNoiseModel_ |= model->isConstrained();
494 allNoiseModelsAreUnit_ &= model->isUnit();
500 indices_.reserve(factors_.size());
501 for (
const auto& f : factors_) {
502 std::array<DenseIndex, FactorType::N> indices_i;
503 for (
size_t k = 0; k < FactorType::N; ++k) {
504 auto it = std::lower_bound(
keys_.begin(),
keys_.end(), f.keys()[k]);
505 indices_i[k] = std::distance(
keys_.begin(), it);
507 indices_.push_back(indices_i);
Concept check for values that can be used in unit tests.
Contains the HessianFactor class, a general quadratic factor.
Row-sparse fixed-dimension batch Jacobian factor.
Non-linear factor base classes.
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
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
noiseModel::Base::shared_ptr SharedNoiseModel
Aliases.
Definition NoiseModel.h:846
std::uint64_t Key
Integer nonlinear key type.
Definition types.h:43
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
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
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
size_t size() const
Definition Factor.h:160
Fixed-dimension row-sparse batch Jacobian factor.
Definition BatchJacobianFactor.h:351
static shared_ptr MixedSigmas(const Vector &mu, const Vector &sigmas)
A diagonal noise model created by specifying a Vector of standard deviations, some of which might be ...
Definition NoiseModel.cpp:404
static shared_ptr Create(size_t dim)
Create a unit covariance noise model.
Definition NoiseModel.h:673
bool equals(const NonlinearFactor &f, double tol=1e-9) const override
Check equality with another factor.
Definition BatchFactor.h:381
void updateKeys()
Update keys_ by collecting unique keys from all factors.
Definition BatchFactor.h:449
double error(const Values &c) const override
Calculate the error of the factor.
Definition BatchFactor.h:398
BatchFactor(Key key1, const std::map< Key, Measurement > &measurements, const SharedNoiseModel &model, Args &&... args)
Map-based Constructor (Varying Key2).
Definition BatchFactor.h:333
BatchFactor(const std::vector< FactorType > &factors)
Constructor from a standard vector of factors (copies the vector).
Definition BatchFactor.h:307
void collectKeys(const FactorType &f, std::index_sequence< Is... >)
Helper to collect keys and dimensions using fold expression.
Definition BatchFactor.h:440
BatchFactor(const std::map< Key, Measurement > &measurements, Key key2, const SharedNoiseModel &model, Args &&... args)
Map-based Constructor (Varying Key1).
Definition BatchFactor.h:354
BatchFactor(std::vector< FactorType, Allocator > &&factors)
Constructor from a vector of factors (moves the vector).
Definition BatchFactor.h:295
BatchFactor(std::vector< FactorType > &&factors)
Constructor from a standard vector of factors (moves elements).
Definition BatchFactor.h:314
size_t dim() const override
Get the dimension of the factor (number of rows on linearization).
Definition BatchFactor.h:407
size_t numFactors() const
Return the number of child factors represented by this batch.
Definition BatchFactor.h:292
const FactorVector & factors() const
Return the child factors represented by this batch.
Definition BatchFactor.h:289
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
Print the BatchFactor.
Definition BatchFactor.h:369
std::shared_ptr< GaussianFactor > linearize(const Values &values) const override
Linearize to a single JacobianFactor.
Definition BatchFactor.h:420
BatchFactor()=default
Default constructor.
BatchFactor(const std::vector< FactorType, Allocator > &factors)
Constructor from a vector of factors (copies the vector).
Definition BatchFactor.h:301
NonlinearFactor()
Default constructor for I/O only.
Definition NonlinearFactor.h:86
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
print
Definition NonlinearFactor.cpp:45
A non-templated config holding any types of Manifold-group elements.
Definition Values.h:65
const ValueType at(Key j) const
Retrieve a variable by key j.
Definition Values-inl.h:260