/** * 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. */ import BayesianNetworks.*; 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 bn. * Notes: * 1) bn is a BayesNet * 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 { BayesNet bn; 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; /* * Simple constructor for an InferenceGraph. */ public InferenceGraph(BayesNet b_n) { bn=new BayesNet(b_n); convert_bayes_net(); } /* * Constructor for an InferenceGraph. */ public InferenceGraph(String filename) throws IOException, IFException { bn = new BayesNet(new java.io.DataInputStream(new java.io.FileInputStream(filename))); convert_bayes_net(); } /* * Get the contents of the graph. */ public BayesNet get_bayes_net() { return(convert_graph()); } /* * Convert a BayesNet 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<bn.number_variables(); i++) { pv = bn.get_probability_variable(i); pf = null; for (int j=0; j<bn.number_probability_functions(); j++) { pf = bn.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 BayesNet object. */ BayesNet 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 bn.set_probability_variables(pvs); bn.set_probability_functions(pfs); // Collect all variables and functions in the nodes // into the new BayesNet for (i = 0, e = nodes.elements(); e.hasMoreElements(); i++) { node = (InferenceGraphNode)(e.nextElement()); bn.set_probability_variable(i, node.pv); bn.set_probability_function(i, node.pf); } return(bn); } /* * 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(bn.get_name()); } /** * Set the name of the network. */ public void set_name(String n) { bn.set_name(n); } /** * Get the properties of the network. */ public Vector get_network_properties() { return(bn.get_properties()); } /** * Set the properties of the network. */ public void set_network_properties(Vector prop) { bn.set_properties(prop); } /** * Remove a property from the network. */ public void remove_network_property(int index) { bn.remove_property(index); } /** * Add a property to the network. */ public void add_network_property(String prop) { bn.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) { BayesNet b_n = get_bayes_net(); b_n.print(out); } /** * Save the Bayesian network into a PrintStream in the BIF InterchangeFormat. */ public void save_bif(PrintStream out) { BayesNet b_n = get_bayes_net(); b_n.save_bif(out); } /** * Print method for an InferenceGraph */ public void print() { print(System.out); } /** * Print method for an InferenceGraph */ public void print(PrintStream out) { BayesNet b_n = get_bayes_net(); b_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() { String n = generate_name(nodes.size()); nodes.addElement(new InferenceGraphNode(this, n)); // Synchronize the BayesNet 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 BayesNet object and the graph. convert_graph(); } }