/* * Bucket.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 BayesianInferences; import BayesianNetworks.*; import java.io.*; import java.util.Vector; import java.util.Enumeration; /*******************************************************************/ class Bucket { BucketTree bucket_tree; // BucketTree that holds the Bucket. ProbabilityVariable variable; // The Bucket variable. Vector discrete_functions; // The functions in the Bucket. DiscreteFunction backward_pointers; // The pointers used for maximization. DiscreteFunction separator; // The function that is sent from a Bucket to another. boolean do_produce_clusters; // Whether or not to compute distributions for all variables in the Bucket. DiscreteFunction cluster; // The distribution for all variables involved in the Bucket. Vector non_conditioning_variables; // Variables that are not conditioning variables. Vector parents; // The parents of the Bucket in the BucketTree. Bucket child; // The child of the Bucket in the BucketTree. int bucket_status = EMPTY; private DiscreteFunction ordered_dfs[]; private boolean is_ordered_dfs_ready; static final int EMPTY = 0; static final int REDUCED = 1; static final int DISTRIBUTED = 2; /* * Simple constructor for Bucket. Default behavior is * not to build the distributions for clusters of variables. * @param bs The BucketTree that holds the bucket. * @param pv The bucket variable for the Bucket. */ Bucket(BucketTree bs, ProbabilityVariable pv) { this(bs, pv, false); } /* * Basic constructor for Bucket. * @param bs The BucketTree that holds the bucket. * @param pv The bucket variable for the Bucket. * @param produce_clusters Flag that indicates whether * distributions for clusters of variables are * to be computed or not. */ Bucket(BucketTree bs, ProbabilityVariable pv, boolean dpc) { bucket_tree = bs; variable = pv; discrete_functions = new Vector(); do_produce_clusters = dpc; non_conditioning_variables = new Vector(); parents = new Vector(); } /* * Print method for Bucket. */ void print() { print(System.out); } /* * Print method for Bucket. */ void print(PrintStream out) { Enumeration e; boolean is_explanation_flag = false; DiscreteFunction d_f; if (is_explanation()) is_explanation_flag = true; if (is_explanation_flag) out.print("MAP"); out.println("Bucket; variable " + variable.get_name() + " with " + discrete_functions.size() + " function(s)."); switch (bucket_status) { case EMPTY: out.println("Bucket is empty."); break; case REDUCED: out.println("Bucket has been reduced."); break; case DISTRIBUTED: out.println("Bucket has been distributed."); break; } for (e = discrete_functions.elements(); e.hasMoreElements(); ) { d_f = (DiscreteFunction)(e.nextElement()); d_f.print(out); } if (is_explanation_flag && (backward_pointers != null)) { out.println("Backward pointers:"); backward_pointers.print(out); } if (cluster != null) { out.println("Cluster:"); cluster.print(out); } if (separator != null) { out.println("Separator:"); separator.print(out); } if (parents.size() > 0) { out.println("\tParents:"); for (e = parents.elements(); e.hasMoreElements(); ) out.println("\t" + ( (Bucket)(e.nextElement()) ).variable.get_name()); } if (child != null) { out.println("\tChild:"); out.println("\t" + child.variable.get_name()); } } /* * Reduce the Bucket, either by summation or * maximization. The final result is in the Bucket's separator. * Notice that if all functions in a * bucket have a single variable, then the separator is null. */ void reduce() { // Order all the probability functions in the bucket order_dfs(); // If the bucket is empty, return null if (ordered_dfs.length == 0) { separator = null; return; } // Create a ProbabilityFunction with the relevant variables DiscreteFunction new_df = build_new_function(false); // If new_df is null, then the only remaining variable // in the Bucket is the bucket variable. In this case, combine the functions. if (new_df == null) { combine(); separator = null; return; } // Either sum out or maximize out the bucket variable. if (is_explanation()) max_out(new_df); else sum_out(new_df); // Mark the Bucket as REDUCED; bucket_status = REDUCED; // Set the separator. separator = new_df; } /* * Combine a number of functions in the bucket into a single function. */ DiscreteFunction combine() { int i, j, k, m, p, current; int indexes[] = new int[ bucket_tree.bn.number_variables() ]; int value_lengths[] = new int[ bucket_tree.bn.number_variables() ]; double t, v; // Order all the probability functions in the bucket order_dfs(); // If the bucket is empty, return null if (ordered_dfs.length == 0) return(null); // Create the combined DiscreteFunction object DiscreteFunction new_df = build_new_function(true); // Initialize some necessary values for (i=0; i<bucket_tree.bn.number_variables(); i++) { indexes[i] = 0; value_lengths[i] = bucket_tree.bn.get_probability_variable(i).number_values(); } // Build all values for the combined ProbabilityFunction object for (i=0; i<new_df.number_values(); i++) { // Calculate the combined value v = 1.0; for (m=0; m<ordered_dfs.length; m++) v *= ordered_dfs[m].evaluate(bucket_tree.bn.get_probability_variables(), indexes); p = new_df.get_position_from_indexes(bucket_tree.bn.get_probability_variables(), indexes); new_df.set_value(p, v); // Update the indexes indexes[ new_df.get_index(new_df.number_variables() - 1) ]++; for (j=(new_df.number_variables() - 1); j>0; j--) { current = new_df.get_index(j); if (indexes[current] >= value_lengths[current]) { indexes[current] = 0; indexes[ new_df.get_index(j - 1) ]++; } else break; } } // Maximize if necessary. If the combined function // has conditioning variables, only the first // combination of conditioning variables is analyzed. if (is_explanation()) { int jump = 1; for (i=1; i<new_df.number_variables(); i++) { jump *= new_df.get_variable(i).number_values(); } j = 0; t = 0.0; backward_pointers = new DiscreteFunction(1, 1); backward_pointers.set_variable(0, variable); for (i=0; i<variable.number_values(); i++) { if (new_df.get_value(i) > t) { t = new_df.get_value(i * jump); j = i; } } backward_pointers.set_value(0, j); } if (do_produce_clusters) cluster = new_df; return(new_df); } /* * Sum out all variables in the cluster, except the bucket variable, * and put the summation in the bucket_tree.result. */ void reduce_cluster() { // Check whether the cluster is null. if (cluster == null) { bucket_tree.unnormalized_result = null; return; } // Construct the markers. boolean markers[] = new boolean[ bucket_tree.bn.number_variables() ]; for (int i=0; i<markers.length; i++) markers[i] = true; markers[ variable.get_index() ] = false; // Fill in the bucket_tree.result. bucket_tree.unnormalized_result = cluster.sum_out(bucket_tree.bn.get_probability_variables(), markers); } /* * Detect whether the bucket variable is an explanatory variable. */ boolean is_explanation() { if (bucket_tree.explanation_status == Inference.IGNORE_EXPLANATION) return(false); if (bucket_tree.explanation_status == Inference.FULL_EXPLANATION) return(true); return(variable.is_explanation()); } /* * Order the probability functions in the Bucket. */ private void order_dfs() { if (is_ordered_dfs_ready == true) return; is_ordered_dfs_ready = true; ordered_dfs = new DiscreteFunction[ discrete_functions.size() ]; for (int i=0; i<ordered_dfs.length; i++) ordered_dfs[i] = (DiscreteFunction)(discrete_functions.elementAt(i)); } /* * Join the indexes of the Bucket by marking the * variable markers with true. */ private int join_indexes(boolean variable_markers[]) { int i, j, k, n=0; for (i=0; i<variable_markers.length; i++) variable_markers[i] = false; for (i=0; i<ordered_dfs.length; i++) { for (j=0; j<ordered_dfs[i].number_variables(); j++) { k = ordered_dfs[i].get_index(j); if (variable_markers[k] == false) { variable_markers[k] = true; n++; } } } return(n); } /* * Construct a DiscreteFunction which holds all the * variables in the Bucket (maybe with the exception of * the bucket variable). */ private DiscreteFunction build_new_function(boolean is_bucket_variable_included) { int i, j=0, n, v=1; boolean variable_markers[] = new boolean[bucket_tree.bn.number_variables()]; // Join the indexes in the bucket n = join_indexes(variable_markers); if (is_bucket_variable_included == false) { n--; variable_markers[ variable.get_index() ] = false; } // If the only variable is the bucket variable, then ignore if (n == 0) return(null); // Calculate necessary quantities int joined_indexes[] = new int[n]; for (i=0; i<variable_markers.length; i++) { if (variable_markers[i] == true) { joined_indexes[j] = i; j++; v *= bucket_tree.bn.get_probability_variable(i).number_values(); } } // Create new function to be filled with joined variables DiscreteFunction new_df = new DiscreteFunction(n, v); build_new_variables(new_df, joined_indexes, is_bucket_variable_included, n); return(new_df); } /* * Construct an array of variables that contains the variables * in a new function; if the bucket variable is present, it is * the first variable. */ private void build_new_variables(DiscreteFunction new_df, int joined_indexes[], boolean is_bucket_variable_included, int n) { // Bucket variable comes first if present if (is_bucket_variable_included == true) { for (int i=0, j=1; i<n; i++) { if (joined_indexes[i] == variable.get_index()) { new_df.set_variable(0, bucket_tree.bn.get_probability_variable( variable.get_index() )); } else { new_df.set_variable(j, bucket_tree.bn.get_probability_variable( joined_indexes[i] )); j++; } } } else { for (int i=0; i<n; i++) new_df.set_variable(i, bucket_tree.bn.get_probability_variable( joined_indexes[i] )); } } /* * Obtain the values for the reduced_function. * Attention: the array ordered_dfs is supposed to be ready! */ private void sum_out(DiscreteFunction new_df) { DiscreteVariable dvs[]; int i, j, k, l, m, p, p_cluster, last, current; int n = variable.number_values(); int indexes[] = new int[bucket_tree.bn.number_variables()]; int value_lengths[] = new int[bucket_tree.bn.number_variables()]; double t, v; // Initialize some necessary values. dvs = bucket_tree.bn.get_probability_variables(); for (i=0; i<bucket_tree.bn.number_variables(); i++) { indexes[i] = 0; value_lengths[i] = bucket_tree.bn.get_probability_variable(i).number_values(); } if (do_produce_clusters) // If necessary, start up the cluster for the Bucket. cluster = build_new_function(true); // Do the whole summation. last = new_df.number_variables() - 1; // Auxiliary variable to hold last valid index. for (i=0; i<new_df.number_values(); i++) { // Compute all values of the new_df. v = 0.0; for (l=0; l<n; l++) { // For each value of the bucket variable, indexes[ variable.get_index() ] = l; // mark the current value in the indexes, t = 1.0; for (m=0; m<ordered_dfs.length; m++) // loop through the functions in the Bucket. t *= ordered_dfs[m].evaluate(dvs, indexes); if (do_produce_clusters) { // If necessary, insert value in the cluster. p_cluster = cluster.get_position_from_indexes(dvs, indexes); cluster.set_value(p_cluster, t); } v += t; // Finally, do the summation for each value of the new_df. } // Insert the summation for the value of new_df into new_df. p = new_df.get_position_from_indexes(dvs, indexes); new_df.set_value(p, v); // Update the indexes. indexes[ new_df.get_index(last) ]++; // Increment the last index. for (j=last; j>0; j--) { // Now do the updating of all indexes. current = new_df.get_index(j); if (indexes[current] >= value_lengths[current]) { // If overflow in an index, indexes[current] = 0; indexes[ new_df.get_index(j - 1) ]++; // then update the next index. } else break; } } } /* * Obtain the values for the reduced_function through * maximization. * Attention: the array ordered_dfs is supposed to be ready! */ private void max_out(DiscreteFunction new_df) { int i, j, k, l, m, p, u, last, current; int n = variable.number_values(); int indexes[] = new int[bucket_tree.bn.number_variables()]; int value_lengths[] = new int[bucket_tree.bn.number_variables()]; double t, v = 0.0; // Initialize some necessary values create_backward_pointers(new_df); for (i=0; i<bucket_tree.bn.number_variables(); i++) { indexes[i] = 0; value_lengths[i] = bucket_tree.bn.get_probability_variable(i).number_values(); } // Run through all the values of the bucket variable last = new_df.number_variables() - 1; for (i=0; i<new_df.number_values(); i++) { v = 0.0; u = BayesNet.INVALID_INDEX; for (l=0; l<n; l++) { t = 1.0; indexes[ variable.get_index() ] = l; // Combine the values through all the functions in the bucket for (m=0; m<ordered_dfs.length; m++) t *= ordered_dfs[m].evaluate(bucket_tree.bn.get_probability_variables(), indexes); // Perform the maximization if (v <= t) { v = t; u = l; } } // Update functions p = new_df.get_position_from_indexes(bucket_tree.bn.get_probability_variables(), indexes); new_df.set_value(p, v); backward_pointers.set_value(p, (double)u); // Update the indexes indexes[ new_df.get_index(last) ]++; for (j=last; j>0; j--) { current = new_df.get_index(j); if (indexes[current] >= value_lengths[current]) { indexes[current] = 0; indexes[ new_df.get_index(j - 1) ]++; } else break; } } } /* * Allocate and initialize the backward_pointers in the Bucket. */ private void create_backward_pointers(DiscreteFunction new_df) { int i; DiscreteVariable new_df_variables[] = new DiscreteVariable[ new_df.number_variables() ]; double new_df_values[] = new double[ new_df.number_values() ]; for (i=0; i<new_df.number_variables(); i++) new_df_variables[i] = new_df.get_variable(i); for (i=0; i<new_df.number_values(); i++) new_df_values[i] = new_df.get_value(i); backward_pointers = new DiscreteFunction(new_df_variables, new_df_values); } }