ConcurrentBatchSmoother.cpp
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010, 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 
22 #include <gtsam/base/timing.h>
23 #include <gtsam/base/debug.h>
24 
25 namespace gtsam {
26 
27 /* ************************************************************************* */
28 void ConcurrentBatchSmoother::print(const std::string& s, const KeyFormatter& keyFormatter) const {
29  std::cout << s;
30  std::cout << " Factors:" << std::endl;
31  for(const NonlinearFactor::shared_ptr& factor: factors_) {
32  PrintNonlinearFactor(factor, " ", keyFormatter);
33  }
34  theta_.print("Values:\n");
35 }
36 
37 /* ************************************************************************* */
38 bool ConcurrentBatchSmoother::equals(const ConcurrentSmoother& rhs, double tol) const {
39  const ConcurrentBatchSmoother* smoother = dynamic_cast<const ConcurrentBatchSmoother*>(&rhs);
40  return smoother
41  && factors_.equals(smoother->factors_)
42  && theta_.equals(smoother->theta_)
43  && ordering_.equals(smoother->ordering_)
44  && delta_.equals(smoother->delta_)
46 }
47 
48 /* ************************************************************************* */
50  const std::optional< std::vector<size_t> >& removeFactorIndices) {
51 
52  gttic(update);
53 
54  // Create the return result meta-data
55  Result result;
56 
57  // Update all of the internal variables with the new information
58  gttic(augment_system);
59  {
60  // Add the new variables to theta
61  theta_.insert(newTheta);
62 
63  // Add new variables to the end of the ordering
64  for(const auto key: newTheta.keys()) {
65  ordering_.push_back(key);
66  }
67 
68  // Augment Delta
69  delta_.insert(newTheta.zeroVectors());
70 
71  // Add the new factors to the graph, updating the variable index
72  insertFactors(newFactors);
73 
74  if(removeFactorIndices)
75  removeFactors(*removeFactorIndices);
76  }
77  gttoc(augment_system);
78 
79  if(factors_.size() > 0) {
80  // Reorder the system to ensure efficient optimization (and marginalization) performance
81  gttic(reorder);
82  reorder();
83  gttoc(reorder);
84 
85  // Optimize the factors using a modified version of L-M
86  gttic(optimize);
87  result = optimize();
88  gttoc(optimize);
89  }
90 
91  // TODO: The following code does considerable work, much of which could be redundant given the previous optimization step
92  // Refactor this code to reduce computational burden
93 
94  // Calculate the marginal on the separator from the smoother factors
95  if(separatorValues_.size() > 0) {
96  gttic(presync);
98  gttoc(presync);
99  }
100 
101  gttoc(update);
102 
103  return result;
104 }
105 
106 /* ************************************************************************* */
108 
109  gttic(presync);
110 
111  gttoc(presync);
112 }
113 
114 /* ************************************************************************* */
116 
117  gttic(get_summarized_factors);
118 
119  // Copy the previous calculated smoother summarization factors into the output
120  summarizedFactors.push_back(smootherSummarization_);
121 
122  // Copy the separator values into the output
123  separatorValues.insert(separatorValues_);
124 
125  gttoc(get_summarized_factors);
126 }
127 
128 /* ************************************************************************* */
129 void ConcurrentBatchSmoother::synchronize(const NonlinearFactorGraph& smootherFactors, const Values& smootherValues,
130  const NonlinearFactorGraph& summarizedFactors, const Values& separatorValues) {
131 
133 
134  // Remove the previous filter summarization from the graph
136 
137  // Insert new linpoints into the values, augment the ordering, and store new dims to augment delta
138  for(const auto key: smootherValues.keys()) {
139  if(!theta_.exists(key)) {
140  // If this a new key for theta_, also add to ordering and delta.
141  const auto& value = smootherValues.at(key);
142  delta_.insert(key, Vector::Zero(value.dim()));
144  ordering_.push_back(key);
145  } else {
146  // If the key already existed in theta_, just update.
147  const auto& value = smootherValues.at(key);
149  }
150  }
151  for(const auto key: separatorValues.keys()) {
152  if(!theta_.exists(key)) {
153  // If this a new key for theta_, also add to ordering and delta.
154  const auto& value = separatorValues.at(key);
155  delta_.insert(key, Vector::Zero(value.dim()));
157  ordering_.push_back(key);
158  } else {
159  // If the key already existed in theta_, just update.
160  const auto& value = separatorValues.at(key);
162  }
163  }
164 
165  // Insert the new smoother factors
166  insertFactors(smootherFactors);
167 
168  // Insert the new filter summarized factors
169  filterSummarizationSlots_ = insertFactors(summarizedFactors);
170 
171  // Update the list of root keys
172  separatorValues_ = separatorValues;
173 
175 }
176 
177 /* ************************************************************************* */
179 
180  gttic(postsync);
181 
182  gttoc(postsync);
183 }
184 
185 /* ************************************************************************* */
187 
188  gttic(insert_factors);
189 
190  // create the output vector
191  std::vector<size_t> slots;
192  slots.reserve(factors.size());
193 
194  // Insert the factor into an existing hole in the factor graph, if possible
195  for(const NonlinearFactor::shared_ptr& factor: factors) {
196  size_t slot;
197  if(availableSlots_.size() > 0) {
198  slot = availableSlots_.front();
199  availableSlots_.pop();
200  factors_.replace(slot, factor);
201  } else {
202  slot = factors_.size();
203  factors_.push_back(factor);
204  }
205  slots.push_back(slot);
206  }
207 
208  gttoc(insert_factors);
209 
210  return slots;
211 }
212 
213 /* ************************************************************************* */
214 void ConcurrentBatchSmoother::removeFactors(const std::vector<size_t>& slots) {
215 
216  gttic(remove_factors);
217 
218  // For each factor slot to delete...
219  for(size_t slot: slots) {
220 
221  // Remove the factor from the graph
222  factors_.remove(slot);
223 
224  // Mark the factor slot as available
225  availableSlots_.push(slot);
226  }
227 
228  gttoc(remove_factors);
229 }
230 
231 /* ************************************************************************* */
233 
234  // Recalculate the variable index
236 
237  KeyVector separatorKeys = separatorValues_.keys();
238  ordering_ = Ordering::ColamdConstrainedLast(variableIndex_, KeyVector(separatorKeys.begin(), separatorKeys.end()));
239 
240 }
241 
242 /* ************************************************************************* */
244 
245  // Create output result structure
246  Result result;
249 
250  // Pull out parameters we'll use
253 
254  // Create a Values that holds the current evaluation point
255  Values evalpoint = theta_.retract(delta_);
256  result.error = factors_.error(evalpoint);
257  if(result.error < parameters_.errorTol) {
258  return result;
259  }
260 
261  // Use a custom optimization loop so the linearization points can be controlled
262  double previousError;
263  VectorValues newDelta;
264  do {
265  previousError = result.error;
266 
267  // Do next iteration
268  gttic(optimizer_iteration);
269  {
270  // Linearize graph around the linearization point
271  GaussianFactorGraph linearFactorGraph = *factors_.linearize(theta_);
272 
273  // Keep increasing lambda until we make make progress
274  while(true) {
275  if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
276  std::cout << "trying lambda = " << lambda << std::endl;
277 
278  // Add prior factors at the current solution
279  gttic(damp);
280  GaussianFactorGraph dampedFactorGraph(linearFactorGraph);
281  dampedFactorGraph.reserve(linearFactorGraph.size() + delta_.size());
282  {
283  // for each of the variables, add a prior at the current solution
284  for(const VectorValues::KeyValuePair& key_value: delta_) {
285  size_t dim = key_value.second.size();
286  Matrix A = Matrix::Identity(dim,dim);
287  Vector b = key_value.second;
289  GaussianFactor::shared_ptr prior(new JacobianFactor(key_value.first, A, b, model));
290  dampedFactorGraph.push_back(prior);
291  }
292  }
293  gttoc(damp);
294  if (lmVerbosity >= LevenbergMarquardtParams::DAMPED)
295  dampedFactorGraph.print("damped");
296  result.lambdas++;
297 
298  gttic(solve);
299  // Solve Damped Gaussian Factor Graph
300  newDelta = dampedFactorGraph.optimize(ordering_, parameters_.getEliminationFunction());
301  // update the evalpoint with the new delta
302  evalpoint = theta_.retract(newDelta);
303  gttoc(solve);
304 
305  if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
306  std::cout << "linear delta norm = " << newDelta.norm() << std::endl;
307  if (lmVerbosity >= LevenbergMarquardtParams::TRYDELTA)
308  newDelta.print("delta");
309 
310  // Evaluate the new error
311  gttic(compute_error);
312  double error = factors_.error(evalpoint);
313  gttoc(compute_error);
314 
315  if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
316  std::cout << "next error = " << error << std::endl;
317 
318  if(error < result.error) {
319  // Keep this change
320  // Update the error value
321  result.error = error;
322  // Update the linearization point
323  theta_ = evalpoint;
324  // Reset the deltas to zeros
325  delta_.setZero();
326  // Put the linearization points and deltas back for specific variables
327  if(separatorValues_.size() > 0) {
329  for(const auto key: separatorValues_.keys()) {
330  delta_.at(key) = newDelta.at(key);
331  }
332  }
333 
334  // Decrease lambda for next time
335  lambda /= parameters_.lambdaFactor;
336  // End this lambda search iteration
337  break;
338  } else {
339  // Reject this change
340  if(lambda >= parameters_.lambdaUpperBound) {
341  // The maximum lambda has been used. Print a warning and end the search.
342  std::cout << "Warning: Levenberg-Marquardt giving up because cannot decrease error with maximum lambda" << std::endl;
343  break;
344  } else {
345  // Increase lambda and continue searching
346  lambda *= parameters_.lambdaFactor;
347  }
348  }
349  } // end while
350  }
351  gttoc(optimizer_iteration);
352 
353  if (lmVerbosity >= LevenbergMarquardtParams::LAMBDA)
354  std::cout << "using lambda = " << lambda << std::endl;
355 
356  result.iterations++;
357  } while(result.iterations < (size_t)parameters_.maxIterations &&
359 
360  return result;
361 }
362 
363 /* ************************************************************************* */
365 
366  // The smoother summarization factors are the resulting marginal factors on the separator
367  // variables that result from marginalizing out all of the other variables
368  // These marginal factors will be cached for later transmission to the filter using
369  // linear container factors
370 
371  // Create a nonlinear factor graph without the filter summarization factors
373  for(size_t slot: filterSummarizationSlots_) {
374  graph.remove(slot);
375  }
376 
377  // Get the set of separator keys
378  const KeySet separatorKeys = separatorValues_.keySet();
379 
380  // Calculate the marginal factors on the separator
382 }
383 
384 /* ************************************************************************* */
385 void ConcurrentBatchSmoother::PrintNonlinearFactor(const NonlinearFactor::shared_ptr& factor, const std::string& indent, const KeyFormatter& keyFormatter) {
386  std::cout << indent;
387  if(factor) {
388  if(std::dynamic_pointer_cast<LinearContainerFactor>(factor)) {
389  std::cout << "l( ";
390  } else {
391  std::cout << "f( ";
392  }
393  for(Key key: *factor) {
394  std::cout << keyFormatter(key) << " ";
395  }
396  std::cout << ")" << std::endl;
397  } else {
398  std::cout << "{ nullptr }" << std::endl;
399  }
400 }
401 
402 /* ************************************************************************* */
403 void ConcurrentBatchSmoother::PrintLinearFactor(const GaussianFactor::shared_ptr& factor, const std::string& indent, const KeyFormatter& keyFormatter) {
404  std::cout << indent;
405  if(factor) {
406  std::cout << "g( ";
407  for(Key key: *factor) {
408  std::cout << keyFormatter(key) << " ";
409  }
410  std::cout << ")" << std::endl;
411  } else {
412  std::cout << "{ nullptr }" << std::endl;
413  }
414 }
415 
416 /* ************************************************************************* */
417 }
const gtsam::Symbol key('X', 0)
NonlinearFactorGraph smootherSummarization_
A temporary holding place for calculated smoother summarization.
static void PrintNonlinearFactor(const NonlinearFactor::shared_ptr &factor, const std::string &indent="", const KeyFormatter &keyFormatter=DefaultKeyFormatter)
bool equals(const ConcurrentSmoother &rhs, double tol=1e-9) const override
void replace(size_t index, sharedFactor factor)
Definition: FactorGraph.h:396
size_t nonlinearVariables
The number of variables that can be relinearized.
Wrap Jacobian and Hessian linear factors to allow simple injection into a nonlinear graph...
Global debugging flags.
size_t lambdas
The number of different L-M lambda factors that were tried during optimization.
void print(const std::string &str="VectorValues", const KeyFormatter &formatter=DefaultKeyFormatter) const
Point2 prior(const Point2 &x)
Prior on a single pose.
Definition: simulated2D.h:88
noiseModel::Diagonal::shared_ptr model
static void PrintLinearFactor(const GaussianFactor::shared_ptr &factor, const std::string &indent="", const KeyFormatter &keyFormatter=DefaultKeyFormatter)
const ValueType at(Key j) const
Definition: Values-inl.h:204
IsDerived< DERIVEDFACTOR > push_back(std::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:190
double lambdaFactor
The amount by which to multiply or divide lambda when adjusting lambda (default: 10.0)
Ordering ordering_
The current ordering used to calculate the linear deltas.
Eigen::MatrixXd Matrix
Definition: base/Matrix.h:39
KeyVector keys() const
Definition: Values.cpp:218
Values separatorValues_
The linearization points of the separator variables. These should not be updated during optimization...
const GaussianFactorGraph factors
void update(Key j, const Value &val)
Definition: Values.cpp:169
VectorValues delta_
The current set of linear deltas from the linearization point.
void remove(size_t i)
Definition: FactorGraph.h:393
iterator insert(const std::pair< Key, Vector > &key_value)
Vector & at(Key j)
Definition: VectorValues.h:139
NonlinearFactorGraph graph
double absoluteErrorTol
The maximum absolute error decrease to stop iterating (default 1e-5)
virtual void print(const std::string &s="FactorGraph", const KeyFormatter &formatter=DefaultKeyFormatter) const
Print out graph to std::cout, with optional key formatter.
void print(const std::string &s="Concurrent Batch Smoother:\, const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
VectorValues optimize(const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
Values retract(const VectorValues &delta) const
Definition: Values.cpp:98
std::shared_ptr< GaussianFactorGraph > linearize(const Values &linearizationPoint) const
Linearize a nonlinear factor graph.
double relativeErrorTol
The maximum relative error decrease to stop iterating (default 1e-5)
bool checkConvergence(double relativeErrorTreshold, double absoluteErrorTreshold, double errorThreshold, double currentError, double newError, NonlinearOptimizerParams::Verbosity verbosity)
void getSummarizedFactors(NonlinearFactorGraph &summarizedFactors, Values &separatorValues) override
double errorTol
The maximum total error to stop iterating (default 0.0)
size_t size() const
Definition: FactorGraph.h:334
#define gttic(label)
Definition: timing.h:295
void print(const std::string &str="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const
Definition: Values.cpp:66
bool equals(const VectorValues &x, double tol=1e-9) const
Eigen::VectorXd Vector
Definition: Vector.h:38
Values result
VectorValues zeroVectors() const
Definition: Values.cpp:260
size_t iterations
The number of optimizer iterations performed.
double error
The final factor graph error.
virtual Result update(const NonlinearFactorGraph &newFactors=NonlinearFactorGraph(), const Values &newTheta=Values(), const std::optional< std::vector< size_t > > &removeFactorIndices={})
VariableIndex variableIndex_
The current variable index, which allows efficient factor lookup by variable.
NonlinearFactorGraph calculateMarginalFactors(const NonlinearFactorGraph &graph, const Values &theta, const KeySet &remainingKeys, const GaussianFactorGraph::Eliminate &eliminateFunction)
RealScalar s
size_t size() const
Definition: Values.h:178
value_type KeyValuePair
Typedef to pair<Key, Vector>
Definition: VectorValues.h:85
bool equals(const Values &other, double tol=1e-9) const
Definition: Values.cpp:77
bool equals(const NonlinearFactorGraph &other, double tol=1e-9) const
double error(const Values &values) const
std::vector< size_t > insertFactors(const NonlinearFactorGraph &factors)
LevenbergMarquardtParams parameters_
LM parameters.
Values theta_
Current linearization point of all variables in the smoother.
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
const G & b
Definition: Group.h:86
size_t linearVariables
The number of variables that must keep a constant linearization point.
GaussianFactorGraph::Eliminate getEliminationFunction() const
std::shared_ptr< This > shared_ptr
shared_ptr to this class
A Levenberg-Marquardt Batch Smoother that implements the Concurrent Filtering and Smoothing interface...
cout<< "The eigenvalues of A are:"<< endl<< ces.eigenvalues()<< endl;cout<< "The matrix of eigenvectors, V, is:"<< endl<< ces.eigenvectors()<< endl<< endl;complex< float > lambda
noiseModel::Diagonal::shared_ptr SharedDiagonal
Definition: NoiseModel.h:743
traits
Definition: chartTesting.h:28
GTSAM_EXPORT bool equals(const Ordering &other, double tol=1e-9) const
Definition: Ordering.cpp:296
std::vector< size_t > filterSummarizationSlots_
The slots in factor graph that correspond to the current filter summarization factors.
NonlinearFactorGraph factors_
The set of all factors currently in the smoother.
void synchronize(const NonlinearFactorGraph &smootherFactors, const Values &smootherValues, const NonlinearFactorGraph &summarizedFactors, const Values &separatorValues) override
#define gttoc(label)
Definition: timing.h:296
std::shared_ptr< This > shared_ptr
double norm() const
size_t size() const
Definition: VectorValues.h:127
void insert(Key j, const Value &val)
Definition: Values.cpp:155
static double error
Definition: testRot3.cpp:37
void reserve(size_t size)
Definition: FactorGraph.h:186
Jet< T, N > sqrt(const Jet< T, N > &f)
Definition: jet.h:418
void removeFactors(const std::vector< size_t > &slots)
const G double tol
Definition: Group.h:86
double lambdaUpperBound
The maximum lambda to try before assuming the optimization has failed (default: 1e5) ...
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
VerbosityLM verbosityLM
The verbosity level for Levenberg-Marquardt (default: SILENT), see also NonlinearOptimizerParams::ver...
std::queue< size_t > availableSlots_
The set of available factor graph slots caused by deleting factors.
bool exists(Key j) const
Definition: Values.cpp:93
static Ordering ColamdConstrainedLast(const FACTOR_GRAPH &graph, const KeyVector &constrainLast, bool forceOrder=false)
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:102
KeySet keySet() const
Definition: Values.cpp:227
Timing utilities.
size_t maxIterations
The maximum iterations to stop iterating (default 100)
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)
Definition: NoiseModel.cpp:594
double lambdaInitial
The initial Levenberg-Marquardt damping term (default: 1e-5)


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:34:03