dcsam
Factored inference for discrete-continuous smoothing and mapping
DCSAM_utils.h
Go to the documentation of this file.
1 
9 #pragma once
10 
11 #include <gtsam/base/Vector.h>
12 #include <math.h>
13 
14 #include <limits>
15 #include <string>
16 #include <vector>
17 
18 namespace dcsam {
19 
20 inline std::vector<double> expNormalize(const std::vector<double> &logProbs) {
21  /*
22  * Normalizing a set of log probabilities in a numerically stable way is
23  * tricky. To avoid overflow/underflow issues, we compute the largest
24  * (finite) log probability and subtract it from each log probability before
25  * normalizing. This comes from the observation that if:
26  * p_i = exp(L_i) / ( sum_j exp(L_j) ),
27  * Then,
28  * p_i = exp(Z) exp(L_i - Z) / (exp(Z) sum_j exp(L_j - Z)),
29  * = exp(L_i - Z) / ( sum_j exp(L_j - Z) )
30  *
31  * Setting Z = max_j L_j, we can avoid numerical issues that arise when all
32  * of the (unnormalized) log probabilities are either very large or very
33  * small.
34  */
35 
36  std::vector<double> cleanLogProbs;
37  double maxLogProb = -std::numeric_limits<double>::infinity();
38  for (size_t i = 0; i < logProbs.size(); i++) {
39  double logProb = (!std::isnan(logProbs[i]))
40  ? logProbs[i]
41  : -std::numeric_limits<double>::infinity();
42  if ((logProb != std::numeric_limits<double>::infinity()) &&
43  logProb > maxLogProb) {
44  maxLogProb = logProb;
45  }
46  cleanLogProbs.push_back(logProb);
47  }
48 
49  // After computing the max = "Z" of the log probabilities L_i, we compute
50  // the log of the normalizing constant, log S, where S = sum_j exp(L_j - Z).
51  double total = 0.0;
52  for (size_t i = 0; i < cleanLogProbs.size(); i++) {
53  double probPrime = exp(cleanLogProbs[i] - maxLogProb);
54  total += probPrime;
55  }
56  double logTotal = log(total);
57 
58  // Now we compute the (normalized) probability (for each i):
59  // p_i = exp(L_i - Z - log S)
60  double checkNormalization = 0.0;
61  std::vector<double> probs;
62  for (size_t i = 0; i < cleanLogProbs.size(); i++) {
63  double prob = exp(cleanLogProbs[i] - maxLogProb - logTotal);
64  probs.push_back(prob);
65  checkNormalization += prob;
66  }
67 
68  // Numerical tolerance for floating point comparisons
69  double tol = 1e-9;
70 
71  if (!gtsam::fpEqual(checkNormalization, 1.0, tol)) {
72  std::string errMsg =
73  std::string("expNormalize failed to normalize probabilities. ") +
74  std::string("Expected normalization constant = 1.0. Got value: ") +
75  std::to_string(checkNormalization) +
76  std::string(
77  "\n This could have resulted from numerical overflow/underflow.");
78  throw std::logic_error(errMsg);
79  }
80  return probs;
81 }
82 
83 } // namespace dcsam
Definition: DCContinuousFactor.h:24
std::vector< double > expNormalize(const std::vector< double > &logProbs)
Definition: DCSAM_utils.h:20
const double tol
Definition: testDCSAM.cpp:40