// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_BAYES_UTILs_
#define DLIB_BAYES_UTILs_
#include "bayes_utils_abstract.h"
#include "../string.h"
#include "../map.h"
#include "../matrix.h"
#include "../rand.h"
#include "../array.h"
#include "../set.h"
#include "../algs.h"
#include "../noncopyable.h"
#include "../smart_pointers.h"
#include "../graph.h"
#include <vector>
#include <algorithm>
#include <ctime>
namespace dlib
{
// ----------------------------------------------------------------------------------------
class assignment
{
public:
assignment()
{
}
assignment(
const assignment& a
)
{
a.reset();
while (a.move_next())
{
unsigned long idx = a.element().key();
unsigned long value = a.element().value();
vals.add(idx,value);
}
}
assignment& operator = (
const assignment& rhs
)
{
if (this == &rhs)
return *this;
assignment(rhs).swap(*this);
return *this;
}
void clear()
{
vals.clear();
}
bool operator < (
const assignment& item
) const
{
if (size() < item.size())
return true;
else if (size() > item.size())
return false;
reset();
item.reset();
while (move_next())
{
item.move_next();
if (element().key() < item.element().key())
return true;
else if (element().key() > item.element().key())
return false;
else if (element().value() < item.element().value())
return true;
else if (element().value() > item.element().value())
return false;
}
return false;
}
bool has_index (
unsigned long idx
) const
{
return vals.is_in_domain(idx);
}
void add (
unsigned long idx,
unsigned long value = 0
)
{
// make sure requires clause is not broken
DLIB_ASSERT( has_index(idx) == false ,
"\tvoid assignment::add(idx)"
<< "\n\tYou can't add the same index to an assignment object more than once"
<< "\n\tidx: " << idx
<< "\n\tthis: " << this
);
vals.add(idx, value);
}
unsigned long& operator[] (
const long idx
)
{
// make sure requires clause is not broken
DLIB_ASSERT( has_index(idx) == true ,
"\tunsigned long assignment::operator[](idx)"
<< "\n\tYou can't access an index value if it isn't already in the object"
<< "\n\tidx: " << idx
<< "\n\tthis: " << this
);
return vals[idx];
}
const unsigned long& operator[] (
const long idx
) const
{
// make sure requires clause is not broken
DLIB_ASSERT( has_index(idx) == true ,
"\tunsigned long assignment::operator[](idx)"
<< "\n\tYou can't access an index value if it isn't already in the object"
<< "\n\tidx: " << idx
<< "\n\tthis: " << this
);
return vals[idx];
}
void swap (
assignment& item
)
{
vals.swap(item.vals);
}
void remove (
unsigned long idx
)
{
// make sure requires clause is not broken
DLIB_ASSERT( has_index(idx) == true ,
"\tunsigned long assignment::remove(idx)"
<< "\n\tYou can't remove an index value if it isn't already in the object"
<< "\n\tidx: " << idx
<< "\n\tthis: " << this
);
vals.destroy(idx);
}
unsigned long size() const { return vals.size(); }
void reset() const { vals.reset(); }
bool move_next() const { return vals.move_next(); }
map_pair<unsigned long, unsigned long>& element()
{
// make sure requires clause is not broken
DLIB_ASSERT(current_element_valid() == true,
"\tmap_pair<unsigned long,unsigned long>& assignment::element()"
<< "\n\tyou can't access the current element if it doesn't exist"
<< "\n\tthis: " << this
);
return vals.element();
}
const map_pair<unsigned long, unsigned long>& element() const
{
// make sure requires clause is not broken
DLIB_ASSERT(current_element_valid() == true,
"\tconst map_pair<unsigned long,unsigned long>& assignment::element() const"
<< "\n\tyou can't access the current element if it doesn't exist"
<< "\n\tthis: " << this
);
return vals.element();
}
bool at_start() const { return vals.at_start(); }
bool current_element_valid() const { return vals.current_element_valid(); }
friend inline void serialize (
const assignment& item,
std::ostream& out
)
{
serialize(item.vals, out);
}
friend inline void deserialize (
assignment& item,
std::istream& in
)
{
deserialize(item.vals, in);
}
private:
mutable dlib::map<unsigned long, unsigned long>::kernel_1b_c vals;
};
inline std::ostream& operator << (
std::ostream& out,
const assignment& a
)
{
a.reset();
out << "(";
if (a.move_next())
out << a.element().key() << ":" << a.element().value();
while (a.move_next())
{
out << ", " << a.element().key() << ":" << a.element().value();
}
out << ")";
return out;
}
inline void swap (
assignment& a,
assignment& b
)
{
a.swap(b);
}
// ------------------------------------------------------------------------
class joint_probability_table
{
/*!
INITIAL VALUE
- table.size() == 0
CONVENTION
- size() == table.size()
- probability(a) == table[a]
!*/
public:
joint_probability_table (
const joint_probability_table& t
)
{
t.reset();
while (t.move_next())
{
assignment a = t.element().key();
double p = t.element().value();
set_probability(a,p);
}
}
joint_probability_table() {}
joint_probability_table& operator= (
const joint_probability_table& rhs
)
{
if (this == &rhs)
return *this;
joint_probability_table(rhs).swap(*this);
return *this;
}
void set_probability (
const assignment& a,
double p
)
{
// make sure requires clause is not broken
DLIB_ASSERT(0.0 <= p && p <= 1.0,
"\tvoid& joint_probability_table::set_probability(a,p)"
<< "\n\tyou have given an invalid probability value"
<< "\n\ttp: " << p
<< "\n\tta: " << a
<< "\n\tthis: " << this
);
if (table.is_in_domain(a))
{
table[a] = p;
}
else
{
assignment temp(a);
table.add(temp,p);
}
}
bool has_entry_for (
const assignment& a
) const
{
return table.is_in_domain(a);
}
void add_probability (
const assignment& a,
double p
)
{
// make sure requires clause is not broken
DLIB_ASSERT(0.0 <= p && p <= 1.0,
"\tvoid& joint_probability_table::add_probability(a,p)"
<< "\n\tyou have given an invalid probability value"
<< "\n\ttp: " << p
<< "\n\tta: " << a
<< "\n\tthis: " << this
);
if (table.is_in_domain(a))
{
table[a] += p;
}
else
{
assignment temp(a);
table.add(temp,p);
}
}
const double probability (
const assignment& a
) const
{
return table[a];
}
void clear()
{
table.clear();
}
unsigned long size () const { return table.size(); }
bool move_next() const { return table.move_next(); }
void reset() const { table.reset(); }
map_pair<assignment,double>& element()
{
// make sure requires clause is not broken
DLIB_ASSERT(current_element_valid() == true,
"\tmap_pair<assignment,double>& joint_probability_table::element()"
<< "\n\tyou can't access the current element if it doesn't exist"
<< "\n\tthis: " << this
);
return table.element();
}
const map_pair<assignment,double>& element() const
{
// make sure requires clause is not broken
DLIB_ASSERT(current_element_valid() == true,
"\tconst map_pair<assignment,double>& joint_probability_table::element() const"
<< "\n\tyou can't access the current element if it doesn't exist"
<< "\n\tthis: " << this
);
return table.element();
}
bool at_start() const { return table.at_start(); }
bool current_element_valid() const { return table.current_element_valid(); }
template <typename T>
void marginalize (
const T& vars,
joint_probability_table& out
) const
{
out.clear();
double p;
reset();
while (move_next())
{
assignment a;
const assignment& asrc = element().key();
p = element().value();
asrc.reset();
while (asrc.move_next())
{
if (vars.is_member(asrc.element().key()))
a.add(asrc.element().key(), asrc.element().value());
}
out.add_probability(a,p);
}
}
void marginalize (
const unsigned long var,
joint_probability_table& out
) const
{
out.clear();
double p;
reset();
while (move_next())
{
assignment a;
const assignment& asrc = element().key();
p = element().value();
asrc.reset();
while (asrc.move_next())
{
if (var == asrc.element().key())
a.add(asrc.element().key(), asrc.element().value());
}
out.add_probability(a,p);
}
}
void normalize (
)
{
double sum = 0;
reset();
while (move_next())
sum += element().value();
reset();
while (move_next())
element().value() /= sum;
}
void swap (
joint_probability_table& item
)
{
table.swap(item.table);
}
friend inline void serialize (
const joint_probability_table& item,
std::ostream& out
)
{
serialize(item.table, out);
}
friend inline void deserialize (
joint_probability_table& item,
std::istream& in
)
{
deserialize(item.table, in);
}
private:
dlib::map<assignment, double >::kernel_1b_c table;
};
inline void swap (
joint_probability_table& a,
joint_probability_table& b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
class conditional_probability_table : noncopyable
{
/*!
INITIAL VALUE
- table.size() == 0
CONVENTION
- if (table.is_in_domain(ps) && value < num_vals && table[ps](value) >= 0) then
- has_entry_for(value,ps) == true
- probability(value,ps) == table[ps](value)
- else
- has_entry_for(value,ps) == false
- num_values() == num_vals
!*/
public:
conditional_probability_table()
{
clear();
}
void set_num_values (
unsigned long num
)
{
num_vals = num;
table.clear();
}
bool has_entry_for (
unsigned long value,
const assignment& ps
) const
{
if (table.is_in_domain(ps) && value < num_vals && table[ps](value) >= 0)
return true;
else
return false;
}
unsigned long num_values (
) const { return num_vals; }
void set_probability (
unsigned long value,
const assignment& ps,
double p
)
{
// make sure requires clause is not broken
DLIB_ASSERT( value < num_values() && 0.0 <= p && p <= 1.0 ,
"\tvoid conditional_probability_table::set_probability()"
<< "\n\tinvalid arguments to set_probability"
<< "\n\tvalue: " << value
<< "\n\tnum_values(): " << num_values()
<< "\n\tp: " << p
<< "\n\tps: " << ps
<< "\n\tthis: " << this
);
if (table.is_in_domain(ps))
{
table[ps](value) = p;
}
else
{
matrix<double,1> dist(num_vals);
set_all_elements(dist,-1);
dist(value) = p;
assignment temp(ps);
table.add(temp,dist);
}
}
double probability(
unsigned long value,
const assignment& ps
) const
{
// make sure requires clause is not broken
DLIB_ASSERT( value < num_values() && has_entry_for(value,ps) ,
"\tvoid conditional_probability_table::probability()"
<< "\n\tinvalid arguments to set_probability"
<< "\n\tvalue: " << value
<< "\n\tnum_values(): " << num_values()
<< "\n\tps: " << ps
<< "\n\tthis: " << this
);
return table[ps](value);
}
void clear()
{
table.clear();
num_vals = 0;
}
void empty_table ()
{
table.clear();
}
void swap (
conditional_probability_table& item
)
{
exchange(num_vals, item.num_vals);
table.swap(item.table);
}
friend inline void serialize (
const conditional_probability_table& item,
std::ostream& out
)
{
serialize(item.table, out);
serialize(item.num_vals, out);
}
friend inline void deserialize (
conditional_probability_table& item,
std::istream& in
)
{
deserialize(item.table, in);
deserialize(item.num_vals, in);
}
private:
dlib::map<assignment, matrix<double,1> >::kernel_1b_c table;
unsigned long num_vals;
};
inline void swap (
conditional_probability_table& a,
conditional_probability_table& b
) { a.swap(b); }
// ------------------------------------------------------------------------
class bayes_node : noncopyable
{
public:
bayes_node ()
{
is_instantiated = false;
value_ = 0;
}
unsigned long value (
) const { return value_;}
void set_value (
unsigned long new_value
)
{
// make sure requires clause is not broken
DLIB_ASSERT( new_value < table().num_values(),
"\tvoid bayes_node::set_value(new_value)"
<< "\n\tnew_value must be less than the number of possible values for this node"
<< "\n\tnew_value: " << new_value
<< "\n\ttable().num_values(): " << table().num_values()
<< "\n\tthis: " << this
);
value_ = new_value;
}
conditional_probability_table& table (
) { return table_; }
const conditional_probability_table& table (
) const { return table_; }
bool is_evidence (
) const { return is_instantiated; }
void set_as_nonevidence (
) { is_instantiated = false; }
void set_as_evidence (
) { is_instantiated = true; }
void swap (
bayes_node& item
)
{
exchange(value_, item.value_);
exchange(is_instantiated, item.is_instantiated);
table_.swap(item.table_);
}
friend inline void serialize (
const bayes_node& item,
std::ostream& out
)
{
serialize(item.value_, out);
serialize(item.is_instantiated, out);
serialize(item.table_, out);
}
friend inline void deserialize (
bayes_node& item,
std::istream& in
)
{
deserialize(item.value_, in);
deserialize(item.is_instantiated, in);
deserialize(item.table_, in);
}
private:
unsigned long value_;
bool is_instantiated;
conditional_probability_table table_;
};
inline void swap (
bayes_node& a,
bayes_node& b
) { a.swap(b); }
// ------------------------------------------------------------------------
namespace bayes_node_utils
{
template <typename T>
void set_node_value (
T& bn,
unsigned long n,
unsigned long val
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes() && val < node_num_values(bn,n),
"\tvoid bayes_node_utils::set_node_value(bn, n, val)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tval: " << val
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
<< "\n\tnode_num_values(bn,n): " << node_num_values(bn,n)
);
bn.node(n).data.set_value(val);
}
// ----------------------------------------------------------------------------------------
template <typename T>
unsigned long node_value (
const T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tunsigned long bayes_node_utils::node_value(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
return bn.node(n).data.value();
}
// ----------------------------------------------------------------------------------------
template <typename T>
bool node_is_evidence (
const T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tbool bayes_node_utils::node_is_evidence(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
return bn.node(n).data.is_evidence();
}
// ----------------------------------------------------------------------------------------
template <typename T>
void set_node_as_evidence (
T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tvoid bayes_node_utils::set_node_as_evidence(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
bn.node(n).data.set_as_evidence();
}
// ----------------------------------------------------------------------------------------
template <typename T>
void set_node_as_nonevidence (
T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tvoid bayes_node_utils::set_node_as_nonevidence(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
bn.node(n).data.set_as_nonevidence();
}
// ----------------------------------------------------------------------------------------
template <typename T>
void set_node_num_values (
T& bn,
unsigned long n,
unsigned long num
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tvoid bayes_node_utils::set_node_num_values(bn, n, num)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
bn.node(n).data.table().set_num_values(num);
}
// ----------------------------------------------------------------------------------------
template <typename T>
unsigned long node_num_values (
const T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tvoid bayes_node_utils::node_num_values(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
return bn.node(n).data.table().num_values();
}
// ----------------------------------------------------------------------------------------
template <typename T>
const double node_probability (
const T& bn,
unsigned long n,
unsigned long value,
const assignment& parents
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes() && value < node_num_values(bn,n),
"\tdouble bayes_node_utils::node_probability(bn, n, value, parents)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tvalue: " << value
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
<< "\n\tnode_num_values(bn,n): " << node_num_values(bn,n)
);
DLIB_ASSERT( parents.size() == bn.node(n).number_of_parents(),
"\tdouble bayes_node_utils::node_probability(bn, n, value, parents)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tparents.size(): " << parents.size()
<< "\n\tb.node(n).number_of_parents(): " << bn.node(n).number_of_parents()
);
#ifdef ENABLE_ASSERTS
parents.reset();
while (parents.move_next())
{
const unsigned long x = parents.element().key();
DLIB_ASSERT( bn.has_edge(x, n),
"\tdouble bayes_node_utils::node_probability(bn, n, value, parents)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
);
DLIB_ASSERT( parents[x] < node_num_values(bn,x),
"\tdouble bayes_node_utils::node_probability(bn, n, value, parents)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
<< "\n\tparents[x]: " << parents[x]
<< "\n\tnode_num_values(bn,x): " << node_num_values(bn,x)
);
}
#endif
return bn.node(n).data.table().probability(value, parents);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void set_node_probability (
T& bn,
unsigned long n,
unsigned long value,
const assignment& parents,
double p
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes() && value < node_num_values(bn,n),
"\tvoid bayes_node_utils::set_node_probability(bn, n, value, parents, p)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tp: " << p
<< "\n\tvalue: " << value
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
<< "\n\tnode_num_values(bn,n): " << node_num_values(bn,n)
);
DLIB_ASSERT( parents.size() == bn.node(n).number_of_parents(),
"\tvoid bayes_node_utils::set_node_probability(bn, n, value, parents, p)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tp: " << p
<< "\n\tparents.size(): " << parents.size()
<< "\n\tbn.node(n).number_of_parents(): " << bn.node(n).number_of_parents()
);
DLIB_ASSERT( 0.0 <= p && p <= 1.0,
"\tvoid bayes_node_utils::set_node_probability(bn, n, value, parents, p)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tp: " << p
);
#ifdef ENABLE_ASSERTS
parents.reset();
while (parents.move_next())
{
const unsigned long x = parents.element().key();
DLIB_ASSERT( bn.has_edge(x, n),
"\tvoid bayes_node_utils::set_node_probability(bn, n, value, parents, p)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
);
DLIB_ASSERT( parents[x] < node_num_values(bn,x),
"\tvoid bayes_node_utils::set_node_probability(bn, n, value, parents, p)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
<< "\n\tparents[x]: " << parents[x]
<< "\n\tnode_num_values(bn,x): " << node_num_values(bn,x)
);
}
#endif
bn.node(n).data.table().set_probability(value,parents,p);
}
// ----------------------------------------------------------------------------------------
template <typename T>
const assignment node_first_parent_assignment (
const T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tconst assignment bayes_node_utils::node_first_parent_assignment(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
);
assignment a;
const unsigned long num_parents = bn.node(n).number_of_parents();
for (unsigned long i = 0; i < num_parents; ++i)
{
a.add(bn.node(n).parent(i).index(), 0);
}
return a;
}
// ----------------------------------------------------------------------------------------
template <typename T>
bool node_next_parent_assignment (
const T& bn,
unsigned long n,
assignment& a
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tbool bayes_node_utils::node_next_parent_assignment(bn, n, a)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
);
DLIB_ASSERT( a.size() == bn.node(n).number_of_parents(),
"\tbool bayes_node_utils::node_next_parent_assignment(bn, n, a)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\ta.size(): " << a.size()
<< "\n\tbn.node(n).number_of_parents(): " << bn.node(n).number_of_parents()
);
#ifdef ENABLE_ASSERTS
a.reset();
while (a.move_next())
{
const unsigned long x = a.element().key();
DLIB_ASSERT( bn.has_edge(x, n),
"\tbool bayes_node_utils::node_next_parent_assignment(bn, n, a)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
);
DLIB_ASSERT( a[x] < node_num_values(bn,x),
"\tbool bayes_node_utils::node_next_parent_assignment(bn, n, a)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tx: " << x
<< "\n\ta[x]: " << a[x]
<< "\n\tnode_num_values(bn,x): " << node_num_values(bn,x)
);
}
#endif
// basically this loop just adds 1 to the assignment but performs
// carries if necessary
for (unsigned long p = 0; p < a.size(); ++p)
{
const unsigned long pindex = bn.node(n).parent(p).index();
a[pindex] += 1;
// if we need to perform a carry
if (a[pindex] >= node_num_values(bn,pindex))
{
a[pindex] = 0;
}
else
{
// no carry necessary so we are done
return true;
}
}
// we got through the entire loop which means a carry propagated all the way out
// so there must not be any more valid assignments left
return false;
}
// ----------------------------------------------------------------------------------------
template <typename T>
bool node_cpt_filled_out (
const T& bn,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT( n < bn.number_of_nodes(),
"\tbool bayes_node_utils::node_cpt_filled_out(bn, n)"
<< "\n\tInvalid arguments to this function"
<< "\n\tn: " << n
<< "\n\tbn.number_of_nodes(): " << bn.number_of_nodes()
);
const unsigned long num_values = node_num_values(bn,n);
const conditional_probability_table& table = bn.node(n).data.table();
// now loop over all the possible parent assignments for this node
assignment a(node_first_parent_assignment(bn,n));
do
{
double sum = 0;
// make sure that this assignment has an entry for all the values this node can take one
for (unsigned long value = 0; value < num_values; ++value)
{
if (table.has_entry_for(value,a) == false)
return false;
else
sum += table.probability(value,a);
}
// check if the sum of probabilities equals 1 as it should
if (std::abs(sum-1.0) > 1e-5)
return false;
} while (node_next_parent_assignment(bn,n,a));
return true;
}
}
// ----------------------------------------------------------------------------------------
class bayesian_network_gibbs_sampler : noncopyable
{
public:
bayesian_network_gibbs_sampler ()
{
rnd.set_seed(cast_to_string(std::time(0)));
}
template <
typename T
>
void sample_graph (
T& bn
)
{
using namespace bayes_node_utils;
for (unsigned long n = 0; n < bn.number_of_nodes(); ++n)
{
if (node_is_evidence(bn, n))
continue;
samples.set_size(node_num_values(bn,n));
// obtain the probability distribution for this node
for (long i = 0; i < samples.nc(); ++i)
{
set_node_value(bn, n, i);
samples(i) = node_probability(bn, n);
for (unsigned long j = 0; j < bn.node(n).number_of_children(); ++j)
samples(i) *= nod