Working recursive (but not cached) shortcut.

release/4.3a0
Frank Dellaert 2009-11-08 04:41:01 +00:00
parent d1b8925617
commit 58007a8167
3 changed files with 150 additions and 36 deletions

View File

@ -43,20 +43,68 @@ namespace gtsam {
/* ************************************************************************* */
template<class Conditional>
template<class Factor>
typename BayesTree<Conditional>::sharedBayesNet
BayesTree<Conditional>::Clique::shortcut() {
BayesTree<Conditional>::Clique::shortcut(shared_ptr R) {
// The shortcut density is a conditional P(S|R) of the separator of this
// clique on the root. We can compute it recursively from the parent shortcut
// P(S_p|R) as \int P(F_p|S_p) P(S_p|R), where F_p are the frontal nodes in p
// P(Sp|R) as \int P(Fp|Sp) P(Sp|R), where Fp are the frontal nodes in p
// The base case is when we are the root or the parent is the root,
// in which case we return an empty Bayes net
sharedBayesNet p_S_R(new BayesNet<Conditional>);
if (parent_==NULL || parent_->parent_==NULL) return p_S_R;
// A first base case is when this clique or its parent is the root,
// in which case we return an empty Bayes net.
if (R.get()==this || parent_==R) {
sharedBayesNet empty(new BayesNet<Conditional>);
return empty;
}
// If not the base case, calculate the parent shortcut P(S_p|R)
sharedBayesNet p_Sp_R = parent_->shortcut();
// The parent clique has a Conditional for each frontal node in Fp
// so we can obtain P(Fp|Sp) in factor graph form
FactorGraph<Factor> p_Fp_Sp(*parent_);
//p_Fp_Sp.print("p_Fp_Sp");
// If not the base case, obtain the parent shortcut P(Sp|R) as factors
FactorGraph<Factor> p_Sp_R(*parent_->shortcut<Factor>(R));
//p_Sp_R.print("p_Sp_R");
// now combine P(Cp|R) = P(Fp|Sp) * P(Sp|R)
FactorGraph<Factor> p_Cp_R = combine(p_Fp_Sp, p_Sp_R);
// Eliminate into a Bayes net with ordering designed to integrate out
// any variables not in *our* separator. Variables to integrate out must be
// eliminated first hence the desired ordering is [Cp\S S].
// However, an added wrinkle is that Cp might overlap with the root.
// Keys corresponding to the root should not be added to the ordering at all.
// Get the key list Cp=Fp+Sp, which will form the basis for the integrands
Ordering integrands;
{
Ordering Fp = parent_->ordering(), Sp = parent_->separator_;
integrands.splice(integrands.end(),Fp);
integrands.splice(integrands.end(),Sp);
}
// Start ordering with the separator
Ordering ordering = separator_;
// remove any variables in the root, after this integrands = Cp\R, ordering = S\R
BOOST_FOREACH(string key, R->ordering()) {
integrands.remove(key);
ordering.remove(key);
}
// remove any variables in the separator, after this integrands = Cp\R\S
BOOST_FOREACH(string key, separator_) integrands.remove(key);
// form the ordering as [Cp\R\S S\R]
BOOST_REVERSE_FOREACH(string key, integrands) ordering.push_front(key);
// eliminate to get marginal
sharedBayesNet p_S_R = _eliminate<Factor,Conditional>(p_Cp_R,ordering);
// remove all integrands
BOOST_FOREACH(string key, integrands) p_S_R->pop_front();
// return the parent shortcut P(Sp|R)
return p_S_R;
}

View File

@ -38,7 +38,7 @@ namespace gtsam {
*/
struct Clique: public BayesNet<Conditional> {
typedef boost::shared_ptr<Clique> shared_ptr;
typedef typename boost::shared_ptr<Clique> shared_ptr;
shared_ptr parent_;
std::list<shared_ptr> children_;
std::list<std::string> separator_; /** separator keys */
@ -46,19 +46,23 @@ namespace gtsam {
//* Constructor */
Clique(const sharedConditional& conditional);
/** print this node */
void print(const std::string& s = "Bayes tree node") const;
/** The size *includes* the separator */
size_t size() const {
return this->conditionals_.size() + separator_.size();
}
/** print this node */
void print(const std::string& s = "Bayes tree node") const;
/** is this the root of a Bayes tree ? */
inline bool isRoot() const { return parent_==NULL;}
/** print this node and entire subtree below it */
void printTree(const std::string& indent) const;
/** return the conditional P(S|Root) on the separator given the root */
sharedBayesNet shortcut();
template<class Factor>
sharedBayesNet shortcut(shared_ptr R);
};
typedef boost::shared_ptr<Clique> sharedClique;
@ -119,7 +123,7 @@ namespace gtsam {
sharedClique operator[](const std::string& key) const {
typename Nodes::const_iterator it = nodes_.find(key);
if (it == nodes_.end()) throw(std::invalid_argument(
"BayesTree::operator['" + key + "'): key not found"));
"BayesTree::operator['" + key + "']: key not found"));
sharedClique clique = it->second;
return clique;
}

View File

@ -101,21 +101,54 @@ TEST( BayesTree, smoother )
Gaussian bayesTree(*chordalBayesNet);
LONGS_EQUAL(7,bayesTree.size());
// Get the conditional P(S6|Root)
Vector sigma1 = repeat(2, 0.786153);
ConditionalGaussian::shared_ptr cg(new ConditionalGaussian("x2", zero(2), eye(2), sigma1));
BayesNet<ConditionalGaussian> expected; expected.push_back(cg);
Gaussian::sharedClique C6 = bayesTree["x1"];
Gaussian::sharedBayesNet actual = C6->shortcut();
//CHECK(assert_equal(expected,*actual,1e-4));
// Check the conditional P(Root|Root)
BayesNet<ConditionalGaussian> empty;
Gaussian::sharedClique R = bayesTree.root();
Gaussian::sharedBayesNet actual1 = R->shortcut<LinearFactor>(R);
CHECK(assert_equal(empty,*actual1,1e-4));
// Check the conditional P(C2|Root)
Gaussian::sharedClique C2 = bayesTree["x5"];
Gaussian::sharedBayesNet actual2 = C2->shortcut<LinearFactor>(R);
CHECK(assert_equal(empty,*actual2,1e-4));
// Check the conditional P(C3|Root)
Vector sigma3 = repeat(2, 0.61808);
Matrix A56 = Matrix_(2,2,-0.382022,0.,0.,-0.382022);
ConditionalGaussian::shared_ptr cg3(new ConditionalGaussian("x5", zero(2), eye(2), "x6", A56, sigma3));
BayesNet<ConditionalGaussian> expected3; expected3.push_back(cg3);
Gaussian::sharedClique C3 = bayesTree["x4"];
Gaussian::sharedBayesNet actual3 = C3->shortcut<LinearFactor>(R);
CHECK(assert_equal(expected3,*actual3,1e-4));
// Check the conditional P(C4|Root)
Vector sigma4 = repeat(2, 0.661968);
Matrix A46 = Matrix_(2,2,-0.146067,0.,0.,-0.146067);
ConditionalGaussian::shared_ptr cg4(new ConditionalGaussian("x4", zero(2), eye(2), "x6", A46, sigma4));
BayesNet<ConditionalGaussian> expected4; expected4.push_back(cg4);
Gaussian::sharedClique C4 = bayesTree["x3"];
Gaussian::sharedBayesNet actual4 = C4->shortcut<LinearFactor>(R);
CHECK(assert_equal(expected4,*actual4,1e-4));
}
/* ************************************************************************* *
Bayes tree for smoother with "nested dissection" ordering:
x5 x6 x4
x3 x2 : x4
x1 : x2
x7 : x6
Node[x1] P(x1 | x2)
Node[x3] P(x3 | x2 x4)
Node[x5] P(x5 | x4 x6)
Node[x7] P(x7 | x6)
Node[x2] P(x2 | x4)
Node[x6] P(x6 | x4)
Node[x4] P(x4)
becomes
C1 x5 x6 x4
C2 x3 x2 : x4
C3 x1 : x2
C4 x7 : x6
/* ************************************************************************* */
TEST( BayesTree, balanced_smoother_marginals )
{
@ -126,27 +159,22 @@ TEST( BayesTree, balanced_smoother_marginals )
// eliminate using a "nested dissection" ordering
GaussianBayesNet::shared_ptr chordalBayesNet = smoother.eliminate(ordering);
boost::shared_ptr<VectorConfig> actualSolution = chordalBayesNet->optimize();
// SymbolicBayesNet symbolic(*chordalBayesNet);
// symbolic.print("chordalBayesNet");
VectorConfig expectedSolution;
Vector delta = zero(2);
expectedSolution.insert("x1",delta);
expectedSolution.insert("x2",delta);
expectedSolution.insert("x3",delta);
expectedSolution.insert("x4",delta);
expectedSolution.insert("x5",delta);
expectedSolution.insert("x6",delta);
expectedSolution.insert("x7",delta);
BOOST_FOREACH(string key, ordering)
expectedSolution.insert(key,delta);
boost::shared_ptr<VectorConfig> actualSolution = chordalBayesNet->optimize();
CHECK(assert_equal(expectedSolution,*actualSolution,1e-4));
// Create the Bayes tree
Gaussian bayesTree(*chordalBayesNet);
LONGS_EQUAL(7,bayesTree.size());
// Check root clique
//BayesNet<ConditionalGaussian> expected_root;
//BayesNet<ConditionalGaussian> actual_root = bayesTree.root();
//CHECK(assert_equal(expected_root,actual_root));
// Marginals
// Marginal will always be axis-parallel Gaussian on delta=(0,0)
Matrix R = eye(2);
@ -170,6 +198,40 @@ TEST( BayesTree, balanced_smoother_marginals )
CHECK(assert_equal(expected3,*actual3,1e-4));
}
/* ************************************************************************* */
TEST( BayesTree, balanced_smoother_shortcuts )
{
// Create smoother with 7 nodes
LinearFactorGraph smoother = createSmoother(7);
Ordering ordering;
ordering += "x1","x3","x5","x7","x2","x6","x4";
// eliminate using a "nested dissection" ordering
GaussianBayesNet::shared_ptr chordalBayesNet = smoother.eliminate(ordering);
boost::shared_ptr<VectorConfig> actualSolution = chordalBayesNet->optimize();
// Create the Bayes tree
Gaussian bayesTree(*chordalBayesNet);
// Check the conditional P(Root|Root)
BayesNet<ConditionalGaussian> empty;
Gaussian::sharedClique R = bayesTree.root();
Gaussian::sharedBayesNet actual1 = R->shortcut<LinearFactor>(R);
CHECK(assert_equal(empty,*actual1,1e-4));
// Check the conditional P(C2|Root)
Gaussian::sharedClique C2 = bayesTree["x3"];
Gaussian::sharedBayesNet actual2 = C2->shortcut<LinearFactor>(R);
CHECK(assert_equal(empty,*actual2,1e-4));
// Check the conditional P(C3|Root), which should be equal to P(x2|x4)
ConditionalGaussian::shared_ptr p_x2_x4 = (*chordalBayesNet)["x2"];
BayesNet<ConditionalGaussian> expected3; expected3.push_back(p_x2_x4);
Gaussian::sharedClique C3 = bayesTree["x1"];
Gaussian::sharedBayesNet actual3 = C3->shortcut<LinearFactor>(R);
CHECK(assert_equal(expected3,*actual3,1e-4));
}
/* ************************************************************************* */
int main() {
TestResult tr;