Skip to content

Commit 814a734

Browse files
authored
Merge pull request #2017 from borglab/feature/city_relin
2 parents c37eb49 + 6f11b4a commit 814a734

31 files changed

+314
-123
lines changed

examples/Hybrid_City10000.cpp

+83-64
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,15 @@ class Experiment {
6868

6969
size_t maxNrHypotheses = 10;
7070

71+
size_t reLinearizationFrequency = 10;
72+
73+
double marginalThreshold = 0.9999;
74+
7175
private:
7276
std::string filename_;
7377
HybridSmoother smoother_;
74-
HybridNonlinearFactorGraph graph_;
78+
HybridNonlinearFactorGraph newFactors_, allFactors_;
7579
Values initial_;
76-
Values result_;
7780

7881
/**
7982
* @brief Write the result of optimization to file.
@@ -83,7 +86,7 @@ class Experiment {
8386
* @param filename The file name to save the result to.
8487
*/
8588
void writeResult(const Values& result, size_t numPoses,
86-
const std::string& filename = "Hybrid_city10000.txt") {
89+
const std::string& filename = "Hybrid_city10000.txt") const {
8790
std::ofstream outfile;
8891
outfile.open(filename);
8992

@@ -100,9 +103,9 @@ class Experiment {
100103
* @brief Create a hybrid loop closure factor where
101104
* 0 - loose noise model and 1 - loop noise model.
102105
*/
103-
HybridNonlinearFactor hybridLoopClosureFactor(size_t loopCounter, size_t keyS,
104-
size_t keyT,
105-
const Pose2& measurement) {
106+
HybridNonlinearFactor hybridLoopClosureFactor(
107+
size_t loopCounter, size_t keyS, size_t keyT,
108+
const Pose2& measurement) const {
106109
DiscreteKey l(L(loopCounter), 2);
107110

108111
auto f0 = std::make_shared<BetweenFactor<Pose2>>(
@@ -119,7 +122,7 @@ class Experiment {
119122
/// @brief Create hybrid odometry factor with discrete measurement choices.
120123
HybridNonlinearFactor hybridOdometryFactor(
121124
size_t numMeasurements, size_t keyS, size_t keyT, const DiscreteKey& m,
122-
const std::vector<Pose2>& poseArray) {
125+
const std::vector<Pose2>& poseArray) const {
123126
auto f0 = std::make_shared<BetweenFactor<Pose2>>(
124127
X(keyS), X(keyT), poseArray[0], kPoseNoiseModel);
125128
auto f1 = std::make_shared<BetweenFactor<Pose2>>(
@@ -132,25 +135,59 @@ class Experiment {
132135
}
133136

134137
/// @brief Perform smoother update and optimize the graph.
135-
void smootherUpdate(HybridSmoother& smoother,
136-
HybridNonlinearFactorGraph& graph, const Values& initial,
137-
size_t maxNrHypotheses, Values* result) {
138-
HybridGaussianFactorGraph linearized = *graph.linearize(initial);
139-
smoother.update(linearized, maxNrHypotheses);
140-
// throw if x0 not in hybridBayesNet_:
141-
const KeySet& keys = smoother.hybridBayesNet().keys();
142-
if (keys.find(X(0)) == keys.end()) {
143-
throw std::runtime_error("x0 not in hybridBayesNet_");
138+
auto smootherUpdate(size_t maxNrHypotheses) {
139+
std::cout << "Smoother update: " << newFactors_.size() << std::endl;
140+
gttic_(SmootherUpdate);
141+
clock_t beforeUpdate = clock();
142+
auto linearized = newFactors_.linearize(initial_);
143+
smoother_.update(*linearized, maxNrHypotheses);
144+
allFactors_.push_back(newFactors_);
145+
newFactors_.resize(0);
146+
clock_t afterUpdate = clock();
147+
return afterUpdate - beforeUpdate;
148+
}
149+
150+
/// @brief Re-linearize, solve ALL, and re-initialize smoother.
151+
auto reInitialize() {
152+
std::cout << "================= Re-Initialize: " << allFactors_.size()
153+
<< std::endl;
154+
clock_t beforeUpdate = clock();
155+
allFactors_ = allFactors_.restrict(smoother_.fixedValues());
156+
auto linearized = allFactors_.linearize(initial_);
157+
auto bayesNet = linearized->eliminateSequential();
158+
HybridValues delta = bayesNet->optimize();
159+
initial_ = initial_.retract(delta.continuous());
160+
smoother_.reInitialize(std::move(*bayesNet));
161+
clock_t afterUpdate = clock();
162+
std::cout << "Took " << (afterUpdate - beforeUpdate) / CLOCKS_PER_SEC
163+
<< " seconds." << std::endl;
164+
return afterUpdate - beforeUpdate;
165+
}
166+
167+
// Parse line from file
168+
std::pair<std::vector<Pose2>, std::pair<size_t, size_t>> parseLine(
169+
const std::string& line) const {
170+
std::vector<std::string> parts;
171+
split(parts, line, is_any_of(" "));
172+
173+
size_t keyS = stoi(parts[1]);
174+
size_t keyT = stoi(parts[3]);
175+
176+
int numMeasurements = stoi(parts[5]);
177+
std::vector<Pose2> poseArray(numMeasurements);
178+
for (int i = 0; i < numMeasurements; ++i) {
179+
double x = stod(parts[6 + 3 * i]);
180+
double y = stod(parts[7 + 3 * i]);
181+
double rad = stod(parts[8 + 3 * i]);
182+
poseArray[i] = Pose2(x, y, rad);
144183
}
145-
graph.resize(0);
146-
// HybridValues delta = smoother.hybridBayesNet().optimize();
147-
// result->insert_or_assign(initial.retract(delta.continuous()));
184+
return {poseArray, {keyS, keyT}};
148185
}
149186

150187
public:
151188
/// Construct with filename of experiment to run
152189
explicit Experiment(const std::string& filename)
153-
: filename_(filename), smoother_(0.99) {}
190+
: filename_(filename), smoother_(marginalThreshold) {}
154191

155192
/// @brief Run the main experiment with a given maxLoopCount.
156193
void run() {
@@ -162,49 +199,34 @@ class Experiment {
162199
}
163200

164201
// Initialize local variables
165-
size_t discreteCount = 0, index = 0;
166-
size_t loopCount = 0;
202+
size_t discreteCount = 0, index = 0, loopCount = 0, updateCount = 0;
167203

168204
std::list<double> timeList;
169205

170206
// Set up initial prior
171-
double x = 0.0;
172-
double y = 0.0;
173-
double rad = 0.0;
174-
175-
Pose2 priorPose(x, y, rad);
207+
Pose2 priorPose(0, 0, 0);
176208
initial_.insert(X(0), priorPose);
177-
graph_.push_back(PriorFactor<Pose2>(X(0), priorPose, kPriorNoiseModel));
209+
newFactors_.push_back(
210+
PriorFactor<Pose2>(X(0), priorPose, kPriorNoiseModel));
178211

179212
// Initial update
180-
clock_t beforeUpdate = clock();
181-
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
182-
clock_t afterUpdate = clock();
213+
auto time = smootherUpdate(maxNrHypotheses);
183214
std::vector<std::pair<size_t, double>> smootherUpdateTimes;
184-
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
215+
smootherUpdateTimes.push_back({index, time});
185216

186217
// Flag to decide whether to run smoother update
187218
size_t numberOfHybridFactors = 0;
188219

189220
// Start main loop
221+
Values result;
190222
size_t keyS = 0, keyT = 0;
191223
clock_t startTime = clock();
192224
std::string line;
193225
while (getline(in, line) && index < maxLoopCount) {
194-
std::vector<std::string> parts;
195-
split(parts, line, is_any_of(" "));
196-
197-
keyS = stoi(parts[1]);
198-
keyT = stoi(parts[3]);
199-
200-
int numMeasurements = stoi(parts[5]);
201-
std::vector<Pose2> poseArray(numMeasurements);
202-
for (int i = 0; i < numMeasurements; ++i) {
203-
x = stod(parts[6 + 3 * i]);
204-
y = stod(parts[7 + 3 * i]);
205-
rad = stod(parts[8 + 3 * i]);
206-
poseArray[i] = Pose2(x, y, rad);
207-
}
226+
auto [poseArray, keys] = parseLine(line);
227+
keyS = keys.first;
228+
keyT = keys.second;
229+
size_t numMeasurements = poseArray.size();
208230

209231
// Take the first one as the initial estimate
210232
Pose2 odomPose = poseArray[0];
@@ -215,13 +237,13 @@ class Experiment {
215237
DiscreteKey m(M(discreteCount), numMeasurements);
216238
HybridNonlinearFactor mixtureFactor =
217239
hybridOdometryFactor(numMeasurements, keyS, keyT, m, poseArray);
218-
graph_.push_back(mixtureFactor);
240+
newFactors_.push_back(mixtureFactor);
219241
discreteCount++;
220242
numberOfHybridFactors += 1;
221243
std::cout << "mixtureFactor: " << keyS << " " << keyT << std::endl;
222244
} else {
223-
graph_.add(BetweenFactor<Pose2>(X(keyS), X(keyT), odomPose,
224-
kPoseNoiseModel));
245+
newFactors_.add(BetweenFactor<Pose2>(X(keyS), X(keyT), odomPose,
246+
kPoseNoiseModel));
225247
}
226248
// Insert next pose initial guess
227249
initial_.insert(X(keyT), initial_.at<Pose2>(X(keyS)) * odomPose);
@@ -231,21 +253,20 @@ class Experiment {
231253
hybridLoopClosureFactor(loopCount, keyS, keyT, odomPose);
232254
// print loop closure event keys:
233255
std::cout << "Loop closure: " << keyS << " " << keyT << std::endl;
234-
graph_.add(loopFactor);
256+
newFactors_.add(loopFactor);
235257
numberOfHybridFactors += 1;
236258
loopCount++;
237259
}
238260

239261
if (numberOfHybridFactors >= updateFrequency) {
240-
// print the keys involved in the smoother update
241-
std::cout << "Smoother update: " << graph_.size() << std::endl;
242-
gttic_(SmootherUpdate);
243-
beforeUpdate = clock();
244-
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
245-
afterUpdate = clock();
246-
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
247-
gttoc_(SmootherUpdate);
262+
auto time = smootherUpdate(maxNrHypotheses);
263+
smootherUpdateTimes.push_back({index, time});
248264
numberOfHybridFactors = 0;
265+
updateCount++;
266+
267+
if (updateCount % reLinearizationFrequency == 0) {
268+
reInitialize();
269+
}
249270
}
250271

251272
// Record timing for odometry edges only
@@ -270,17 +291,15 @@ class Experiment {
270291
}
271292

272293
// Final update
273-
beforeUpdate = clock();
274-
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
275-
afterUpdate = clock();
276-
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
294+
time = smootherUpdate(maxNrHypotheses);
295+
smootherUpdateTimes.push_back({index, time});
277296

278297
// Final optimize
279298
gttic_(HybridSmootherOptimize);
280299
HybridValues delta = smoother_.optimize();
281300
gttoc_(HybridSmootherOptimize);
282301

283-
result_.insert_or_assign(initial_.retract(delta.continuous()));
302+
result.insert_or_assign(initial_.retract(delta.continuous()));
284303

285304
std::cout << "Final error: " << smoother_.hybridBayesNet().error(delta)
286305
<< std::endl;
@@ -291,7 +310,7 @@ class Experiment {
291310
<< std::endl;
292311

293312
// Write results to file
294-
writeResult(result_, keyT + 1, "Hybrid_City10000.txt");
313+
writeResult(result, keyT + 1, "Hybrid_City10000.txt");
295314

296315
// TODO Write to file
297316
// for (size_t i = 0; i < smoother_update_times.size(); i++) {

gtsam/discrete/DecisionTree.h

+7
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,13 @@ namespace gtsam {
393393
return DecisionTree(newRoot);
394394
}
395395

396+
/** Choose multiple values. */
397+
DecisionTree restrict(const Assignment<L>& assignment) const {
398+
NodePtr newRoot = root_;
399+
for (const auto& [l, v] : assignment) newRoot = newRoot->choose(l, v);
400+
return DecisionTree(newRoot);
401+
}
402+
396403
/** combine subtrees on key with binary operation "op" */
397404
DecisionTree combine(const L& label, size_t cardinality,
398405
const Binary& op) const;

gtsam/discrete/DecisionTreeFactor.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -536,5 +536,11 @@ namespace gtsam {
536536
return DecisionTreeFactor(this->discreteKeys(), thresholded);
537537
}
538538

539+
/* ************************************************************************ */
540+
DiscreteFactor::shared_ptr DecisionTreeFactor::restrict(
541+
const DiscreteValues& assignment) const {
542+
throw std::runtime_error("DecisionTreeFactor::restrict not implemented");
543+
}
544+
539545
/* ************************************************************************ */
540546
} // namespace gtsam

gtsam/discrete/DecisionTreeFactor.h

+4
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ namespace gtsam {
220220
return combine(keys, Ring::max);
221221
}
222222

223+
/// Restrict the factor to the given assignment.
224+
DiscreteFactor::shared_ptr restrict(
225+
const DiscreteValues& assignment) const override;
226+
223227
/// @}
224228
/// @name Advanced Interface
225229
/// @{

gtsam/discrete/DiscreteFactor.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ class GTSAM_EXPORT DiscreteFactor : public Factor {
167167
/**
168168
* @brief Scale the factor values by the maximum
169169
* to prevent underflow/overflow.
170-
*
171-
* @return DiscreteFactor::shared_ptr
170+
*
171+
* @return DiscreteFactor::shared_ptr
172172
*/
173173
DiscreteFactor::shared_ptr scale() const;
174174

@@ -178,6 +178,10 @@ class GTSAM_EXPORT DiscreteFactor : public Factor {
178178
*/
179179
virtual uint64_t nrValues() const = 0;
180180

181+
/// Restrict the factor to the given assignment.
182+
virtual DiscreteFactor::shared_ptr restrict(
183+
const DiscreteValues& assignment) const = 0;
184+
181185
/// @}
182186
/// @name Wrapper support
183187
/// @{

gtsam/discrete/TableFactor.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,12 @@ void TableFactor::print(const string& s, const KeyFormatter& formatter) const {
391391

392392
/* ************************************************************************ */
393393
DiscreteFactor::shared_ptr TableFactor::sum(size_t nrFrontals) const {
394-
return combine(nrFrontals, Ring::add);
394+
return combine(nrFrontals, Ring::add);
395395
}
396396

397397
/* ************************************************************************ */
398398
DiscreteFactor::shared_ptr TableFactor::sum(const Ordering& keys) const {
399-
return combine(keys, Ring::add);
399+
return combine(keys, Ring::add);
400400
}
401401

402402
/* ************************************************************************ */
@@ -418,7 +418,6 @@ DiscreteFactor::shared_ptr TableFactor::max(const Ordering& keys) const {
418418
return combine(keys, Ring::max);
419419
}
420420

421-
422421
/* ************************************************************************ */
423422
TableFactor TableFactor::apply(Unary op) const {
424423
// Initialize new factor.
@@ -781,5 +780,11 @@ TableFactor TableFactor::prune(size_t maxNrAssignments) const {
781780
return TableFactor(this->discreteKeys(), pruned_vec);
782781
}
783782

783+
/* ************************************************************************ */
784+
DiscreteFactor::shared_ptr TableFactor::restrict(
785+
const DiscreteValues& assignment) const {
786+
throw std::runtime_error("TableFactor::restrict not implemented");
787+
}
788+
784789
/* ************************************************************************ */
785790
} // namespace gtsam

gtsam/discrete/TableFactor.h

+4
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor {
342342
*/
343343
uint64_t nrValues() const override { return sparse_table_.nonZeros(); }
344344

345+
/// Restrict the factor to the given assignment.
346+
DiscreteFactor::shared_ptr restrict(
347+
const DiscreteValues& assignment) const override;
348+
345349
/// @}
346350
/// @name Wrapper support
347351
/// @{

gtsam/discrete/tests/testAlgebraicDecisionTree.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,30 @@ TEST(ADT, arithmetic) {
5959

6060
// Negate and subtraction
6161
CHECK(assert_equal(-a, zero - a));
62+
#ifdef GTSAM_DT_MERGING
6263
CHECK(assert_equal({zero}, a - a));
64+
#else
65+
CHECK(assert_equal({A, 0, 0}, a - a));
66+
#endif
6367
CHECK(assert_equal(a + b, b + a));
6468
CHECK(assert_equal({A, 3, 4}, a + 2));
6569
CHECK(assert_equal({B, 1, 2}, b - 2));
6670

6771
// Multiplication
72+
#ifdef GTSAM_DT_MERGING
6873
CHECK(assert_equal(zero, zero * a));
69-
CHECK(assert_equal(zero, a * zero));
74+
#else
75+
CHECK(assert_equal({A, 0, 0}, zero * a));
76+
#endif
7077
CHECK(assert_equal(a, one * a));
7178
CHECK(assert_equal(a, a * one));
7279
CHECK(assert_equal(a * b, b * a));
7380

81+
#ifdef GTSAM_DT_MERGING
7482
// division
7583
// CHECK(assert_equal(a, (a * b) / b)); // not true because no pruning
7684
CHECK(assert_equal(b, (a * b) / a));
85+
#endif
7786
}
7887

7988
/* ************************************************************************** */

0 commit comments

Comments
 (0)