Skip to content

Commit 99161db

Browse files
authored
Merge pull request #2329 from borglab/feature/collect_and_gemv
2 parents 3eebc5b + 43dd472 commit 99161db

File tree

11 files changed

+207
-111
lines changed

11 files changed

+207
-111
lines changed

gtsam/base/ConcurrentMap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#pragma once
2020

2121
#include <gtsam/global_includes.h>
22+
#include <gtsam/base/FastMap.h>
2223

2324
// Change class depending on whether we are using TBB
2425
#ifdef GTSAM_USE_TBB
@@ -41,8 +42,7 @@ using ConcurrentMapBase = tbb::concurrent_unordered_map<
4142

4243
#else
4344

44-
// If we're not using TBB, use a FastMap for ConcurrentMap
45-
#include <gtsam/base/FastMap.h>
45+
// If we're not using TBB, use a std::map
4646
template <typename KEY, typename VALUE>
4747
using ConcurrentMapBase = gtsam::FastMap<KEY, VALUE>;
4848

gtsam/inference/BayesTree-inst.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,35 @@ namespace gtsam {
269269
/* ************************************************************************* */
270270
template<class CLIQUE>
271271
bool BayesTree<CLIQUE>::equals(const BayesTree<CLIQUE>& other, double tol) const {
272-
return size()==other.size() &&
273-
std::equal(nodes_.begin(), nodes_.end(), other.nodes_.begin(), &check_sharedCliques<CLIQUE>);
272+
// Compare number of cliques first.
273+
if (size() != other.size())
274+
return false;
275+
276+
// Compare number of variables (nodes index size).
277+
if (nodes_.size() != other.nodes_.size())
278+
return false;
279+
280+
// Compare cliques by key so equality does not depend on the
281+
// iteration order of the underlying ConcurrentMap.
282+
for (const auto& kv : nodes_) {
283+
const Key key = kv.first;
284+
const sharedClique& clique = kv.second;
285+
286+
auto it = other.nodes_.find(key);
287+
if (it == other.nodes_.end())
288+
return false;
289+
290+
const sharedClique& otherClique = it->second;
291+
292+
if (!clique && !otherClique)
293+
continue;
294+
if (!clique || !otherClique)
295+
return false;
296+
if (!clique->equals(*otherClique, tol))
297+
return false;
298+
}
299+
300+
return true;
274301
}
275302

276303
/* ************************************************************************* */

gtsam/linear/Errors.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ namespace gtsam {
2727
/* ************************************************************************* */
2828
Errors createErrors(const VectorValues& V) {
2929
Errors result;
30-
for (const auto& [key, e] : V) {
31-
result.push_back(e);
32-
}
30+
// Use a key-sorted view of VectorValues so the resulting Errors
31+
// order is deterministic and independent of the underlying map.
32+
for (const auto& [key, e] : V.sorted()) result.push_back(e);
3333
return result;
3434
}
3535

gtsam/linear/GaussianConditional.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -305,23 +305,36 @@ namespace gtsam {
305305
const Vector x =
306306
frontalValues.vector(KeyVector(beginFrontals(), endFrontals()));
307307

308-
// Copy the augmented Jacobian matrix:
309-
auto newAb = Ab_;
308+
// Compute updated right-hand side: d - R * x
309+
const auto RR = R().triangularView<Eigen::Upper>();
310+
const Vector rhs = d() - RR * x;
310311

311-
// Restrict view to parent blocks
312-
newAb.firstBlock() += nrFrontals_;
312+
// Collect parent dimensions
313+
FastVector<DenseIndex> parentDims;
314+
parentDims.reserve(nrParents());
315+
for (auto it = beginParents(); it != endParents(); ++it) {
316+
parentDims.push_back(getDim(it));
317+
}
313318

314-
// Update right-hand-side (last column)
315-
auto last = newAb.matrix().cols() - 1;
316-
const auto RR = R().triangularView<Eigen::Upper>();
317-
newAb.matrix().col(last) -= RR * x;
319+
// Build a VerticalBlockMatrix containing only parent blocks and RHS.
320+
const DenseIndex m = rows();
321+
VerticalBlockMatrix newAb(parentDims, m, true);
322+
323+
// Copy parent blocks (S matrices).
324+
DenseIndex blockIndex = 0;
325+
for (auto it = beginParents(); it != endParents(); ++it, ++blockIndex) {
326+
newAb(blockIndex) = S(it);
327+
}
328+
329+
// Set the RHS block.
330+
const DenseIndex lastBlock = newAb.nBlocks() - 1;
331+
newAb(lastBlock).col(0) = rhs;
318332

319333
// The keys now do not include the frontal keys:
320334
KeyVector newKeys;
321335
newKeys.reserve(nrParents());
322336
for (auto&& key : parents()) newKeys.push_back(key);
323337

324-
// Hopefully second newAb copy below is optimized out...
325338
return std::make_shared<JacobianFactor>(newKeys, newAb, model_);
326339
}
327340

gtsam/linear/JacobianFactor.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,17 @@ bool JacobianFactor::equals(const GaussianFactor& f_, double tol) const {
491491

492492
/* ************************************************************************* */
493493
Vector JacobianFactor::unweighted_error(const VectorValues& c) const {
494-
Vector e = -getb();
495-
for (size_t pos = 0; pos < size(); ++pos)
496-
e += Ab_(pos) * c[keys_[pos]];
497-
return e;
494+
const DenseIndex totalDim = c.totalDim(keys_);
495+
Vector w(totalDim + 1);
496+
c.fillVector(keys_, w);
497+
w(totalDim) = -1.0;
498+
// Fast path when the active view is the full matrix (no row/column offsets).
499+
if (Ab_.firstBlock() == 0 && Ab_.rowStart() == 0 &&
500+
Ab_.rowEnd() == Ab_.matrix().rows()) {
501+
return Ab_.matrix() * w;
502+
}
503+
// Fallback that respects firstBlock/rowStart/rowEnd for subviews.
504+
return Ab_.full() * w;
498505
}
499506

500507
/* ************************************************************************* */

gtsam/linear/SubgraphPreconditioner.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ Errors SubgraphPreconditioner::operator*(const VectorValues &y) const {
139139
void SubgraphPreconditioner::multiplyInPlace(const VectorValues& y, Errors& e) const {
140140

141141
Errors::iterator ei = e.begin();
142-
for(const auto& key_value: y) {
143-
*ei = key_value.second;
142+
// Fill the identity-part errors in key-sorted order, to match createErrors.
143+
for (const auto& [key, value] : y.sorted()) {
144+
*ei = value;
144145
++ei;
145146
}
146147

@@ -155,8 +156,9 @@ VectorValues SubgraphPreconditioner::operator^(const Errors& e) const {
155156

156157
Errors::const_iterator it = e.begin();
157158
VectorValues y = zero();
158-
for(auto& key_value: y) {
159-
key_value.second = *it;
159+
// Map the identity-part errors back into y using key-sorted order.
160+
for (const auto& [key, value] : y.sorted()) {
161+
y.at(key) = *it;
160162
++it;
161163
}
162164
transposeMultiplyAdd2(1.0, it, e.end(), y);
@@ -169,9 +171,11 @@ void SubgraphPreconditioner::transposeMultiplyAdd
169171
(double alpha, const Errors& e, VectorValues& y) const {
170172

171173
Errors::const_iterator it = e.begin();
172-
for(auto& key_value: y) {
174+
// Add the identity-part contribution in key-sorted order so it
175+
// matches the layout produced by createErrors().
176+
for (const auto& [key, value] : y.sorted()) {
173177
const Vector& ei = *it;
174-
key_value.second += alpha * ei;
178+
y.at(key) += alpha * ei;
175179
++it;
176180
}
177181
transposeMultiplyAdd2(alpha, it, e.end(), y);

0 commit comments

Comments
 (0)