Made cartesian product static, and added specialization in DiscreteValues. Added markdown and html methods for the latter.

release/4.3a0
Frank Dellaert 2022-01-09 14:46:23 -05:00
parent 3ea5aed26e
commit c1561dba02
12 changed files with 272 additions and 92 deletions

View File

@ -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);

View File

@ -19,32 +19,30 @@
#pragma once
#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <vector>
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 L>
class Assignment: public std::map<L, size_t> {
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 L>
class Assignment : public std::map<L, size_t> {
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<typename L>
std::vector<Assignment<L> > cartesianProduct(
const std::vector<std::pair<L, size_t> >& keys) {
std::vector<Assignment<L> > allPossValues;
Assignment<L> values;
template <typename Derived = Assignment<L>>
static std::vector<Derived> CartesianProduct(
const std::vector<std::pair<L, size_t>>& keys) {
std::vector<Derived> allPossValues;
Derived values;
typedef std::pair<L, size_t> 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

View File

@ -22,6 +22,7 @@
#include <gtsam/base/FastSet.h>
#include <boost/make_shared.hpp>
#include <utility>
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<std::pair<Key, size_t>> rpairs(pairs.rbegin(), pairs.rend());
const auto assignments = cartesianProduct(rpairs);
const auto assignments = DiscreteValues::CartesianProduct(rpairs);
// Construct unordered_map with values
std::vector<std::pair<DiscreteValues, double>> 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 << "<th>" << Translate(names, key, index) << "</th>";
ss << "<th>" << DiscreteValues::Translate(names, key, index) << "</th>";
}
ss << "<td>" << kv.second << "</td>"; // value
ss << "</tr>\n";

View File

@ -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<Assignment<Key>> DiscreteConditional::frontalAssignments() const {
vector<DiscreteValues> DiscreteConditional::frontalAssignments() const {
vector<pair<Key, size_t>> pairs;
for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key));
vector<pair<Key, size_t>> rpairs(pairs.rbegin(), pairs.rend());
return cartesianProduct(rpairs);
return DiscreteValues::CartesianProduct(rpairs);
}
/* ************************************************************************* */
vector<Assignment<Key>> DiscreteConditional::allAssignments() const {
vector<DiscreteValues> DiscreteConditional::allAssignments() const {
vector<pair<Key, size_t>> pairs;
for (Key key : parents()) pairs.emplace_back(key, cardinalities_.at(key));
for (Key key : frontals()) pairs.emplace_back(key, cardinalities_.at(key));
vector<pair<Key, size_t>> 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 << "<th>";
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 << "</th>";
}
@ -429,7 +424,7 @@ string DiscreteConditional::html(const KeyFormatter& keyFormatter,
ss << " <tr>";
for (auto&& it = beginParents(); it != endParents(); ++it) {
size_t index = a.at(*it);
ss << "<th>" << Translate(names, *it, index) << "</th>";
ss << "<th>" << DiscreteValues::Translate(names, *it, index) << "</th>";
}
}
ss << "<td>" << operator()(a) << "</td>"; // value

View File

@ -25,6 +25,7 @@
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
namespace gtsam {
@ -182,10 +183,10 @@ class GTSAM_EXPORT DiscreteConditional
void sampleInPlace(DiscreteValues* parentsValues) const;
/// Return all assignments for frontal variables.
std::vector<Assignment<Key>> frontalAssignments() const;
std::vector<DiscreteValues> frontalAssignments() const;
/// Return all assignments for frontal *and* parent variables.
std::vector<Assignment<Key>> allAssignments() const;
std::vector<DiscreteValues> allAssignments() const;
/// @}
/// @name Wrapper support

View File

@ -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

View File

@ -22,6 +22,7 @@
#include <gtsam/inference/Factor.h>
#include <gtsam/base/Testable.h>
#include <string>
namespace gtsam {
class DecisionTreeFactor;
@ -90,14 +91,11 @@ public:
/// @{
/// Translation table from values to strings.
using Names = std::map<Key, std::vector<std::string>>;
/// 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.

View File

@ -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 <gtsam/discrete/DiscreteValues.h>
#include <sstream>
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 << "<div>\n<table class=\'DiscreteValues\'>\n <thead>\n";
// Print out header row.
ss << " <tr><th>Variable</th><th>value</th></tr>\n";
// Finish header and start body.
ss << " </thead>\n <tbody>\n";
// Print out all rows.
for (const auto& kv : *this) {
ss << " <tr>";
ss << "<th>" << keyFormatter(kv.first) << "</th><td>\'"
<< Translate(names, kv.first, kv.second) << "</td>";
ss << "</tr>\n";
}
ss << " </tbody>\n</table>\n</div>";
return ss.str();
}
} // namespace gtsam

View File

@ -18,8 +18,11 @@
#pragma once
#include <gtsam/discrete/Assignment.h>
#include <gtsam/discrete/DiscreteKey.h>
#include <gtsam/inference/Key.h>
#include <string>
namespace gtsam {
/** A map from keys to values
@ -34,25 +37,57 @@ namespace gtsam {
*/
class DiscreteValues : public Assignment<Key> {
public:
using Assignment::Assignment; // all constructors
using Base = Assignment<Key>; // base class
using Assignment::Assignment; // all constructors
// Define the implicit default constructor.
DiscreteValues() = default;
// Construct from assignment.
DiscreteValues(const Assignment<Key>& a) : Assignment<Key>(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<DiscreteValues> CartesianProduct(
const DiscreteKeys& keys) {
return Base::CartesianProduct<DiscreteValues>(keys);
}
/// @name Wrapper support
/// @{
/// Translation table from values to strings.
using Names = std::map<Key, std::vector<std::string>>;
/// 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<DiscreteValues> : public Testable<DiscreteValues> {};
template <>
struct traits<DiscreteValues> : public Testable<DiscreteValues> {};
} // namespace gtsam

View File

@ -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);

View File

@ -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];

View File

@ -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 <CppUnitLite/TestHarness.h>
#include <gtsam/base/Testable.h>
#include <gtsam/discrete/DiscreteValues.h>
#include <gtsam/discrete/Signature.h>
#include <boost/assign/std/map.hpp>
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 =
"<div>\n"
"<table class='DiscreteValues'>\n"
" <thead>\n"
" <tr><th>Variable</th><th>value</th></tr>\n"
" </thead>\n"
" <tbody>\n"
" <tr><th>B</th><td>'-</td></tr>\n"
" <tr><th>A</th><td>'One</td></tr>\n"
" </tbody>\n"
"</table>\n"
"</div>";
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);
}
/* ************************************************************************* */