Skip to content

Commit c37eb49

Browse files
authored
Merge pull request #2016 from borglab/feature/hybrid_profiling
Lots of savings!
2 parents 0d4b0d7 + ee2f3c5 commit c37eb49

File tree

5 files changed

+157
-106
lines changed

5 files changed

+157
-106
lines changed

examples/Hybrid_City10000.cpp

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,43 @@
3131

3232
#include <boost/algorithm/string/classification.hpp>
3333
#include <boost/algorithm/string/split.hpp>
34+
#include <cstdlib>
3435
#include <fstream>
36+
#include <iostream>
3537
#include <string>
3638
#include <vector>
3739

38-
using namespace std;
3940
using namespace gtsam;
4041
using namespace boost::algorithm;
4142

4243
using symbol_shorthand::L;
4344
using symbol_shorthand::M;
4445
using symbol_shorthand::X;
4546

46-
const size_t kMaxLoopCount = 2000; // Example default value
47-
const size_t kMaxNrHypotheses = 10;
48-
4947
auto kOpenLoopModel = noiseModel::Diagonal::Sigmas(Vector3::Ones() * 10);
48+
const double kOpenLoopConstant = kOpenLoopModel->negLogConstant();
5049

5150
auto kPriorNoiseModel = noiseModel::Diagonal::Sigmas(
5251
(Vector(3) << 0.0001, 0.0001, 0.0001).finished());
5352

5453
auto kPoseNoiseModel = noiseModel::Diagonal::Sigmas(
5554
(Vector(3) << 1.0 / 30.0, 1.0 / 30.0, 1.0 / 100.0).finished());
55+
const double kPoseNoiseConstant = kPoseNoiseModel->negLogConstant();
5656

5757
// Experiment Class
5858
class Experiment {
59+
public:
60+
// Parameters with default values
61+
size_t maxLoopCount = 3000;
62+
63+
// 3000: {1: 62s, 2: 21s, 3: 20s, 4: 31s, 5: 39s} No DT optimizations
64+
// 3000: {1: 65s, 2: 20s, 3: 16s, 4: 21s, 5: 28s} With DT optimizations
65+
// 3000: {1: 59s, 2: 19s, 3: 18s, 4: 26s, 5: 33s} With DT optimizations +
66+
// merge
67+
size_t updateFrequency = 3;
68+
69+
size_t maxNrHypotheses = 10;
70+
5971
private:
6072
std::string filename_;
6173
HybridSmoother smoother_;
@@ -72,7 +84,7 @@ class Experiment {
7284
*/
7385
void writeResult(const Values& result, size_t numPoses,
7486
const std::string& filename = "Hybrid_city10000.txt") {
75-
ofstream outfile;
87+
std::ofstream outfile;
7688
outfile.open(filename);
7789

7890
for (size_t i = 0; i < numPoses; ++i) {
@@ -98,34 +110,33 @@ class Experiment {
98110
auto f1 = std::make_shared<BetweenFactor<Pose2>>(
99111
X(keyS), X(keyT), measurement, kPoseNoiseModel);
100112

101-
std::vector<NonlinearFactorValuePair> factors{
102-
{f0, kOpenLoopModel->negLogConstant()},
103-
{f1, kPoseNoiseModel->negLogConstant()}};
113+
std::vector<NonlinearFactorValuePair> factors{{f0, kOpenLoopConstant},
114+
{f1, kPoseNoiseConstant}};
104115
HybridNonlinearFactor mixtureFactor(l, factors);
105116
return mixtureFactor;
106117
}
107118

108119
/// @brief Create hybrid odometry factor with discrete measurement choices.
109120
HybridNonlinearFactor hybridOdometryFactor(
110121
size_t numMeasurements, size_t keyS, size_t keyT, const DiscreteKey& m,
111-
const std::vector<Pose2>& poseArray,
112-
const SharedNoiseModel& poseNoiseModel) {
122+
const std::vector<Pose2>& poseArray) {
113123
auto f0 = std::make_shared<BetweenFactor<Pose2>>(
114-
X(keyS), X(keyT), poseArray[0], poseNoiseModel);
124+
X(keyS), X(keyT), poseArray[0], kPoseNoiseModel);
115125
auto f1 = std::make_shared<BetweenFactor<Pose2>>(
116-
X(keyS), X(keyT), poseArray[1], poseNoiseModel);
126+
X(keyS), X(keyT), poseArray[1], kPoseNoiseModel);
117127

118-
std::vector<NonlinearFactorValuePair> factors{{f0, 0.0}, {f1, 0.0}};
128+
std::vector<NonlinearFactorValuePair> factors{{f0, kPoseNoiseConstant},
129+
{f1, kPoseNoiseConstant}};
119130
HybridNonlinearFactor mixtureFactor(m, factors);
120131
return mixtureFactor;
121132
}
122133

123134
/// @brief Perform smoother update and optimize the graph.
124135
void smootherUpdate(HybridSmoother& smoother,
125136
HybridNonlinearFactorGraph& graph, const Values& initial,
126-
size_t kMaxNrHypotheses, Values* result) {
137+
size_t maxNrHypotheses, Values* result) {
127138
HybridGaussianFactorGraph linearized = *graph.linearize(initial);
128-
smoother.update(linearized, kMaxNrHypotheses);
139+
smoother.update(linearized, maxNrHypotheses);
129140
// throw if x0 not in hybridBayesNet_:
130141
const KeySet& keys = smoother.hybridBayesNet().keys();
131142
if (keys.find(X(0)) == keys.end()) {
@@ -142,11 +153,11 @@ class Experiment {
142153
: filename_(filename), smoother_(0.99) {}
143154

144155
/// @brief Run the main experiment with a given maxLoopCount.
145-
void run(size_t maxLoopCount) {
156+
void run() {
146157
// Prepare reading
147-
ifstream in(filename_);
158+
std::ifstream in(filename_);
148159
if (!in.is_open()) {
149-
cerr << "Failed to open file: " << filename_ << endl;
160+
std::cerr << "Failed to open file: " << filename_ << std::endl;
150161
return;
151162
}
152163

@@ -167,11 +178,14 @@ class Experiment {
167178

168179
// Initial update
169180
clock_t beforeUpdate = clock();
170-
smootherUpdate(smoother_, graph_, initial_, kMaxNrHypotheses, &result_);
181+
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
171182
clock_t afterUpdate = clock();
172183
std::vector<std::pair<size_t, double>> smootherUpdateTimes;
173184
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
174185

186+
// Flag to decide whether to run smoother update
187+
size_t numberOfHybridFactors = 0;
188+
175189
// Start main loop
176190
size_t keyS = 0, keyT = 0;
177191
clock_t startTime = clock();
@@ -192,21 +206,18 @@ class Experiment {
192206
poseArray[i] = Pose2(x, y, rad);
193207
}
194208

195-
// Flag to decide whether to run smoother update
196-
bool doSmootherUpdate = false;
197-
198209
// Take the first one as the initial estimate
199210
Pose2 odomPose = poseArray[0];
200211
if (keyS == keyT - 1) {
201212
// Odometry factor
202213
if (numMeasurements > 1) {
203214
// Add hybrid factor
204215
DiscreteKey m(M(discreteCount), numMeasurements);
205-
HybridNonlinearFactor mixtureFactor = hybridOdometryFactor(
206-
numMeasurements, keyS, keyT, m, poseArray, kPoseNoiseModel);
216+
HybridNonlinearFactor mixtureFactor =
217+
hybridOdometryFactor(numMeasurements, keyS, keyT, m, poseArray);
207218
graph_.push_back(mixtureFactor);
208219
discreteCount++;
209-
doSmootherUpdate = true;
220+
numberOfHybridFactors += 1;
210221
std::cout << "mixtureFactor: " << keyS << " " << keyT << std::endl;
211222
} else {
212223
graph_.add(BetweenFactor<Pose2>(X(keyS), X(keyT), odomPose,
@@ -221,18 +232,20 @@ class Experiment {
221232
// print loop closure event keys:
222233
std::cout << "Loop closure: " << keyS << " " << keyT << std::endl;
223234
graph_.add(loopFactor);
224-
doSmootherUpdate = true;
235+
numberOfHybridFactors += 1;
225236
loopCount++;
226237
}
227238

228-
if (doSmootherUpdate) {
239+
if (numberOfHybridFactors >= updateFrequency) {
240+
// print the keys involved in the smoother update
241+
std::cout << "Smoother update: " << graph_.size() << std::endl;
229242
gttic_(SmootherUpdate);
230243
beforeUpdate = clock();
231-
smootherUpdate(smoother_, graph_, initial_, kMaxNrHypotheses, &result_);
244+
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
232245
afterUpdate = clock();
233246
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
234247
gttoc_(SmootherUpdate);
235-
doSmootherUpdate = false;
248+
numberOfHybridFactors = 0;
236249
}
237250

238251
// Record timing for odometry edges only
@@ -258,7 +271,7 @@ class Experiment {
258271

259272
// Final update
260273
beforeUpdate = clock();
261-
smootherUpdate(smoother_, graph_, initial_, kMaxNrHypotheses, &result_);
274+
smootherUpdate(smoother_, graph_, initial_, maxNrHypotheses, &result_);
262275
afterUpdate = clock();
263276
smootherUpdateTimes.push_back({index, afterUpdate - beforeUpdate});
264277

@@ -288,7 +301,7 @@ class Experiment {
288301
// }
289302

290303
// Write timing info to file
291-
ofstream outfileTime;
304+
std::ofstream outfileTime;
292305
std::string timeFileName = "Hybrid_City10000_time.txt";
293306
outfileTime.open(timeFileName);
294307
for (auto accTime : timeList) {
@@ -300,15 +313,47 @@ class Experiment {
300313
};
301314

302315
/* ************************************************************************* */
303-
int main() {
316+
// Function to parse command-line arguments
317+
void parseArguments(int argc, char* argv[], size_t& maxLoopCount,
318+
size_t& updateFrequency, size_t& maxNrHypotheses) {
319+
for (int i = 1; i < argc; ++i) {
320+
std::string arg = argv[i];
321+
if (arg == "--max-loop-count" && i + 1 < argc) {
322+
maxLoopCount = std::stoul(argv[++i]);
323+
} else if (arg == "--update-frequency" && i + 1 < argc) {
324+
updateFrequency = std::stoul(argv[++i]);
325+
} else if (arg == "--max-nr-hypotheses" && i + 1 < argc) {
326+
maxNrHypotheses = std::stoul(argv[++i]);
327+
} else if (arg == "--help") {
328+
std::cout << "Usage: " << argv[0] << " [options]\n"
329+
<< "Options:\n"
330+
<< " --max-loop-count <value> Set the maximum loop "
331+
"count (default: 3000)\n"
332+
<< " --update-frequency <value> Set the update frequency "
333+
"(default: 3)\n"
334+
<< " --max-nr-hypotheses <value> Set the maximum number of "
335+
"hypotheses (default: 10)\n"
336+
<< " --help Show this help message\n";
337+
std::exit(0);
338+
}
339+
}
340+
}
341+
342+
/* ************************************************************************* */
343+
// Main function
344+
int main(int argc, char* argv[]) {
304345
Experiment experiment(findExampleDataFile("T1_city10000_04.txt"));
305346
// Experiment experiment("../data/mh_T1_city10000_04.txt"); //Type #1 only
306347
// Experiment experiment("../data/mh_T3b_city10000_10.txt"); //Type #3 only
307348
// Experiment experiment("../data/mh_T1_T3_city10000_04.txt"); //Type #1 +
308349
// Type #3
309350

351+
// Parse command-line arguments
352+
parseArguments(argc, argv, experiment.maxLoopCount,
353+
experiment.updateFrequency, experiment.maxNrHypotheses);
354+
310355
// Run the experiment
311-
experiment.run(kMaxLoopCount);
356+
experiment.run();
312357

313358
return 0;
314359
}

0 commit comments

Comments
 (0)