From f65bd4d90d7597987bb75573d9fc66476acd6c0f Mon Sep 17 00:00:00 2001 From: Fan Jiang Date: Thu, 6 Jan 2022 20:10:03 -0500 Subject: [PATCH 01/15] Remove Potentials --- gtsam/discrete/AlgebraicDecisionTree.h | 1 + gtsam/discrete/DecisionTreeFactor.cpp | 36 +++- gtsam/discrete/DecisionTreeFactor.h | 27 ++- gtsam/discrete/DiscreteConditional.cpp | 2 +- gtsam/discrete/DiscreteConditional.h | 2 +- gtsam/discrete/Potentials.cpp | 184 +++++++++--------- gtsam/discrete/Potentials.h | 72 +------ gtsam/discrete/tests/testDiscreteBayesNet.cpp | 10 +- gtsam/inference/BayesTree.h | 7 +- gtsam_unstable/discrete/Scheduler.cpp | 4 +- 10 files changed, 158 insertions(+), 187 deletions(-) diff --git a/gtsam/discrete/AlgebraicDecisionTree.h b/gtsam/discrete/AlgebraicDecisionTree.h index 60f3017f4f..d2e05927a3 100644 --- a/gtsam/discrete/AlgebraicDecisionTree.h +++ b/gtsam/discrete/AlgebraicDecisionTree.h @@ -179,5 +179,6 @@ namespace gtsam { }; // AlgebraicDecisionTree +template struct traits> : public Testable> {}; } // namespace gtsam diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index 4f3e3f7f14..ba24b58b90 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -34,12 +34,13 @@ namespace gtsam { /* ******************************************************************************** */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys& keys, const ADT& potentials) : - DiscreteFactor(keys.indices()), Potentials(keys, potentials) { + DiscreteFactor(keys.indices()), ADT(potentials), + cardinalities_(keys.cardinalities()) { } /* *************************************************************************/ DecisionTreeFactor::DecisionTreeFactor(const DiscreteConditional& c) : - DiscreteFactor(c.keys()), Potentials(c) { + DiscreteFactor(c.keys()), AlgebraicDecisionTree(c), cardinalities_(c.cardinalities_) { } /* ************************************************************************* */ @@ -48,16 +49,25 @@ namespace gtsam { return false; } else { - const DecisionTreeFactor& f(static_cast(other)); - return Potentials::equals(f, tol); + const auto& f(static_cast(other)); + return ADT::equals(f, tol); } } + /* ************************************************************************* */ + double DecisionTreeFactor::safe_div(const double &a, const double &b) { + // cout << boost::format("%g / %g = %g\n") % a % b % ((a == 0) ? 0 : (a / b)); + // The use for safe_div is when we divide the product factor by the sum + // factor. If the product or sum is zero, we accord zero probability to the + // event. + return (a == 0 || b == 0) ? 0 : (a / b); + } + /* ************************************************************************* */ void DecisionTreeFactor::print(const string& s, const KeyFormatter& formatter) const { cout << s; - Potentials::print("Potentials:",formatter); + ADT::print("Potentials:",formatter); } /* ************************************************************************* */ @@ -162,20 +172,20 @@ namespace gtsam { void DecisionTreeFactor::dot(std::ostream& os, const KeyFormatter& keyFormatter, bool showZero) const { - Potentials::dot(os, keyFormatter, valueFormatter, showZero); + ADT::dot(os, keyFormatter, valueFormatter, showZero); } /** output to graphviz format, open a file */ void DecisionTreeFactor::dot(const std::string& name, const KeyFormatter& keyFormatter, bool showZero) const { - Potentials::dot(name, keyFormatter, valueFormatter, showZero); + ADT::dot(name, keyFormatter, valueFormatter, showZero); } /** output to graphviz format string */ std::string DecisionTreeFactor::dot(const KeyFormatter& keyFormatter, bool showZero) const { - return Potentials::dot(keyFormatter, valueFormatter, showZero); + return ADT::dot(keyFormatter, valueFormatter, showZero); } /* ************************************************************************* */ @@ -209,5 +219,15 @@ namespace gtsam { return ss.str(); } + DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys &keys, const vector &table) : + DiscreteFactor(keys.indices()), AlgebraicDecisionTree(keys, table), + cardinalities_(keys.cardinalities()) { + } + + DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys &keys, const string &table) : + DiscreteFactor(keys.indices()), AlgebraicDecisionTree(keys, table), + cardinalities_(keys.cardinalities()) { + } + /* ************************************************************************* */ } // namespace gtsam diff --git a/gtsam/discrete/DecisionTreeFactor.h b/gtsam/discrete/DecisionTreeFactor.h index f8832c2237..f7c50d5b5f 100644 --- a/gtsam/discrete/DecisionTreeFactor.h +++ b/gtsam/discrete/DecisionTreeFactor.h @@ -19,7 +19,8 @@ #pragma once #include -#include +#include +#include #include #include @@ -35,7 +36,7 @@ namespace gtsam { /** * A discrete probabilistic factor */ - class GTSAM_EXPORT DecisionTreeFactor: public DiscreteFactor, public Potentials { + class GTSAM_EXPORT DecisionTreeFactor: public DiscreteFactor, public AlgebraicDecisionTree { public: @@ -43,6 +44,10 @@ namespace gtsam { typedef DecisionTreeFactor This; typedef DiscreteFactor Base; ///< Typedef to base class typedef boost::shared_ptr shared_ptr; + typedef AlgebraicDecisionTree ADT; + + protected: + std::map cardinalities_; public: @@ -55,11 +60,11 @@ namespace gtsam { /** Constructor from Indices, Ordering, and AlgebraicDecisionDiagram */ DecisionTreeFactor(const DiscreteKeys& keys, const ADT& potentials); - /** Constructor from Indices and (string or doubles) */ - template - DecisionTreeFactor(const DiscreteKeys& keys, SOURCE table) : - DiscreteFactor(keys.indices()), Potentials(keys, table) { - } + /** Constructor from doubles */ + DecisionTreeFactor(const DiscreteKeys& keys, const std::vector& table); + + /** Constructor from string */ + DecisionTreeFactor(const DiscreteKeys& keys, const std::string& table); /// Single-key specialization template @@ -71,7 +76,7 @@ namespace gtsam { : DecisionTreeFactor(DiscreteKeys{key}, row) {} /** Construct from a DiscreteConditional type */ - DecisionTreeFactor(const DiscreteConditional& c); + explicit DecisionTreeFactor(const DiscreteConditional& c); /// @} /// @name Testable @@ -90,7 +95,7 @@ namespace gtsam { /// Value is just look up in AlgebraicDecisonTree double operator()(const DiscreteValues& values) const override { - return Potentials::operator()(values); + return ADT::operator()(values); } /// multiply two factors @@ -98,6 +103,10 @@ namespace gtsam { return apply(f, ADT::Ring::mul); } + static double safe_div(const double& a, const double& b); + + size_t cardinality(Key j) const { return cardinalities_.at(j);} + /// divide by factor f (safely) DecisionTreeFactor operator/(const DecisionTreeFactor& f) const { return apply(f, safe_div); diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 8ee93eb774..951c0b6cab 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -80,7 +80,7 @@ void DiscreteConditional::print(const string& s, } } cout << ")"; - Potentials::print(""); + ADT::print(""); cout << endl; } diff --git a/gtsam/discrete/DiscreteConditional.h b/gtsam/discrete/DiscreteConditional.h index 7ce3dc9308..4c2e964fda 100644 --- a/gtsam/discrete/DiscreteConditional.h +++ b/gtsam/discrete/DiscreteConditional.h @@ -128,7 +128,7 @@ class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, /// Evaluate, just look up in AlgebraicDecisonTree double operator()(const DiscreteValues& values) const override { - return Potentials::operator()(values); + return ADT::operator()(values); } /** Convert to a factor */ diff --git a/gtsam/discrete/Potentials.cpp b/gtsam/discrete/Potentials.cpp index 057b6a2655..b0ddcc822a 100644 --- a/gtsam/discrete/Potentials.cpp +++ b/gtsam/discrete/Potentials.cpp @@ -1,96 +1,96 @@ -/* ---------------------------------------------------------------------------- - - * GTSAM Copyright 2010, Georgia Tech Research Corporation, - * Atlanta, Georgia 30332-0415 - * All Rights Reserved - * Authors: Frank Dellaert, et al. (see THANKS for the full author list) - - * See LICENSE for the license information - - * -------------------------------------------------------------------------- */ - -/** - * @file Potentials.cpp - * @date March 24, 2011 - * @author Frank Dellaert - */ - -#include -#include - -#include - -#include - -using namespace std; - -namespace gtsam { - -/* ************************************************************************* */ -double Potentials::safe_div(const double& a, const double& b) { - // cout << boost::format("%g / %g = %g\n") % a % b % ((a == 0) ? 0 : (a / b)); - // The use for safe_div is when we divide the product factor by the sum - // factor. If the product or sum is zero, we accord zero probability to the - // event. - return (a == 0 || b == 0) ? 0 : (a / b); -} - -/* ******************************************************************************** - */ -Potentials::Potentials() : ADT(1.0) {} - -/* ******************************************************************************** - */ -Potentials::Potentials(const DiscreteKeys& keys, const ADT& decisionTree) - : ADT(decisionTree), cardinalities_(keys.cardinalities()) {} - -/* ************************************************************************* */ -bool Potentials::equals(const Potentials& other, double tol) const { - return ADT::equals(other, tol); -} - -/* ************************************************************************* */ -void Potentials::print(const string& s, const KeyFormatter& formatter) const { - cout << s << "\n Cardinalities: { "; - for (const std::pair& key : cardinalities_) - cout << formatter(key.first) << ":" << key.second << ", "; - cout << "}" << endl; - ADT::print(" ", formatter); -} +///* ---------------------------------------------------------------------------- // -// /* ************************************************************************* */ -// template -// void Potentials::remapIndices(const P& remapping) { -// // Permute the _cardinalities (TODO: Inefficient Consider Improving) -// DiscreteKeys keys; -// map ordering; -// -// // Get the original keys from cardinalities_ -// for(const DiscreteKey& key: cardinalities_) -// keys & key; -// -// // Perform Permutation -// for(DiscreteKey& key: keys) { -// ordering[key.first] = remapping[key.first]; -// key.first = ordering[key.first]; -// } -// -// // Change *this -// AlgebraicDecisionTree permuted((*this), ordering); -// *this = permuted; -// cardinalities_ = keys.cardinalities(); -// } +// * GTSAM Copyright 2010, Georgia Tech Research Corporation, +// * Atlanta, Georgia 30332-0415 +// * All Rights Reserved +// * Authors: Frank Dellaert, et al. (see THANKS for the full author list) // -// /* ************************************************************************* */ -// void Potentials::permuteWithInverse(const Permutation& inversePermutation) { -// remapIndices(inversePermutation); -// } +// * See LICENSE for the license information +// +// * -------------------------------------------------------------------------- */ +// +///** +// * @file Potentials.cpp +// * @date March 24, 2011 +// * @author Frank Dellaert +// */ +// +//#include +//#include +// +//#include +// +//#include +// +//using namespace std; +// +//namespace gtsam { +// +///* ************************************************************************* */ +//double Potentials::safe_div(const double& a, const double& b) { +// // cout << boost::format("%g / %g = %g\n") % a % b % ((a == 0) ? 0 : (a / b)); +// // The use for safe_div is when we divide the product factor by the sum +// // factor. If the product or sum is zero, we accord zero probability to the +// // event. +// return (a == 0 || b == 0) ? 0 : (a / b); +//} +// +///* ******************************************************************************** +// */ +//Potentials::Potentials() : ADT(1.0) {} +// +///* ******************************************************************************** +// */ +//Potentials::Potentials(const DiscreteKeys& keys, const ADT& decisionTree) +// : ADT(decisionTree), cardinalities_(keys.cardinalities()) {} +// +///* ************************************************************************* */ +//bool Potentials::equals(const Potentials& other, double tol) const { +// return ADT::equals(other, tol); +//} +// +///* ************************************************************************* */ +//void Potentials::print(const string& s, const KeyFormatter& formatter) const { +// cout << s << "\n Cardinalities: { "; +// for (const std::pair& key : cardinalities_) +// cout << formatter(key.first) << ":" << key.second << ", "; +// cout << "}" << endl; +// ADT::print(" ", formatter); +//} +//// +//// /* ************************************************************************* */ +//// template +//// void Potentials::remapIndices(const P& remapping) { +//// // Permute the _cardinalities (TODO: Inefficient Consider Improving) +//// DiscreteKeys keys; +//// map ordering; +//// +//// // Get the original keys from cardinalities_ +//// for(const DiscreteKey& key: cardinalities_) +//// keys & key; +//// +//// // Perform Permutation +//// for(DiscreteKey& key: keys) { +//// ordering[key.first] = remapping[key.first]; +//// key.first = ordering[key.first]; +//// } +//// +//// // Change *this +//// AlgebraicDecisionTree permuted((*this), ordering); +//// *this = permuted; +//// cardinalities_ = keys.cardinalities(); +//// } +//// +//// /* ************************************************************************* */ +//// void Potentials::permuteWithInverse(const Permutation& inversePermutation) { +//// remapIndices(inversePermutation); +//// } +//// +//// /* ************************************************************************* */ +//// void Potentials::reduceWithInverse(const internal::Reduction& inverseReduction) { +//// remapIndices(inverseReduction); +//// } // // /* ************************************************************************* */ -// void Potentials::reduceWithInverse(const internal::Reduction& inverseReduction) { -// remapIndices(inverseReduction); -// } - - /* ************************************************************************* */ - -} // namespace gtsam +// +//} // namespace gtsam diff --git a/gtsam/discrete/Potentials.h b/gtsam/discrete/Potentials.h index 856b928168..93452d8fb0 100644 --- a/gtsam/discrete/Potentials.h +++ b/gtsam/discrete/Potentials.h @@ -26,72 +26,10 @@ namespace gtsam { - /** - * A base class for both DiscreteFactor and DiscreteConditional - */ - class GTSAM_EXPORT Potentials: public AlgebraicDecisionTree { - - public: - - typedef AlgebraicDecisionTree ADT; - - protected: - - /// Cardinality for each key, used in combine - std::map cardinalities_; - - /** Constructor from ColumnIndex, and ADT */ - Potentials(const ADT& potentials) : - ADT(potentials) { - } - - // Safe division for probabilities - static double safe_div(const double& a, const double& b); - -// // Apply either a permutation or a reduction -// template -// void remapIndices(const P& remapping); - - public: - - /** Default constructor for I/O */ - Potentials(); - - /** Constructor from Indices and ADT */ - Potentials(const DiscreteKeys& keys, const ADT& decisionTree); - - /** Constructor from Indices and (string or doubles) */ - template - Potentials(const DiscreteKeys& keys, SOURCE table) : - ADT(keys, table), cardinalities_(keys.cardinalities()) { - } - - // Testable - bool equals(const Potentials& other, double tol = 1e-9) const; - void print(const std::string& s = "Potentials: ", - const KeyFormatter& formatter = DefaultKeyFormatter) const; - - size_t cardinality(Key j) const { return cardinalities_.at(j);} - -// /** -// * @brief Permutes the keys in Potentials -// * -// * This permutes the Indices and performs necessary re-ordering of ADD. -// * This is virtual so that derived types e.g. DecisionTreeFactor can -// * re-implement it. -// */ -// GTSAM_EXPORT virtual void permuteWithInverse(const Permutation& inversePermutation); -// -// /** -// * Apply a reduction, which is a remapping of variable indices. -// */ -// GTSAM_EXPORT virtual void reduceWithInverse(const internal::Reduction& inverseReduction); - - }; // Potentials - -// traits -template<> struct traits : public Testable {}; -template<> struct traits : public Testable {}; - + /* + * @deprecated + * @brief Deprecated class for storing an ADT with some convenience methods + * */ + typedef GTSAM_DEPRECATED AlgebraicDecisionTree Potentials; } // namespace gtsam diff --git a/gtsam/discrete/tests/testDiscreteBayesNet.cpp b/gtsam/discrete/tests/testDiscreteBayesNet.cpp index 251978c99b..0686b3920c 100644 --- a/gtsam/discrete/tests/testDiscreteBayesNet.cpp +++ b/gtsam/discrete/tests/testDiscreteBayesNet.cpp @@ -41,21 +41,23 @@ using namespace gtsam; static const DiscreteKey Asia(0, 2), Smoking(4, 2), Tuberculosis(3, 2), LungCancer(6, 2), Bronchitis(7, 2), Either(5, 2), XRay(2, 2), Dyspnea(1, 2); +using ADT = AlgebraicDecisionTree; + /* ************************************************************************* */ TEST(DiscreteBayesNet, bayesNet) { DiscreteBayesNet bayesNet; DiscreteKey Parent(0, 2), Child(1, 2); auto prior = boost::make_shared(Parent % "6/4"); - CHECK(assert_equal(Potentials::ADT({Parent}, "0.6 0.4"), - (Potentials::ADT)*prior)); + CHECK(assert_equal(ADT({Parent}, "0.6 0.4"), + (ADT)*prior)); bayesNet.push_back(prior); auto conditional = boost::make_shared(Child | Parent = "7/3 8/2"); EXPECT_LONGS_EQUAL(1, *(conditional->beginFrontals())); - Potentials::ADT expected(Child & Parent, "0.7 0.8 0.3 0.2"); - CHECK(assert_equal(expected, (Potentials::ADT)*conditional)); + ADT expected(Child & Parent, "0.7 0.8 0.3 0.2"); + CHECK(assert_equal(expected, (ADT)*conditional)); bayesNet.push_back(conditional); DiscreteFactorGraph fg(bayesNet); diff --git a/gtsam/inference/BayesTree.h b/gtsam/inference/BayesTree.h index 68a45a014a..132098d0a7 100644 --- a/gtsam/inference/BayesTree.h +++ b/gtsam/inference/BayesTree.h @@ -72,8 +72,9 @@ namespace gtsam { public: typedef CLIQUE Clique; ///< The clique type, normally BayesTreeClique typedef boost::shared_ptr sharedClique; ///< Shared pointer to a clique - typedef Clique Node; ///< Synonym for Clique (TODO: remove) - typedef sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) + + typedef GTSAM_DEPRECATED Clique Node; ///< Synonym for Clique (TODO: remove) + typedef GTSAM_DEPRECATED sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) typedef typename CLIQUE::ConditionalType ConditionalType; typedef boost::shared_ptr sharedConditional; typedef typename CLIQUE::BayesNetType BayesNetType; @@ -143,7 +144,7 @@ namespace gtsam { const Nodes& nodes() const { return nodes_; } /** Access node by variable */ - const sharedNode operator[](Key j) const { return nodes_.at(j); } + sharedClique operator[](Key j) const { return nodes_.at(j); } /** return root cliques */ const Roots& roots() const { return roots_; } diff --git a/gtsam_unstable/discrete/Scheduler.cpp b/gtsam_unstable/discrete/Scheduler.cpp index e34613c3b3..f166405932 100644 --- a/gtsam_unstable/discrete/Scheduler.cpp +++ b/gtsam_unstable/discrete/Scheduler.cpp @@ -130,9 +130,9 @@ void Scheduler::addStudentSpecificConstraints(size_t i, // get all constraints then specialize to slot size_t dummyIndex = maxNrStudents_ * 3 + maxNrStudents_; DiscreteKey dummy(dummyIndex, nrTimeSlots()); - Potentials::ADT p(dummy & areaKey, + AlgebraicDecisionTree p(dummy & areaKey, available_); // available_ is Doodle string - Potentials::ADT q = p.choose(dummyIndex, *slot); + auto q = p.choose(dummyIndex, *slot); CSP::add(areaKey, q); } else { DiscreteKeys keys {s.key_, areaKey}; From f7f208e0f52d97434d8a183470893d4172296557 Mon Sep 17 00:00:00 2001 From: Fan Jiang Date: Thu, 6 Jan 2022 20:12:10 -0500 Subject: [PATCH 02/15] remove Potentials.cpp --- gtsam/discrete/Potentials.cpp | 96 ----------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 gtsam/discrete/Potentials.cpp diff --git a/gtsam/discrete/Potentials.cpp b/gtsam/discrete/Potentials.cpp deleted file mode 100644 index b0ddcc822a..0000000000 --- a/gtsam/discrete/Potentials.cpp +++ /dev/null @@ -1,96 +0,0 @@ -///* ---------------------------------------------------------------------------- -// -// * GTSAM Copyright 2010, Georgia Tech Research Corporation, -// * Atlanta, Georgia 30332-0415 -// * All Rights Reserved -// * Authors: Frank Dellaert, et al. (see THANKS for the full author list) -// -// * See LICENSE for the license information -// -// * -------------------------------------------------------------------------- */ -// -///** -// * @file Potentials.cpp -// * @date March 24, 2011 -// * @author Frank Dellaert -// */ -// -//#include -//#include -// -//#include -// -//#include -// -//using namespace std; -// -//namespace gtsam { -// -///* ************************************************************************* */ -//double Potentials::safe_div(const double& a, const double& b) { -// // cout << boost::format("%g / %g = %g\n") % a % b % ((a == 0) ? 0 : (a / b)); -// // The use for safe_div is when we divide the product factor by the sum -// // factor. If the product or sum is zero, we accord zero probability to the -// // event. -// return (a == 0 || b == 0) ? 0 : (a / b); -//} -// -///* ******************************************************************************** -// */ -//Potentials::Potentials() : ADT(1.0) {} -// -///* ******************************************************************************** -// */ -//Potentials::Potentials(const DiscreteKeys& keys, const ADT& decisionTree) -// : ADT(decisionTree), cardinalities_(keys.cardinalities()) {} -// -///* ************************************************************************* */ -//bool Potentials::equals(const Potentials& other, double tol) const { -// return ADT::equals(other, tol); -//} -// -///* ************************************************************************* */ -//void Potentials::print(const string& s, const KeyFormatter& formatter) const { -// cout << s << "\n Cardinalities: { "; -// for (const std::pair& key : cardinalities_) -// cout << formatter(key.first) << ":" << key.second << ", "; -// cout << "}" << endl; -// ADT::print(" ", formatter); -//} -//// -//// /* ************************************************************************* */ -//// template -//// void Potentials::remapIndices(const P& remapping) { -//// // Permute the _cardinalities (TODO: Inefficient Consider Improving) -//// DiscreteKeys keys; -//// map ordering; -//// -//// // Get the original keys from cardinalities_ -//// for(const DiscreteKey& key: cardinalities_) -//// keys & key; -//// -//// // Perform Permutation -//// for(DiscreteKey& key: keys) { -//// ordering[key.first] = remapping[key.first]; -//// key.first = ordering[key.first]; -//// } -//// -//// // Change *this -//// AlgebraicDecisionTree permuted((*this), ordering); -//// *this = permuted; -//// cardinalities_ = keys.cardinalities(); -//// } -//// -//// /* ************************************************************************* */ -//// void Potentials::permuteWithInverse(const Permutation& inversePermutation) { -//// remapIndices(inversePermutation); -//// } -//// -//// /* ************************************************************************* */ -//// void Potentials::reduceWithInverse(const internal::Reduction& inverseReduction) { -//// remapIndices(inverseReduction); -//// } -// -// /* ************************************************************************* */ -// -//} // namespace gtsam From 3fe3a47221c226216012373c30621ec4751749aa Mon Sep 17 00:00:00 2001 From: Fan Jiang Date: Fri, 7 Jan 2022 10:39:11 -0500 Subject: [PATCH 03/15] Address comments --- gtsam/discrete/DecisionTreeFactor.cpp | 1 - gtsam/discrete/Potentials.h | 35 --------------------------- gtsam/inference/BayesTree.h | 8 ++++-- 3 files changed, 6 insertions(+), 38 deletions(-) delete mode 100644 gtsam/discrete/Potentials.h diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index ba24b58b90..2607a80ef5 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -56,7 +56,6 @@ namespace gtsam { /* ************************************************************************* */ double DecisionTreeFactor::safe_div(const double &a, const double &b) { - // cout << boost::format("%g / %g = %g\n") % a % b % ((a == 0) ? 0 : (a / b)); // The use for safe_div is when we divide the product factor by the sum // factor. If the product or sum is zero, we accord zero probability to the // event. diff --git a/gtsam/discrete/Potentials.h b/gtsam/discrete/Potentials.h deleted file mode 100644 index 93452d8fb0..0000000000 --- a/gtsam/discrete/Potentials.h +++ /dev/null @@ -1,35 +0,0 @@ -/* ---------------------------------------------------------------------------- - - * GTSAM Copyright 2010, Georgia Tech Research Corporation, - * Atlanta, Georgia 30332-0415 - * All Rights Reserved - * Authors: Frank Dellaert, et al. (see THANKS for the full author list) - - * See LICENSE for the license information - - * -------------------------------------------------------------------------- */ - -/** - * @file Potentials.h - * @date March 24, 2011 - * @author Frank Dellaert - */ - -#pragma once - -#include -#include -#include - -#include -#include - -namespace gtsam { - - /* - * @deprecated - * @brief Deprecated class for storing an ADT with some convenience methods - * */ - typedef GTSAM_DEPRECATED AlgebraicDecisionTree Potentials; - -} // namespace gtsam diff --git a/gtsam/inference/BayesTree.h b/gtsam/inference/BayesTree.h index 132098d0a7..d368bd4864 100644 --- a/gtsam/inference/BayesTree.h +++ b/gtsam/inference/BayesTree.h @@ -73,8 +73,6 @@ namespace gtsam { typedef CLIQUE Clique; ///< The clique type, normally BayesTreeClique typedef boost::shared_ptr sharedClique; ///< Shared pointer to a clique - typedef GTSAM_DEPRECATED Clique Node; ///< Synonym for Clique (TODO: remove) - typedef GTSAM_DEPRECATED sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) typedef typename CLIQUE::ConditionalType ConditionalType; typedef boost::shared_ptr sharedConditional; typedef typename CLIQUE::BayesNetType BayesNetType; @@ -270,6 +268,12 @@ namespace gtsam { /// @} +#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V42 + public: + typedef GTSAM_DEPRECATED Clique Node; ///< Synonym for Clique (TODO: remove) + typedef GTSAM_DEPRECATED sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) +#endif + }; // BayesTree /* ************************************************************************* */ From 79cb4d067f891cf9e565ca1d90240f62b9325f04 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Fri, 7 Jan 2022 12:40:49 -0500 Subject: [PATCH 04/15] undo deprecation in BayesTree until later --- gtsam/inference/BayesTree.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/gtsam/inference/BayesTree.h b/gtsam/inference/BayesTree.h index d368bd4864..a32b3ce22b 100644 --- a/gtsam/inference/BayesTree.h +++ b/gtsam/inference/BayesTree.h @@ -72,7 +72,8 @@ namespace gtsam { public: typedef CLIQUE Clique; ///< The clique type, normally BayesTreeClique typedef boost::shared_ptr sharedClique; ///< Shared pointer to a clique - + typedef Clique Node; ///< Synonym for Clique (TODO: remove) + typedef sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) typedef typename CLIQUE::ConditionalType ConditionalType; typedef boost::shared_ptr sharedConditional; typedef typename CLIQUE::BayesNetType BayesNetType; @@ -268,12 +269,6 @@ namespace gtsam { /// @} -#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V42 - public: - typedef GTSAM_DEPRECATED Clique Node; ///< Synonym for Clique (TODO: remove) - typedef GTSAM_DEPRECATED sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove) -#endif - }; // BayesTree /* ************************************************************************* */ From 9087d3d81b12ce0557198cbdbdd89e1674e58a94 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 08:19:44 -0500 Subject: [PATCH 05/15] Initial attempt at html --- gtsam/discrete/DecisionTreeFactor.cpp | 55 ++++++++++++++++++- gtsam/discrete/DecisionTreeFactor.h | 10 ++++ gtsam/discrete/DiscreteFactor.h | 11 ++++ gtsam/discrete/discrete.i | 8 +++ .../discrete/tests/testDecisionTreeFactor.cpp | 45 +++++++++++++++ gtsam_unstable/discrete/Constraint.h | 6 ++ 6 files changed, 134 insertions(+), 1 deletion(-) diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index 2607a80ef5..e9c393c7d3 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -187,12 +187,13 @@ namespace gtsam { return ADT::dot(keyFormatter, valueFormatter, showZero); } + // Print out header. /* ************************************************************************* */ string DecisionTreeFactor::markdown(const KeyFormatter& keyFormatter, const Names& names) const { stringstream ss; - // Print out header and construct argument for `cartesianProduct`. + // Print out header. ss << "|"; for (auto& key : keys()) { ss << keyFormatter(key) << "|"; @@ -218,11 +219,63 @@ namespace gtsam { return ss.str(); } + /* ************************************************************************ */ + string DecisionTreeFactor::html(const KeyFormatter& keyFormatter, + const Names& names) const { + const string style = + "\n"; + + stringstream ss; + + // Print out preamble. + ss << "
\n" + << style + << "\n" + " \n"; + + // Print out header row. + ss << " "; + for (auto& key : keys()) { + ss << ""; + } + ss << "\n"; + + // Finish header and start body. + ss << " \n \n"; + + // Print out all rows. + auto rows = enumerate(); + for (const auto& kv : rows) { + ss << " "; + auto assignment = kv.first; + for (auto& key : keys()) { + size_t index = assignment.at(key); + ss << ""; + } + ss << ""; // value + ss << "\n"; + } + ss << " \n
" << keyFormatter(key) << "value
" << Translate(names, key, index) << "" << kv.second << "
\n
"; + return ss.str(); + } + + /* ************************************************************************* */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys &keys, const vector &table) : DiscreteFactor(keys.indices()), AlgebraicDecisionTree(keys, table), cardinalities_(keys.cardinalities()) { } + /* ************************************************************************* */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys &keys, const string &table) : DiscreteFactor(keys.indices()), AlgebraicDecisionTree(keys, table), cardinalities_(keys.cardinalities()) { diff --git a/gtsam/discrete/DecisionTreeFactor.h b/gtsam/discrete/DecisionTreeFactor.h index f7c50d5b5f..b5f6c0c4af 100644 --- a/gtsam/discrete/DecisionTreeFactor.h +++ b/gtsam/discrete/DecisionTreeFactor.h @@ -211,6 +211,16 @@ namespace gtsam { std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const Names& names = {}) const override; + /** + * @brief Render as html table + * + * @param keyFormatter GTSAM-style Key formatter. + * @param names optional, category names corresponding to choices. + * @return std::string a html string. + */ + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const override; + /// @} }; diff --git a/gtsam/discrete/DiscreteFactor.h b/gtsam/discrete/DiscreteFactor.h index e30c0a6fec..43486c5aea 100644 --- a/gtsam/discrete/DiscreteFactor.h +++ b/gtsam/discrete/DiscreteFactor.h @@ -106,6 +106,17 @@ class GTSAM_EXPORT DiscreteFactor: public Factor { const KeyFormatter& keyFormatter = DefaultKeyFormatter, const Names& names = {}) const = 0; + /** + * @brief Render as html table + * + * @param keyFormatter GTSAM-style Key formatter. + * @param names optional, category names corresponding to choices. + * @return std::string a html string. + */ + virtual std::string html( + const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const = 0; + /// @} }; // DiscreteFactor diff --git a/gtsam/discrete/discrete.i b/gtsam/discrete/discrete.i index d17401e44f..3e8013ce8f 100644 --- a/gtsam/discrete/discrete.i +++ b/gtsam/discrete/discrete.i @@ -54,6 +54,10 @@ virtual class DecisionTreeFactor : gtsam::DiscreteFactor { gtsam::DefaultKeyFormatter) const; string markdown(const gtsam::KeyFormatter& keyFormatter, std::map> names) const; + string html(const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + string html(const gtsam::KeyFormatter& keyFormatter, + std::map> names) const; }; #include @@ -93,6 +97,10 @@ virtual class DiscreteConditional : gtsam::DecisionTreeFactor { gtsam::DefaultKeyFormatter) const; string markdown(const gtsam::KeyFormatter& keyFormatter, std::map> names) const; + string html(const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + string html(const gtsam::KeyFormatter& keyFormatter, + std::map> names) const; }; #include diff --git a/gtsam/discrete/tests/testDecisionTreeFactor.cpp b/gtsam/discrete/tests/testDecisionTreeFactor.cpp index 716a77127c..c9f692497e 100644 --- a/gtsam/discrete/tests/testDecisionTreeFactor.cpp +++ b/gtsam/discrete/tests/testDecisionTreeFactor.cpp @@ -154,6 +154,51 @@ TEST(DecisionTreeFactor, markdownWithValueFormatter) { EXPECT(actual == expected); } +/* ************************************************************************* */ +// Check html representation with a value formatter. +TEST(DecisionTreeFactor, htmlWithValueFormatter) { + DiscreteKey A(12, 3), B(5, 2); + DecisionTreeFactor f(A & B, "1 2 3 4 5 6"); + string expected = + "
\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
ABvalue
Zero-1
Zero+2
One-3
One+4
Two-5
Two+6
\n" + "
"; + auto keyFormatter = [](Key key) { return key == 12 ? "A" : "B"; }; + DecisionTreeFactor::Names names{{12, {"Zero", "One", "Two"}}, + {5, {"-", "+"}}}; + string actual = f.html(keyFormatter, names); + cout << expected << endl; + cout << actual << endl; + ofstream ef("expected=html.txt"), af("actual-html.txt"); + ef << expected << endl; + af << actual << endl; + EXPECT(actual == expected); +} + /* ************************************************************************* */ int main() { TestResult tr; diff --git a/gtsam_unstable/discrete/Constraint.h b/gtsam_unstable/discrete/Constraint.h index 85748f0546..4ee7b85eb4 100644 --- a/gtsam_unstable/discrete/Constraint.h +++ b/gtsam_unstable/discrete/Constraint.h @@ -91,6 +91,12 @@ class GTSAM_EXPORT Constraint : public DiscreteFactor { return (boost::format("`Constraint` on %1% variables\n") % (size())).str(); } + /// Render as html table. + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const override { + return (boost::format("

Constraint on %1% variables

") % (size())).str(); + } + /// @} }; // DiscreteFactor From bd53a0a6d92ed1b60679d10c8f2682248a6e4206 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 08:24:06 -0500 Subject: [PATCH 06/15] Remove styling: use css --- gtsam/discrete/DecisionTreeFactor.cpp | 20 ++---------------- .../discrete/tests/testDecisionTreeFactor.cpp | 21 ++----------------- 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index e9c393c7d3..3f40ddf3b2 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -222,29 +222,13 @@ namespace gtsam { /* ************************************************************************ */ string DecisionTreeFactor::html(const KeyFormatter& keyFormatter, const Names& names) const { - const string style = - "\n"; - stringstream ss; // Print out preamble. - ss << "
\n" - << style - << "\n" - " \n"; + ss << "
\n
\n \n"; // Print out header row. - ss << " "; + ss << " "; for (auto& key : keys()) { ss << ""; } diff --git a/gtsam/discrete/tests/testDecisionTreeFactor.cpp b/gtsam/discrete/tests/testDecisionTreeFactor.cpp index c9f692497e..594134edf7 100644 --- a/gtsam/discrete/tests/testDecisionTreeFactor.cpp +++ b/gtsam/discrete/tests/testDecisionTreeFactor.cpp @@ -161,21 +161,9 @@ TEST(DecisionTreeFactor, htmlWithValueFormatter) { DecisionTreeFactor f(A & B, "1 2 3 4 5 6"); string expected = "
\n" - "\n" - "
" << keyFormatter(key) << "
\n" + "
\n" " \n" - " \n" + " \n" " \n" " \n" " \n" @@ -191,11 +179,6 @@ TEST(DecisionTreeFactor, htmlWithValueFormatter) { DecisionTreeFactor::Names names{{12, {"Zero", "One", "Two"}}, {5, {"-", "+"}}}; string actual = f.html(keyFormatter, names); - cout << expected << endl; - cout << actual << endl; - ofstream ef("expected=html.txt"), af("actual-html.txt"); - ef << expected << endl; - af << actual << endl; EXPECT(actual == expected); } From e8127792f20dec389fa7d0fbc01d0452804f6ce4 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 09:10:08 -0500 Subject: [PATCH 07/15] Refactor of markdown --- gtsam/discrete/DiscreteConditional.cpp | 134 ++++++++++++++++++------- gtsam/discrete/DiscreteConditional.h | 69 +++++++------ 2 files changed, 139 insertions(+), 64 deletions(-) diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 951c0b6cab..1a93d89de8 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -29,9 +29,12 @@ #include #include #include +#include using namespace std; - +using std::stringstream; +using std::vector; +using std::pair; namespace gtsam { // Instantiate base class @@ -292,55 +295,72 @@ size_t DiscreteConditional::sample() const { } /* ************************************************************************* */ -std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, - const Names& names) const { - std::stringstream ss; +vector> DiscreteConditional::frontalAssignments() const { + vector> pairs; + for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key)); + vector> rpairs(pairs.rbegin(), pairs.rend()); + return cartesianProduct(rpairs); +} + +/* ************************************************************************* */ +vector> DiscreteConditional::allAssignments() const { + vector> pairs; + for (Key key : parents()) pairs.emplace_back(key, cardinalities_.at(key)); + for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key)); + vector> rpairs(pairs.rbegin(), pairs.rend()); + return cartesianProduct(rpairs); +} - // Print out signature. - ss << " *P("; +/* ************************************************************************* */ +// Print out signature. +static void streamSignature(const DiscreteConditional& conditional, + const KeyFormatter& keyFormatter, + stringstream* ss) { + *ss << "P("; bool first = true; - for (Key key : frontals()) { - if (!first) ss << ","; - ss << keyFormatter(key); + for (Key key : conditional.frontals()) { + if (!first) *ss << ","; + *ss << keyFormatter(key); first = false; } + if (conditional.nrParents() > 0) { + *ss << "|"; + bool first = true; + for (Key parent : conditional.parents()) { + if (!first) *ss << ","; + *ss << keyFormatter(parent); + first = false; + } + } + *ss << "):"; +} + +/* ************************************************************************* */ +std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, + const Names& names) const { + stringstream ss; + ss << " *"; + streamSignature(*this, keyFormatter, &ss); + ss << "*\n" << std::endl; if (nrParents() == 0) { - // We have no parents, call factor method. - ss << ")*:\n" << std::endl; + // We have no parents, call factor method. ss << DecisionTreeFactor::markdown(keyFormatter, names); return ss.str(); } - // We have parents, continue signature and do custom print. - ss << "|"; - first = true; - for (Key parent : parents()) { - if (!first) ss << ","; - ss << keyFormatter(parent); - first = false; - } - ss << ")*:\n" << std::endl; - // Print out header and construct argument for `cartesianProduct`. - std::vector> pairs; ss << "|"; - const_iterator it; - for(Key parent: parents()) { + for (Key parent : parents()) { ss << "*" << keyFormatter(parent) << "*|"; - pairs.emplace_back(parent, cardinalities_.at(parent)); } size_t n = 1; - for(Key key: frontals()) { + for (Key key : frontals()) { size_t k = cardinalities_.at(key); - pairs.emplace_back(key, k); n *= k; } - std::vector> slatnorf(pairs.rbegin(), - pairs.rend() - nrParents()); - const auto frontal_assignments = cartesianProduct(slatnorf); - for (const auto& a : frontal_assignments) { - for (it = beginFrontals(); it != endFrontals(); ++it) { + for (const auto& a : frontalAssignments()) { + for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { size_t index = a.at(*it); ss << Translate(names, *it, index); } @@ -354,13 +374,11 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, ss << "\n"; // Print out all rows. - std::vector> rpairs(pairs.rbegin(), pairs.rend()); - const auto assignments = cartesianProduct(rpairs); size_t count = 0; - for (const auto& a : assignments) { + for (const auto& a : allAssignments()) { if (count == 0) { ss << "|"; - for (it = beginParents(); it != endParents(); ++it) { + for (auto&& it = beginParents(); it != endParents(); ++it) { size_t index = a.at(*it); ss << Translate(names, *it, index) << "|"; } @@ -371,6 +389,50 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, } return ss.str(); } + +/* ************************************************************************ */ +string DiscreteConditional::html(const KeyFormatter& keyFormatter, + const Names& names) const { + stringstream ss; + ss << "
\n

"; + streamSignature(*this, keyFormatter, &ss); + ss << "

\n"; + if (nrParents() == 0) { + // We have no parents, call factor method. + ss << DecisionTreeFactor::html(keyFormatter, names); + return ss.str(); + } + + // Print out preamble. + ss << "
ABvalue
ABvalue
Zero-1
\n \n"; + + // Print out header row. + ss << " "; + for (auto& key : keys()) { + ss << ""; + } + ss << "\n"; + + // Finish header and start body. + ss << " \n \n"; + + // Print out all rows. + auto rows = enumerate(); + for (const auto& kv : rows) { + ss << " "; + auto assignment = kv.first; + for (auto& key : keys()) { + size_t index = assignment.at(key); + ss << ""; + } + ss << ""; // value + ss << "\n"; + } + ss << " \n
" << keyFormatter(key) << "value
" << Translate(names, key, index) << "" << kv.second << "
\n
"; + + return ss.str(); +} + /* ************************************************************************* */ } // namespace gtsam diff --git a/gtsam/discrete/DiscreteConditional.h b/gtsam/discrete/DiscreteConditional.h index 4c2e964fda..3d258dbe72 100644 --- a/gtsam/discrete/DiscreteConditional.h +++ b/gtsam/discrete/DiscreteConditional.h @@ -21,9 +21,9 @@ #include #include #include -#include -#include +#include +#include #include namespace gtsam { @@ -32,24 +32,24 @@ namespace gtsam { * Discrete Conditional Density * Derives from DecisionTreeFactor */ -class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, - public Conditional { - -public: +class GTSAM_EXPORT DiscreteConditional + : public DecisionTreeFactor, + public Conditional { + public: // typedefs needed to play nice with gtsam - typedef DiscreteConditional This; ///< Typedef to this class - typedef boost::shared_ptr shared_ptr; ///< shared_ptr to this class - typedef DecisionTreeFactor BaseFactor; ///< Typedef to our factor base class - typedef Conditional BaseConditional; ///< Typedef to our conditional base class + typedef DiscreteConditional This; ///< Typedef to this class + typedef boost::shared_ptr shared_ptr; ///< shared_ptr to this class + typedef DecisionTreeFactor BaseFactor; ///< Typedef to our factor base class + typedef Conditional + BaseConditional; ///< Typedef to our conditional base class - using Values = DiscreteValues; ///< backwards compatibility + using Values = DiscreteValues; ///< backwards compatibility /// @name Standard Constructors /// @{ /** default constructor needed for serialization */ - DiscreteConditional() { - } + DiscreteConditional() {} /** constructor from factor */ DiscreteConditional(size_t nFrontals, const DecisionTreeFactor& f); @@ -87,29 +87,33 @@ class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, /** construct P(X|Y)=P(X,Y)/P(Y) from P(X,Y) and P(Y) */ DiscreteConditional(const DecisionTreeFactor& joint, - const DecisionTreeFactor& marginal); + const DecisionTreeFactor& marginal); /** construct P(X|Y)=P(X,Y)/P(Y) from P(X,Y) and P(Y) */ DiscreteConditional(const DecisionTreeFactor& joint, - const DecisionTreeFactor& marginal, const Ordering& orderedKeys); + const DecisionTreeFactor& marginal, + const Ordering& orderedKeys); /** * Combine several conditional into a single one. - * The conditionals must be given in increasing order, meaning that the parents - * of any conditional may not include a conditional coming before it. - * @param firstConditional Iterator to the first conditional to combine, must dereference to a shared_ptr. - * @param lastConditional Iterator to after the last conditional to combine, must dereference to a shared_ptr. + * The conditionals must be given in increasing order, meaning that the + * parents of any conditional may not include a conditional coming before it. + * @param firstConditional Iterator to the first conditional to combine, must + * dereference to a shared_ptr. + * @param lastConditional Iterator to after the last conditional to combine, + * must dereference to a shared_ptr. * */ - template + template static shared_ptr Combine(ITERATOR firstConditional, - ITERATOR lastConditional); + ITERATOR lastConditional); /// @} /// @name Testable /// @{ /// GTSAM-style print - void print(const std::string& s = "Discrete Conditional: ", + void print( + const std::string& s = "Discrete Conditional: ", const KeyFormatter& formatter = DefaultKeyFormatter) const override; /// GTSAM-style equals @@ -161,7 +165,6 @@ class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, */ size_t sample(const DiscreteValues& parentsValues) const; - /// Single parent version. size_t sample(size_t parent_value) const; @@ -178,6 +181,12 @@ class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, /// sample in place, stores result in partial solution void sampleInPlace(DiscreteValues* parentsValues) const; + /// Return all assignments for frontal variables. + std::vector> frontalAssignments() const; + + /// Return all assignments for frontal *and* parent variables. + std::vector> allAssignments() const; + /// @} /// @name Wrapper support /// @{ @@ -186,15 +195,20 @@ class GTSAM_EXPORT DiscreteConditional: public DecisionTreeFactor, std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const Names& names = {}) const override; + /// Render as html table. + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const override; + /// @} }; // DiscreteConditional // traits -template<> struct traits : public Testable {}; +template <> +struct traits : public Testable {}; /* ************************************************************************* */ -template +template DiscreteConditional::shared_ptr DiscreteConditional::Combine( ITERATOR firstConditional, ITERATOR lastConditional) { // TODO: check for being a clique @@ -203,7 +217,7 @@ DiscreteConditional::shared_ptr DiscreteConditional::Combine( size_t nrFrontals = 0; DecisionTreeFactor product; for (ITERATOR it = firstConditional; it != lastConditional; - ++it, ++nrFrontals) { + ++it, ++nrFrontals) { DiscreteConditional::shared_ptr c = *it; DecisionTreeFactor::shared_ptr factor = c->toFactor(); product = (*factor) * product; @@ -212,5 +226,4 @@ DiscreteConditional::shared_ptr DiscreteConditional::Combine( return boost::make_shared(nrFrontals, product); } -} // gtsam - +} // namespace gtsam From a7b7a8b0fa5c051dd84e5408ed6aa11be16d9f91 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 10:16:25 -0500 Subject: [PATCH 08/15] Working html for conditionals --- gtsam/discrete/DiscreteConditional.cpp | 48 +++++++++++-------- gtsam/discrete/tests/testDiscreteBayesNet.cpp | 4 +- .../tests/testDiscreteConditional.cpp | 38 +++++++++++++-- .../gtsam/tests/test_DiscreteConditional.py | 2 +- python/gtsam/tests/test_DiscretePrior.py | 2 +- 5 files changed, 65 insertions(+), 29 deletions(-) diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 1a93d89de8..48f8fd3225 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -348,18 +348,14 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, return ss.str(); } - // Print out header and construct argument for `cartesianProduct`. + // Print out header. ss << "|"; for (Key parent : parents()) { ss << "*" << keyFormatter(parent) << "*|"; } - size_t n = 1; - for (Key key : frontals()) { - size_t k = cardinalities_.at(key); - n *= k; - } - for (const auto& a : frontalAssignments()) { + auto frontalAssignments = this->frontalAssignments(); + for (const auto& a : frontalAssignments) { for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { size_t index = a.at(*it); ss << Translate(names, *it, index); @@ -370,6 +366,7 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, // Print out separator with alignment hints. ss << "|"; + size_t n = frontalAssignments.size(); for (size_t j = 0; j < nrParents() + n; j++) ss << ":-:|"; ss << "\n"; @@ -408,28 +405,37 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter, // Print out header row. ss << " "; - for (auto& key : keys()) { - ss << "" << keyFormatter(key) << ""; + for (Key parent : parents()) { + ss << "" << keyFormatter(parent) << ""; + } + auto frontalAssignments = this->frontalAssignments(); + for (const auto& a : frontalAssignments) { + for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { + size_t index = a.at(*it); + ss << "" << Translate(names, *it, index) << ""; + } } - ss << "value\n"; + ss << "\n"; // Finish header and start body. ss << " \n \n"; - // Print out all rows. - auto rows = enumerate(); - for (const auto& kv : rows) { - ss << " "; - auto assignment = kv.first; - for (auto& key : keys()) { - size_t index = assignment.at(key); - ss << "" << Translate(names, key, index) << ""; + size_t count = 0, n = frontalAssignments.size(); + for (const auto& a : allAssignments()) { + if (count == 0) { + ss << " "; + for (auto&& it = beginParents(); it != endParents(); ++it) { + size_t index = a.at(*it); + ss << "" << Translate(names, *it, index) << ""; + } } - ss << "" << kv.second << ""; // value - ss << "\n"; + ss << "" << operator()(a) << ""; // value + count = (count + 1) % n; + if (count == 0) ss << "\n"; } - ss << " \n\n"; + // Finish up + ss << " \n\n"; return ss.str(); } diff --git a/gtsam/discrete/tests/testDiscreteBayesNet.cpp b/gtsam/discrete/tests/testDiscreteBayesNet.cpp index 0686b3920c..0ba53c69ab 100644 --- a/gtsam/discrete/tests/testDiscreteBayesNet.cpp +++ b/gtsam/discrete/tests/testDiscreteBayesNet.cpp @@ -182,13 +182,13 @@ TEST(DiscreteBayesNet, markdown) { string expected = "`DiscreteBayesNet` of size 2\n" "\n" - " *P(Asia)*:\n\n" + " *P(Asia):*\n\n" "|Asia|value|\n" "|:-:|:-:|\n" "|0|0.99|\n" "|1|0.01|\n" "\n" - " *P(Smoking|Asia)*:\n\n" + " *P(Smoking|Asia):*\n\n" "|*Asia*|0|1|\n" "|:-:|:-:|:-:|\n" "|0|0.8|0.2|\n" diff --git a/gtsam/discrete/tests/testDiscreteConditional.cpp b/gtsam/discrete/tests/testDiscreteConditional.cpp index 6d2af3cff9..3fb67a615c 100644 --- a/gtsam/discrete/tests/testDiscreteConditional.cpp +++ b/gtsam/discrete/tests/testDiscreteConditional.cpp @@ -125,7 +125,7 @@ TEST(DiscreteConditional, markdown_prior) { DiscreteKey A(Symbol('x', 1), 3); DiscreteConditional conditional(A % "1/2/2"); string expected = - " *P(x1)*:\n\n" + " *P(x1):*\n\n" "|x1|value|\n" "|:-:|:-:|\n" "|0|0.2|\n" @@ -142,7 +142,7 @@ TEST(DiscreteConditional, markdown_prior_names) { DiscreteKey A(x1, 3); DiscreteConditional conditional(A % "1/2/2"); string expected = - " *P(x1)*:\n\n" + " *P(x1):*\n\n" "|x1|value|\n" "|:-:|:-:|\n" "|A0|0.2|\n" @@ -160,7 +160,7 @@ TEST(DiscreteConditional, markdown_multivalued) { DiscreteConditional conditional( A | B = "2/88/10 2/20/78 33/33/34 33/33/34 95/2/3"); string expected = - " *P(a1|b1)*:\n\n" + " *P(a1|b1):*\n\n" "|*b1*|0|1|2|\n" "|:-:|:-:|:-:|:-:|\n" "|0|0.02|0.88|0.1|\n" @@ -178,7 +178,7 @@ TEST(DiscreteConditional, markdown) { DiscreteKey A(2, 2), B(1, 2), C(0, 3); DiscreteConditional conditional(A, {B, C}, "0/1 1/3 1/1 3/1 0/1 1/0"); string expected = - " *P(A|B,C)*:\n\n" + " *P(A|B,C):*\n\n" "|*B*|*C*|T|F|\n" "|:-:|:-:|:-:|:-:|\n" "|-|Zero|0|1|\n" @@ -195,6 +195,36 @@ TEST(DiscreteConditional, markdown) { EXPECT(actual == expected); } +/* ************************************************************************* */ +// Check html representation looks as expected, two parents + names. +TEST(DiscreteConditional, html) { + DiscreteKey A(2, 2), B(1, 2), C(0, 3); + DiscreteConditional conditional(A, {B, C}, "0/1 1/3 1/1 3/1 0/1 1/0"); + string expected = + "
\n" + "

P(A|B,C):

\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
BCTF
-Zero01
-One0.250.75
-Two0.50.5
+Zero0.750.25
+One01
+Two10
\n" + "
"; + vector keyNames{"C", "B", "A"}; + auto formatter = [keyNames](Key key) { return keyNames[key]; }; + DecisionTreeFactor::Names names{ + {0, {"Zero", "One", "Two"}}, {1, {"-", "+"}}, {2, {"T", "F"}}}; + string actual = conditional.html(formatter, names); + EXPECT(actual == expected); +} + /* ************************************************************************* */ int main() { TestResult tr; diff --git a/python/gtsam/tests/test_DiscreteConditional.py b/python/gtsam/tests/test_DiscreteConditional.py index 86bc303a9a..0ae66c2d40 100644 --- a/python/gtsam/tests/test_DiscreteConditional.py +++ b/python/gtsam/tests/test_DiscreteConditional.py @@ -49,7 +49,7 @@ def test_markdown(self): conditional = DiscreteConditional(A, parents, "0/1 1/3 1/1 3/1 0/1 1/0") expected = \ - " *P(A|B,C)*:\n\n" \ + " *P(A|B,C):*\n\n" \ "|*B*|*C*|0|1|\n" \ "|:-:|:-:|:-:|:-:|\n" \ "|0|0|0|1|\n" \ diff --git a/python/gtsam/tests/test_DiscretePrior.py b/python/gtsam/tests/test_DiscretePrior.py index 5bf6a8d196..2c923589ce 100644 --- a/python/gtsam/tests/test_DiscretePrior.py +++ b/python/gtsam/tests/test_DiscretePrior.py @@ -51,7 +51,7 @@ def test_markdown(self): """Test the _repr_markdown_ method.""" prior = DiscretePrior(X, "2/3") - expected = " *P(0)*:\n\n" \ + expected = " *P(0):*\n\n" \ "|0|value|\n" \ "|:-:|:-:|\n" \ "|0|0.4|\n" \ From 918b037dde7fbe1dec100b85677b67c9ff03e73a Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 11:42:46 -0500 Subject: [PATCH 09/15] Fix include --- gtsam_unstable/discrete/CSP.cpp | 1 + gtsam_unstable/discrete/Scheduler.h | 1 + 2 files changed, 2 insertions(+) diff --git a/gtsam_unstable/discrete/CSP.cpp b/gtsam_unstable/discrete/CSP.cpp index 283c992f13..e204a67796 100644 --- a/gtsam_unstable/discrete/CSP.cpp +++ b/gtsam_unstable/discrete/CSP.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include diff --git a/gtsam_unstable/discrete/Scheduler.h b/gtsam_unstable/discrete/Scheduler.h index 7559cdea6b..a97368bb25 100644 --- a/gtsam_unstable/discrete/Scheduler.h +++ b/gtsam_unstable/discrete/Scheduler.h @@ -8,6 +8,7 @@ #pragma once #include +#include namespace gtsam { From 3ea5aed26e3e5d220e1de7be5d8e738940649304 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 11:42:56 -0500 Subject: [PATCH 10/15] html for all graphs --- gtsam/discrete/DiscreteBayesNet.cpp | 17 +++++++++++++++-- gtsam/discrete/DiscreteBayesNet.h | 21 ++++++++++++++------- gtsam/discrete/DiscreteBayesTree.cpp | 18 ++++++++++++++++++ gtsam/discrete/DiscreteBayesTree.h | 6 +++++- gtsam/discrete/DiscreteConditional.cpp | 5 ++++- gtsam/discrete/DiscreteFactorGraph.cpp | 17 +++++++++++++++-- gtsam/discrete/DiscreteFactorGraph.h | 17 +++++++++++++---- gtsam/discrete/discrete.i | 12 ++++++++++++ 8 files changed, 96 insertions(+), 17 deletions(-) diff --git a/gtsam/discrete/DiscreteBayesNet.cpp b/gtsam/discrete/DiscreteBayesNet.cpp index 510fb56389..c0dfd747c3 100644 --- a/gtsam/discrete/DiscreteBayesNet.cpp +++ b/gtsam/discrete/DiscreteBayesNet.cpp @@ -61,16 +61,29 @@ namespace gtsam { return result; } - /* ************************************************************************* */ + /* *********************************************************************** */ std::string DiscreteBayesNet::markdown( const KeyFormatter& keyFormatter, const DiscreteFactor::Names& names) const { using std::endl; std::stringstream ss; ss << "`DiscreteBayesNet` of size " << size() << endl << endl; - for(const DiscreteConditional::shared_ptr& conditional: *this) + for (const DiscreteConditional::shared_ptr& conditional : *this) ss << conditional->markdown(keyFormatter, names) << endl; return ss.str(); } + + /* *********************************************************************** */ + std::string DiscreteBayesNet::html( + const KeyFormatter& keyFormatter, + const DiscreteFactor::Names& names) const { + using std::endl; + std::stringstream ss; + ss << "

DiscreteBayesNet of size " << size() << "

"; + for (const DiscreteConditional::shared_ptr& conditional : *this) + ss << conditional->html(keyFormatter, names) << endl; + return ss.str(); + } + /* ************************************************************************* */ } // namespace diff --git a/gtsam/discrete/DiscreteBayesNet.h b/gtsam/discrete/DiscreteBayesNet.h index 5332b51dd0..17dfe2c5ff 100644 --- a/gtsam/discrete/DiscreteBayesNet.h +++ b/gtsam/discrete/DiscreteBayesNet.h @@ -18,13 +18,16 @@ #pragma once -#include -#include -#include +#include +#include #include #include -#include -#include + +#include +#include +#include +#include +#include namespace gtsam { @@ -107,13 +110,17 @@ namespace gtsam { /// @name Wrapper support /// @{ - /// Render as markdown table. + /// Render as markdown tables. std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DiscreteFactor::Names& names = {}) const; + /// Render as html tables. + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const DiscreteFactor::Names& names = {}) const; + /// @} - private: + private: /** Serialization function */ friend class boost::serialization::access; template diff --git a/gtsam/discrete/DiscreteBayesTree.cpp b/gtsam/discrete/DiscreteBayesTree.cpp index 07d6e0f0ee..139292eeef 100644 --- a/gtsam/discrete/DiscreteBayesTree.cpp +++ b/gtsam/discrete/DiscreteBayesTree.cpp @@ -72,5 +72,23 @@ namespace gtsam { return ss.str(); } + /* **************************************************************************/ + std::string DiscreteBayesTree::html( + const KeyFormatter& keyFormatter, + const DiscreteFactor::Names& names) const { + using std::endl; + std::stringstream ss; + ss << "

DiscreteBayesTree of size " << nodes_.size() + << "

"; + auto visitor = [&](const DiscreteBayesTreeClique::shared_ptr& clique, + size_t& indent) { + ss << clique->conditional()->html(keyFormatter, names); + return indent + 1; + }; + size_t indent; + treeTraversal::DepthFirstForest(*this, indent, visitor); + return ss.str(); + } + /* **************************************************************************/ } // namespace gtsam diff --git a/gtsam/discrete/DiscreteBayesTree.h b/gtsam/discrete/DiscreteBayesTree.h index 6189f25d54..809ce9c835 100644 --- a/gtsam/discrete/DiscreteBayesTree.h +++ b/gtsam/discrete/DiscreteBayesTree.h @@ -92,10 +92,14 @@ class GTSAM_EXPORT DiscreteBayesTree /// @name Wrapper support /// @{ - /// Render as markdown table. + /// Render as markdown tables. std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DiscreteFactor::Names& names = {}) const; + /// Render as html tables. + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const DiscreteFactor::Names& names = {}) const; + /// @} }; diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 48f8fd3225..512ff88b4d 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -410,16 +410,19 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter, } auto frontalAssignments = this->frontalAssignments(); for (const auto& a : frontalAssignments) { + ss << ""; for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { size_t index = a.at(*it); - ss << "" << Translate(names, *it, index) << ""; + ss << Translate(names, *it, index); } + ss << ""; } ss << "\n"; // Finish header and start body. ss << " \n \n"; + // Output all rows, one per assignment: size_t count = 0, n = frontalAssignments.size(); for (const auto& a : allAssignments()) { if (count == 0) { diff --git a/gtsam/discrete/DiscreteFactorGraph.cpp b/gtsam/discrete/DiscreteFactorGraph.cpp index be046d2902..c1248c60b9 100644 --- a/gtsam/discrete/DiscreteFactorGraph.cpp +++ b/gtsam/discrete/DiscreteFactorGraph.cpp @@ -131,7 +131,7 @@ namespace gtsam { return std::make_pair(cond, sum); } - /* ************************************************************************* */ + /* ************************************************************************ */ string DiscreteFactorGraph::markdown( const KeyFormatter& keyFormatter, const DiscreteFactor::Names& names) const { @@ -145,5 +145,18 @@ namespace gtsam { return ss.str(); } - /* ************************************************************************* */ + /* ************************************************************************ */ + string DiscreteFactorGraph::html(const KeyFormatter& keyFormatter, + const DiscreteFactor::Names& names) const { + using std::endl; + std::stringstream ss; + ss << "

DiscreteFactorGraph of size " << size() << "

"; + for (size_t i = 0; i < factors_.size(); i++) { + ss << "

factor " << i << ":

"; + ss << factors_[i]->html(keyFormatter, names) << endl; + } + return ss.str(); + } + + /* ************************************************************************ */ } // namespace gtsam diff --git a/gtsam/discrete/DiscreteFactorGraph.h b/gtsam/discrete/DiscreteFactorGraph.h index 9aa04d6497..08c3d893d9 100644 --- a/gtsam/discrete/DiscreteFactorGraph.h +++ b/gtsam/discrete/DiscreteFactorGraph.h @@ -22,18 +22,17 @@ #include #include #include -#include #include #include #include +#include #include namespace gtsam { // Forward declarations class DiscreteFactorGraph; -class DiscreteFactor; class DiscreteConditional; class DiscreteBayesNet; class DiscreteEliminationTree; @@ -144,8 +143,8 @@ public EliminateableFactorGraph { /// @{ /** - * @brief Render as markdown table - * + * @brief Render as markdown tables + * * @param keyFormatter GTSAM-style Key formatter. * @param names optional, a map from Key to category names. * @return std::string a (potentially long) markdown string. @@ -153,6 +152,16 @@ public EliminateableFactorGraph { std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DiscreteFactor::Names& names = {}) const; + /** + * @brief Render as html tables + * + * @param keyFormatter GTSAM-style Key formatter. + * @param names optional, a map from Key to category names. + * @return std::string a (potentially long) html string. + */ + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const DiscreteFactor::Names& names = {}) const; + /// @} }; // \ DiscreteFactorGraph diff --git a/gtsam/discrete/discrete.i b/gtsam/discrete/discrete.i index 3e8013ce8f..9bf324fd31 100644 --- a/gtsam/discrete/discrete.i +++ b/gtsam/discrete/discrete.i @@ -144,6 +144,10 @@ class DiscreteBayesNet { gtsam::DefaultKeyFormatter) const; string markdown(const gtsam::KeyFormatter& keyFormatter, std::map> names) const; + string html(const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + string html(const gtsam::KeyFormatter& keyFormatter, + std::map> names) const; }; #include @@ -180,6 +184,10 @@ class DiscreteBayesTree { gtsam::DefaultKeyFormatter) const; string markdown(const gtsam::KeyFormatter& keyFormatter, std::map> names) const; + string html(const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + string html(const gtsam::KeyFormatter& keyFormatter, + std::map> names) const; }; #include @@ -229,6 +237,10 @@ class DiscreteFactorGraph { gtsam::DefaultKeyFormatter) const; string markdown(const gtsam::KeyFormatter& keyFormatter, std::map> names) const; + string html(const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + string html(const gtsam::KeyFormatter& keyFormatter, + std::map> names) const; }; } // namespace gtsam From c1561dba02e3299a769ad788f1587c79bdfbeedc Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 14:46:23 -0500 Subject: [PATCH 11/15] Made cartesian product static, and added specialization in DiscreteValues. Added markdown and html methods for the latter. --- examples/UGM_small.cpp | 3 +- gtsam/discrete/Assignment.h | 65 +++++++------- gtsam/discrete/DecisionTreeFactor.cpp | 9 +- gtsam/discrete/DiscreteConditional.cpp | 37 ++++---- gtsam/discrete/DiscreteConditional.h | 5 +- gtsam/discrete/DiscreteFactor.cpp | 10 --- gtsam/discrete/DiscreteFactor.h | 8 +- gtsam/discrete/DiscreteValues.cpp | 86 +++++++++++++++++++ gtsam/discrete/DiscreteValues.h | 53 ++++++++++-- .../discrete/tests/testDiscreteBayesTree.cpp | 8 +- .../discrete/tests/testDiscreteMarginals.cpp | 4 +- gtsam/discrete/tests/testDiscreteValues.cpp | 76 ++++++++++++++++ 12 files changed, 272 insertions(+), 92 deletions(-) create mode 100644 gtsam/discrete/DiscreteValues.cpp create mode 100644 gtsam/discrete/tests/testDiscreteValues.cpp diff --git a/examples/UGM_small.cpp b/examples/UGM_small.cpp index 3829a5c917..24bd0c0ba7 100644 --- a/examples/UGM_small.cpp +++ b/examples/UGM_small.cpp @@ -50,7 +50,8 @@ int main(int argc, char** argv) { // Print the UGM distribution cout << "\nUGM distribution:" << endl; - auto allPosbValues = cartesianProduct(Cathy & Heather & Mark & Allison); + auto allPosbValues = + DiscreteValues::CartesianProduct(Cathy & Heather & Mark & Allison); for (size_t i = 0; i < allPosbValues.size(); ++i) { DiscreteFactor::Values values = allPosbValues[i]; double prodPot = graph(values); diff --git a/gtsam/discrete/Assignment.h b/gtsam/discrete/Assignment.h index 3665d6dfa9..cdbf0a2e96 100644 --- a/gtsam/discrete/Assignment.h +++ b/gtsam/discrete/Assignment.h @@ -19,32 +19,30 @@ #pragma once #include -#include #include - +#include +#include namespace gtsam { - /** - * An assignment from labels to value index (size_t). - * Assigns to each label a value. Implemented as a simple map. - * A discrete factor takes an Assignment and returns a value. - */ - template - class Assignment: public std::map { - public: - void print(const std::string& s = "Assignment: ") const { - std::cout << s << ": "; - for(const typename Assignment::value_type& keyValue: *this) - std::cout << "(" << keyValue.first << ", " << keyValue.second << ")"; - std::cout << std::endl; - } - - bool equals(const Assignment& other, double tol = 1e-9) const { - return (*this == other); - } - }; //Assignment +/** + * An assignment from labels to value index (size_t). + * Assigns to each label a value. Implemented as a simple map. + * A discrete factor takes an Assignment and returns a value. + */ +template +class Assignment : public std::map { + public: + void print(const std::string& s = "Assignment: ") const { + std::cout << s << ": "; + for (const typename Assignment::value_type& keyValue : *this) + std::cout << "(" << keyValue.first << ", " << keyValue.second << ")"; + std::cout << std::endl; + } + bool equals(const Assignment& other, double tol = 1e-9) const { + return (*this == other); + } /** * @brief Get Cartesian product consisting all possible configurations @@ -58,29 +56,28 @@ namespace gtsam { * variables with each having cardinalities 4, we get 4096 possible * configurations!! */ - template - std::vector > cartesianProduct( - const std::vector >& keys) { - std::vector > allPossValues; - Assignment values; + template > + static std::vector CartesianProduct( + const std::vector>& keys) { + std::vector allPossValues; + Derived values; typedef std::pair DiscreteKey; - for(const DiscreteKey& key: keys) - values[key.first] = 0; //Initialize from 0 + for (const DiscreteKey& key : keys) + values[key.first] = 0; // Initialize from 0 while (1) { allPossValues.push_back(values); size_t j = 0; for (j = 0; j < keys.size(); j++) { L idx = keys[j].first; values[idx]++; - if (values[idx] < keys[j].second) - break; - //Wrap condition + if (values[idx] < keys[j].second) break; + // Wrap condition values[idx] = 0; } - if (j == keys.size()) - break; + if (j == keys.size()) break; } return allPossValues; } +}; // Assignment -} // namespace gtsam +} // namespace gtsam diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index 3f40ddf3b2..c50811a506 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -22,6 +22,7 @@ #include #include +#include using namespace std; @@ -150,9 +151,9 @@ namespace gtsam { for (auto& key : keys()) { pairs.emplace_back(key, cardinalities_.at(key)); } - // Reverse to make cartesianProduct output a more natural ordering. + // Reverse to make cartesian product output a more natural ordering. std::vector> rpairs(pairs.rbegin(), pairs.rend()); - const auto assignments = cartesianProduct(rpairs); + const auto assignments = DiscreteValues::CartesianProduct(rpairs); // Construct unordered_map with values std::vector> result; @@ -212,7 +213,7 @@ namespace gtsam { auto assignment = kv.first; for (auto& key : keys()) { size_t index = assignment.at(key); - ss << Translate(names, key, index) << "|"; + ss << DiscreteValues::Translate(names, key, index) << "|"; } ss << kv.second << "|\n"; } @@ -244,7 +245,7 @@ namespace gtsam { auto assignment = kv.first; for (auto& key : keys()) { size_t index = assignment.at(key); - ss << "" << Translate(names, key, index) << ""; + ss << "" << DiscreteValues::Translate(names, key, index) << ""; } ss << "" << kv.second << ""; // value ss << "\n"; diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 512ff88b4d..82018abea6 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -180,26 +180,21 @@ DecisionTreeFactor::shared_ptr DiscreteConditional::likelihood( return likelihood(values); } -/* ******************************************************************************** */ +/* ************************************************************************** */ void DiscreteConditional::solveInPlace(DiscreteValues* values) const { - // TODO: Abhijit asks: is this really the fastest way? He thinks it is. - ADT pFS = Choose(*this, *values); // P(F|S=parentsValues) + // TODO(Abhijit): is this really the fastest way? He thinks it is. + ADT pFS = Choose(*this, *values); // P(F|S=parentsValues) // Initialize DiscreteValues mpe; double maxP = 0; - DiscreteKeys keys; - for(Key idx: frontals()) { - DiscreteKey dk(idx, cardinality(idx)); - keys & dk; - } // Get all Possible Configurations - const auto allPosbValues = cartesianProduct(keys); + const auto allPosbValues = frontalAssignments(); // Find the MPE - for(const auto& frontalVals: allPosbValues) { - double pValueS = pFS(frontalVals); // P(F=value|S=parentsValues) + for (const auto& frontalVals : allPosbValues) { + double pValueS = pFS(frontalVals); // P(F=value|S=parentsValues) // Update MPE solution if better if (pValueS > maxP) { maxP = pValueS; @@ -207,8 +202,8 @@ void DiscreteConditional::solveInPlace(DiscreteValues* values) const { } } - //set values (inPlace) to mpe - for(Key j: frontals()) { + // set values (inPlace) to mpe + for (Key j : frontals()) { (*values)[j] = mpe[j]; } } @@ -295,20 +290,20 @@ size_t DiscreteConditional::sample() const { } /* ************************************************************************* */ -vector> DiscreteConditional::frontalAssignments() const { +vector DiscreteConditional::frontalAssignments() const { vector> pairs; for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key)); vector> rpairs(pairs.rbegin(), pairs.rend()); - return cartesianProduct(rpairs); + return DiscreteValues::CartesianProduct(rpairs); } /* ************************************************************************* */ -vector> DiscreteConditional::allAssignments() const { +vector DiscreteConditional::allAssignments() const { vector> pairs; for (Key key : parents()) pairs.emplace_back(key, cardinalities_.at(key)); for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key)); vector> rpairs(pairs.rbegin(), pairs.rend()); - return cartesianProduct(rpairs); + return DiscreteValues::CartesianProduct(rpairs); } /* ************************************************************************* */ @@ -358,7 +353,7 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, for (const auto& a : frontalAssignments) { for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { size_t index = a.at(*it); - ss << Translate(names, *it, index); + ss << DiscreteValues::Translate(names, *it, index); } ss << "|"; } @@ -377,7 +372,7 @@ std::string DiscreteConditional::markdown(const KeyFormatter& keyFormatter, ss << "|"; for (auto&& it = beginParents(); it != endParents(); ++it) { size_t index = a.at(*it); - ss << Translate(names, *it, index) << "|"; + ss << DiscreteValues::Translate(names, *it, index) << "|"; } } ss << operator()(a) << "|"; @@ -413,7 +408,7 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter, ss << ""; for (auto&& it = beginFrontals(); it != endFrontals(); ++it) { size_t index = a.at(*it); - ss << Translate(names, *it, index); + ss << DiscreteValues::Translate(names, *it, index); } ss << ""; } @@ -429,7 +424,7 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter, ss << " "; for (auto&& it = beginParents(); it != endParents(); ++it) { size_t index = a.at(*it); - ss << "" << Translate(names, *it, index) << ""; + ss << "" << DiscreteValues::Translate(names, *it, index) << ""; } } ss << "" << operator()(a) << ""; // value diff --git a/gtsam/discrete/DiscreteConditional.h b/gtsam/discrete/DiscreteConditional.h index 3d258dbe72..4a83ff83a0 100644 --- a/gtsam/discrete/DiscreteConditional.h +++ b/gtsam/discrete/DiscreteConditional.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace gtsam { @@ -182,10 +183,10 @@ class GTSAM_EXPORT DiscreteConditional void sampleInPlace(DiscreteValues* parentsValues) const; /// Return all assignments for frontal variables. - std::vector> frontalAssignments() const; + std::vector frontalAssignments() const; /// Return all assignments for frontal *and* parent variables. - std::vector> allAssignments() const; + std::vector allAssignments() const; /// @} /// @name Wrapper support diff --git a/gtsam/discrete/DiscreteFactor.cpp b/gtsam/discrete/DiscreteFactor.cpp index 1a12ef405a..0cf7f2a5e8 100644 --- a/gtsam/discrete/DiscreteFactor.cpp +++ b/gtsam/discrete/DiscreteFactor.cpp @@ -25,14 +25,4 @@ using namespace std; namespace gtsam { -string DiscreteFactor::Translate(const Names& names, Key key, size_t index) { - if (names.empty()) { - stringstream ss; - ss << index; - return ss.str(); - } else { - return names.at(key)[index]; - } -} - } // namespace gtsam diff --git a/gtsam/discrete/DiscreteFactor.h b/gtsam/discrete/DiscreteFactor.h index 43486c5aea..8f39fbc23f 100644 --- a/gtsam/discrete/DiscreteFactor.h +++ b/gtsam/discrete/DiscreteFactor.h @@ -22,6 +22,7 @@ #include #include +#include namespace gtsam { class DecisionTreeFactor; @@ -90,14 +91,11 @@ class GTSAM_EXPORT DiscreteFactor: public Factor { /// @{ /// Translation table from values to strings. - using Names = std::map>; - - /// Translate an integer index value for given key to a string. - static std::string Translate(const Names& names, Key key, size_t index); + using Names = DiscreteValues::Names; /** * @brief Render as markdown table - * + * * @param keyFormatter GTSAM-style Key formatter. * @param names optional, category names corresponding to choices. * @return std::string a markdown string. diff --git a/gtsam/discrete/DiscreteValues.cpp b/gtsam/discrete/DiscreteValues.cpp new file mode 100644 index 0000000000..9bd0572bd4 --- /dev/null +++ b/gtsam/discrete/DiscreteValues.cpp @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file DiscreteValues.cpp + * @date January, 2022 + * @author Frank Dellaert + */ + +#include + +#include + +using std::cout; +using std::endl; +using std::string; +using std::stringstream; + +namespace gtsam { + +void DiscreteValues::print(const string& s, + const KeyFormatter& keyFormatter) const { + cout << s << ": "; + for (auto&& kv : *this) + cout << "(" << keyFormatter(kv.first) << ", " << kv.second << ")"; + cout << endl; +} + +string DiscreteValues::Translate(const Names& names, Key key, size_t index) { + if (names.empty()) { + stringstream ss; + ss << index; + return ss.str(); + } else { + return names.at(key)[index]; + } +} + +string DiscreteValues::markdown(const KeyFormatter& keyFormatter, + const Names& names) const { + stringstream ss; + + // Print out header and separator with alignment hints. + ss << "|Variable|value|\n|:-:|:-:|\n"; + + // Print out all rows. + for (const auto& kv : *this) { + ss << "|" << keyFormatter(kv.first) << "|" + << Translate(names, kv.first, kv.second) << "|\n"; + } + + return ss.str(); +} + +std::string DiscreteValues::html(const KeyFormatter& keyFormatter, + const Names& names) const { + stringstream ss; + + // Print out preamble. + ss << "
\n\n \n"; + + // Print out header row. + ss << " \n"; + + // Finish header and start body. + ss << " \n \n"; + + // Print out all rows. + for (const auto& kv : *this) { + ss << " "; + ss << ""; + ss << "\n"; + } + ss << " \n
Variablevalue
" << keyFormatter(kv.first) << "\'" + << Translate(names, kv.first, kv.second) << "
\n
"; + return ss.str(); +} +} // namespace gtsam diff --git a/gtsam/discrete/DiscreteValues.h b/gtsam/discrete/DiscreteValues.h index 2d9c8d3cfb..3d0c54ab68 100644 --- a/gtsam/discrete/DiscreteValues.h +++ b/gtsam/discrete/DiscreteValues.h @@ -18,8 +18,11 @@ #pragma once #include +#include #include +#include + namespace gtsam { /** A map from keys to values @@ -34,25 +37,57 @@ namespace gtsam { */ class DiscreteValues : public Assignment { public: - using Assignment::Assignment; // all constructors + using Base = Assignment; // base class + + using Assignment::Assignment; // all constructors // Define the implicit default constructor. DiscreteValues() = default; // Construct from assignment. - DiscreteValues(const Assignment& a) : Assignment(a) {} + explicit DiscreteValues(const Base& a) : Base(a) {} void print(const std::string& s = "", - const KeyFormatter& keyFormatter = DefaultKeyFormatter) const { - std::cout << s << ": "; - for (const typename Assignment::value_type& keyValue : *this) - std::cout << "(" << keyFormatter(keyValue.first) << ", " - << keyValue.second << ")"; - std::cout << std::endl; + const KeyFormatter& keyFormatter = DefaultKeyFormatter) const; + + static std::vector CartesianProduct( + const DiscreteKeys& keys) { + return Base::CartesianProduct(keys); } + + /// @name Wrapper support + /// @{ + + /// Translation table from values to strings. + using Names = std::map>; + + /// Translate an integer index value for given key to a string. + static std::string Translate(const Names& names, Key key, size_t index); + + /** + * @brief Output as a markdown table. + * + * @param keyFormatter function that formats keys. + * @param names translation table for values. + * @return string markdown output. + */ + std::string markdown(const KeyFormatter& keyFormatter, + const Names& names) const; + + /** + * @brief Output as a html table. + * + * @param keyFormatter function that formats keys. + * @param names translation table for values. + * @return string html output. + */ + std::string html(const KeyFormatter& keyFormatter, const Names& names) const; + + /// @} }; // traits -template<> struct traits : public Testable {}; +template <> +struct traits : public Testable {}; } // namespace gtsam diff --git a/gtsam/discrete/tests/testDiscreteBayesTree.cpp b/gtsam/discrete/tests/testDiscreteBayesTree.cpp index edb5ea46c6..26356be3d8 100644 --- a/gtsam/discrete/tests/testDiscreteBayesTree.cpp +++ b/gtsam/discrete/tests/testDiscreteBayesTree.cpp @@ -101,10 +101,10 @@ TEST(DiscreteBayesTree, ThinTree) { auto R = self.bayesTree->roots().front(); // Check whether BN and BT give the same answer on all configurations - auto allPosbValues = - cartesianProduct(keys[0] & keys[1] & keys[2] & keys[3] & keys[4] & - keys[5] & keys[6] & keys[7] & keys[8] & keys[9] & - keys[10] & keys[11] & keys[12] & keys[13] & keys[14]); + auto allPosbValues = DiscreteValues::CartesianProduct( + keys[0] & keys[1] & keys[2] & keys[3] & keys[4] & keys[5] & keys[6] & + keys[7] & keys[8] & keys[9] & keys[10] & keys[11] & keys[12] & keys[13] & + keys[14]); for (size_t i = 0; i < allPosbValues.size(); ++i) { DiscreteValues x = allPosbValues[i]; double expected = self.bayesNet.evaluate(x); diff --git a/gtsam/discrete/tests/testDiscreteMarginals.cpp b/gtsam/discrete/tests/testDiscreteMarginals.cpp index e75016b683..3208f81c53 100644 --- a/gtsam/discrete/tests/testDiscreteMarginals.cpp +++ b/gtsam/discrete/tests/testDiscreteMarginals.cpp @@ -164,8 +164,8 @@ TEST_UNSAFE(DiscreteMarginals, truss2) { graph.add(key[2] & key[3] & key[4], "1 2 3 4 5 6 7 8"); // Calculate the marginals by brute force - auto allPosbValues = - cartesianProduct(key[0] & key[1] & key[2] & key[3] & key[4]); + auto allPosbValues = DiscreteValues::CartesianProduct( + key[0] & key[1] & key[2] & key[3] & key[4]); Vector T = Z_5x1, F = Z_5x1; for (size_t i = 0; i < allPosbValues.size(); ++i) { DiscreteValues x = allPosbValues[i]; diff --git a/gtsam/discrete/tests/testDiscreteValues.cpp b/gtsam/discrete/tests/testDiscreteValues.cpp new file mode 100644 index 0000000000..5e7c0ac6f1 --- /dev/null +++ b/gtsam/discrete/tests/testDiscreteValues.cpp @@ -0,0 +1,76 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/* + * testDiscreteValues.cpp + * + * @date Jan, 2022 + * @author Frank Dellaert + */ + +#include +#include +#include +#include + +#include +using namespace boost::assign; + +using namespace std; +using namespace gtsam; + +/* ************************************************************************* */ +// Check markdown representation with a value formatter. +TEST(DiscreteValues, markdownWithValueFormatter) { + DiscreteValues values; + values[12] = 1; // A + values[5] = 0; // B + string expected = + "|Variable|value|\n" + "|:-:|:-:|\n" + "|B|-|\n" + "|A|One|\n"; + auto keyFormatter = [](Key key) { return key == 12 ? "A" : "B"; }; + DiscreteValues::Names names{{12, {"Zero", "One", "Two"}}, {5, {"-", "+"}}}; + string actual = values.markdown(keyFormatter, names); + EXPECT(actual == expected); +} + +/* ************************************************************************* */ +// Check html representation with a value formatter. +TEST(DiscreteValues, htmlWithValueFormatter) { + DiscreteValues values; + values[12] = 1; // A + values[5] = 0; // B + string expected = + "
\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
Variablevalue
B'-
A'One
\n" + "
"; + auto keyFormatter = [](Key key) { return key == 12 ? "A" : "B"; }; + DiscreteValues::Names names{{12, {"Zero", "One", "Two"}}, {5, {"-", "+"}}}; + string actual = values.html(keyFormatter, names); + EXPECT(actual == expected); +} + +/* ************************************************************************* */ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************* */ From fd48028f24ca5b2193c880b7be26a7a99a010491 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 15:06:57 -0500 Subject: [PATCH 12/15] Make free versions and wrap --- gtsam/discrete/DiscreteValues.cpp | 15 +++++++++++++-- gtsam/discrete/DiscreteValues.h | 19 ++++++++++++++++--- gtsam/discrete/discrete.i | 12 ++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/gtsam/discrete/DiscreteValues.cpp b/gtsam/discrete/DiscreteValues.cpp index 9bd0572bd4..8a09de7425 100644 --- a/gtsam/discrete/DiscreteValues.cpp +++ b/gtsam/discrete/DiscreteValues.cpp @@ -60,8 +60,8 @@ string DiscreteValues::markdown(const KeyFormatter& keyFormatter, return ss.str(); } -std::string DiscreteValues::html(const KeyFormatter& keyFormatter, - const Names& names) const { +string DiscreteValues::html(const KeyFormatter& keyFormatter, + const Names& names) const { stringstream ss; // Print out preamble. @@ -83,4 +83,15 @@ std::string DiscreteValues::html(const KeyFormatter& keyFormatter, ss << " \n\n
"; return ss.str(); } + +string markdown(const DiscreteValues& values, const KeyFormatter& keyFormatter, + const DiscreteValues::Names& names) { + return values.markdown(keyFormatter, names); +} + +string html(const DiscreteValues& values, const KeyFormatter& keyFormatter, + const DiscreteValues::Names& names) { + return values.html(keyFormatter, names); +} + } // namespace gtsam diff --git a/gtsam/discrete/DiscreteValues.h b/gtsam/discrete/DiscreteValues.h index 3d0c54ab68..81997a7831 100644 --- a/gtsam/discrete/DiscreteValues.h +++ b/gtsam/discrete/DiscreteValues.h @@ -21,7 +21,9 @@ #include #include +#include #include +#include namespace gtsam { @@ -71,8 +73,8 @@ class DiscreteValues : public Assignment { * @param names translation table for values. * @return string markdown output. */ - std::string markdown(const KeyFormatter& keyFormatter, - const Names& names) const; + std::string markdown(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const; /** * @brief Output as a html table. @@ -81,11 +83,22 @@ class DiscreteValues : public Assignment { * @param names translation table for values. * @return string html output. */ - std::string html(const KeyFormatter& keyFormatter, const Names& names) const; + std::string html(const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const Names& names = {}) const; /// @} }; +/// Free version of markdown. +std::string markdown(const DiscreteValues& values, + const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const DiscreteValues::Names& names = {}); + +/// Free version of html. +std::string html(const DiscreteValues& values, + const KeyFormatter& keyFormatter = DefaultKeyFormatter, + const DiscreteValues::Names& names = {}); + // traits template <> struct traits : public Testable {}; diff --git a/gtsam/discrete/discrete.i b/gtsam/discrete/discrete.i index 9bf324fd31..218b790e88 100644 --- a/gtsam/discrete/discrete.i +++ b/gtsam/discrete/discrete.i @@ -17,6 +17,18 @@ class DiscreteKeys { }; // DiscreteValues is added in specializations/discrete.h as a std::map +string markdown( + const gtsam::DiscreteValues& values, + const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter); +string markdown(const gtsam::DiscreteValues& values, + const gtsam::KeyFormatter& keyFormatter, + std::map> names); +string html( + const gtsam::DiscreteValues& values, + const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter); +string html(const gtsam::DiscreteValues& values, + const gtsam::KeyFormatter& keyFormatter, + std::map> names); #include class DiscreteFactor { From 28b087364ee570fae88822d07469d98c5402059d Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 16:38:05 -0500 Subject: [PATCH 13/15] Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21d8d1b601..d040f9e82a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ endif() set (GTSAM_VERSION_MAJOR 4) set (GTSAM_VERSION_MINOR 2) set (GTSAM_VERSION_PATCH 0) -set (GTSAM_PRERELEASE_VERSION "a1") +set (GTSAM_PRERELEASE_VERSION "a2") math (EXPR GTSAM_VERSION_NUMERIC "10000 * ${GTSAM_VERSION_MAJOR} + 100 * ${GTSAM_VERSION_MINOR} + ${GTSAM_VERSION_PATCH}") if (${GTSAM_VERSION_PATCH} EQUAL 0) From f76cb6d1f3add2de1c2b0764a1bd91aeff6eaf9f Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 15:59:40 -0500 Subject: [PATCH 14/15] Fix failing test --- gtsam_unstable/discrete/tests/testLoopyBelief.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gtsam_unstable/discrete/tests/testLoopyBelief.cpp b/gtsam_unstable/discrete/tests/testLoopyBelief.cpp index 6561949b14..eac0d834e6 100644 --- a/gtsam_unstable/discrete/tests/testLoopyBelief.cpp +++ b/gtsam_unstable/discrete/tests/testLoopyBelief.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include From f1f3c04c2e8ac19648066819e97082f129588500 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sun, 9 Jan 2022 17:00:41 -0500 Subject: [PATCH 15/15] Fix single quotes --- gtsam/discrete/DecisionTreeFactor.cpp | 2 +- gtsam/discrete/DiscreteConditional.cpp | 2 +- gtsam/discrete/DiscreteValues.cpp | 4 ++-- gtsam/discrete/tests/testDiscreteValues.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index c50811a506..ad4cbad434 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -226,7 +226,7 @@ namespace gtsam { stringstream ss; // Print out preamble. - ss << "
\n\n \n"; + ss << "
\n
\n \n"; // Print out header row. ss << " "; diff --git a/gtsam/discrete/DiscreteConditional.cpp b/gtsam/discrete/DiscreteConditional.cpp index 82018abea6..0bdc7d7b5a 100644 --- a/gtsam/discrete/DiscreteConditional.cpp +++ b/gtsam/discrete/DiscreteConditional.cpp @@ -396,7 +396,7 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter, } // Print out preamble. - ss << "
\n \n"; + ss << "
\n \n"; // Print out header row. ss << " "; diff --git a/gtsam/discrete/DiscreteValues.cpp b/gtsam/discrete/DiscreteValues.cpp index 8a09de7425..5d0c8dd3d5 100644 --- a/gtsam/discrete/DiscreteValues.cpp +++ b/gtsam/discrete/DiscreteValues.cpp @@ -65,7 +65,7 @@ string DiscreteValues::html(const KeyFormatter& keyFormatter, stringstream ss; // Print out preamble. - ss << "
\n
\n \n"; + ss << "
\n
\n \n"; // Print out header row. ss << " \n"; @@ -76,7 +76,7 @@ string DiscreteValues::html(const KeyFormatter& keyFormatter, // Print out all rows. for (const auto& kv : *this) { ss << " "; - ss << ""; ss << "\n"; } diff --git a/gtsam/discrete/tests/testDiscreteValues.cpp b/gtsam/discrete/tests/testDiscreteValues.cpp index 5e7c0ac6f1..c8a1fa1680 100644 --- a/gtsam/discrete/tests/testDiscreteValues.cpp +++ b/gtsam/discrete/tests/testDiscreteValues.cpp @@ -57,8 +57,8 @@ TEST(DiscreteValues, htmlWithValueFormatter) { " \n" " \n" " \n" - " \n" - " \n" + " \n" + " \n" " \n" "
Variablevalue
" << keyFormatter(kv.first) << "\'" + ss << "" << keyFormatter(kv.first) << "" << Translate(names, kv.first, kv.second) << "
Variablevalue
B'-
A'One
B-
AOne
\n" "
";