Package weka.classifiers.bayes.net

Source Code of weka.classifiers.bayes.net.EditableBayesNet$DeleteArcAction

package weka.classifiers.bayes.net;
/*
* This program 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.
*
* This program is distributed 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
* EditableBayesNet.java
*
*/

import java.io.Serializable;
import java.io.StringReader;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import weka.classifiers.bayes.BayesNet;
import weka.classifiers.bayes.net.estimate.DiscreteEstimatorBayes;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instances;
import weka.core.RevisionUtils;
import weka.core.SerializedObject;
import weka.estimators.Estimator;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.Reorder;


/**
<!-- globalinfo-start -->
* Bayes Network learning using various search algorithms and quality measures.<br/>
* Base class for a Bayes Network classifier. Provides datastructures (network structure, conditional probability distributions, etc.) and facilities common to Bayes Network learning algorithms like K2 and B.<br/>
* <br/>
* For more information see:<br/>
* <br/>
* http://www.cs.waikato.ac.nz/~remco/weka.pdf
* <p/>
<!-- globalinfo-end -->
*
<!-- options-start -->
* Valid options are: <p/>
*
* <pre> -D
*  Do not use ADTree data structure
* </pre>
*
* <pre> -B &lt;BIF file&gt;
*  BIF file to compare with
* </pre>
*
* <pre> -Q weka.classifiers.bayes.net.search.SearchAlgorithm
*  Search algorithm
* </pre>
*
* <pre> -E weka.classifiers.bayes.net.estimate.SimpleEstimator
*  Estimator algorithm
* </pre>
*
<!-- options-end -->
*
* @author Remco Bouckaert (rrb@xm.co.nz)
* @version $Revision: 1.3 $
*/

public class EditableBayesNet extends BayesNet {
  /** for serialization */
  static final long serialVersionUID = 746037443258735954L;

  /** location of nodes, used for graph drawing * */
  protected FastVector m_nPositionX;

  protected FastVector m_nPositionY;

  /** marginal distributions * */
  protected FastVector m_fMarginP;

  /** evidence values, used for evidence propagation * */
  protected FastVector m_nEvidence;

  /** standard constructor * */
  public EditableBayesNet() {
    super();
    m_nEvidence = new FastVector(0);
    m_fMarginP = new FastVector(0);
    m_nPositionX = new FastVector();
    m_nPositionY = new FastVector();
    clearUndoStack();
  } // c'tor

  /** constructor, creates empty network with nodes based on the attributes in a data set */
  public EditableBayesNet(Instances instances) {
    try {
      if (instances.classIndex() < 0) {
        instances.setClassIndex(instances.numAttributes() - 1);
      }
      m_Instances = normalizeDataSet(instances);
    } catch (Exception e) {
      e.printStackTrace();
    }

    int nNodes = getNrOfNodes();
    m_ParentSets = new ParentSet[nNodes];
    for (int i = 0; i < nNodes; i++) {
      m_ParentSets[i] = new ParentSet();
    }
    m_Distributions = new Estimator[nNodes][];
    for (int iNode = 0; iNode < nNodes; iNode++) {
      m_Distributions[iNode] = new Estimator[1];
      m_Distributions[iNode][0] = new DiscreteEstimatorBayes(getCardinality(iNode), 0.5);
    }

    m_nEvidence = new FastVector(nNodes);
    for (int i = 0; i < nNodes; i++) {
      m_nEvidence.addElement(-1);
    }
    m_fMarginP = new FastVector(nNodes);
    for (int i = 0; i < nNodes; i++) {
      double[] P = new double[getCardinality(i)];
      m_fMarginP.addElement(P);
    }

    m_nPositionX = new FastVector(nNodes);
    m_nPositionY = new FastVector(nNodes);
    for (int iNode = 0; iNode < nNodes; iNode++) {
      m_nPositionX.addElement(iNode%10 * 50);
      m_nPositionY.addElement(((int)(iNode/10)) * 50);
    }

  } // c'tor

  /** constructor, copies Bayesian network structure from a Bayesian network
   * encapsulated in a BIFReader
   */
  public EditableBayesNet(BIFReader other) {
    m_Instances = other.m_Instances;
    m_ParentSets = other.getParentSets();
    m_Distributions = other.getDistributions();

    int nNodes = getNrOfNodes();
    m_nPositionX = new FastVector(nNodes);
    m_nPositionY = new FastVector(nNodes);
    for (int i = 0; i < nNodes; i++) {
      m_nPositionX.addElement(other.m_nPositionX[i]);
      m_nPositionY.addElement(other.m_nPositionY[i]);
    }
    m_nEvidence = new FastVector(nNodes);
    for (int i = 0; i < nNodes; i++) {
      m_nEvidence.addElement(-1);
    }
    m_fMarginP = new FastVector(nNodes);
    for (int i = 0; i < nNodes; i++) {
      double[] P = new double[getCardinality(i)];
      m_fMarginP.addElement(P);
    }
    clearUndoStack();
  } // c'tor

  /**
   * constructor that potentially initializes instances as well
   *
   * @param bSetInstances
   *            flag indicating whether to initialize instances or not
   */
  public EditableBayesNet(boolean bSetInstances) {
    super();
    m_nEvidence = new FastVector(0);
    m_fMarginP = new FastVector(0);
    m_nPositionX = new FastVector();
    m_nPositionY = new FastVector();
    clearUndoStack();
    if (bSetInstances) {
      m_Instances = new Instances("New Network", new FastVector(0), 0);
    }
  } // c'tor


  /** Assuming a network structure is defined and we want to learn from data,
   * the data set must be put if correct order first and possibly discretized/missing
   * values filled in before proceeding to CPT learning.
   * @param instances data set to learn from
   * @exception when data sets are not compatible, e.g., a variable is missing
   * or a variable has different nr of values.
   */
  public void setData(Instances instances) throws Exception {
    // sync order of variables
    int [] order = new int [getNrOfNodes()];
    for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
      String sName = getNodeName(iNode);
      int nNode = 0;
      while (nNode < getNrOfNodes() && !sName.equals(instances.attribute(nNode).name())) {
        nNode++;
      }
      if (nNode >= getNrOfNodes()) {
        throw new Exception("Cannot find node named [[[" + sName + "]]] in the data");
      }
      order[iNode] = nNode;
    }
    Reorder reorderFilter = new Reorder();
    reorderFilter.setAttributeIndicesArray(order);
    reorderFilter.setInputFormat(instances);
    instances = Filter.useFilter(instances, reorderFilter);
    // filter using discretization/missing values filter
    Instances newInstances = new Instances(m_Instances, 0);
    if (m_DiscretizeFilter == null && m_MissingValuesFilter == null) {
      newInstances = normalizeDataSet(instances);
    } else {
      for (int iInstance = 0; iInstance < instances.numInstances(); iInstance++) {
        newInstances.add(normalizeInstance(instances.instance(iInstance)));
      }
    }
    //sanity check
    for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
      if (newInstances.attribute(iNode).numValues() != getCardinality(iNode)) {
        throw new Exception("Number of values of node [[[" + getNodeName(iNode) + "]]] differs in (discretized) dataset." );
      }
    }
    // if we got this far, all is ok with the data set and
    // we can replace data set of Bayes net
    m_Instances = newInstances;
  } // setData

  /** returns index of node with given name, or -1 if no such node exists
   * @param sNodeName name of the node to get index for
   */
  public int getNode2(String sNodeName) {
    int iNode = 0;
    while (iNode < m_Instances.numAttributes()) {
      if (m_Instances.attribute(iNode).name().equals(sNodeName)) {
        return iNode;
      }
      iNode++;
    }
    return -1;
  } // getNode2

  /** returns index of node with given name. Throws exception if no such node exists
   * @param sNodeName name of the node to get index for
   */
  public int getNode(String sNodeName) throws Exception {
    int iNode = getNode2(sNodeName);
    if (iNode < 0) {
      throw new Exception("Could not find node [[" + sNodeName + "]]");
    }
    return iNode;
  } // getNode

  /**
   * Add new node to the network, initializing instances, parentsets,
   * distributions. Used for manual manipulation of the Bayesian network.
   *
   * @param sName
   *            name of the node. If the name already exists, an x is appended
   *            to the name
   * @param nCardinality
   *            number of values for this node
   * @throws Exception
   */
  public void addNode(String sName, int nCardinality) throws Exception {
    addNode(sName, nCardinality, 100 + getNrOfNodes() * 10, 100 + getNrOfNodes() * 10);
  } // addNode

  /** Add node to network at a given position, initializing instances, parentsets,
   * distributions. Used for manual manipulation of the Bayesian network.
   *
   * @param sName
   *            name of the node. If the name already exists, an x is appended
   *            to the name
   * @param nCardinality
   *            number of values for this node
   * @param nPosX x-coordiate of the position to place this node
   * @param nPosY y-coordiate of the position to place this node
   * @throws Exception
   */
  public void addNode(String sName, int nCardinality, int nPosX, int nPosY) throws Exception {
    if (getNode2(sName) >= 0) {
      addNode(sName + "x", nCardinality);
      return ;
    }
    // update instances
    FastVector values = new FastVector(nCardinality);
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      values.addElement("Value" + (iValue + 1));
    }
    Attribute att = new Attribute(sName, values);
    m_Instances.insertAttributeAt(att, m_Instances.numAttributes());
    int nAtts = m_Instances.numAttributes();
    // update parentsets
    ParentSet[] parentSets = new ParentSet[nAtts];
    for (int iParentSet = 0; iParentSet < nAtts - 1; iParentSet++) {
      parentSets[iParentSet] = m_ParentSets[iParentSet];
    }
    parentSets[nAtts - 1] = new ParentSet();
    m_ParentSets = parentSets;
    // update distributions
    Estimator[][] distributions = new Estimator[nAtts][];
    for (int iNode = 0; iNode < nAtts - 1; iNode++) {
      distributions[iNode] = m_Distributions[iNode];
    }
    distributions[nAtts - 1] = new Estimator[1];
    distributions[nAtts - 1][0] = new DiscreteEstimatorBayes(nCardinality, 0.5);
    m_Distributions = distributions;
    // update positions
    m_nPositionX.addElement(nPosX);
    m_nPositionY.addElement(nPosY);
    // update evidence & margins
    m_nEvidence.addElement(-1);
    double[] fMarginP = new double[nCardinality];
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      fMarginP[iValue] = 1.0 / nCardinality;
    }
    m_fMarginP.addElement(fMarginP);
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new AddNodeAction(sName, nCardinality, nPosX, nPosY));
    }
  } // addNode

  /**
   * Delete node from the network, updating instances, parentsets,
   * distributions Conditional distributions are condensed by taking the
   * values for the target node to be its first value. Used for manual
   * manipulation of the Bayesian network.
   *
   * @param sName
   *            name of the node. If the name does not exists an exception is
   *            thrown
   * @throws Exception
   */
  public void deleteNode(String sName) throws Exception {
    int nTargetNode = getNode(sName);
    deleteNode(nTargetNode);
  } // deleteNode

  /**
   * Delete node from the network, updating instances, parentsets,
   * distributions Conditional distributions are condensed by taking the
   * values for the target node to be its first value. Used for manual
   * manipulation of the Bayesian network.
   *
   * @param nTargetNode
   *            index of the node to delete.
   * @throws Exception
   */
  public void deleteNode(int nTargetNode) throws Exception {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new DeleteNodeAction(nTargetNode));
    }
    int nAtts = m_Instances.numAttributes() - 1;
    int nTargetCard = m_Instances.attribute(nTargetNode).numValues();
    // update distributions
    Estimator[][] distributions = new Estimator[nAtts][];
    for (int iNode = 0; iNode < nAtts; iNode++) {
      int iNode2 = iNode;
      if (iNode >= nTargetNode) {
        iNode2++;
      }
      Estimator[] distribution = m_Distributions[iNode2];
      if (m_ParentSets[iNode2].contains(nTargetNode)) {
        // condense distribution, use values for targetnode = 0
        int nParentCard = m_ParentSets[iNode2].getCardinalityOfParents();
        nParentCard = nParentCard / nTargetCard;
        Estimator[] distribution2 = new Estimator[nParentCard];
        for (int iParent = 0; iParent < nParentCard; iParent++) {
          distribution2[iParent] = distribution[iParent];
        }
        distribution = distribution2;
      }
      distributions[iNode] = distribution;
    }
    m_Distributions = distributions;
    // update parentsets
    ParentSet[] parentSets = new ParentSet[nAtts];
    for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
      int iParentSet2 = iParentSet;
      if (iParentSet >= nTargetNode) {
        iParentSet2++;
      }
      ParentSet parentset = m_ParentSets[iParentSet2];
      parentset.deleteParent(nTargetNode, m_Instances);
      for (int iParent = 0; iParent < parentset.getNrOfParents(); iParent++) {
        int nParent = parentset.getParent(iParent);
        if (nParent > nTargetNode) {
          parentset.SetParent(iParent, nParent - 1);
        }
      }
      parentSets[iParentSet] = parentset;
    }
    m_ParentSets = parentSets;
    // update instances
    m_Instances.setClassIndex(-1);
    m_Instances.deleteAttributeAt(nTargetNode);
    m_Instances.setClassIndex(nAtts - 1);

    // update positions
    m_nPositionX.removeElementAt(nTargetNode);
    m_nPositionY.removeElementAt(nTargetNode);
    // update evidence & margins
    m_nEvidence.removeElementAt(nTargetNode);
    m_fMarginP.removeElementAt(nTargetNode);
  } // deleteNode

  /**
   * Delete nodes with indexes in selection from the network, updating instances, parentsets,
   * distributions Conditional distributions are condensed by taking the
   * values for the target node to be its first value. Used for manual
   * manipulation of the Bayesian network.
   *
   * @param nodes
   *            array of indexes of nodes to delete.
   * @throws Exception
   */
  public void deleteSelection(FastVector nodes) {
    // sort before proceeding
    for (int i = 0; i < nodes.size(); i++) {
      for (int j = i + 1; j < nodes.size(); j++) {
        if ((Integer) nodes.elementAt(i) > (Integer) nodes.elementAt(j)) {
          int h = (Integer) nodes.elementAt(i);
          nodes.setElementAt(nodes.elementAt(j), i);
          nodes.setElementAt(h, j);
        }
      }
    }
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new DeleteSelectionAction(nodes));
    }
    boolean bNeedsUndoAction = m_bNeedsUndoAction;
    m_bNeedsUndoAction = false;
    try {
      for (int iNode = nodes.size() - 1; iNode >= 0; iNode--) {
        deleteNode((Integer) nodes.elementAt(iNode));
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    m_bNeedsUndoAction = bNeedsUndoAction;
  } // deleteSelection

  /** XML helper function for selecting elements under a node with a given name
   * @param item XMLNode to select items from
   * @param sElement name of the element to return
   */
  FastVector selectElements(Node item, String sElement) throws Exception {
    NodeList children = item.getChildNodes();
    FastVector nodelist = new FastVector();
    for (int iNode = 0; iNode < children.getLength(); iNode++) {
      Node node = children.item(iNode);
      if ((node.getNodeType() == Node.ELEMENT_NODE) && node.getNodeName().equals(sElement)) {
        nodelist.addElement(node);
      }
    }
    return nodelist;
  } // selectElements

  /**
   * XML helper function. Returns all TEXT children of the given node in one string. Between the
   * node values new lines are inserted.
   *
   * @param node
   *            the node to return the content for
   * @return the content of the node
   */
  public String getContent(Element node) {
    NodeList list;
    Node item;
    int i;
    String result;

    result = "";
    list = node.getChildNodes();

    for (i = 0; i < list.getLength(); i++) {
      item = list.item(i);
      if (item.getNodeType() == Node.TEXT_NODE)
        result += "\n" + item.getNodeValue();
    }

    return result;
  }

  /** XML helper function that returns DEFINITION element from a XMLBIF document
   * for a node with a given name.
   * @param doc XMLBIF document
   * @param sName name of the node to get the definition for
   */
  Element getDefinition(Document doc, String sName) throws Exception {
    NodeList nodelist = doc.getElementsByTagName("DEFINITION");
    for (int iNode = 0; iNode < nodelist.getLength(); iNode++) {
      Node node = nodelist.item(iNode);
      FastVector list = selectElements(node, "FOR");
      if (list.size() > 0) {
        Node forNode = (Node) list.elementAt(0);
        if (getContent((Element) forNode).trim().equals(sName)) {
          return (Element) node;
        }
      }
    }
    throw new Exception("Could not find definition for ((" + sName + "))");
  } // getDefinition


  /** Paste modes. This allows for verifying that a past action does not cause
   * any problems before actually performing the paste operation.
   */
  final static int TEST = 0;
  final static int EXECUTE = 1;

  /** Apply paste operation with XMLBIF fragment. This adds nodes in the XMLBIF fragment
   * to the network, together with its parents. First, paste in test mode to verify
   * no problems occur, then execute paste operation. If a problem occurs (e.g. parent
   * does not exist) then a exception is thrown.
   * @param sXML XMLBIF fragment to paste into the network
   */
  public void paste(String sXML) throws Exception {
    try {
      paste(sXML, TEST);
    } catch (Exception e) {
      throw e;
    }
    paste(sXML, EXECUTE);
  } // paste

  /** Apply paste operation with XMLBIF fragment. Depending on the paste mode, the
   * nodes are actually added to the network or it is just tested that the nodes can
   * be added to the network.
   * @param sXML XMLBIF fragment to paste into the network
   * @param mode paste mode TEST or EXECUTE
   */
  void paste(String sXML, int mode) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setValidating(true);
    Document doc = factory.newDocumentBuilder().parse(new org.xml.sax.InputSource(new StringReader(sXML)));
    doc.normalize();

    // create nodes first
    NodeList nodelist = doc.getElementsByTagName("VARIABLE");
    FastVector sBaseNames = new FastVector();
    Instances instances = new Instances(m_Instances, 0);
    int nBase = instances.numAttributes();
    for (int iNode = 0; iNode < nodelist.getLength(); iNode++) {
      // Get element
      FastVector valueslist;
      // Get the name of the node
      valueslist = selectElements(nodelist.item(iNode), "OUTCOME");

      int nValues = valueslist.size();
      // generate value strings
      FastVector nomStrings = new FastVector(nValues + 1);
      for (int iValue = 0; iValue < nValues; iValue++) {
        Node node = ((Node) valueslist.elementAt(iValue)).getFirstChild();
        String sValue = ((CharacterData) (node)).getData();
        if (sValue == null) {
          sValue = "Value" + (iValue + 1);
        }
        nomStrings.addElement(sValue);
      }
      FastVector nodelist2;
      // Get the name of the network
      nodelist2 = selectElements(nodelist.item(iNode), "NAME");
      if (nodelist2.size() == 0) {
        throw new Exception("No name specified for variable");
      }
      String sBaseName = ((CharacterData) (((Node) nodelist2.elementAt(0)).getFirstChild())).getData();
      sBaseNames.addElement(sBaseName);
      String sNodeName = sBaseName;
      if (getNode2(sNodeName) >= 0) {
        sNodeName = "Copy of " + sBaseName;
      }
      int iAttempt = 2;
      while (getNode2(sNodeName) >= 0) {
        sNodeName = "Copy (" + iAttempt + ") of " + sBaseName;
        iAttempt++;
      }

      Attribute att = new Attribute(sNodeName, nomStrings);
      instances.insertAttributeAt(att, instances.numAttributes());

      valueslist = selectElements(nodelist.item(iNode), "PROPERTY");
      nValues = valueslist.size();
      // generate value strings
      int nPosX = iAttempt * 10;
      int nPosY = iAttempt * 10;
      for (int iValue = 0; iValue < nValues; iValue++) {
        // parsing for strings of the form "position = (73, 165)"
        Node node = ((Node) valueslist.elementAt(iValue)).getFirstChild();
        String sValue = ((CharacterData) (node)).getData();
        if (sValue.startsWith("position")) {
          int i0 = sValue.indexOf('(');
          int i1 = sValue.indexOf(',');
          int i2 = sValue.indexOf(')');
          String sX = sValue.substring(i0 + 1, i1).trim();
          String sY = sValue.substring(i1 + 1, i2).trim();
          try {
            nPosX = (Integer.parseInt(sX) + iAttempt * 10);
            nPosY = (Integer.parseInt(sY) + iAttempt * 10);
          } catch (NumberFormatException e) {
            System.err.println("Wrong number format in position :(" + sX + "," + sY + ")");
          }
        }
      }
      if (mode == EXECUTE) {
        m_nPositionX.addElement(nPosX);
        m_nPositionY.addElement(nPosY);
      }

    }

    FastVector nodelist2;
    Estimator[][] distributions = new Estimator[nBase + sBaseNames.size()][];
    ParentSet[] parentsets = new ParentSet[nBase + sBaseNames.size()];
    for (int iNode = 0; iNode < nBase; iNode++) {
      distributions[iNode] = m_Distributions[iNode];
      parentsets[iNode] = m_ParentSets[iNode];
    }
    if (mode == EXECUTE) {
      m_Instances = instances;
    }
    // create arrows & create distributions
    for (int iNode = 0; iNode < sBaseNames.size(); iNode++) {
      // find definition that goes with this node
      String sName = (String) sBaseNames.elementAt(iNode);
      Element definition = getDefinition(doc, sName);
      parentsets[nBase + iNode] = new ParentSet();

      // get the parents for this node
      // resolve structure
      nodelist2 = selectElements(definition, "GIVEN");
      for (int iParent = 0; iParent < nodelist2.size(); iParent++) {
        Node parentName = ((Node) nodelist2.elementAt(iParent)).getFirstChild();
        String sParentName = ((CharacterData) (parentName)).getData();
        int nParent = -1;
        for (int iBase = 0; iBase < sBaseNames.size(); iBase++) {
          if (sParentName.equals((String) sBaseNames.elementAt(iBase))) {
            nParent = nBase + iBase;
          }
        }
        if (nParent < 0) {
          nParent = getNode(sParentName);
        }
        parentsets[nBase + iNode].addParent(nParent, instances);
      }
      // resolve conditional probability table
      int nCardinality = parentsets[nBase + iNode].getCardinalityOfParents();
      int nValues = instances.attribute(nBase + iNode).numValues();
      distributions[nBase + iNode] = new Estimator[nCardinality];
      for (int i = 0; i < nCardinality; i++) {
        distributions[nBase + iNode][i] = new DiscreteEstimatorBayes(nValues, 0.0f);
      }

      String sTable = getContent((Element) selectElements(definition, "TABLE").elementAt(0));
      sTable = sTable.replaceAll("\\n", " ");
      StringTokenizer st = new StringTokenizer(sTable.toString());

      for (int i = 0; i < nCardinality; i++) {
        DiscreteEstimatorBayes d = (DiscreteEstimatorBayes) distributions[nBase + iNode][i];
        for (int iValue = 0; iValue < nValues; iValue++) {
          String sWeight = st.nextToken();
          d.addValue(iValue, new Double(sWeight).doubleValue());
        }
      }
      if (mode == EXECUTE) {
        m_nEvidence.insertElementAt(-1, nBase + iNode);
        m_fMarginP.insertElementAt(new double[getCardinality(nBase + iNode)], nBase + iNode);
      }
    }
    if (mode == EXECUTE) {
      m_Distributions = distributions;
      m_ParentSets = parentsets;
    }
    // update undo stack
    if (mode == EXECUTE && m_bNeedsUndoAction) {
      addUndoAction(new PasteAction(sXML, nBase));
    }
  } // paste

  /**
   * Add arc between two nodes Distributions are updated by duplication for
   * every value of the parent node.
   *
   * @param sParent
   *            name of the parent node
   * @param sChild
   *            name of the child node
   * @throws Exception
   *             if parent or child cannot be found in network
   */
  public void addArc(String sParent, String sChild) throws Exception {
    int nParent = getNode(sParent);
    int nChild = getNode(sChild);
    addArc(nParent, nChild);
  } // addArc

  /**
   * Add arc between two nodes Distributions are updated by duplication for
   * every value of the parent node.
   *
   * @param nParent
   *            index of the parent node
   * @param nChild
   *            index of the child node
   * @throws Exception
   */
  public void addArc(int nParent, int nChild) throws Exception {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new AddArcAction(nParent, nChild));
    }
    int nOldCard = m_ParentSets[nChild].getCardinalityOfParents();
    // update parentsets
    m_ParentSets[nChild].addParent(nParent, m_Instances);
    // update distributions
    int nNewCard = m_ParentSets[nChild].getCardinalityOfParents();
    Estimator[] ds = new Estimator[nNewCard];
    for (int iParent = 0; iParent < nNewCard; iParent++) {
      ds[iParent] = Estimator.clone(m_Distributions[nChild][iParent % nOldCard]);
    }
    m_Distributions[nChild] = ds;
  } // addArc

  /**
   * Add arc between parent node and each of the nodes in a given list.
   * Distributions are updated as above.
   *
   * @param sParent
   *            name of the parent node
   * @param nodes
   *            array of indexes of child nodes
   * @throws Exception
   */
  public void addArc(String sParent, FastVector nodes) throws Exception {
    int nParent = getNode(sParent);
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new AddArcAction(nParent, nodes));
    }
    boolean bNeedsUndoAction = m_bNeedsUndoAction;
    m_bNeedsUndoAction = false;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      addArc(nParent, nNode);
    }
    m_bNeedsUndoAction = bNeedsUndoAction;
  } // addArc

  /**
   * Delete arc between two nodes. Distributions are updated by condensing for
   * the parent node taking its first value.
   *
   * @param sParent
   *            name of the parent node
   * @param sChild
   *            name of the child node
   * @throws Exception
   *             if parent or child cannot be found in network
   */
  public void deleteArc(String sParent, String sChild) throws Exception {
    int nParent = getNode(sParent);
    int nChild = getNode(sChild);
    deleteArc(nParent, nChild);
  } // deleteArc

  /**
   * Delete arc between two nodes. Distributions are updated by condensing for
   * the parent node taking its first value.
   *
   * @param nParent
   *            index of the parent node
   * @param nChild
   *            index of the child node
   * @throws Exception
   */
  public void deleteArc(int nParent, int nChild) throws Exception {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new DeleteArcAction(nParent, nChild));
    }
    // update distributions
    // condense distribution, use values for targetnode = 0
    int nParentCard = m_ParentSets[nChild].getCardinalityOfParents();
    int nTargetCard = m_Instances.attribute(nChild).numValues();
    nParentCard = nParentCard / nTargetCard;
    Estimator[] distribution2 = new Estimator[nParentCard];
    for (int iParent = 0; iParent < nParentCard; iParent++) {
      distribution2[iParent] = m_Distributions[nChild][iParent];
    }
    m_Distributions[nChild] = distribution2;
    // update parentsets
    m_ParentSets[nChild].deleteParent(nParent, m_Instances);
  } // deleteArc


  /** specify distribution of a node
   * @param sName name of the node to specify distribution for
   * @param P matrix representing distribution with P[i][j] = P(node = j | parent configuration = i)
   * @throws Exception
   *             if parent or child cannot be found in network
   */
  public void setDistribution(String sName, double[][] P) throws Exception {
    int nTargetNode = getNode(sName);
    setDistribution(nTargetNode, P);
  } // setDistribution

  /** specify distribution of a node
   * @param nTargetNode index of the node to specify distribution for
   * @param P matrix representing distribution with P[i][j] = P(node = j | parent configuration = i)
   * @throws Exception
   *             if parent or child cannot be found in network
   */
  public void setDistribution(int nTargetNode, double[][] P) throws Exception {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new SetDistributionAction(nTargetNode, P));
    }
    Estimator[] distributions = m_Distributions[nTargetNode];
    for (int iParent = 0; iParent < distributions.length; iParent++) {
      DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(P[0].length, 0);
      for (int iValue = 0; iValue < distribution.getNumSymbols(); iValue++) {
        distribution.addValue(iValue, P[iParent][iValue]);
      }
      distributions[iParent] = distribution;
    }
    // m_Distributions[nTargetNode] = distributions;
  } // setDistribution

  /** returns distribution of a node in matrix form with matrix representing distribution
   * with P[i][j] = P(node = j | parent configuration = i)
   * @param sName name of the node to get distribution from
   */
  public double[][] getDistribution(String sName) {
    int nTargetNode = getNode2(sName);
    return getDistribution(nTargetNode);
  } // getDistribution

  /** returns distribution of a node in matrix form with matrix representing distribution
   * with P[i][j] = P(node = j | parent configuration = i)
   * @param nTargetNode index of the node to get distribution from
   */
  public double[][] getDistribution(int nTargetNode) {
    int nParentCard = m_ParentSets[nTargetNode].getCardinalityOfParents();
    int nCard = m_Instances.attribute(nTargetNode).numValues();
    double[][] P = new double[nParentCard][nCard];
    for (int iParent = 0; iParent < nParentCard; iParent++) {
      for (int iValue = 0; iValue < nCard; iValue++) {
        P[iParent][iValue] = m_Distributions[nTargetNode][iParent].getProbability(iValue);
      }
    }
    return P;
  } // getDistribution

  /** returns array of values of a node
   * @param sName name of the node to get values from
   */
  public String[] getValues(String sName) {
    int nTargetNode = getNode2(sName);
    return getValues(nTargetNode);
  } // getValues

  /** returns array of values of a node
   * @param nTargetNode index of the node to get values from
   */
  public String[] getValues(int nTargetNode) {
    String[] values = new String[getCardinality(nTargetNode)];
    for (int iValue = 0; iValue < values.length; iValue++) {
      values[iValue] = m_Instances.attribute(nTargetNode).value(iValue);
    }
    return values;
  } // getValues

  /** returns value of a node
   * @param nTargetNode index of the node to get values from
   * @param iValue index of the value
   */
  public String getValueName(int nTargetNode, int iValue) {
    return m_Instances.attribute(nTargetNode).value(iValue);
  } // getNodeValue

  /** change the name of a node
   * @param nTargetNode index of the node to set name for
   * @param sName new name to assign
   */
  public void setNodeName(int nTargetNode, String sName) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new RenameAction(nTargetNode, getNodeName(nTargetNode), sName));
    }
    Attribute att = m_Instances.attribute(nTargetNode);
    int nCardinality = att.numValues();
    FastVector values = new FastVector(nCardinality);
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      values.addElement(att.value(iValue));
    }
    replaceAtt(nTargetNode, sName, values);
  } // setNodeName

  /** change the name of a value of a node
   * @param nTargetNode index of the node to set name for
   * @param sValue current name of the value
   * @param sNewValue new name of the value
   */
  public void renameNodeValue(int nTargetNode, String sValue, String sNewValue) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new RenameValueAction(nTargetNode, sValue, sNewValue));
    }
    Attribute att = m_Instances.attribute(nTargetNode);
    int nCardinality = att.numValues();
    FastVector values = new FastVector(nCardinality);
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      if (att.value(iValue).equals(sValue)) {
        values.addElement(sNewValue);
      } else {
        values.addElement(att.value(iValue));
      }
    }
    replaceAtt(nTargetNode, att.name(), values);
  } // renameNodeValue


  /** Add node value to a node. Distributions for the node assign zero probability
   * to the new value. Child nodes duplicate CPT conditioned on the new value.
   * @param nTargetNode index of the node to add value for
   * @param sNewValue name of the value
   */
  public void addNodeValue(int nTargetNode, String sNewValue) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new AddValueAction(nTargetNode, sNewValue));
    }
    Attribute att = m_Instances.attribute(nTargetNode);
    int nCardinality = att.numValues();
    FastVector values = new FastVector(nCardinality);
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      values.addElement(att.value(iValue));
    }
    values.addElement(sNewValue);
    replaceAtt(nTargetNode, att.name(), values);

    // update distributions of this node
    Estimator[] distributions = m_Distributions[nTargetNode];
    int nNewCard = values.size();
    for (int iParent = 0; iParent < distributions.length; iParent++) {
      DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nNewCard, 0);
      for (int iValue = 0; iValue < nNewCard - 1; iValue++) {
        distribution.addValue(iValue, distributions[iParent].getProbability(iValue));
      }
      distributions[iParent] = distribution;
    }

    // update distributions of all children
    for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
      if (m_ParentSets[iNode].contains(nTargetNode)) {
        distributions = m_Distributions[iNode];
        ParentSet parentSet = m_ParentSets[iNode];
        int nParentCard = parentSet.getFreshCardinalityOfParents(m_Instances);
        Estimator[] newDistributions = new Estimator[nParentCard];
        int nCard = getCardinality(iNode);
        int nParents = parentSet.getNrOfParents();
        int[] values2 = new int[nParents];
        int iOldPos = 0;
        int iTargetNode = 0;
        while (parentSet.getParent(iTargetNode) != nTargetNode) {
          iTargetNode++;
        }
        for (int iPos = 0; iPos < nParentCard; iPos++) {
          DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nCard, 0);
          for (int iValue = 0; iValue < nCard; iValue++) {
            distribution.addValue(iValue, distributions[iOldPos].getProbability(iValue));
          }
          newDistributions[iPos] = distribution;
          // update values
          int i = 0;
          values2[i]++;
          while (i < nParents && values2[i] == getCardinality(parentSet.getParent(i))) {
            values2[i] = 0;
            i++;
            if (i < nParents) {
              values2[i]++;
            }
          }
          if (values2[iTargetNode] != nNewCard - 1) {
            iOldPos++;
          }
        }
        m_Distributions[iNode] = newDistributions;
      }
    }
  } // addNodeValue


  /** Delete node value from a node. Distributions for the node are scaled
   * up proportional to existing distribution
   * (or made uniform if zero probability is assigned to remainder of values).
  .* Child nodes delete CPTs conditioned on the new value.
   * @param nTargetNode index of the node to delete value from
   * @param sValue name of the value to delete
   */
  public void delNodeValue(int nTargetNode, String sValue) throws Exception {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new DelValueAction(nTargetNode, sValue));
    }
    Attribute att = m_Instances.attribute(nTargetNode);
    int nCardinality = att.numValues();
    FastVector values = new FastVector(nCardinality);
    int nValue = -1;
    for (int iValue = 0; iValue < nCardinality; iValue++) {
      if (att.value(iValue).equals(sValue)) {
        nValue = iValue;
      } else {
        values.addElement(att.value(iValue));
      }
    }
    if (nValue < 0) {
      // could not find value
      throw new Exception("Node " + nTargetNode + " does not have value (" + sValue + ")");
    }
    replaceAtt(nTargetNode, att.name(), values);

    // update distributions
    Estimator[] distributions = m_Distributions[nTargetNode];
    int nCard = values.size();
    for (int iParent = 0; iParent < distributions.length; iParent++) {
      DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nCard, 0);
      double sum = 0;
      for (int iValue = 0; iValue < nCard; iValue++) {
        sum += distributions[iParent].getProbability(iValue);
      }
      if (sum > 0) {
        for (int iValue = 0; iValue < nCard; iValue++) {
          distribution.addValue(iValue, distributions[iParent].getProbability(iValue) / sum);
        }
      } else {
        for (int iValue = 0; iValue < nCard; iValue++) {
          distribution.addValue(iValue, 1.0 / nCard);
        }
      }
      distributions[iParent] = distribution;
    }

    // update distributions of all children
    for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
      if (m_ParentSets[iNode].contains(nTargetNode)) {
        ParentSet parentSet = m_ParentSets[iNode];
        distributions = m_Distributions[iNode];
        Estimator[] newDistributions = new Estimator[distributions.length * nCard / (nCard + 1)];
        int iCurrentDist = 0;

        int nParents = parentSet.getNrOfParents();
        int[] values2 = new int[nParents];
        // fill in the values
        int nParentCard = parentSet.getFreshCardinalityOfParents(m_Instances) * (nCard + 1) / nCard;
        int iTargetNode = 0;
        while (parentSet.getParent(iTargetNode) != nTargetNode) {
          iTargetNode++;
        }
        int[] nCards = new int[nParents];
        for (int iParent = 0; iParent < nParents; iParent++) {
          nCards[iParent] = getCardinality(parentSet.getParent(iParent));
        }
        nCards[iTargetNode]++;
        for (int iPos = 0; iPos < nParentCard; iPos++) {
          if (values2[iTargetNode] != nValue) {
            newDistributions[iCurrentDist++] = distributions[iPos];
          }
          // update values
          int i = 0;
          values2[i]++;
          while (i < nParents && values2[i] == nCards[i]) {
            values2[i] = 0;
            i++;
            if (i < nParents) {
              values2[i]++;
            }
          }
        }

        m_Distributions[iNode] = newDistributions;
      }
    }
    // update evidence
    if (getEvidence(nTargetNode) > nValue) {
      setEvidence(nTargetNode, getEvidence(nTargetNode) - 1);
    }
  } // delNodeValue

  /** set position of node
   * @param iNode index of node to set position for
   * @param nX x position of new position
   * @param nY y position of new position
   */
  public void setPosition(int iNode, int nX, int nY) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      boolean isUpdate = false;
      UndoAction undoAction = null;
      try {
        if (m_undoStack.size() > 0) {
          undoAction = (UndoAction) m_undoStack.elementAt(m_undoStack.size() - 1);
          SetPositionAction posAction = (SetPositionAction) undoAction;
          if (posAction.m_nTargetNode == iNode) {
            isUpdate = true;
            posAction.setUndoPosition(nX, nY);
          }
        }
      } catch (Exception e) {
        // ignore. it's not a SetPositionAction
      }
      if (!isUpdate) {
        addUndoAction(new SetPositionAction(iNode, nX, nY));
      }
    }
    m_nPositionX.setElementAt(nX, iNode);
    m_nPositionY.setElementAt(nY, iNode);
  } // setPosition

  /** Set position of node. Move set of nodes with the same displacement
   * as a specified node.
   * @param iNode index of node to set position for
   * @param nX x position of new position
   * @param nY y position of new position
   * @param nodes array of indexes of nodes to move
   */
  public void setPosition(int nNode, int nX, int nY, FastVector nodes) {
    int dX = nX - getPositionX(nNode);
    int dY = nY - getPositionY(nNode);
    // update undo stack
    if (m_bNeedsUndoAction) {
      boolean isUpdate = false;
      try {
        UndoAction undoAction = null;
        if (m_undoStack.size() > 0) {
          undoAction = (UndoAction) m_undoStack.elementAt(m_undoStack.size() - 1);
            SetGroupPositionAction posAction = (SetGroupPositionAction) undoAction;
            isUpdate = true;
            int iNode = 0;
            while (isUpdate && iNode < posAction.m_nodes.size()) {
              if ((Integer)posAction.m_nodes.elementAt(iNode) != (Integer) nodes.elementAt(iNode)) {
                isUpdate = false;
              }
              iNode++;
            }
            if (isUpdate == true) {
              posAction.setUndoPosition(dX, dY);
            }
        }
      } catch (Exception e) {
        // ignore. it's not a SetPositionAction
      }
      if (!isUpdate) {
        addUndoAction(new SetGroupPositionAction(nodes, dX, dY));
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionX.setElementAt(getPositionX(nNode) + dX, nNode);
      m_nPositionY.setElementAt(getPositionY(nNode) + dY, nNode);
    }
  } // setPosition

  /** set positions of all nodes
   * @param nPosX new x positions for all nodes
   * @param nPosY new y positions for all nodes
   */
  public void layoutGraph(FastVector nPosX, FastVector nPosY) {
    if (m_bNeedsUndoAction) {
      addUndoAction(new LayoutGraphAction(nPosX, nPosY));
    }
    m_nPositionX = nPosX;
    m_nPositionY = nPosY;
  } // layoutGraph

  /** get x position of a node
   * @param iNode index of node of interest
   */
  public int getPositionX(int iNode) {
    return (Integer) (m_nPositionX.elementAt(iNode));
  }

  /** get y position of a node
   * @param iNode index of node of interest
   */
  public int getPositionY(int iNode) {
    return (Integer) (m_nPositionY.elementAt(iNode));
  }

  /** align set of nodes with the left most node in the list
   * @param nodes list of indexes of nodes to align
   */
  public void alignLeft(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new alignLeftAction(nodes));
    }
    int nMinX = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nX = getPositionX((Integer) nodes.elementAt(iNode));
      if (nX < nMinX || iNode == 0) {
        nMinX = nX;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionX.setElementAt(nMinX, nNode);
    }
  } // alignLeft

  /** align set of nodes with the right most node in the list
   * @param nodes list of indexes of nodes to align
   */
  public void alignRight(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new alignRightAction(nodes));
    }
    int nMaxX = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nX = getPositionX((Integer) nodes.elementAt(iNode));
      if (nX > nMaxX || iNode == 0) {
        nMaxX = nX;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionX.setElementAt(nMaxX, nNode);
    }
  } // alignRight

  /** align set of nodes with the top most node in the list
   * @param nodes list of indexes of nodes to align
   */
  public void alignTop(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new alignTopAction(nodes));
    }
    int nMinY = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nY = getPositionY((Integer) nodes.elementAt(iNode));
      if (nY < nMinY || iNode == 0) {
        nMinY = nY;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionY.setElementAt(nMinY, nNode);
    }
  } // alignTop

  /** align set of nodes with the bottom most node in the list
   * @param nodes list of indexes of nodes to align
   */
  public void alignBottom(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new alignBottomAction(nodes));
    }
    int nMaxY = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nY = getPositionY((Integer) nodes.elementAt(iNode));
      if (nY > nMaxY || iNode == 0) {
        nMaxY = nY;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionY.setElementAt(nMaxY, nNode);
    }
  } // alignBottom

  /** center set of nodes half way between left and right most node in the list
   * @param nodes list of indexes of nodes to center
   */
  public void centerHorizontal(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new centerHorizontalAction(nodes));
    }
    int nMinY = -1;
    int nMaxY = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nY = getPositionY((Integer) nodes.elementAt(iNode));
      if (nY < nMinY || iNode == 0) {
        nMinY = nY;
      }
      if (nY > nMaxY || iNode == 0) {
        nMaxY = nY;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionY.setElementAt((nMinY + nMaxY) / 2, nNode);
    }
  } // centerHorizontal

  /** center set of nodes half way between top and bottom most node in the list
   * @param nodes list of indexes of nodes to center
   */
  public void centerVertical(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new centerVerticalAction(nodes));
    }
    int nMinX = -1;
    int nMaxX = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nX = getPositionX((Integer) nodes.elementAt(iNode));
      if (nX < nMinX || iNode == 0) {
        nMinX = nX;
      }
      if (nX > nMaxX || iNode == 0) {
        nMaxX = nX;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionX.setElementAt((nMinX + nMaxX) / 2, nNode);
    }
  } // centerVertical

  /** space out set of nodes evenly between left and right most node in the list
   * @param nodes list of indexes of nodes to space out
   */
  public void spaceHorizontal(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new spaceHorizontalAction(nodes));
    }
    int nMinX = -1;
    int nMaxX = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nX = getPositionX((Integer) nodes.elementAt(iNode));
      if (nX < nMinX || iNode == 0) {
        nMinX = nX;
      }
      if (nX > nMaxX || iNode == 0) {
        nMaxX = nX;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionX.setElementAt((int) (nMinX + iNode * (nMaxX - nMinX) / (nodes.size() - 1.0)), nNode);
    }
  } // spaceHorizontal

  /** space out set of nodes evenly between top and bottom most node in the list
   * @param nodes list of indexes of nodes to space out
   */
  public void spaceVertical(FastVector nodes) {
    // update undo stack
    if (m_bNeedsUndoAction) {
      addUndoAction(new spaceVerticalAction(nodes));
    }
    int nMinY = -1;
    int nMaxY = -1;
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nY = getPositionY((Integer) nodes.elementAt(iNode));
      if (nY < nMinY || iNode == 0) {
        nMinY = nY;
      }
      if (nY > nMaxY || iNode == 0) {
        nMaxY = nY;
      }
    }
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      m_nPositionY.setElementAt((int) (nMinY + iNode * (nMaxY - nMinY) / (nodes.size() - 1.0)), nNode);
    }
  } // spaceVertical


  /** replace attribute with specified name and values
   * @param nTargetNode index of node the replace specification for
   * @param sName new name of the node
   * @param values array of values of the node
   */
  void replaceAtt(int nTargetNode, String sName, FastVector values) {
    Attribute newAtt = new Attribute(sName, values);
    if (m_Instances.classIndex() == nTargetNode) {
      m_Instances.setClassIndex(-1);
      m_Instances.insertAttributeAt(newAtt, nTargetNode);
      m_Instances.deleteAttributeAt(nTargetNode + 1);
      m_Instances.setClassIndex(nTargetNode);
    } else {
      m_Instances.insertAttributeAt(newAtt, nTargetNode);
      m_Instances.deleteAttributeAt(nTargetNode + 1);
    }
  } // replaceAtt

  /** return marginal distibution for a node
   * @param iNode index of node of interest
   */
  public double[] getMargin(int iNode) {
    return (double[]) m_fMarginP.elementAt(iNode);
  };

  /** set marginal distibution for a node
   * @param iNode index of node to set marginal distribution for
   * @param fMarginP marginal distribution
   */
  public void setMargin(int iNode, double[] fMarginP) {
    m_fMarginP.setElementAt(fMarginP, iNode);
  }

  /** get evidence state of a node. -1 represents no evidence set, otherwise
   * the index of a value of the node
   * @param iNode index of node of interest
   */
  public int getEvidence(int iNode) {
    return (Integer) m_nEvidence.elementAt(iNode);
  }

  /** set evidence state of a node. -1 represents no evidence set, otherwise
   * the index of a value of the node
    * @param iNode index of node of interest
   * @param iValue evidence value to set
   */
  public void setEvidence(int iNode, int iValue) {
    m_nEvidence.setElementAt(iValue, iNode);
  }

  /** return list of children of a node
   * @param iNode index of node of interest
   */
  public FastVector getChildren(int nTargetNode) {
    FastVector children = new FastVector();
    for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
      if (m_ParentSets[iNode].contains(nTargetNode)) {
        children.addElement(iNode);
      }
    }
    return children;
  } // getChildren

  /** returns network in XMLBIF format
  */
  public String toXMLBIF03() {
    if (m_Instances == null) {
      return ("<!--No model built yet-->");
    }

    StringBuffer text = new StringBuffer();
    text.append(getBIFHeader());
    text.append("\n");
    text.append("\n");
    text.append("<BIF VERSION=\"0.3\">\n");
    text.append("<NETWORK>\n");
    text.append("<NAME>" + XMLNormalize(m_Instances.relationName()) + "</NAME>\n");
    for (int iAttribute = 0; iAttribute < m_Instances.numAttributes(); iAttribute++) {
      text.append("<VARIABLE TYPE=\"nature\">\n");
      text.append("<NAME>" + XMLNormalize(m_Instances.attribute(iAttribute).name()) + "</NAME>\n");
      for (int iValue = 0; iValue < m_Instances.attribute(iAttribute).numValues(); iValue++) {
        text.append("<OUTCOME>" + XMLNormalize(m_Instances.attribute(iAttribute).value(iValue))
            + "</OUTCOME>\n");
      }
      text.append("<PROPERTY>position = (" + getPositionX(iAttribute) + "," + getPositionY(iAttribute)
          + ")</PROPERTY>\n");
      text.append("</VARIABLE>\n");
    }

    for (int iAttribute = 0; iAttribute < m_Instances.numAttributes(); iAttribute++) {
      text.append("<DEFINITION>\n");
      text.append("<FOR>" + XMLNormalize(m_Instances.attribute(iAttribute).name()) + "</FOR>\n");
      for (int iParent = 0; iParent < m_ParentSets[iAttribute].getNrOfParents(); iParent++) {
        text.append("<GIVEN>"
            + XMLNormalize(m_Instances.attribute(m_ParentSets[iAttribute].getParent(iParent)).name())
            + "</GIVEN>\n");
      }
      text.append("<TABLE>\n");
      for (int iParent = 0; iParent < m_ParentSets[iAttribute].getCardinalityOfParents(); iParent++) {
        for (int iValue = 0; iValue < m_Instances.attribute(iAttribute).numValues(); iValue++) {
          text.append(m_Distributions[iAttribute][iParent].getProbability(iValue));
          text.append(' ');
        }
        text.append('\n');
      }
      text.append("</TABLE>\n");
      text.append("</DEFINITION>\n");
    }
    text.append("</NETWORK>\n");
    text.append("</BIF>\n");
    return text.toString();
  } // toXMLBIF03

  /** return fragment of network in XMLBIF format
   * @param nodes array of indexes of nodes that should be in the fragment
   */
  public String toXMLBIF03(FastVector nodes) {
    StringBuffer text = new StringBuffer();
    text.append(getBIFHeader());
    text.append("\n");
    text.append("\n");
    text.append("<BIF VERSION=\"0.3\">\n");
    text.append("<NETWORK>\n");
    text.append("<NAME>" + XMLNormalize(m_Instances.relationName()) + "</NAME>\n");
    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      text.append("<VARIABLE TYPE=\"nature\">\n");
      text.append("<NAME>" + XMLNormalize(m_Instances.attribute(nNode).name()) + "</NAME>\n");
      for (int iValue = 0; iValue < m_Instances.attribute(nNode).numValues(); iValue++) {
        text.append("<OUTCOME>" + XMLNormalize(m_Instances.attribute(nNode).value(iValue)) + "</OUTCOME>\n");
      }
      text.append("<PROPERTY>position = (" + getPositionX(nNode) + "," + getPositionY(nNode) + ")</PROPERTY>\n");
      text.append("</VARIABLE>\n");
    }

    for (int iNode = 0; iNode < nodes.size(); iNode++) {
      int nNode = (Integer) nodes.elementAt(iNode);
      text.append("<DEFINITION>\n");
      text.append("<FOR>" + XMLNormalize(m_Instances.attribute(nNode).name()) + "</FOR>\n");
      for (int iParent = 0; iParent < m_ParentSets[nNode].getNrOfParents(); iParent++) {
        text.append("<GIVEN>"
            + XMLNormalize(m_Instances.attribute(m_ParentSets[nNode].getParent(iParent)).name())
            + "</GIVEN>\n");
      }
      text.append("<TABLE>\n");
      for (int iParent = 0; iParent < m_ParentSets[nNode].getCardinalityOfParents(); iParent++) {
        for (int iValue = 0; iValue < m_Instances.attribute(nNode).numValues(); iValue++) {
          text.append(m_Distributions[nNode][iParent].getProbability(iValue));
          text.append(' ');
        }
        text.append('\n');
      }
      text.append("</TABLE>\n");
      text.append("</DEFINITION>\n");
    }
    text.append("</NETWORK>\n");
    text.append("</BIF>\n");
    return text.toString();
  } // toXMLBIF03

  /** undo stack for undoin edit actions, or redo edit actions */
  FastVector m_undoStack = new FastVector();

  /** current action in undo stack */
  int m_nCurrentEditAction = -1;

  /** action that the network is saved */
  int m_nSavedPointer = -1;

  /***************************************************************************
   * flag to indicate whether an edit action needs to introduce an undo
   * action. This is only false when an undo or redo action is performed.
   **************************************************************************/
  boolean m_bNeedsUndoAction = true;

  /** return whether there is something on the undo stack that can be performed */
  public boolean canUndo() {
    return m_nCurrentEditAction >= 0;
  }

  /** return whether there is something on the undo stack that can be performed */
  public boolean canRedo() {
    return m_nCurrentEditAction < m_undoStack.size() - 1;
  }

  /** return true when current state differs from the state the network was last saved */
  public boolean isChanged() {
    return m_nCurrentEditAction != m_nSavedPointer;
  }

  /** indicate the network state was saved */
  public void isSaved() {
    m_nSavedPointer = m_nCurrentEditAction;
  }

  /** get message representing the last action performed on the network */
  public String lastActionMsg() {
    if (m_undoStack.size() == 0) {
      return "";
    }
    return ((UndoAction) m_undoStack.lastElement()).getRedoMsg();
  } // lastActionMsg


  /** undo the last edit action performed on the network.
   * returns message representing the action performed.
   */
  public String undo() {
    if (!canUndo()) {
      return "";
    }
    UndoAction undoAction = (UndoAction) m_undoStack.elementAt(m_nCurrentEditAction);
    m_bNeedsUndoAction = false;
    undoAction.undo();
    m_bNeedsUndoAction = true;
    m_nCurrentEditAction--;

    // undo stack debugging
    /*
    if (m_nCurrentEditAction>0) {
      String sXML = (String) m_sXMLStack.elementAt(m_nCurrentEditAction);
      String sXMLCurrent = toXMLBIF03();
      if (!sXML.equals(sXMLCurrent)) {
        String sDiff = "";
        String sDiff2 = "";
        for (int i = 0; i < sXML.length() && sDiff.length() < 80; i++) {
          if (sXML.charAt(i) != sXMLCurrent.charAt(i)) {
            sDiff += sXML.charAt(i);
            sDiff2 += sXMLCurrent.charAt(i);
          }
        }

        JOptionPane.showMessageDialog(null,"Undo error\n" + sDiff + " \n" + sDiff2);
      }
    }
    */
    return undoAction.getUndoMsg();
  } // undo

  /** redo the last edit action performed on the network.
   * returns message representing the action performed.
   */
  public String redo() {
    if (!canRedo()) {
      return "";
    }
    m_nCurrentEditAction++;
    UndoAction undoAction = (UndoAction) m_undoStack.elementAt(m_nCurrentEditAction);
    m_bNeedsUndoAction = false;
    undoAction.redo();
    m_bNeedsUndoAction = true;

    // undo stack debugging
    /*
    if (m_nCurrentEditAction < m_sXMLStack.size()) {
      String sXML = (String) m_sXMLStack.elementAt(m_nCurrentEditAction);
      String sXMLCurrent = toXMLBIF03();
      if (!sXML.equals(sXMLCurrent)) {
        String sDiff = "";
        String sDiff2 = "";
        for (int i = 0; i < sXML.length() && sDiff.length() < 80; i++) {
          if (sXML.charAt(i) != sXMLCurrent.charAt(i)) {
            sDiff += sXML.charAt(i);
            sDiff2 += sXMLCurrent.charAt(i);
          }
        }

        JOptionPane.showMessageDialog(null,"redo error\n" + sDiff + " \n" + sDiff2);
      }
    }
    */
    return undoAction.getRedoMsg();
  } // redo

  /** add undo action to the undo stack.
   * @param action operation that needs to be added to the undo stack
   */
  void addUndoAction(UndoAction action) {
    int iAction = m_undoStack.size() - 1;
    while (iAction > m_nCurrentEditAction) {
      m_undoStack.removeElementAt(iAction--);
    }
    if (m_nSavedPointer > m_nCurrentEditAction) {
      m_nSavedPointer = -2;
    }
    m_undoStack.addElement(action);
    //m_sXMLStack.addElement(toXMLBIF03());
    m_nCurrentEditAction++;
  } // addUndoAction

  /** remove all actions from the undo stack */
  public void clearUndoStack() {
    m_undoStack = new FastVector();
    //m_sXMLStack = new FastVector();
    m_nCurrentEditAction = -1;
    m_nSavedPointer = -1;
  } // clearUndoStack

  /** base class for actions representing operations on the Bayesian network
   * that can be undone/redone
   */
  class UndoAction implements Serializable {
    /** for serialization */
    static final long serialVersionUID = 1;
    public void undo() {
    }

    public void redo() {
    }

    public String getUndoMsg() {
      return getMsg();
    }

    public String getRedoMsg() {
      return getMsg();
    }
    String getMsg() {
      String sStr = toString();
      int iStart = sStr.indexOf('$');
      int iEnd = sStr.indexOf('@');
      StringBuffer sBuffer = new StringBuffer();
      for(int i= iStart + 1; i < iEnd; i++) {
        char c = sStr.charAt(i);
        if (Character.isUpperCase(c)) {
          sBuffer.append(' ');
        }
        sBuffer.append(sStr.charAt(i));
      }
      return sBuffer.toString();
    } // getMsg
  } // class UndoAction

  class AddNodeAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    String m_sName;
    int m_nPosX;
    int m_nPosY;

    int m_nCardinality;

    AddNodeAction(String sName, int nCardinality, int nPosX, int nPosY) {
      m_sName = sName;
      m_nCardinality = nCardinality;
      m_nPosX = nPosX;
      m_nPosY = nPosY;
    } // c'tor

    public void undo() {
      try {
        deleteNode(getNrOfNodes() - 1);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        addNode(m_sName, m_nCardinality, m_nPosX, m_nPosY);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class AddNodeAction

  class DeleteNodeAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    Attribute m_att;

    Estimator[] m_CPT;

    ParentSet m_ParentSet;

    FastVector m_deleteArcActions;

    int m_nPosX;

    int m_nPosY;

    DeleteNodeAction(int nTargetNode) {
      m_nTargetNode = nTargetNode;
      m_att = m_Instances.attribute(nTargetNode);
      try {
        SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
        m_CPT = (Estimator[]) so.getObject();
        ;
        so = new SerializedObject(m_ParentSets[nTargetNode]);
        m_ParentSet = (ParentSet) so.getObject();
      } catch (Exception e) {
        e.printStackTrace();
      }
      m_deleteArcActions = new FastVector();
      for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
        if (m_ParentSets[iNode].contains(nTargetNode)) {
          m_deleteArcActions.addElement(new DeleteArcAction(nTargetNode, iNode));
        }
      }
      m_nPosX = getPositionX(m_nTargetNode);
      m_nPosY = getPositionY(m_nTargetNode);
    } // c'tor

    public void undo() {
      try {
        m_Instances.insertAttributeAt(m_att, m_nTargetNode);
        int nAtts = m_Instances.numAttributes();
        // update parentsets
        ParentSet[] parentSets = new ParentSet[nAtts];
        int nX = 0;
        for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
          if (iParentSet == m_nTargetNode) {
            SerializedObject so = new SerializedObject(m_ParentSet);
            parentSets[iParentSet] = (ParentSet) so.getObject();
            nX = 1;
          } else {
            parentSets[iParentSet] = m_ParentSets[iParentSet - nX];
            for (int iParent = 0; iParent < parentSets[iParentSet].getNrOfParents(); iParent++) {
              int nParent = parentSets[iParentSet].getParent(iParent);
              if (nParent >= m_nTargetNode) {
                parentSets[iParentSet].SetParent(iParent, nParent + 1);
              }
            }
          }
        }
        m_ParentSets = parentSets;
        // update distributions
        Estimator[][] distributions = new Estimator[nAtts][];
        nX = 0;
        for (int iNode = 0; iNode < nAtts; iNode++) {
          if (iNode == m_nTargetNode) {
            SerializedObject so = new SerializedObject(m_CPT);
            distributions[iNode] = (Estimator[]) so.getObject();
            nX = 1;
          } else {
            distributions[iNode] = m_Distributions[iNode - nX];
          }
        }
        m_Distributions = distributions;

        for (int deletedArc = 0; deletedArc < m_deleteArcActions.size(); deletedArc++) {
          DeleteArcAction action = (DeleteArcAction) m_deleteArcActions.elementAt(deletedArc);
          action.undo();
        }
        m_nPositionX.insertElementAt(m_nPosX, m_nTargetNode);
        m_nPositionY.insertElementAt(m_nPosY, m_nTargetNode);
        m_nEvidence.insertElementAt(-1, m_nTargetNode);
        m_fMarginP.insertElementAt(new double[getCardinality(m_nTargetNode)], m_nTargetNode);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        deleteNode(m_nTargetNode);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class DeleteNodeAction

  class DeleteSelectionAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    FastVector m_nodes;

    Attribute[] m_att;

    Estimator[][] m_CPT;

    ParentSet[] m_ParentSet;

    FastVector m_deleteArcActions;

    int[] m_nPosX;

    int[] m_nPosY;

    public DeleteSelectionAction(FastVector nodes) {
      m_nodes = new FastVector();
      int nNodes = nodes.size();
      m_att = new Attribute[nNodes];
      m_CPT = new Estimator[nNodes][];
      m_ParentSet = new ParentSet[nNodes];
      m_nPosX = new int[nNodes];
      m_nPosY = new int[nNodes];
      m_deleteArcActions = new FastVector();
      for (int iNode = 0; iNode < nodes.size(); iNode++) {
        int nTargetNode = (Integer) nodes.elementAt(iNode);
        m_nodes.addElement(nTargetNode);
        m_att[iNode] = m_Instances.attribute(nTargetNode);
        try {
          SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
          m_CPT[iNode] = (Estimator[]) so.getObject();
          ;
          so = new SerializedObject(m_ParentSets[nTargetNode]);
          m_ParentSet[iNode] = (ParentSet) so.getObject();
        } catch (Exception e) {
          e.printStackTrace();
        }
        m_nPosX[iNode] = getPositionX(nTargetNode);
        m_nPosY[iNode] = getPositionY(nTargetNode);
        for (int iNode2 = 0; iNode2 < getNrOfNodes(); iNode2++) {
          if (!nodes.contains(iNode2) && m_ParentSets[iNode2].contains(nTargetNode)) {
            m_deleteArcActions.addElement(new DeleteArcAction(nTargetNode, iNode2));
          }
        }
      }
    } // c'tor

    public void undo() {
      try {
        for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
          int nTargetNode = (Integer) m_nodes.elementAt(iNode);
          m_Instances.insertAttributeAt(m_att[iNode], nTargetNode);
        }
        int nAtts = m_Instances.numAttributes();
        // update parentsets
        ParentSet[] parentSets = new ParentSet[nAtts];
        int[] offset = new int[nAtts];
        for (int iNode = 0; iNode < nAtts; iNode++) {
          offset[iNode] = iNode;
        }
        for (int iNode = m_nodes.size() - 1; iNode >= 0; iNode--) {
          int nTargetNode = (Integer) m_nodes.elementAt(iNode);
          for (int i = nTargetNode; i < nAtts - 1; i++) {
            offset[i] = offset[i + 1];
          }
        }

        int iTargetNode = 0;
        for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
          if (iTargetNode < m_nodes.size()
              && (Integer) m_nodes.elementAt(iTargetNode) == (Integer) iParentSet) {
            SerializedObject so = new SerializedObject(m_ParentSet[iTargetNode]);
            parentSets[iParentSet] = (ParentSet) so.getObject();
            iTargetNode++;
          } else {
            parentSets[iParentSet] = m_ParentSets[iParentSet - iTargetNode];
            for (int iParent = 0; iParent < parentSets[iParentSet].getNrOfParents(); iParent++) {
              int nParent = parentSets[iParentSet].getParent(iParent);
              parentSets[iParentSet].SetParent(iParent, offset[nParent]);
            }
          }
        }
        m_ParentSets = parentSets;
        // update distributions
        Estimator[][] distributions = new Estimator[nAtts][];
        iTargetNode = 0;
        for (int iNode = 0; iNode < nAtts; iNode++) {
          if (iTargetNode < m_nodes.size() && (Integer) m_nodes.elementAt(iTargetNode) == (Integer) iNode) {
            SerializedObject so = new SerializedObject(m_CPT[iTargetNode]);
            distributions[iNode] = (Estimator[]) so.getObject();
            iTargetNode++;
          } else {
            distributions[iNode] = m_Distributions[iNode - iTargetNode];
          }
        }
        m_Distributions = distributions;

        for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
          int nTargetNode = (Integer) m_nodes.elementAt(iNode);
          m_nPositionX.insertElementAt(m_nPosX[iNode], nTargetNode);
          m_nPositionY.insertElementAt(m_nPosY[iNode], nTargetNode);
          m_nEvidence.insertElementAt(-1, nTargetNode);
          m_fMarginP.insertElementAt(new double[getCardinality(nTargetNode)], nTargetNode);
        }
        for (int deletedArc = 0; deletedArc < m_deleteArcActions.size(); deletedArc++) {
          DeleteArcAction action = (DeleteArcAction) m_deleteArcActions.elementAt(deletedArc);
          action.undo();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        for (int iNode = m_nodes.size() - 1; iNode >= 0; iNode--) {
          int nNode = (Integer) m_nodes.elementAt(iNode);
          deleteNode(nNode);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class DeleteSelectionAction

  class AddArcAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    //int m_nChild;
    FastVector m_children;

    int m_nParent;

    Estimator[][] m_CPT;

    AddArcAction(int nParent, int nChild) {
      try {
        m_nParent = nParent;
        m_children = new FastVector();
        m_children.addElement(nChild);
        //m_nChild = nChild;
        SerializedObject so = new SerializedObject(m_Distributions[nChild]);
        m_CPT = new Estimator[1][];
        m_CPT[0] = (Estimator[]) so.getObject();
        ;
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // c'tor

    AddArcAction(int nParent, FastVector children) {
      try {
        m_nParent = nParent;
        m_children = new FastVector();
        m_CPT = new Estimator[children.size()][];
        for (int iChild = 0; iChild < children.size(); iChild++) {
          int nChild = (Integer) children.elementAt(iChild);
          m_children.addElement(nChild);
          SerializedObject so = new SerializedObject(m_Distributions[nChild]);
          m_CPT[iChild] = (Estimator[]) so.getObject();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // c'tor

    public void undo() {
      try {
        for (int iChild = 0; iChild < m_children.size(); iChild++) {
          int nChild = (Integer) m_children.elementAt(iChild);
          deleteArc(m_nParent, nChild);
          SerializedObject so = new SerializedObject(m_CPT[iChild]);
          m_Distributions[nChild] = (Estimator[]) so.getObject();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        for (int iChild = 0; iChild < m_children.size(); iChild++) {
          int nChild = (Integer) m_children.elementAt(iChild);
          addArc(m_nParent, nChild);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class AddArcAction

  class DeleteArcAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int[] m_nParents;
    int m_nChild;
    int m_nParent;
    Estimator[] m_CPT;

    DeleteArcAction(int nParent, int nChild) {
      try {
      m_nChild = nChild;
      m_nParent = nParent;
      m_nParents = new int[getNrOfParents(nChild)];
      for (int iParent = 0; iParent < m_nParents.length; iParent++) {
        m_nParents[iParent] = getParent(nChild, iParent);
      }
      SerializedObject so = new SerializedObject(m_Distributions[nChild]);
      m_CPT = (Estimator[]) so.getObject();
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // c'tor

    public void undo() {
      try {
        SerializedObject so = new SerializedObject(m_CPT);
        m_Distributions[m_nChild] = (Estimator[]) so.getObject();
        ParentSet parentSet = new ParentSet();
        for (int iParent = 0; iParent < m_nParents.length; iParent++) {
          parentSet.addParent(m_nParents[iParent], m_Instances);
        }
        m_ParentSets[m_nChild] = parentSet;
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        deleteArc(m_nParent, m_nChild);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class DeleteArcAction

  class SetDistributionAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    Estimator[] m_CPT;

    double[][] m_P;

    SetDistributionAction(int nTargetNode, double[][] P) {
      try {
        m_nTargetNode = nTargetNode;
        SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
        m_CPT = (Estimator[]) so.getObject();
        ;
        m_P = P;
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // c'tor

    public void undo() {
      try {
        SerializedObject so = new SerializedObject(m_CPT);
        m_Distributions[m_nTargetNode] = (Estimator[]) so.getObject();
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        setDistribution(m_nTargetNode, m_P);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Distribution of node " + getNodeName(m_nTargetNode) + " changed";
    }

    public String getRedoMsg() {
      return "Distribution of node " + getNodeName(m_nTargetNode) + " changed";
    }
  } // class SetDistributionAction

  class RenameAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    String m_sNewName;

    String m_sOldName;

    RenameAction(int nTargetNode, String sOldName, String sNewName) {
      m_nTargetNode = nTargetNode;
      m_sNewName = sNewName;
      m_sOldName = sOldName;
    } // c'tor

    public void undo() {
      setNodeName(m_nTargetNode, m_sOldName);
    } // undo

    public void redo() {
      setNodeName(m_nTargetNode, m_sNewName);
    } // redo
  } // class RenameAction

  class RenameValueAction extends RenameAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    RenameValueAction(int nTargetNode, String sOldName, String sNewName) {
      super(nTargetNode, sOldName, sNewName);
    } // c'tor

    public void undo() {
      renameNodeValue(m_nTargetNode, m_sNewName, m_sOldName);
    } // undo

    public void redo() {
      renameNodeValue(m_nTargetNode, m_sOldName, m_sNewName);
    } // redo

    public String getUndoMsg() {
      return "Value of node " + getNodeName(m_nTargetNode) + " changed from " + m_sNewName + " to " + m_sOldName;
    }

    public String getRedoMsg() {
      return "Value of node " + getNodeName(m_nTargetNode) + " changed from " + m_sOldName + " to " + m_sNewName;
    }
  } // class RenameValueAction

  class AddValueAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    String m_sValue;

    AddValueAction(int nTargetNode, String sValue) {
      m_nTargetNode = nTargetNode;
      m_sValue = sValue;
    } // c'tor

    public void undo() {
      try {
        delNodeValue(m_nTargetNode, m_sValue);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      addNodeValue(m_nTargetNode, m_sValue);
    } // redo

    public String getUndoMsg() {
      return "Value " + m_sValue + " removed from node " + getNodeName(m_nTargetNode);
    }

    public String getRedoMsg() {
      return "Value " + m_sValue + " added to node " + getNodeName(m_nTargetNode);
    }
  } // class AddValueAction

  class DelValueAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    String m_sValue;

    Estimator[] m_CPT;

    FastVector m_children;

    Estimator[][] m_childAtts;

    Attribute m_att;

    DelValueAction(int nTargetNode, String sValue) {
      try {
        m_nTargetNode = nTargetNode;
        m_sValue = sValue;
        m_att = m_Instances.attribute(nTargetNode);
        SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
        m_CPT = (Estimator[]) so.getObject();
        ;
        m_children = new FastVector();
        for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
          if (m_ParentSets[iNode].contains(nTargetNode)) {
            m_children.addElement(iNode);
          }
        }
        m_childAtts = new Estimator[m_children.size()][];
        for (int iChild = 0; iChild < m_children.size(); iChild++) {
          int nChild = (Integer) m_children.elementAt(iChild);
          m_childAtts[iChild] = m_Distributions[nChild];
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // c'tor

    public void undo() {
      try {
        m_Instances.insertAttributeAt(m_att, m_nTargetNode);
        SerializedObject so = new SerializedObject(m_CPT);
        m_Distributions[m_nTargetNode] = (Estimator[]) so.getObject();
        for (int iChild = 0; iChild < m_children.size(); iChild++) {
          int nChild = (Integer) m_children.elementAt(iChild);
          m_Instances.insertAttributeAt(m_att, m_nTargetNode);
          so = new SerializedObject(m_childAtts[iChild]);
          m_Distributions[nChild] = (Estimator[]) so.getObject();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        delNodeValue(m_nTargetNode, m_sValue);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Value " + m_sValue + " added to node " + getNodeName(m_nTargetNode);
    }

    public String getRedoMsg() {
      return "Value " + m_sValue + " removed from node " + getNodeName(m_nTargetNode);
    }
  } // class DelValueAction

  class alignAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    FastVector m_nodes;

    FastVector m_posX;

    FastVector m_posY;

    alignAction(FastVector nodes) {
      m_nodes = new FastVector(nodes.size());
      m_posX = new FastVector(nodes.size());
      m_posY = new FastVector(nodes.size());
      for (int iNode = 0; iNode < nodes.size(); iNode++) {
        int nNode = (Integer) nodes.elementAt(iNode);
        m_nodes.addElement(nNode);
        m_posX.addElement(getPositionX(nNode));
        m_posY.addElement(getPositionY(nNode));
      }
    } // c'tor

    public void undo() {
      try {
        for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
          int nNode = (Integer) m_nodes.elementAt(iNode);
          setPosition(nNode, (Integer) m_posX.elementAt(iNode), (Integer) m_posY.elementAt(iNode));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo
  } // class alignAction

  class alignLeftAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public alignLeftAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        alignLeft(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from aliging nodes to the left.";
    }

    public String getRedoMsg() {
      return "Aligning " + m_nodes.size() + " nodes to the left.";
    }
  } // class alignLeftAction

  class alignRightAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public alignRightAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        alignRight(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from aliging nodes to the right.";
    }

    public String getRedoMsg() {
      return "Aligning " + m_nodes.size() + " nodes to the right.";
    }
  } // class alignLeftAction

  class alignTopAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public alignTopAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        alignTop(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from aliging nodes to the top.";
    }

    public String getRedoMsg() {
      return "Aligning " + m_nodes.size() + " nodes to the top.";
    }
  } // class alignTopAction

  class alignBottomAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public alignBottomAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        alignBottom(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from aliging nodes to the bottom.";
    }

    public String getRedoMsg() {
      return "Aligning " + m_nodes.size() + " nodes to the bottom.";
    }
  } // class alignBottomAction

  class centerHorizontalAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public centerHorizontalAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        centerHorizontal(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from centering horizontally.";
    }

    public String getRedoMsg() {
      return "Centering " + m_nodes.size() + " nodes horizontally.";
    }
  } // class centerHorizontalAction

  class centerVerticalAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public centerVerticalAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        centerVertical(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from centering vertically.";
    }

    public String getRedoMsg() {
      return "Centering " + m_nodes.size() + " nodes vertically.";
    }
  } // class centerVerticalAction

  class spaceHorizontalAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public spaceHorizontalAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        spaceHorizontal(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from spaceing horizontally.";
    }

    public String getRedoMsg() {
      return "spaceing " + m_nodes.size() + " nodes horizontally.";
    }
  } // class spaceHorizontalAction

  class spaceVerticalAction extends alignAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    public spaceVerticalAction(FastVector nodes) {
      super(nodes);
    } // c'tor

    public void redo() {
      try {
        spaceVertical(m_nodes);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo

    public String getUndoMsg() {
      return "Returning " + m_nodes.size() + " from spaceng vertically.";
    }

    public String getRedoMsg() {
      return "Spaceng " + m_nodes.size() + " nodes vertically.";
    }
  } // class spaceVerticalAction

  class SetPositionAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nTargetNode;

    int m_nX;

    int m_nY;

    int m_nX2;

    int m_nY2;

    SetPositionAction(int nTargetNode, int nX, int nY) {
      m_nTargetNode = nTargetNode;
      m_nX2 = nX;
      m_nY2 = nY;
      m_nX = getPositionX(nTargetNode);
      m_nY = getPositionY(nTargetNode);
    } // c'tor

    public void undo() {
      setPosition(m_nTargetNode, m_nX, m_nY);
    } // undo

    public void redo() {
      setPosition(m_nTargetNode, m_nX2, m_nY2);
    } // redo

    public void setUndoPosition(int nX, int nY) {
      m_nX2 = nX;
      m_nY2 = nY;
    } // setPosition
  } // class SetPositionAction

  class SetGroupPositionAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    FastVector m_nodes;
    int m_dX;
    int m_dY;

    SetGroupPositionAction(FastVector nodes, int dX, int dY) {
      m_nodes = new FastVector(nodes.size());
      for (int iNode = 0; iNode < nodes.size(); iNode++) {
        m_nodes.addElement(nodes.elementAt(iNode));
      }
      m_dX = dX;
      m_dY = dY;
    } // c'tor

    public void undo() {
      for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
        int nNode = (Integer) m_nodes.elementAt(iNode);
        setPosition(nNode, getPositionX(nNode) - m_dX,  getPositionY(nNode) - m_dY);
      }
    } // undo

    public void redo() {
      for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
        int nNode = (Integer) m_nodes.elementAt(iNode);
        setPosition(nNode, getPositionX(nNode) + m_dX,  getPositionY(nNode) + m_dY);
      }
    } // redo
    public void setUndoPosition(int dX, int dY) {
      m_dX += dX;
      m_dY += dY;
    } // setPosition
  } // class SetGroupPositionAction

  class LayoutGraphAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    FastVector m_nPosX;
    FastVector m_nPosY;
    FastVector m_nPosX2;
    FastVector m_nPosY2;

    LayoutGraphAction(FastVector nPosX, FastVector nPosY) {
      m_nPosX = new FastVector(nPosX.size());
      m_nPosY = new FastVector(nPosX.size());
      m_nPosX2 = new FastVector(nPosX.size());
      m_nPosY2 = new FastVector(nPosX.size());
      for (int iNode = 0; iNode < nPosX.size(); iNode++) {
        m_nPosX.addElement(m_nPositionX.elementAt(iNode));
        m_nPosY.addElement(m_nPositionY.elementAt(iNode));
        m_nPosX2.addElement(nPosX.elementAt(iNode));
        m_nPosY2.addElement(nPosY.elementAt(iNode));
      }
    } // c'tor

    public void undo() {
      for (int iNode = 0; iNode < m_nPosX.size(); iNode++) {
        setPosition(iNode, (Integer) m_nPosX.elementAt(iNode), (Integer) m_nPosY.elementAt(iNode));
      }
    } // undo

    public void redo() {
      for (int iNode = 0; iNode < m_nPosX.size(); iNode++) {
        setPosition(iNode, (Integer) m_nPosX2.elementAt(iNode), (Integer) m_nPosY2.elementAt(iNode));
      }
    } // redo
  } // class LayoutGraphAction

  class PasteAction extends UndoAction {
    /** for serialization */
    static final long serialVersionUID = 1;
    int m_nBase;

    String m_sXML;

    PasteAction(String sXML, int nBase) {
      m_sXML = sXML;
      m_nBase = nBase;
    } // c'tor

    public void undo() {
      try {
        int iNode = getNrOfNodes() - 1;
        while (iNode >= m_nBase) {
          deleteNode(iNode);
          iNode--;
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // undo

    public void redo() {
      try {
        paste(m_sXML, EXECUTE);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } // redo
  } // class PasteAction
   
    /**
     * Returns the revision string.
     *
     * @return    the revision
     */
    public String getRevision() {
      return RevisionUtils.extract("$Revision: 1.3 $");
    }

  /**
   * @param args
   */
  public static void main(String[] args) {
  } // main
} // class EditableBayesNet
TOP

Related Classes of weka.classifiers.bayes.net.EditableBayesNet$DeleteArcAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.