Commit d89d784a authored by Davis King's avatar Davis King

Just added a try/catch block around the whole program.

parent cbcb80bf
...@@ -51,246 +51,257 @@ using namespace std; ...@@ -51,246 +51,257 @@ using namespace std;
int main() int main()
{ {
// There are many useful convenience functions in this namespace. They all try
// perform simple access or modify operations on the nodes of a bayesian network.
// You don't have to use them but they are convenient and they also will check for
// various errors in your bayesian network when your application is built with
// the DEBUG or ENABLE_ASSERTS preprocessor definitions defined. So their use
// is recommended. In fact, most of the global functions used in this example
// program are from this namespace.
using namespace bayes_node_utils;
// This statement declares a bayesian network called bn. Note that a bayesian network
// in the dlib world is just a directed_graph object that contains a special kind
// of node called a bayes_node.
directed_graph<bayes_node>::kernel_1a_c bn;
// Use an enum to make some more readable names for our nodes.
enum nodes
{ {
A = 0, // There are many useful convenience functions in this namespace. They all
B = 1, // perform simple access or modify operations on the nodes of a bayesian network.
C = 2, // You don't have to use them but they are convenient and they also will check for
D = 3 // various errors in your bayesian network when your application is built with
}; // the DEBUG or ENABLE_ASSERTS preprocessor definitions defined. So their use
// is recommended. In fact, most of the global functions used in this example
// The next few blocks of code setup our bayesian network. // program are from this namespace.
using namespace bayes_node_utils;
// The first thing we do is tell the bn object how many nodes it has
// and also add the three edges. Again, we are using the network // This statement declares a bayesian network called bn. Note that a bayesian network
// shown in ASCII art at the top of this file. // in the dlib world is just a directed_graph object that contains a special kind
bn.set_number_of_nodes(4); // of node called a bayes_node.
bn.add_edge(A, D); directed_graph<bayes_node>::kernel_1a_c bn;
bn.add_edge(B, A);
bn.add_edge(C, A); // Use an enum to make some more readable names for our nodes.
enum nodes
{
// Now we inform all the nodes in the network that they are binary A = 0,
// nodes. That is, they only have two possible values. B = 1,
set_node_num_values(bn, A, 2); C = 2,
set_node_num_values(bn, B, 2); D = 3
set_node_num_values(bn, C, 2); };
set_node_num_values(bn, D, 2);
// The next few blocks of code setup our bayesian network.
assignment parent_state;
// Now we will enter all the conditional probability information for each node. // The first thing we do is tell the bn object how many nodes it has
// Each node's conditional probability is dependent on the state of its parents. // and also add the three edges. Again, we are using the network
// To specify this state we need to use the assignment object. This assignment // shown in ASCII art at the top of this file.
// object allows us to specify the state of each nodes parents. bn.set_number_of_nodes(4);
bn.add_edge(A, D);
bn.add_edge(B, A);
// Here we specify that p(B=1) = 0.01 bn.add_edge(C, A);
// parent_state is empty in this case since B is a root node.
set_node_probability(bn, B, 1, parent_state, 0.01);
// Here we specify that p(B=0) = 1-0.01 // Now we inform all the nodes in the network that they are binary
set_node_probability(bn, B, 0, parent_state, 1-0.01); // nodes. That is, they only have two possible values.
set_node_num_values(bn, A, 2);
set_node_num_values(bn, B, 2);
// Here we specify that p(C=1) = 0.001 set_node_num_values(bn, C, 2);
// parent_state is empty in this case since B is a root node. set_node_num_values(bn, D, 2);
set_node_probability(bn, C, 1, parent_state, 0.001);
// Here we specify that p(C=0) = 1-0.001 assignment parent_state;
set_node_probability(bn, C, 0, parent_state, 1-0.001); // Now we will enter all the conditional probability information for each node.
// Each node's conditional probability is dependent on the state of its parents.
// To specify this state we need to use the assignment object. This assignment
// This is our first node that has parents. So we set the parent_state // object allows us to specify the state of each nodes parents.
// object to reflect that A has both B and C as parents.
parent_state.add(B, 1);
parent_state.add(C, 1); // Here we specify that p(B=1) = 0.01
// Here we specify that p(A=1 | B=1, C=1) = 0.99 // parent_state is empty in this case since B is a root node.
set_node_probability(bn, A, 1, parent_state, 0.99); set_node_probability(bn, B, 1, parent_state, 0.01);
// Here we specify that p(A=0 | B=1, C=1) = 1-0.99 // Here we specify that p(B=0) = 1-0.01
set_node_probability(bn, A, 0, parent_state, 1-0.99); set_node_probability(bn, B, 0, parent_state, 1-0.01);
// Here we use the [] notation because B and C have already
// been added into parent state. // Here we specify that p(C=1) = 0.001
parent_state[B] = 1; // parent_state is empty in this case since B is a root node.
parent_state[C] = 0; set_node_probability(bn, C, 1, parent_state, 0.001);
// Here we specify that p(A=1 | B=1, C=0) = 0.9 // Here we specify that p(C=0) = 1-0.001
set_node_probability(bn, A, 1, parent_state, 0.9); set_node_probability(bn, C, 0, parent_state, 1-0.001);
set_node_probability(bn, A, 0, parent_state, 1-0.9);
parent_state[B] = 0; // This is our first node that has parents. So we set the parent_state
parent_state[C] = 1; // object to reflect that A has both B and C as parents.
// Here we specify that p(A=1 | B=0, C=1) = 0.5 parent_state.add(B, 1);
set_node_probability(bn, A, 1, parent_state, 0.5); parent_state.add(C, 1);
set_node_probability(bn, A, 0, parent_state, 1-0.5); // Here we specify that p(A=1 | B=1, C=1) = 0.99
set_node_probability(bn, A, 1, parent_state, 0.99);
parent_state[B] = 0; // Here we specify that p(A=0 | B=1, C=1) = 1-0.99
parent_state[C] = 0; set_node_probability(bn, A, 0, parent_state, 1-0.99);
// Here we specify that p(A=1 | B=0, C=0) = 0.01
set_node_probability(bn, A, 1, parent_state, 0.01); // Here we use the [] notation because B and C have already
set_node_probability(bn, A, 0, parent_state, 1-0.01); // been added into parent state.
parent_state[B] = 1;
parent_state[C] = 0;
// Here we set probabilities for node D. // Here we specify that p(A=1 | B=1, C=0) = 0.9
// First we clear out parent state so that it doesn't have any of set_node_probability(bn, A, 1, parent_state, 0.9);
// the assignments for the B and C nodes used above. set_node_probability(bn, A, 0, parent_state, 1-0.9);
parent_state.clear();
parent_state.add(A,1); parent_state[B] = 0;
// Here we specify that p(D=1 | A=1) = 0.5 parent_state[C] = 1;
set_node_probability(bn, D, 1, parent_state, 0.5); // Here we specify that p(A=1 | B=0, C=1) = 0.5
set_node_probability(bn, D, 0, parent_state, 1-0.5); set_node_probability(bn, A, 1, parent_state, 0.5);
set_node_probability(bn, A, 0, parent_state, 1-0.5);
parent_state[A] = 0;
// Here we specify that p(D=1 | A=0) = 0.2 parent_state[B] = 0;
set_node_probability(bn, D, 1, parent_state, 0.2); parent_state[C] = 0;
set_node_probability(bn, D, 0, parent_state, 1-0.2); // Here we specify that p(A=1 | B=0, C=0) = 0.01
set_node_probability(bn, A, 1, parent_state, 0.01);
set_node_probability(bn, A, 0, parent_state, 1-0.01);
// We have now finished setting up our bayesian network. So lets compute some
// probability values. The first thing we will do is compute the prior probability // Here we set probabilities for node D.
// of each node in the network. To do this we will use the join tree algorithm which // First we clear out parent state so that it doesn't have any of
// is an algorithm for performing exact inference in a bayesian network. // the assignments for the B and C nodes used above.
parent_state.clear();
// First we need to create an undirected graph which contains set objects at each node and parent_state.add(A,1);
// edge. This long declaration does the trick. // Here we specify that p(D=1 | A=1) = 0.5
typedef set<unsigned long>::compare_1b_c set_type; set_node_probability(bn, D, 1, parent_state, 0.5);
typedef graph<set_type, set_type>::kernel_1a_c join_tree_type; set_node_probability(bn, D, 0, parent_state, 1-0.5);
join_tree_type join_tree;
parent_state[A] = 0;
// Now we need to populate the join_tree with data from our bayesian network. The next // Here we specify that p(D=1 | A=0) = 0.2
// function calls do this. Explaining exactly what they do is outside the scope of this set_node_probability(bn, D, 1, parent_state, 0.2);
// example. Just think of them as filling join_tree with information that is useful set_node_probability(bn, D, 0, parent_state, 1-0.2);
// later on for dealing with our bayesian network.
create_moral_graph(bn, join_tree);
create_join_tree(join_tree, join_tree);
// We have now finished setting up our bayesian network. So lets compute some
// Now that we have a proper join_tree we can use it to obtain a solution to our // probability values. The first thing we will do is compute the prior probability
// bayesian network. Doing this is as simple as declaring an instance of // of each node in the network. To do this we will use the join tree algorithm which
// the bayesian_network_join_tree object as follows: // is an algorithm for performing exact inference in a bayesian network.
bayesian_network_join_tree solution(bn, join_tree);
// First we need to create an undirected graph which contains set objects at each node and
// edge. This long declaration does the trick.
// now print out the probabilities for each node typedef set<unsigned long>::compare_1b_c set_type;
cout << "Using the join tree algorithm:\n"; typedef graph<set_type, set_type>::kernel_1a_c join_tree_type;
cout << "p(A=1) = " << solution.probability(A)(1) << endl; join_tree_type join_tree;
cout << "p(A=0) = " << solution.probability(A)(0) << endl;
cout << "p(B=1) = " << solution.probability(B)(1) << endl; // Now we need to populate the join_tree with data from our bayesian network. The next
cout << "p(B=0) = " << solution.probability(B)(0) << endl; // function calls do this. Explaining exactly what they do is outside the scope of this
cout << "p(C=1) = " << solution.probability(C)(1) << endl; // example. Just think of them as filling join_tree with information that is useful
cout << "p(C=0) = " << solution.probability(C)(0) << endl; // later on for dealing with our bayesian network.
cout << "p(D=1) = " << solution.probability(D)(1) << endl; create_moral_graph(bn, join_tree);
cout << "p(D=0) = " << solution.probability(D)(0) << endl; create_join_tree(join_tree, join_tree);
cout << "\n\n\n";
// Now that we have a proper join_tree we can use it to obtain a solution to our
// bayesian network. Doing this is as simple as declaring an instance of
// Now to make things more interesting lets say that we have discovered that the C // the bayesian_network_join_tree object as follows:
// node really has a value of 1. That is to say, we now have evidence that bayesian_network_join_tree solution(bn, join_tree);
// C is 1. We can represent this in the network using the following two function
// calls.
set_node_value(bn, C, 1); // now print out the probabilities for each node
set_node_as_evidence(bn, C); cout << "Using the join tree algorithm:\n";
cout << "p(A=1) = " << solution.probability(A)(1) << endl;
// Now we want to compute the probabilities of all the nodes in the network again cout << "p(A=0) = " << solution.probability(A)(0) << endl;
// given that we now know that C is 1. We can do this as follows: cout << "p(B=1) = " << solution.probability(B)(1) << endl;
bayesian_network_join_tree solution_with_evidence(bn, join_tree); cout << "p(B=0) = " << solution.probability(B)(0) << endl;
cout << "p(C=1) = " << solution.probability(C)(1) << endl;
// now print out the probabilities for each node cout << "p(C=0) = " << solution.probability(C)(0) << endl;
cout << "Using the join tree algorithm:\n"; cout << "p(D=1) = " << solution.probability(D)(1) << endl;
cout << "p(A=1 | C=1) = " << solution_with_evidence.probability(A)(1) << endl; cout << "p(D=0) = " << solution.probability(D)(0) << endl;
cout << "p(A=0 | C=1) = " << solution_with_evidence.probability(A)(0) << endl; cout << "\n\n\n";
cout << "p(B=1 | C=1) = " << solution_with_evidence.probability(B)(1) << endl;
cout << "p(B=0 | C=1) = " << solution_with_evidence.probability(B)(0) << endl;
cout << "p(C=1 | C=1) = " << solution_with_evidence.probability(C)(1) << endl; // Now to make things more interesting lets say that we have discovered that the C
cout << "p(C=0 | C=1) = " << solution_with_evidence.probability(C)(0) << endl; // node really has a value of 1. That is to say, we now have evidence that
cout << "p(D=1 | C=1) = " << solution_with_evidence.probability(D)(1) << endl; // C is 1. We can represent this in the network using the following two function
cout << "p(D=0 | C=1) = " << solution_with_evidence.probability(D)(0) << endl; // calls.
cout << "\n\n\n"; set_node_value(bn, C, 1);
set_node_as_evidence(bn, C);
// Note that when we made our solution_with_evidence object we reused our join_tree object.
// This saves us the time it takes to calculate the join_tree object from scratch. But // Now we want to compute the probabilities of all the nodes in the network again
// it is important to note that we can only reuse the join_tree object if we haven't changed // given that we now know that C is 1. We can do this as follows:
// the structure of our bayesian network. That is, if we have added or removed nodes or bayesian_network_join_tree solution_with_evidence(bn, join_tree);
// edges from our bayesian network then we must recompute our join_tree. But in this example
// all we did was change the value of a bayes_node object (we made node C be evidence) // now print out the probabilities for each node
// so we are ok. cout << "Using the join tree algorithm:\n";
cout << "p(A=1 | C=1) = " << solution_with_evidence.probability(A)(1) << endl;
cout << "p(A=0 | C=1) = " << solution_with_evidence.probability(A)(0) << endl;
cout << "p(B=1 | C=1) = " << solution_with_evidence.probability(B)(1) << endl;
cout << "p(B=0 | C=1) = " << solution_with_evidence.probability(B)(0) << endl;
cout << "p(C=1 | C=1) = " << solution_with_evidence.probability(C)(1) << endl;
// Next this example will show you how to use the bayesian_network_gibbs_sampler object cout << "p(C=0 | C=1) = " << solution_with_evidence.probability(C)(0) << endl;
// to perform approximate inference in a bayesian network. This is an algorithm cout << "p(D=1 | C=1) = " << solution_with_evidence.probability(D)(1) << endl;
// that doesn't give you an exact solution but it may be necessary to use in some cout << "p(D=0 | C=1) = " << solution_with_evidence.probability(D)(0) << endl;
// instances. For example, the join tree algorithm used above, while fast in many cout << "\n\n\n";
// instances, has exponential runtime in some cases. Moreover, inference in bayesian
// networks is NP-Hard for general networks so sometimes the best you can do is // Note that when we made our solution_with_evidence object we reused our join_tree object.
// find an approximation. // This saves us the time it takes to calculate the join_tree object from scratch. But
// However, it should be noted that the gibbs sampler does not compute the correct // it is important to note that we can only reuse the join_tree object if we haven't changed
// probabilities if the network contains a deterministic node. That is, if any // the structure of our bayesian network. That is, if we have added or removed nodes or
// of the conditional probability tables in the bayesian network have a probability // edges from our bayesian network then we must recompute our join_tree. But in this example
// of 1.0 for something the gibbs sampler should not be used. // all we did was change the value of a bayes_node object (we made node C be evidence)
// so we are ok.
// This Gibbs sampler algorithm works by randomly sampling possibles values of the
// network. So to use it we should set the network to some initial state.
set_node_value(bn, A, 0);
set_node_value(bn, B, 0); // Next this example will show you how to use the bayesian_network_gibbs_sampler object
set_node_value(bn, D, 0); // to perform approximate inference in a bayesian network. This is an algorithm
// that doesn't give you an exact solution but it may be necessary to use in some
// We will leave the C node with a value of 1 and keep it as an evidence node. // instances. For example, the join tree algorithm used above, while fast in many
// instances, has exponential runtime in some cases. Moreover, inference in bayesian
// networks is NP-Hard for general networks so sometimes the best you can do is
// First create an instance of the gibbs sampler object // find an approximation.
bayesian_network_gibbs_sampler sampler; // However, it should be noted that the gibbs sampler does not compute the correct
// probabilities if the network contains a deterministic node. That is, if any
// of the conditional probability tables in the bayesian network have a probability
// To use this algorithm all we do is go into a loop for a certain number of times // of 1.0 for something the gibbs sampler should not be used.
// and each time through we sample the bayesian network. Then we count how
// many times a node has a certain state. Then the probability of that node
// having that state is just its count/total times through the loop. // This Gibbs sampler algorithm works by randomly sampling possibles values of the
// network. So to use it we should set the network to some initial state.
// The following code illustrates the general procedure.
unsigned long A_count = 0; set_node_value(bn, A, 0);
unsigned long B_count = 0; set_node_value(bn, B, 0);
unsigned long C_count = 0; set_node_value(bn, D, 0);
unsigned long D_count = 0;
// We will leave the C node with a value of 1 and keep it as an evidence node.
// The more times you let the loop run the more accurate the result will be. Here we loop
// 2000 times.
const long rounds = 2000; // First create an instance of the gibbs sampler object
for (long i = 0; i < rounds; ++i) bayesian_network_gibbs_sampler sampler;
// To use this algorithm all we do is go into a loop for a certain number of times
// and each time through we sample the bayesian network. Then we count how
// many times a node has a certain state. Then the probability of that node
// having that state is just its count/total times through the loop.
// The following code illustrates the general procedure.
unsigned long A_count = 0;
unsigned long B_count = 0;
unsigned long C_count = 0;
unsigned long D_count = 0;
// The more times you let the loop run the more accurate the result will be. Here we loop
// 2000 times.
const long rounds = 2000;
for (long i = 0; i < rounds; ++i)
{
sampler.sample_graph(bn);
if (node_value(bn, A) == 1)
++A_count;
if (node_value(bn, B) == 1)
++B_count;
if (node_value(bn, C) == 1)
++C_count;
if (node_value(bn, D) == 1)
++D_count;
}
cout << "Using the approximate Gibbs Sampler algorithm:\n";
cout << "p(A=1 | C=1) = " << (double)A_count/(double)rounds << endl;
cout << "p(B=1 | C=1) = " << (double)B_count/(double)rounds << endl;
cout << "p(C=1 | C=1) = " << (double)C_count/(double)rounds << endl;
cout << "p(D=1 | C=1) = " << (double)D_count/(double)rounds << endl;
}
catch (std::exception& e)
{ {
sampler.sample_graph(bn); cout << "exception thrown: " << endl;
cout << e.what() << endl;
if (node_value(bn, A) == 1) cout << "hit enter to terminate" << endl;
++A_count; cin.get();
if (node_value(bn, B) == 1)
++B_count;
if (node_value(bn, C) == 1)
++C_count;
if (node_value(bn, D) == 1)
++D_count;
} }
cout << "Using the approximate Gibbs Sampler algorithm:\n";
cout << "p(A=1 | C=1) = " << (double)A_count/(double)rounds << endl;
cout << "p(B=1 | C=1) = " << (double)B_count/(double)rounds << endl;
cout << "p(C=1 | C=1) = " << (double)C_count/(double)rounds << endl;
cout << "p(D=1 | C=1) = " << (double)D_count/(double)rounds << endl;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment