Merge pull request #1363 from borglab/hybrid/test_with_evaluate-2

release/4.3a0
Varun Agrawal 2023-01-03 15:24:22 -05:00 committed by GitHub
commit 385ae34fa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 54 additions and 64 deletions

View File

@ -108,7 +108,7 @@ bool GaussianMixture::equals(const HybridFactor &lf, double tol) const {
// This will return false if either conditionals_ is empty or e->conditionals_
// is empty, but not if both are empty or both are not empty:
if (conditionals_.empty() ^ e->conditionals_.empty()) return false;
std::cout << "checking" << std::endl;
// Check the base and the factors:
return BaseFactor::equals(*e, tol) &&
conditionals_.equals(e->conditionals_,

View File

@ -91,14 +91,6 @@ double GaussianMixtureFactor::constant(const DiscreteValues &assignment) const {
return factors_(assignment).constant;
}
/* *******************************************************************************/
// NOTE(dellaert): this was not used and is expensive.
// const GaussianMixtureFactor::Mixture GaussianMixtureFactor::factors() const {
// return Mixture(factors_, [](const FactorAndConstant &factor_z) {
// return factor_z.factor;
// });
// }
/* *******************************************************************************/
GaussianFactorGraphTree GaussianMixtureFactor::add(
const GaussianFactorGraphTree &sum) const {

View File

@ -186,7 +186,4 @@ template <>
struct traits<GaussianMixtureFactor> : public Testable<GaussianMixtureFactor> {
};
template <>
struct traits<GraphAndConstant> : public Testable<GraphAndConstant> {};
} // namespace gtsam

View File

@ -279,7 +279,6 @@ double HybridBayesNet::evaluate(const HybridValues &values) const {
const VectorValues &continuousValues = values.continuous();
double logDensity = 0.0, probability = 1.0;
bool debug = false;
// Iterate over each conditional.
for (auto &&conditional : *this) {
@ -287,23 +286,14 @@ double HybridBayesNet::evaluate(const HybridValues &values) const {
if (auto gm = conditional->asMixture()) {
const auto component = (*gm)(discreteValues);
logDensity += component->logDensity(continuousValues);
if (debug) {
GTSAM_PRINT(continuousValues);
std::cout << "component->logDensity(continuousValues) = "
<< component->logDensity(continuousValues) << std::endl;
}
} else if (auto gc = conditional->asGaussian()) {
// If continuous only, evaluate the probability and multiply.
logDensity += gc->logDensity(continuousValues);
if (debug)
std::cout << "gc->logDensity(continuousValues) = "
<< gc->logDensity(continuousValues) << std::endl;
} else if (auto dc = conditional->asDiscrete()) {
// Conditional is discrete-only, so return its probability.
probability *= dc->operator()(discreteValues);
if (debug)
std::cout << "dc->operator()(discreteValues) = "
<< dc->operator()(discreteValues) << std::endl;
}
}

View File

@ -117,6 +117,9 @@ bool HybridConditional::equals(const HybridFactor &other, double tol) const {
return other != nullptr && dc->equals(*other, tol);
}
return inner_->equals(*(e->inner_), tol);
return inner_ ? (e->inner_ ? inner_->equals(*(e->inner_), tol) : false)
: !(e->inner_);
}
/* ************************************************************************ */

View File

@ -187,6 +187,7 @@ class GTSAM_EXPORT HybridConditional
void serialize(Archive& ar, const unsigned int /*version*/) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(BaseFactor);
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(BaseConditional);
ar& BOOST_SERIALIZATION_NVP(inner_);
}
}; // HybridConditional

View File

@ -192,4 +192,7 @@ class GTSAM_EXPORT HybridFactor : public Factor {
template <>
struct traits<HybridFactor> : public Testable<HybridFactor> {};
template <>
struct traits<GraphAndConstant> : public Testable<GraphAndConstant> {};
} // namespace gtsam

View File

@ -174,7 +174,7 @@ discreteElimination(const HybridGaussianFactorGraph &factors,
}
}
// TODO(dellaert): This does sum-product. For max-product, use EliminateForMPE
// NOTE: This does sum-product. For max-product, use EliminateForMPE.
auto result = EliminateDiscrete(dfg, frontalKeys);
return {boost::make_shared<HybridConditional>(result.first),
@ -194,6 +194,7 @@ GaussianFactorGraphTree removeEmpty(const GaussianFactorGraphTree &sum) {
};
return GaussianFactorGraphTree(sum, emptyGaussian);
}
/* ************************************************************************ */
static std::pair<HybridConditional::shared_ptr, HybridFactor::shared_ptr>
hybridElimination(const HybridGaussianFactorGraph &factors,
@ -209,7 +210,9 @@ hybridElimination(const HybridGaussianFactorGraph &factors,
// decision tree indexed by all discrete keys involved.
GaussianFactorGraphTree sum = factors.assembleGraphTree();
// TODO(dellaert): does assembleGraphTree not guarantee this?
// Convert factor graphs with a nullptr to an empty factor graph.
// This is done after assembly since it is non-trivial to keep track of which
// FG has a nullptr as we're looping over the factors.
sum = removeEmpty(sum);
using EliminationPair = std::pair<boost::shared_ptr<GaussianConditional>,
@ -230,7 +233,8 @@ hybridElimination(const HybridGaussianFactorGraph &factors,
boost::tie(conditional, newFactor) =
EliminatePreferCholesky(graph_z.graph, frontalKeys);
// Get the log of the log normalization constant inverse.
// Get the log of the log normalization constant inverse and
// add it to the previous constant.
const double logZ =
graph_z.constant - conditional->logNormalizationConstant();
// Get the log of the log normalization constant inverse.
@ -273,13 +277,9 @@ hybridElimination(const HybridGaussianFactorGraph &factors,
auto factorProb =
[&](const GaussianMixtureFactor::FactorAndConstant &factor_z) {
// This is the probability q(μ) at the MLE point.
// factor_z.factor is a factor without keys, just containing the
// residual.
// factor_z.factor is a factor without keys,
// just containing the residual.
return exp(-factor_z.error(VectorValues()));
// TODO(dellaert): this is not correct, since VectorValues() is not
// the MLE point. But it does not matter, as at the MLE point the
// error will be zero, hence:
// return exp(factor_z.constant);
};
const DecisionTree<Key, double> fdt(newFactors, factorProb);
@ -301,8 +301,6 @@ hybridElimination(const HybridGaussianFactorGraph &factors,
boost::make_shared<HybridDiscreteFactor>(discreteFactor)};
} else {
// Create a resulting GaussianMixtureFactor on the separator.
// TODO(dellaert): Keys can be computed from the factors, so we should not
// need to pass them in.
return {boost::make_shared<HybridConditional>(gaussianMixture),
boost::make_shared<GaussianMixtureFactor>(
continuousSeparator, discreteSeparator, newFactors)};

View File

@ -34,7 +34,7 @@ const DiscreteKey mode{M(0), 2};
* Create a tiny two variable hybrid model which represents
* the generative probability P(z,x,mode) = P(z|x,mode)P(x)P(mode).
*/
HybridBayesNet createHybridBayesNet(int num_measurements = 1) {
inline HybridBayesNet createHybridBayesNet(int num_measurements = 1) {
HybridBayesNet bayesNet;
// Create Gaussian mixture z_i = x0 + noise for each measurement.
@ -61,8 +61,8 @@ HybridBayesNet createHybridBayesNet(int num_measurements = 1) {
/**
* Convert a hybrid Bayes net to a hybrid Gaussian factor graph.
*/
HybridGaussianFactorGraph convertBayesNet(const HybridBayesNet& bayesNet,
const VectorValues& measurements) {
inline HybridGaussianFactorGraph convertBayesNet(
const HybridBayesNet& bayesNet, const VectorValues& measurements) {
HybridGaussianFactorGraph fg;
int num_measurements = bayesNet.size() - 2;
for (int i = 0; i < num_measurements; i++) {
@ -81,7 +81,7 @@ HybridGaussianFactorGraph convertBayesNet(const HybridBayesNet& bayesNet,
* continuous variable x0. If no measurements are given, they are sampled from
* the generative Bayes net model HybridBayesNet::Example(num_measurements)
*/
HybridGaussianFactorGraph createHybridGaussianFactorGraph(
inline HybridGaussianFactorGraph createHybridGaussianFactorGraph(
int num_measurements = 1,
boost::optional<VectorValues> measurements = boost::none) {
auto bayesNet = createHybridBayesNet(num_measurements);

View File

@ -23,8 +23,8 @@
#include <gtsam/hybrid/HybridBayesTree.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include "TinyHybridExample.h"
#include "Switching.h"
#include "TinyHybridExample.h"
// Include for test suite
#include <CppUnitLite/TestHarness.h>
@ -244,7 +244,7 @@ TEST(HybridBayesNet, Error) {
EXPECT(assert_equal(expected_pruned_error, pruned_error_tree, 1e-9));
// Verify error computation and check for specific error value
DiscreteValues discrete_values {{M(0), 1}, {M(1), 1}};
DiscreteValues discrete_values{{M(0), 1}, {M(1), 1}};
double total_error = 0;
for (size_t idx = 0; idx < hybridBayesNet->size(); idx++) {
@ -337,9 +337,11 @@ TEST(HybridBayesNet, Serialization) {
Ordering ordering = s.linearizedFactorGraph.getHybridOrdering();
HybridBayesNet hbn = *(s.linearizedFactorGraph.eliminateSequential(ordering));
EXPECT(equalsObj<HybridBayesNet>(hbn));
EXPECT(equalsXML<HybridBayesNet>(hbn));
EXPECT(equalsBinary<HybridBayesNet>(hbn));
// TODO(Varun) Serialization of inner factor doesn't work. Requires
// serialization support for all hybrid factors.
// EXPECT(equalsObj<HybridBayesNet>(hbn));
// EXPECT(equalsXML<HybridBayesNet>(hbn));
// EXPECT(equalsBinary<HybridBayesNet>(hbn));
}
/* ****************************************************************************/

View File

@ -155,7 +155,7 @@ TEST(HybridBayesTree, Optimize) {
dfg.push_back(
boost::dynamic_pointer_cast<DecisionTreeFactor>(factor->inner()));
}
// Add the probabilities for each branch
DiscreteKeys discrete_keys = {{M(0), 2}, {M(1), 2}, {M(2), 2}};
vector<double> probs = {0.012519475, 0.041280228, 0.075018647, 0.081663656,
@ -211,10 +211,10 @@ TEST(HybridBayesTree, Choose) {
ordering += M(0);
ordering += M(1);
ordering += M(2);
//TODO(Varun) get segfault if ordering not provided
// TODO(Varun) get segfault if ordering not provided
auto bayesTree = s.linearizedFactorGraph.eliminateMultifrontal(ordering);
auto expected_gbt = bayesTree->choose(assignment);
EXPECT(assert_equal(expected_gbt, gbt));
@ -229,9 +229,11 @@ TEST(HybridBayesTree, Serialization) {
*(s.linearizedFactorGraph.eliminateMultifrontal(ordering));
using namespace gtsam::serializationTestHelpers;
EXPECT(equalsObj<HybridBayesTree>(hbt));
EXPECT(equalsXML<HybridBayesTree>(hbt));
EXPECT(equalsBinary<HybridBayesTree>(hbt));
// TODO(Varun) Serialization of inner factor doesn't work. Requires
// serialization support for all hybrid factors.
// EXPECT(equalsObj<HybridBayesTree>(hbt));
// EXPECT(equalsXML<HybridBayesTree>(hbt));
// EXPECT(equalsBinary<HybridBayesTree>(hbt));
}
/* ************************************************************************* */

View File

@ -177,19 +177,19 @@ TEST(HybridGaussianElimination, IncrementalInference) {
// Test the probability values with regression tests.
DiscreteValues assignment;
EXPECT(assert_equal(0.000956191, m00_prob, 1e-5));
EXPECT(assert_equal(0.0952922, m00_prob, 1e-5));
assignment[M(0)] = 0;
assignment[M(1)] = 0;
EXPECT(assert_equal(0.000956191, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.0952922, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 1;
assignment[M(1)] = 0;
EXPECT(assert_equal(0.00283728, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.282758, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 0;
assignment[M(1)] = 1;
EXPECT(assert_equal(0.00315253, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.314175, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 1;
assignment[M(1)] = 1;
EXPECT(assert_equal(0.00308831, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.307775, (*discreteConditional)(assignment), 1e-5));
// Check if the clique conditional generated from incremental elimination
// matches that of batch elimination.
@ -199,7 +199,7 @@ TEST(HybridGaussianElimination, IncrementalInference) {
isam[M(1)]->conditional()->inner());
// Account for the probability terms from evaluating continuous FGs
DiscreteKeys discrete_keys = {{M(0), 2}, {M(1), 2}};
vector<double> probs = {0.00095619114, 0.0031525308, 0.0028372777, 0.0030883072};
vector<double> probs = {0.095292197, 0.31417524, 0.28275772, 0.30777485};
auto expectedConditional =
boost::make_shared<DecisionTreeFactor>(discrete_keys, probs);
EXPECT(assert_equal(*expectedConditional, *actualConditional, 1e-6));

View File

@ -443,7 +443,7 @@ TEST(HybridFactorGraph, Full_Elimination) {
ordering.clear();
for (size_t k = 0; k < self.K - 1; k++) ordering += M(k);
discreteBayesNet =
*discrete_fg.eliminateSequential(ordering, EliminateForMPE);
*discrete_fg.eliminateSequential(ordering, EliminateDiscrete);
}
// Create ordering.

View File

@ -195,19 +195,19 @@ TEST(HybridNonlinearISAM, IncrementalInference) {
// Test the probability values with regression tests.
DiscreteValues assignment;
EXPECT(assert_equal(0.000956191, m00_prob, 1e-5));
EXPECT(assert_equal(0.0952922, m00_prob, 1e-5));
assignment[M(0)] = 0;
assignment[M(1)] = 0;
EXPECT(assert_equal(0.000956191, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.0952922, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 1;
assignment[M(1)] = 0;
EXPECT(assert_equal(0.00283728, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.282758, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 0;
assignment[M(1)] = 1;
EXPECT(assert_equal(0.00315253, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.314175, (*discreteConditional)(assignment), 1e-5));
assignment[M(0)] = 1;
assignment[M(1)] = 1;
EXPECT(assert_equal(0.00308831, (*discreteConditional)(assignment), 1e-5));
EXPECT(assert_equal(0.307775, (*discreteConditional)(assignment), 1e-5));
// Check if the clique conditional generated from incremental elimination
// matches that of batch elimination.
@ -216,7 +216,7 @@ TEST(HybridNonlinearISAM, IncrementalInference) {
bayesTree[M(1)]->conditional()->inner());
// Account for the probability terms from evaluating continuous FGs
DiscreteKeys discrete_keys = {{M(0), 2}, {M(1), 2}};
vector<double> probs = {0.00095619114, 0.0031525308, 0.0028372777, 0.0030883072};
vector<double> probs = {0.095292197, 0.31417524, 0.28275772, 0.30777485};
auto expectedConditional =
boost::make_shared<DecisionTreeFactor>(discrete_keys, probs);
EXPECT(assert_equal(*expectedConditional, *actualConditional, 1e-6));

View File

@ -507,6 +507,8 @@ TEST(GaussianConditional, Print) {
" R = [ 1 0 ]\n"
" [ 0 1 ]\n"
" d = [ 20 40 ]\n"
" mean: 1 elements\n"
" x0: 20 40\n"
"isotropic dim=2 sigma=3\n";
EXPECT(assert_print_equal(expected, conditional, "GaussianConditional"));