/**
* InferenceGraph.java
* @author Fabio G. Cozman
* Copyright 1996 - 1999, Fabio G. Cozman,
* Carnergie Mellon University, Universidade de Sao Paulo
* fgcozman@usp.br, http://www.cs.cmu.edu/~fgcozman/home.html
*
* The JavaBayes distribution is free software; you can
* redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation (either
* version 2 of the License or, at your option, any later version),
* provided that this notice and the name of the author appear in all
* copies. Upon request to the author, some of the packages in the
* JavaBayes distribution can be licensed under the GNU Lesser General
* Public License as published by the Free Software Foundation (either
* version 2 of the License, or (at your option) any later version).
* If you're using the software, please notify fgcozman@usp.br so
* that you can receive updates and patches. JavaBayes is distributed
* "as is", in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with the JavaBayes distribution. If not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package InferenceGraphs;
import BayesianNetworks.*;
import BayesianInferences.*;
import QuasiBayesianNetworks.*;
import QuasiBayesianInferences.*;
import InterchangeFormat.*;
import java.io.*;
import java.awt.*;
import java.util.*;
import java.net.URL;
/*
* An InferenceGraph contains all the information about the graphical
* structure of a Bayesian network. The information that is specific to
* the network, such as name, presence of credal sets, etc, is stored at
* the variable qbn.
* Notes:
* 1) qbn is a QuasiBayesNet, either holding a BayesNet or a
* QuasiBayesNet,
* 2) All the ProbabilityVariable and ProbabilityFunction objects
* of bn need not correspond to the nodes in the InferenceGraph.
* 3) Note that the QuasiBayesNet stored at the
* InferenceGraph is a copy of the original inserted BayesNet,
* not just a reference to it.
*/
public class InferenceGraph {
QuasiBayesNet qbn;
QBInference qbi;
QBExpectation qbe;
Vector nodes = new Vector();
private final String defaultBayesNetName = "InternalNetwork";
public final static int MARGINAL_POSTERIOR = 1;
public final static int EXPECTATION = 2;
public final static int EXPLANATION = 3;
public final static int FULL_EXPLANATION = 4;
public final static int SENSITIVITY_ANALYSIS = 5;
public final static int NO_CREDAL_SET = QuasiBayesNet.NO_CREDAL_SET;
public final static int CONSTANT_DENSITY_RATIO = QuasiBayesNet.CONSTANT_DENSITY_RATIO;
public final static int EPSILON_CONTAMINATED = QuasiBayesNet.EPSILON_CONTAMINATED;
public final static int CONSTANT_DENSITY_BOUNDED = QuasiBayesNet.CONSTANT_DENSITY_BOUNDED;
public final static int TOTAL_VARIATION = QuasiBayesNet.TOTAL_VARIATION;
/*
* Default constructor for an InferenceGraph.
*/
public InferenceGraph() {
qbn = new QuasiBayesNet(defaultBayesNetName, 0, 0);
}
/*
* Simple constructor for an InferenceGraph.
*/
public InferenceGraph(BayesNet b_n) {
qbn = new QuasiBayesNet(b_n);
convert_bayes_net();
}
/*
* Constructor for an InferenceGraph.
*/
public InferenceGraph(String filename) throws IOException, IFException {
qbn = new QuasiBayesNet(new
java.io.DataInputStream(new java.io.FileInputStream(filename)));
convert_bayes_net();
}
/*
* Constructor for an InferenceGraph.
*/
public InferenceGraph(URL url) throws IOException, IFException {
qbn = new QuasiBayesNet(url);
convert_bayes_net();
}
/*
* Get the contents of the graph.
*/
public QuasiBayesNet get_bayes_net() {
return(convert_graph());
}
/*
* Convert a QuasiBayesNet object to the InferenceGraph
* structure; returns true if the conversion is successful.
*/
boolean convert_bayes_net() {
ProbabilityVariable pv = null;
ProbabilityFunction pf = null;
for (int i=0; i<qbn.number_variables(); i++) {
pv = qbn.get_probability_variable(i);
pf = null;
for (int j=0; j<qbn.number_probability_functions(); j++) {
pf = qbn.get_probability_function(j);
if (pf.get_variable(0) == pv)
break;
}
// The variable does not have a corresponding function
if (pf == null)
return(false);
nodes.addElement(new InferenceGraphNode(this, pv, pf));
}
generate_parents_and_children();
return(true);
}
/*
* Generate the parents and children for the nodes.
*/
private void generate_parents_and_children() {
int i, j;
DiscreteVariable variables[];
ProbabilityFunction pf;
InferenceGraphNode base_node, node;
Enumeration e;
for (e = nodes.elements(); e.hasMoreElements(); ) {
base_node = (InferenceGraphNode)(e.nextElement());
pf = base_node.pf;
variables = pf.get_variables();
for (i=1; i<variables.length; i++) {
node = get_node(variables[i]);
if (node == null) continue;
base_node.parents.addElement(node);
node.children.addElement(base_node);
}
}
}
/*
* Get the node corresponding to a given variable.
*/
private InferenceGraphNode get_node(DiscreteVariable dv) {
InferenceGraphNode node;
for (Enumeration e = nodes.elements(); e.hasMoreElements(); ) {
node = (InferenceGraphNode)e.nextElement();
if (node.pv == dv) return(node);
}
return(null);
}
/*
* Convert the InferenceGraph structure to a QuasiBayesNet object.
*/
QuasiBayesNet convert_graph() {
int i;
Enumeration e;
InferenceGraphNode node;
// Create the arrays of variables and functions
ProbabilityVariable pvs[] = new ProbabilityVariable[nodes.size()];
ProbabilityFunction pfs[] = new ProbabilityFunction[nodes.size()];
// Insert the empty arrays
qbn.set_probability_variables(pvs);
qbn.set_probability_functions(pfs);
// Collect all variables and functions in the nodes
// into the new QuasiBayesNet
for (i = 0, e = nodes.elements(); e.hasMoreElements(); i++) {
node = (InferenceGraphNode)(e.nextElement());
node.update_position();
qbn.set_probability_variable(i, node.pv);
qbn.set_probability_function(i, node.pf);
}
return(qbn);
}
/*
* Generate a valid name for a new variable.
*/
private String generate_name(int i) {
InferenceGraphNode no;
// generate names of the form a..z, a1..z1, a2..z2, etc.
char namec = (char) ((int) 'a' + i % 26);
int suffix = i / 26;
String name;
if (suffix > 0)
name = new String("" + namec + suffix);
else
name = new String("" + namec);
// check whether there is a variable with this name
for (Enumeration e = nodes.elements(); e.hasMoreElements(); ) {
no = (InferenceGraphNode)(e.nextElement());
if (no.get_name().equals(name))
return(generate_name(i+1));
}
return(name);
}
/* *************** PUBLIC METHODS ********************* */
/**
* Get the name of the network.
*/
public String get_name() {
return(qbn.get_name());
}
/**
* Set the name of the network.
*/
public void set_name(String n) {
qbn.set_name(n);
}
/**
* Get the properties of the network.
*/
public Vector get_network_properties() {
return(qbn.get_properties());
}
/**
* Set the properties of the network.
*/
public void set_network_properties(Vector prop) {
qbn.set_properties(prop);
}
/**
* Get the type of global neighborhood modeled by the network.
*/
public int get_global_neighborhood_type() {
return(qbn.get_global_neighborhood_type());
}
/**
* Set the global neighborhood type.
*/
public void set_global_neighborhood(int type) {
qbn.set_global_neighborhood_type(type);
}
/**
* Get the parameter for the global neighborhood modeled by the network.
*/
public double get_global_neighborhood_parameter() {
return(qbn.get_global_neighborhood_parameter());
}
/**
* Set the parameter for the global neighborhood modeled by the network.
*/
public void set_global_neighborhood_parameter(double parameter) {
qbn.set_global_neighborhood_parameter(parameter);
}
/**
* Remove a property from the network.
*/
public void remove_network_property(int index) {
qbn.remove_property(index);
}
/**
* Add a property to the network.
*/
public void add_network_property(String prop) {
qbn.add_property(prop);
}
/**
* Determine whether or not a name is valid and/or repeated.
*/
public String check_name(String n) {
InferenceGraphNode no;
String nn = validate_value(n);
for (Enumeration e = nodes.elements(); e.hasMoreElements(); ) {
no = (InferenceGraphNode)(e.nextElement());
if (no.get_name().equals(nn))
return(null);
}
return(nn);
}
/**
* Check whether a string is a valid name.
*/
public String validate_value(String value) {
StringBuffer str = new StringBuffer(value);
for (int i=0; i < str.length(); i++) {
if (str.charAt(i) == ' ')
str.setCharAt(i, '_');
}
return str.toString();
}
/**
* Print the QuasiBayesNet.
*/
public void print_bayes_net(PrintStream out) {
QuasiBayesNet qb_n = get_bayes_net();
qb_n.print(out);
}
/**
* Print information about a posterior marginal for the Bayesian
* network into the given PrintStream.
* @param queried_variable indicates the variable of interest.
* @param show_bucket_tree determines whether or not to present
* a description of the BucketTree.
*/
public void print_marginal(PrintStream pstream, String queried_variable,
boolean do_compute_clusters, boolean show_bucket_tree) {
if ( (do_compute_clusters == false) || (qbi == null) ||
(qbi.areClustersProduced() == false) )
qbi = new QBInference( get_bayes_net(), do_compute_clusters );
qbi.inference(queried_variable);
qbi.print(pstream, show_bucket_tree);
}
/**
* Reset the QBInference.
*/
public void reset_marginal() {
qbi = null;
}
/**
* Print information about a posterior expectation for the Bayesian
* network into the given PrintStream.
* @param queried_variable indicates the variable of interest.
* @param show_bucket_tree determines whether or not to present
* a description of the BucketTree.
*/
public void print_expectation(PrintStream pstream, String queried_variable,
boolean do_compute_clusters, boolean show_bucket_tree) {
if ( (do_compute_clusters == false) || (qbe == null) ||
(qbi.areClustersProduced() == false) )
qbe = new QBExpectation( get_bayes_net(), do_compute_clusters );
qbe.expectation(queried_variable);
qbe.print(pstream, show_bucket_tree);
}
/**
* Reset the QBExpectation.
*/
public void reset_expectation() {
qbe = null;
}
/**
* Print information about an explanation for the Bayesian
* network into the given PrintStream.
* @param show_bucket_tree determines whether or not to present
* a description of the BucketTree.
*/
public void print_explanation(PrintStream pstream, boolean show_bucket_tree) {
Explanation ex = new Explanation( get_bayes_net() );
ex.explanation();
ex.print(pstream, show_bucket_tree);
}
/**
* Print information about a full explanation for the Bayesian
* network into the given PrintStream.
* @param show_bucket_tree determines whether or not to present
* a description of the BucketTree.
*/
public void print_full_explanation(PrintStream pstream, boolean show_bucket_tree) {
Explanation fex = new Explanation( get_bayes_net() );
fex.full_explanation();
fex.print(pstream, show_bucket_tree);
}
/**
* Print the metrics for sensitivity analysis of the Bayesian
* network into the given PrintStream.
*/
public void print_sensitivity_analysis(PrintStream pstream) {
// SensitivityAnalysis sa = new SensitivityAnalysis( get_bayes_net() );
// sa.compute(queried_variable);
// sa.print(pstream);
/*** FOR NOW: ***/
System.out.print("HEY! Sensitivity analysis not implemented yet!");
}
/**
* Save the Bayesian network into a PrintStream in the BIF InterchangeFormat.
*/
public void save_bif(PrintStream out) {
QuasiBayesNet qb_n = get_bayes_net();
qb_n.save_bif(out);
}
/**
* Save the Bayesian network into a PrintStream in the XML InterchangeFormat.
*/
public void save_xml(PrintStream out) {
QuasiBayesNet qb_n = get_bayes_net();
qb_n.save_xml(out);
}
/**
* Save the Bayesian networks in BUGS format into a PrintStream.
*/
public void save_bugs(PrintStream out) {
QuasiBayesNet qb_n = get_bayes_net();
qb_n.save_bugs(out);
}
/**
* Print method for an InferenceGraph
*/
public void print() {
print(System.out);
}
/**
* Print method for an InferenceGraph
*/
public void print(PrintStream out) {
QuasiBayesNet qb_n = get_bayes_net();
qb_n.print(out);
}
/**
* Get the nodes in the network.
*/
public Vector get_nodes() {
return(nodes);
}
/**
* Get the nodes in the network as an Enumeration object.
*/
public Enumeration elements() {
return(nodes.elements());
}
/**
* Get the number of variables in the network
*/
public int number_nodes() {
return(nodes.size());
}
/**
* Create a new node in the network.
*/
public void create_node(int x, int y) {
Point p = new Point(x, y);
String n = generate_name(nodes.size());
nodes.addElement(new InferenceGraphNode(this, n, p));
// Synchronize the QuasiBayesNet object and the graph.
convert_graph();
}
/**
* Create an arc from parent to child.
*/
public boolean create_arc(InferenceGraphNode parent,
InferenceGraphNode child) {
// Check whether the given parent is already a parent of the
// given child.
for (Enumeration e = child.parents.elements(); e.hasMoreElements(); ) {
if ( parent == ( (InferenceGraphNode)(e.nextElement()) ) )
return(false);
}
// First put child into the children of parent
parent.children.addElement(child);
// Second put parent into the parents of child
child.parents.addElement(parent);
// The parent is not further affected by the arc.
// The child must have its ProbabilityFunction
// object updated.
child.init_dists();
// Synchronize the QuasiBayesNet object and the graph.
convert_graph();
// Return true.
return(true);
}
/**
* Delete a node in the network.
*/
public void delete_node(InferenceGraphNode node) {
Enumeration e;
InferenceGraphNode parent, child;
// First, remove node from all its childrem
for (e = node.children.elements(); e.hasMoreElements(); ) {
child = (InferenceGraphNode)(e.nextElement());
child.parents.removeElement(node);
child.init_dists();
}
// Second remove parent into the parents of child
for (e = node.parents.elements(); e.hasMoreElements(); ) {
parent = (InferenceGraphNode)(e.nextElement());
parent.children.removeElement(node);
}
// Third remove the node itself
nodes.removeElement(node);
// Synchronize the QuasiBayesNet object and the graph.
convert_graph();
}
/**
* Delete the arc from parent to child.
*/
public void delete_arc(InferenceGraphNode parent,
InferenceGraphNode child) {
// First remove child into the children of parent
parent.children.removeElement(child);
// Second remove parent into the parents of child
child.parents.removeElement(parent);
// The parent is not further affected by the arc.
// The child must have its ProbabilityFunction
// object updated.
child.init_dists();
// Synchronize the QuasiBayesNet object and the graph.
convert_graph();
}
/**
* Determines whether the connection of
* bottom_node to head_node would cause the
* network to have a cycle.
*/
public boolean hasCycle(InferenceGraphNode bottom_node,
InferenceGraphNode head_node) {
Vector children;
Enumeration e;
InferenceGraphNode next_node, child_node;
// Array with enough space to have all nodes
InferenceGraphNode listed_nodes[] =
new InferenceGraphNode[nodes.size()];
// Hashtable for efficient lookup of already listed nodes
Hashtable hashed_nodes = new Hashtable();
// Index of last node in listed_nodes
int last_listed_node_index = 0;
// Initialize: head_node is marked and inserted
int current_listed_node_index = 0;
listed_nodes[0] = head_node;
hashed_nodes.put(head_node.pv.get_name(), head_node);
// Now expand for children until no more children, or
// when a child is equal to bottom_node
while (current_listed_node_index <= last_listed_node_index) {
// Select the next node to be expanded
next_node = listed_nodes[ current_listed_node_index ];
// Update the index that indicates nodes to be expanded
current_listed_node_index++;
// Get all children of the node being expanded
children = next_node.children;
// Expand the node: put all its children into list
for (e = children.elements(); e.hasMoreElements(); ) {
child_node = (InferenceGraphNode)(e.nextElement());
if (child_node == bottom_node) // Cycle is detected
return(true);
if (!hashed_nodes.containsKey(child_node.pv.get_name())) {
hashed_nodes.put(child_node.pv.get_name(), child_node);
last_listed_node_index++;
listed_nodes[ last_listed_node_index ] = child_node;
}
}
}
return(false);
}
/**
* Change the values of a variable. Note that, if the number
* of new values is different from the number of current values,
* this operation resets the
* probability values of the variable and all its children.
*/
public void change_values(InferenceGraphNode node, String values[]) {
InferenceGraphNode cnode;
Vector children;
Enumeration e;
if (node.pv.number_values() == values.length) {
node.pv.set_values(values);
return;
}
node.pv.set_values(values);
node.init_dists();
children = node.get_children();
for (e = children.elements(); e.hasMoreElements(); ) {
cnode = (InferenceGraphNode)(e.nextElement());
cnode.init_dists();
}
// Synchronize the QuasiBayesNet object and the graph.
convert_graph();
}
/**
* Set a value for the position of the node.
*/
public void set_pos(InferenceGraphNode node, Point position) {
node.pos = position;
convert_graph();
}
}