/**
* Ordering.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;
/*******************************************************************/
public class Ordering {
BayesNet bn;
String order[];
int explanation_status = Inference.IGNORE_EXPLANATION;
int ordering_type = MINIMUM_WEIGHT;
public static final int USER_DEFINED = 0;
public static final int USER_ORDER = 1;
public static final int MINIMUM_WEIGHT = 2;
/**
* Basic constructor for Ordering.
*/
public Ordering(BayesNet b_n, String objective, int ot) {
bn = b_n;
explanation_status = obtain_explanation_status(b_n);
ordering_type = ot;
order = ordering(objective);
}
/**
* Basic constructor for Ordering.
*/
public Ordering(BayesNet b_n, String or[]) {
bn = b_n;
order = or;
explanation_status = obtain_explanation_status(b_n);
}
/**
* Basic constructor for Ordering.
*/
public Ordering(BayesNet b_n, String objective, int ds, int ot) {
bn = b_n;
explanation_status = ds;
ordering_type = ot;
order = ordering(objective);
}
/**
* Basic constructor for Ordering.
*/
public Ordering(BayesNet b_n, String or[], int ds) {
bn = b_n;
order = or;
explanation_status = ds;
}
/*
* Obtain explanation_status: unless there are explanations
* the status is IGNORE_EXPLANATION.
*/
private int obtain_explanation_status(BayesNet b_n) {
int explanation_status_flag = Inference.IGNORE_EXPLANATION;
for (int i=0; i<b_n.number_variables(); i++) {
if ((!(b_n.get_probability_variable(i).is_observed())) &&
(b_n.get_probability_variable(i).is_explanation())) {
explanation_status_flag = Inference.EXPLANATION;
break;
}
}
return(explanation_status_flag);
}
/*
* Call the appropriate ordering depending on the type of
* ordering.
*/
private String[] ordering(String objective) {
int i;
Vector variables_to_order = new Vector();
int objective_index = bn.index_of_variable(objective);
if (objective_index == BayesNet.INVALID_INDEX)
objective_index = 0;
if (bn.get_probability_variable(objective_index).is_observed()) {
String one_order[] = { bn.get_probability_variable(objective_index).get_name() };
return(one_order);
}
if (ordering_type == USER_ORDER) {
// For user order, just collect all variables.
for (i=0; i<bn.number_variables(); i++)
variables_to_order.addElement(bn.get_probability_variable(i));
return(user_order(variables_to_order, objective_index));
}
else {
// For explanations, just collect all variables.
if (explanation_status != Inference.IGNORE_EXPLANATION) {
for (i=0; i<bn.number_variables(); i++)
variables_to_order.addElement(bn.get_probability_variable(i));
}
else { // For inference, get only the affecting variables.
DSeparation dsep = new DSeparation(bn);
variables_to_order = dsep.all_affecting(objective_index);
}
return(heuristic_order(variables_to_order, objective_index,
ordering_type));
}
}
/*
* Simple ordering for the variables:
* 1) Transparent variables are not included; note that
* transparent variables are only present in cases where
* explanation variables are not to be considered.
* 2) Explanation variables come last in the order they
* were input
* 3) Non-explanation variables come in the order they were
* input. When there are no explanation variables (or
* explanation variables are to be ignored), the objective
* variable comes last. Note that this violates the
* inserted by the user, but the bucket elimination
* algorithm requires the ordering to have this property
* (objective variable last for inference).
*/
private String[] user_order(Vector variables_to_order, int objective_index) {
int i, j;
boolean is_variable_explanation_flag = false;
ProbabilityVariable pv;
Vector non_explanation_variables = new Vector();
Vector explanation_variables = new Vector();
Enumeration e;
String ord[];
// Collect variables into related vectors
for (e = variables_to_order.elements(); e.hasMoreElements(); ) {
pv = (ProbabilityVariable)(e.nextElement());
// Skip transparent variables
if (pv.get_type() == ProbabilityVariable.TRANSPARENT)
continue;
// Check the status of the variable as a explanatory variable
switch (explanation_status) {
case Inference.IGNORE_EXPLANATION:
is_variable_explanation_flag = false;
break;
case Inference.EXPLANATION:
is_variable_explanation_flag = pv.is_explanation();
break;
case Inference.FULL_EXPLANATION:
is_variable_explanation_flag = true;
break;
}
// Observed variables are not explanation variables
// (evidence has precedence over explanations).
if (pv.is_observed())
is_variable_explanation_flag = false;
// Put the variable in the correct vector
if (is_variable_explanation_flag)
explanation_variables.addElement(pv.get_name());
else
non_explanation_variables.addElement(pv.get_name());
}
ord = new String[non_explanation_variables.size() + explanation_variables.size()];
if (explanation_variables.size() == 0) {
for (i=0, e = non_explanation_variables.elements(); e.hasMoreElements(); i++) {
ord[i] = (String)(e.nextElement());
if (ord[i].equals( bn.get_probability_variable(objective_index).get_name() ))
i--;
}
ord[i] = bn.get_probability_variable(objective_index).get_name();
}
else {
for (i=0, e = non_explanation_variables.elements(); e.hasMoreElements(); i++)
ord[i] = (String)(e.nextElement());
for (j=i, e = explanation_variables.elements(); e.hasMoreElements(); j++)
ord[j] = (String)(e.nextElement());
}
return(ord);
}
/*
* Heuristic ordering for the variables:
* 1) Transparent variables are not included
* 2) Decision variables come last in the order they
* were input
* 3) Non-explanation variables come in the order they were
* input, except objective variable which
* is the last of all non-explanation variables
* Produce an ordering for the variables in variables_to_order,
* assuming that all variables are in the BayesNet bn object.
* The ordering_type indicates which heuristic to use in the
* elimination procedure.
*/
private String[] heuristic_order(Vector vo, int objective_index,
int ordering_type) {
int i, j;
int PHASE_ONE = 1;
int PHASE_TWO = 2;
int phase;
long value, min_value;
int min_index;
int number_variables_in_phase;
int number_variables_in_phase_two = 0;
Enumeration e;
ProbabilityVariable pv;
ProbabilityVariable neighbors[];
ProbabilityFunction pf;
// The vector with the filtered variables to order.
Vector variables_to_order = new Vector();
// The vector that will contain the final ordering.
Vector elimination_ordering = new Vector();
// Phase markers: indicates in which phase of the
// algorithm a variable will be eliminated.
int phase_markers[] = new int[bn.number_variables()];
for (i=0; i<phase_markers.length; i++)
phase_markers[i] = PHASE_ONE;
// Filter the incoming variables
for (e = vo.elements(); e.hasMoreElements(); ) {
pv = (ProbabilityVariable)(e.nextElement());
if (pv.is_observed()) { // Put observed variables at the beginning
elimination_ordering.addElement(pv);
}
else { // Skip transparent variables
if (pv.get_type() != ProbabilityVariable.TRANSPARENT) {
// Order all other variables
variables_to_order.addElement(pv);
// Check the status of the variable as an explanatory variable
if ((explanation_status == Inference.FULL_EXPLANATION) ||
((explanation_status == Inference.EXPLANATION) &&
(pv.is_explanation()))) {
phase_markers[ pv.get_index() ] = PHASE_TWO;
number_variables_in_phase_two++;
}
}
}
}
// Define whether the objective variable will be
// processed in the second phase.
if (number_variables_in_phase_two == 0) {
phase_markers[objective_index] = PHASE_TWO;
number_variables_in_phase_two = 1;
}
// Each variable is associated to a vector (the vector contains
// all variables that are linked to the variable).
Vector vectors[] = new Vector[ bn.number_variables() ];
// Initialize the vectors only for the variables that are to be ordered.
for (e = variables_to_order.elements(); e.hasMoreElements(); ) {
pv = (ProbabilityVariable)(e.nextElement());
vectors[ pv.get_index() ] = new Vector();
}
// Moralize the network: build an undirected graph where each variable
// is linked to its parents, children, and parents of its children.
// The idea is to go through the variables and, for each variable,
// interconnect the variable and all its parents. That connects
// all variables to its parents and "moralizes" the graph simultaneously;
// since all variables are analyzed, every variable ends up connected
// to its children.
for (e = variables_to_order.elements(); e.hasMoreElements(); ) {
pv = (ProbabilityVariable)(e.nextElement());
pf = bn.get_function(pv);
vectors[ pv.get_index() ].addElement(pv);
interconnect(bn, vectors, pf.get_variables());
}
// Decide which phase to start;
if (number_variables_in_phase_two == variables_to_order.size())
phase = PHASE_TWO;
else
phase = PHASE_ONE;
// Eliminate the variable that has the smallest value for
// the heuristic of interest, until all variables are eliminated.
// As a variable is eliminated, it is removed from all other
// links and all its neighbors are interconnected.
for (i=0; i<variables_to_order.size(); i++) {
// Get the variable with minimum heuristic value.
min_value = -1;
min_index = -1;
number_variables_in_phase = 0;
for (j=0; j<vectors.length; j++) { // Go through all the variables
// Only proceed if variable is to be ordered in this phase.
if ((vectors[j] != null) && (phase_markers[j] == phase)) {
number_variables_in_phase++;
value = obtain_value(vectors[j], ordering_type); // Get the value for the heuristic.
if ((value < min_value) || (min_index == -1)) { // Minimize the heuristic.
min_index = j;
min_value = value;
}
}
}
if ((phase == PHASE_ONE) && (number_variables_in_phase == 1))
phase = PHASE_TWO;
// Add the variable with minimum value for the heuristic
// to the ordering.
pv = bn.get_probability_variable(min_index);
elimination_ordering.addElement(pv);
// Now remove the variable:
// Remove it from every other list of variables
for (j=0; j<vectors.length; j++) { // Go through all lists of variables.
if (vectors[j] != null) // Only proceed is list is non-null.
vectors[j].removeElement(pv); // Now remove the variable from the vector
}
// Interconnect all its neighbors
neighbors = new ProbabilityVariable[ vectors[min_index].size() ];
for (j = 0, e = vectors[min_index].elements(); e.hasMoreElements(); j++) {
pv = (ProbabilityVariable)(e.nextElement());
neighbors[j] = pv;
}
interconnect(bn, vectors, neighbors);
// Erase its list of neighbors.
vectors[min_index] = null;
}
// Return the ordering
String return_ordering[] = new String[ elimination_ordering.size() ];
for (i = 0, e = elimination_ordering.elements(); e.hasMoreElements(); i++) {
pv = (ProbabilityVariable)(e.nextElement());
return_ordering[i] = pv.get_name();
}
return(return_ordering);
}
/*
* Obtain the heuristic value of eliminating a variable,
* represented by the list of variables linked to it.
*/
private long obtain_value(Vector v, int ordering_type) {
ProbabilityVariable pv;
long value = 0;
if (ordering_type == Ordering.MINIMUM_WEIGHT) {
long weight = 1;
for (Enumeration e = v.elements(); e.hasMoreElements(); ) {
pv = (ProbabilityVariable)(e.nextElement());
weight *= pv.number_values();
}
value = weight;
}
return(value);
}
/*
* Interconnect a group of variables; each variable
* connected to all the others.
*/
private void interconnect(BayesNet bn, Vector vectors[],
DiscreteVariable variables_to_be_interconnected[]) {
int i, j;
for (i=0; i<(variables_to_be_interconnected.length - 1); i++) {
for (j=(i+1); j<variables_to_be_interconnected.length; j++) {
interconnect(bn, vectors,
variables_to_be_interconnected[i],
variables_to_be_interconnected[j]);
}
}
}
/*
* Connect two variables.
*/
private void interconnect(BayesNet bn, Vector vectors[],
DiscreteVariable pvi, DiscreteVariable pvj) {
Vector iv = vectors[pvi.get_index()];
Vector jv = vectors[pvj.get_index()];
// Avoid problems if parent is observed or transparent.
if ((iv == null) || (jv == null))
return;
// Now interconnect.
if (!iv.contains(pvj))
iv.addElement(pvj);
if (!jv.contains(pvi))
jv.addElement(pvi);
}
}