Class TrainedDagSimulatorGNM

java.lang.Object
edu.cmu.tetrad.sem.TrainedDagSimulatorGNM
All Implemented Interfaces:
TetradSerializable, Serializable

public final class TrainedDagSimulatorGNM extends Object implements TetradSerializable

Trained DAG simulator (GNM): learn local mechanisms with non-additive noise and resimulate

This class is a “train-then-resimulate” simulator: given an observed DataSet and a user-supplied acyclic Graph (treated as a DAG), it learns a local conditional mechanism for each node and then generates new samples by running the DAG forward in topological order.

Primary motivation. The purpose is to construct realistic-looking synthetic datasets anchored to an observed dataset while making the assumed causal structure explicit. Because the mechanisms are learned from real data and noise is bootstrapped, substantially different DAGs can yield resimulated data with similar marginals and pairwise structure; however, scoring the resulting data with the wrong graph should degrade in a systematic way. This helps separate “looks plausible” from “fits the causal structure.”

Model family (GNM)

For each node Y with parents Pa(Y), the fitted mechanism is a general-noise model

  Y = g(Pa(Y), e)

where e is a bootstrapped noise term and g is learned by a small neural network that allows the noise to enter non-additively. One simple realization is a two-stage fit:

  1. Fit μ(x) ≈ E[Y | x] using an MLP on parent features x.
  2. Compute residuals e = y - μ(x) on the training rows, bootstrap e, and train a second network to predict y from (x, e).

At simulation time, sample e by bootstrap and generate y = g(x, e).

Mixed data support

  • Continuous child: uses the general-noise mechanism described above; noise values are bootstrapped (optionally stratified by discrete-parent signatures) and injected as an explicit input to g.
  • Discrete child: uses a softmax classifier; optionally mixes unconditional base rates with parent-conditional probabilities to “turn down” parent influence.

Parent encoding

  • Continuous parents enter as z-scored scalars.
  • Discrete parents enter as one-hot blocks (with a configurable maximum number of levels).

Missingness

Training is done node-by-node using only rows where the child and all of its parents are observed. Rows with any missing among {Y} ∪ Pa(Y) are skipped for that node. Simulation produces complete samples given the learned mechanisms.

Notes and limitations

  • This is intentionally a lightweight, dependency-minimal mechanism learner (single hidden layer, SGD, L2 decay), intended for simulation fidelity and portability rather than best-in-class predictive modeling.
  • Because the simulator runs forward under the supplied DAG, unrealistic samples may occur when simulated parent configurations leave the support of the training data (extrapolation). Optional diagnostics may warn when parent z-scores become extreme.
  • The GNM is strictly more expressive than the ANM: it can represent non-additive effects of noise on the child given parents, while still using bootstrapped noise anchored to the observed dataset.
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final class 
    Represents a report for a single node in a trained directed acyclic graph (DAG) structure.
    static final class 
    Represents a configuration class containing various hyperparameters and settings used for the simulation and training of a directed acyclic graph (DAG) model.
    static final class 
    Represents the result of a simulation, containing continuous and discrete data for variables, metadata about the simulation, and support for generating reports.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Constructs a new instance of the TrainedDagSimulatorGNM class.
  • Method Summary

    Modifier and Type
    Method
    Description
    double[]
    childProbsGiven(int childIdx, double[] contRow, int[] discRow)
    For a discrete child, the generative class distribution p(Y | pa) given the parent values in contRow / discRow: exactly the distribution generateChild(int, double[], int[], Random) samples from, including any lambdaParents mixing with base rates.
    void
    fillRowFromData(DataSet source, int row, double[] contRow, int[] discRow)
    Copies one observed row into the parallel continuous / discrete row arrays used by the mechanisms, so a child can be generated conditional on an observed parent configuration.
    void
    fit()
    Fit one mechanism per node given its parents in the supplied DAG.
    void
    generateChild(int childIdx, double[] contRow, int[] discRow, Random rng)
    Generates one value of node childIdx from its fitted mechanism, reading the parents from contRow / discRow and writing the result into the child's slot of the same arrays.
    Human-readable report (one block per node).
    Structured per-node reports.
    int
    Returns the number of variables.
    boolean
    Reports whether a variable is discrete.
    static boolean
    isMissingCell(DataSet data, int row, int col)
    True if the cell holds a missing value: NaN or an infinity for a continuous variable, or a negative category code for a discrete variable.
    double
    predictNode(int nodeIndex, DataSet data, int row)
    Returns the predicted value for the given node at the given row of the supplied dataset, using the already-fitted mechanism for that node.
    double[]
    predictNodeProbs(int nodeIndex, DataSet data, int row)
    Returns the predicted probability distribution for the given node at the given row of the supplied dataset.
    static int
    safeGetInt(DataSet data, int row, int col)
    Safely retrieves an integer value from the specified row and column of a DataSet.
    simulate(int nSamples)
    Simulates data by generating continuous and discrete values for all variables in the trained directed acyclic graph (DAG) model.
    simulate(int nSamples, long seed)
    Simulates data by generating continuous and discrete values for all variables in the trained directed acyclic graph (DAG) model.
    withReducedParents(int nodeIndex, int[] reducedParentIndices, long seed)
    Returns a new simulator that is identical to this one except that the mechanism for nodeIndex has been retrained using only the specified reduced parent set.
    void
    Writes a human-readable textual representation of the fit report to the specified file.

    Methods inherited from class Object

    equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • TrainedDagSimulatorGNM

      public TrainedDagSimulatorGNM(DataSet data, Graph dag, TrainedDagSimulatorGNM.Params params)
      Constructs a new instance of the TrainedDagSimulatorGNM class. This simulator utilizes a directed acyclic graph (DAG) representation and corresponding data to model variable relationships and simulate values based on learned mechanisms.
      Parameters:
      data - A DataSet object containing the variables and observations to be utilized in the simulation. Must not be null.
      dag - A Graph object representing the structure of the DAG to be used. The DAG must be acyclic and must also not be null.
      params - An optional Params object containing additional configuration parameters for the simulator. If this parameter is null, default parameters will be used.
      Throws:
      NullPointerException - If data or dag is null.
      IllegalArgumentException - If dag contains cycles (i.e., is not a valid DAG).
  • Method Details

    • withReducedParents

      public TrainedDagSimulatorGNM withReducedParents(int nodeIndex, int[] reducedParentIndices, long seed)
      Returns a new simulator that is identical to this one except that the mechanism for nodeIndex has been retrained using only the specified reduced parent set. All other node mechanisms are reused unchanged from this instance — they are not refitted.

      This is the correct way to isolate the contribution of a single edge: hold all other mechanisms constant and retrain only the affected child, rather than refitting the whole graph on a modified DAG.

      fit() must have been called on this instance before calling this method.

      Parameters:
      nodeIndex - dataset column index of the node to retrain
      reducedParentIndices - dataset column indices of the parents to use (a strict subset of the node's current parents)
      seed - random seed for retraining
      Returns:
      a new TrainedDagSimulatorGNM with the hybrid mechanism array
      Throws:
      IllegalStateException - if fit() has not been called
    • isMissingCell

      public static boolean isMissingCell(DataSet data, int row, int col)
      True if the cell holds a missing value: NaN or an infinity for a continuous variable, or a negative category code for a discrete variable. Tetrad encodes discrete missing values as DiscreteVariable.MISSING_VALUE (−99); since valid category codes are non-negative, any negative code is treated as missing.
      Parameters:
      data - the dataset
      row - the row index
      col - the column index
      Returns:
      whether the cell is missing
    • safeGetInt

      public static int safeGetInt(DataSet data, int row, int col)
      Safely retrieves an integer value from the specified row and column of a DataSet. Attempts to retrieve the value as an integer, falling back to processing it as a double if an exception occurs. If the value is not finite, it returns -1.
      Parameters:
      data - the DataSet from which the value is to be retrieved
      row - the row index from which the value is to be extracted
      col - the column index from which the value is to be extracted
      Returns:
      the integer value at the specified row and column, or -1 if the value is non-finite or an error occurs
    • getFitReportText

      public String getFitReportText()
      Human-readable report (one block per node).
      Returns:
      A string containing
    • getNodeReports

      public List<TrainedDagSimulatorGNM.NodeReport> getNodeReports()
      Structured per-node reports.
      Returns:
      An unmodifiable list of node reports, each containing fit metrics for a node.
    • writeFitReportTxt

      public void writeFitReportTxt(File file) throws IOException
      Writes a human-readable textual representation of the fit report to the specified file. The report contains information about the trained DAG simulator, including parameters and per-node reports on training fit metrics.
      Parameters:
      file - The target file where the fit report will be written. Must be writable and not null.
      Throws:
      IOException - If an error occurs while writing to the file.
    • predictNode

      public double predictNode(int nodeIndex, DataSet data, int row)
      Returns the predicted value for the given node at the given row of the supplied dataset, using the already-fitted mechanism for that node. The dataset does not need to be the training data — this is intended for OOS evaluation on held-out folds.
      Parameters:
      nodeIndex - dataset column index of the node to predict
      data - the dataset containing the row to predict
      row - the row index
      Returns:
      predicted value, or NaN for root nodes
      Throws:
      IllegalStateException - if fit() has not been called
    • predictNodeProbs

      public double[] predictNodeProbs(int nodeIndex, DataSet data, int row)
      Returns the predicted probability distribution for the given node at the given row of the supplied dataset. For discrete nodes this is the full softmax distribution. For continuous nodes this is a single-element array containing the conditional mean prediction. Returns null for root nodes. Intended for OOS cross-entropy evaluation in k-fold CV.
      Parameters:
      nodeIndex - dataset column index of the node
      data - the held-out dataset
      row - row index within that dataset
      Returns:
      probability array, or null for root nodes
      Throws:
      IllegalStateException - if fit() has not been called
    • fit

      public void fit()
      Fit one mechanism per node given its parents in the supplied DAG.
    • simulate

      public TrainedDagSimulatorGNM.SimResult simulate(int nSamples)
      Simulates data by generating continuous and discrete values for all variables in the trained directed acyclic graph (DAG) model. It performs the simulation using a specified number of samples and a seed for random number generation.
      Parameters:
      nSamples - The number of samples to generate during the simulation. Must be a positive integer.
      Returns:
      A SimResult object containing the simulated continuous and discrete values, along with additional metadata related to the DAG and variables used in the simulation.
    • fillRowFromData

      public void fillRowFromData(DataSet source, int row, double[] contRow, int[] discRow)
      Copies one observed row into the parallel continuous / discrete row arrays used by the mechanisms, so a child can be generated conditional on an observed parent configuration.
      Parameters:
      source - the dataset to read from (any dataset over these variables)
      row - the row index in source
      contRow - output array of length getNumVariables()
      discRow - output array of length getNumVariables()
    • generateChild

      public void generateChild(int childIdx, double[] contRow, int[] discRow, Random rng)
      Generates one value of node childIdx from its fitted mechanism, reading the parents from contRow / discRow and writing the result into the child's slot of the same arrays. Other slots are untouched. Safe to call concurrently from multiple threads.
      Parameters:
      childIdx - index of the child in the variable list
      contRow - continuous slots (parents read; child written if continuous)
      discRow - discrete slots (parents read; child written if discrete)
      rng - source of noise / categorical draws
      Throws:
      IllegalStateException - if fit() has not been called
    • childProbsGiven

      public double[] childProbsGiven(int childIdx, double[] contRow, int[] discRow)
      For a discrete child, the generative class distribution p(Y | pa) given the parent values in contRow / discRow: exactly the distribution generateChild(int, double[], int[], Random) samples from, including any lambdaParents mixing with base rates.
      Parameters:
      childIdx - index of a discrete child
      contRow - continuous parent slots
      discRow - discrete parent slots
      Returns:
      a fresh probability array over the child's categories
      Throws:
      IllegalArgumentException - if the child is not discrete
      IllegalStateException - if fit() has not been called
    • getNumVariables

      public int getNumVariables()
      Returns the number of variables.
      Returns:
      the length of the row arrays used by fillRowFromData(DataSet, int, double[], int[]) and generateChild(int, double[], int[], Random)
    • isDiscreteVariable

      public boolean isDiscreteVariable(int idx)
      Reports whether a variable is discrete.
      Parameters:
      idx - variable index
      Returns:
      true if the variable at idx is discrete
    • simulate

      public TrainedDagSimulatorGNM.SimResult simulate(int nSamples, long seed)
      Simulates data by generating continuous and discrete values for all variables in the trained directed acyclic graph (DAG) model. The simulation uses a specified number of samples and a seed for random number generation, ensuring reproducibility.
      Parameters:
      nSamples - The number of samples to generate during the simulation. Must be a positive integer greater than or equal to 1.
      seed - A long value representing the seed for the random number generator. Provides deterministic simulation results when the same seed is used.
      Returns:
      A SimResult object containing the simulated continuous and discrete values, along with metadata about the DAG and the set of variables used in the simulation.
      Throws:
      IllegalArgumentException - If nSamples is less than 1.