///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below.       //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,       //
// 2007, 2008, 2009, 2010, 2014, 2015 by Peter Spirtes, Richard Scheines, Joseph   //
// Ramsey, and Clark Glymour.                                                //
//                                                                           //
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA //
///////////////////////////////////////////////////////////////////////////////

package edu.cmu.tetrad.bayes;

import edu.cmu.tetrad.data.DataSet;
import edu.cmu.tetrad.data.VariableSource;
import edu.cmu.tetrad.graph.Graph;
import edu.cmu.tetrad.graph.Node;
import edu.cmu.tetrad.data.Simulator;
import edu.cmu.tetrad.util.IM;

import java.util.List;

/**
 * Interface implemented by Bayes instantiated models. For purposes of
 * clarification, we distinguish a Bayes parametric model from a Bayes
 * instantiated model. The former provides enough information for us to know
 * what the parameters of the Bayes net are, given that we know the graph of the
 * Bayes net--i.e., it tells us how many categories each variable has and what
 * the names of those categories are. It does not, however, tell us what the
 * value of each parameter is; information about the value of each
 * parameter in the Bayes net is provided in the Bayes instantiated model. This
 * information is organized, variable by variable, in conditional probability
 * tables. For each variable, a table is stored representing enough information
 * to recover the conditional probability of each value of each variable
 * given each combination of values of the parents of the variable in the graph.
 * The rows of the table are the combinations of parent values of the variable,
 * and the columns of the table are variable values of the variable. Most of the
 * method in this interface are designed mainly to allow these values to be set
 * and retrieved. A few methods are dedicated to bookkeeping chores, like
 * clearing tables or initializing them randomly. One special method
 * (simulateData) is dedicated to the task of generating randomly simulated data
 * sets consistent with the conditional probabilities implied by the information
 * stored in the conditional probability tables of the Bayes net. See
 * implementations for details.
 *
 * @author Joseph Ramsey
 * @see edu.cmu.tetrad.graph.Dag
 * @see BayesPm
 */
public interface BayesIm extends VariableSource, IM, Simulator {

    /**
     * @return the underlying Bayes PM.
     */
    BayesPm getBayesPm();

    /**
     * @return the underlying DAG.
     */
    Graph getDag();

    /**
     * @return the number of nodes in the model.
     */
    int getNumNodes();

    /**
     * @return the node corresponding to the given node index.
     */
    Node getNode(int nodeIndex);

    /**
     * @param name the name of the node.
     * @return the node with the given name in the associated graph.
     */
    Node getNode(String name);

    /**
     * @param node the given node.
     * @return the index for that node, or -1 if the node is not in the
     * BayesIm.
     */
    int getNodeIndex(Node node);

    /**
     * @return the list of variable for this Bayes net.
     */
    List<Node> getVariables();

    /**
     * @return the list of variable names for this Bayes net.
     */
    List<String> getVariableNames();

    /**
     * @return the list of measured variableNodes.
     */
    List<Node> getMeasuredNodes();


    /**
     * @return the number of columns in the table of the given node N with index
     * 'nodeIndex'--that is, the number of possible values that N can take on.
     * That is, if P(N=v0 | P1=v1, P2=v2, ... Pn=vn) is a conditional
     * probability stored in 'probs', then the maximum number of rows in the
     * table for N is #vals(N).
     * @see #getNumRows
     */
    int getNumColumns(int nodeIndex);

    /**
     * @return the number of rows in the table of the given node, which would be
     * the total number of possible combinations of parent values for a given
     * node.  That is, if P(N=v0 | P1=v1, P2=v2, ... Pn=vn) is a conditional
     * probability stored in 'probs', then the maximum number of rows in the
     * table for N is #vals(P1) x #vals(P2) x ... x #vals(Pn).
     * @see #getRowIndex
     * @see #getNumColumns
     */
    int getNumRows(int nodeIndex);

    /**
     * @param nodeIndex the given node.
     * @return the number of parents of the given node.
     */
    int getNumParents(int nodeIndex);

    /**
     * @return the given parent of the given node.
     */
    int getParent(int nodeIndex, int parentIndex);

    /**
     * @return the dimension of the given parent for the given node.
     */
    int getParentDim(int nodeIndex, int parentIndex);

    /**
     * @return (a defensive copy of) the array representing the dimensionality
     * of each parent of a node, that is, the number of values which that node
     * can take on.  The order of entries in this array is the same as the order
     * of entries of nodes returned by getParents() for that node.
     * @see #getParents
     */
    int[] getParentDims(int nodeIndex);

    /**
     * @return (a defensive copy of) the array containing all of the parents of
     * a given node in the order in which they are stored internally.
     * @see #getParentDims
     */
    int[] getParents(int nodeIndex);

    /**
     * @param nodeIndex the index of the node.
     * @param rowIndex  the index of the row in question.
     * @return an array containing the combination of parent values for a given
     * node and given row in the probability table for that node.  To get the
     * combination of parent values from the row number, the row number is
     * represented using a variable-base place value system, where the bases
     * for each place value are the dimensions of the parents in the order in
     * which they are given by getParentDims().  For instance, if the row number
     * (base 10) is 103 and the parent dimension array is [3 5 7], we calculate
     * the first value as 103 / 7 = 14 with a remainder of 5.  We then divide
     * 14 / 5 = 2 with a remainder of 4.  We then divide 2 / 3 = 0 with a
     * remainder of 2.  The variable place value representation is [2 4 5],
     * which is the combination of parent values.  This is the inverse function
     * of getRowIndex().
     * @see #getNodeIndex
     * @see #getRowIndex
     */
    int[] getParentValues(int nodeIndex, int rowIndex);

    /**
     * @return the value in the probability table for the given node, at the
     * given row and column.
     */
    int getParentValue(int nodeIndex, int rowIndex, int colIndex);

    /**
     * @param nodeIndex the index of the node in question.
     * @param rowIndex  the row in the table for this for node which represents
     *                  the combination of parent values in question.
     * @param colIndex  the column in the table for this node which represents
     *                  the value of the node in question.
     * @return the probability for the given node at the given row and column in
     * the table for that node.  To get the node index, use getNodeIndex().  To
     * get the row index, use getRowIndex().  To get the column index, use
     * getCategoryIndex() from the underlying BayesPm().  The value returned
     * will represent a conditional probability of the form P(N=v0 | P1=v1,
     * P2=v2, ... , Pn=vn), where N is the node referenced by nodeIndex, v0 is
     * the value referenced by colIndex, and the combination of parent values
     * indicated is the combination indicated by rowIndex.
     * @see #getNodeIndex
     * @see #getRowIndex
     */
    double getProbability(int nodeIndex, int rowIndex, int colIndex);

    /**
     * @return the row in the table at which the given combination of parent
     * values is represented for the given node.  The row is calculated as a
     * variable-base place-value number.  For instance, if the array of
     * parent dimensions is [3, 5, 7] and the parent value combination is [2,
     * 4, 5], then the row number is (7 * (5 * (3 * 0 + 2) + 4)) + 5 = 103. This
     * is the inverse function to getVariableValues().  <p> Note: If the node
     * has n values, the length of 'values' must be >= the number of parents.
     * Only the first n values are used.
     * @see #getParentValues
     */
    int getRowIndex(int nodeIndex, int[] values);

    /**
     * Normalizes all rows in the tables associated with each of node in turn.
     */
    void normalizeAll();

    /**
     * Normalizes all rows in the table associated with a given node.
     */
    void normalizeNode(int nodeIndex);

    /**
     * Normalizes the given row.
     */
    void normalizeRow(int nodeIndex, int rowIndex);

    /**
     * Sets the probability for the given node at a given row and column in the
     * table for that node.  To get the node index, use getNodeIndex().  To get
     * the row index, use getRowIndex().  To get the column index, use
     * getCategoryIndex() from the underlying BayesPm().  The value returned
     * will represent a conditional probability of the form P(N=v0 | P1=v1,
     * P2=v2, ... , Pn=vn), where N is the node referenced by nodeIndex, v0 is
     * the value referenced by colIndex, and the combination of parent values
     * indicated is the combination indicated by rowIndex.
     *
     * @param nodeIndex the index of the node in question.
     * @param rowIndex  the row in the table for this for node which represents
     *                  the combination of parent values in question.
     * @param colIndex  the column in the table for this node which represents
     *                  the value of the node in question.
     * @param value     the desired probability to be set.
     * @see #getProbability
     */
    void setProbability(int nodeIndex, int rowIndex, int colIndex,
                        double value);

    /**
     * @return the index of the node with the given name in the specified
     * BayesIm.
     */
    int getCorrespondingNodeIndex(int nodeIndex, BayesIm otherBayesIm);

    /**
     * Assigns random probability values to the child values of this row that
     * add to 1.
     *
     * @param nodeIndex the node for the table that this row belongs to.
     * @param rowIndex  the index of the row.
     */
    void clearRow(int nodeIndex, int rowIndex);

    /**
     * Assigns random probability values to the child values of this row that
     * add to 1.
     *
     * @param nodeIndex the node for the table that this row belongs to.
     * @param rowIndex  the index of the row.
     */
    void randomizeRow(int nodeIndex, int rowIndex);

    /**
     * Randomizes any row in the table for the given node index that has a
     * Double.NaN value in it.
     *
     * @param nodeIndex the node for the table whose incomplete rows are to be
     *                  randomized.
     */
    void randomizeIncompleteRows(int nodeIndex);

    /**
     * Randomizes every row in the table for the given node index.
     *
     * @param nodeIndex the node for the table to be randomized.
     */
    void randomizeTable(int nodeIndex);

    /**
     * Randomizes every row in the table for the given node index.
     *
     * @param nodeIndex the node for the table to be randomized.
     */
    void clearTable(int nodeIndex);

    /**
     * @return true iff one of the values in the given row is Double.NaN.
     */
    boolean isIncomplete(int nodeIndex, int rowIndex);

    /**
     * @return true iff any value in the table for the given node is
     * Double.NaN.
     */
    boolean isIncomplete(int nodeIndex);

    /**
     * Simulates a sample with the given sample size.
     *
     * @param sampleSize the sample size.
     * @return the simulated sample as a DataSet.
     */
    DataSet simulateData(int sampleSize, boolean latentDataSaved);

    /**
     * Simulates a sample with the given sample size.
     *
     * @param sampleSize the sample size.
     * @return the simulated sample as a DataSet.
     */
    DataSet simulateData(int sampleSize, long seed, boolean latentDataSaved);

    /**
     * Overwrites the given dataSet with a new simulated dataSet, to avoid
     * allocating memory. The given dataSet must have the necessary number of
     * columns.
     *
     * @return the simulated sample as a DataSet.
     */
    DataSet simulateData(DataSet dataSet, boolean latentDataSaved);

    /**
     * @return true iff this bayes net is equal to the given Bayes net. The
     * sense of equality may vary depending on the type of Bayes net.
     */
    boolean equals(Object o);

    /**
     * @return a string representation for this Bayes net.
     */
    String toString();
}





