code: ///////////////////////////////////////////////////////////////////////////////
  // 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.session;
  
  import edu.cmu.tetrad.util.TetradLogger;
  
  import java.util.*;
  import java.util.prefs.Preferences;
  
  /**
   * Runs a simulation edu.cmu.tetrad.study for a session which traverses a subtree of the
   * session graph in depth-first order and executes each node encountered a
   * specified number of times.
   *
   * @author Joseph Ramsey
   */
  public final class SimulationStudy {
  
      /**
       * The session, nodes of which this simulation edu.cmu.tetrad.study is executing.
       */
      private Session session;
  
      /**
       * Support for firing SessionEvent's.
       */
      private transient SessionSupport sessionSupport;
  
      /**
       * The set of nodes with models; only these should be executed.
       */
      private Set<SessionNode> nodesToExecute;
  
      //===========================CONSTRUCTORS==============================//
  
      /**
       * Constructs a new simulation edu.cmu.tetrad.study for the given session.
       */
      public SimulationStudy(Session session) {
          if (session == null) {
              throw new NullPointerException();
          }
  
          this.session = session;
  
          // Cleanup: when session nodes are removed from the session,
          // remove their repetition numbers from the repetitions map.
          session.addSessionListener(new SessionAdapter() {
              public void nodeRemoved(SessionEvent e) {
                  SessionNode node = e.getNode();
                  removeRepetition(node);
              }
          });
      }
  
      //===========================PUBLIC METHODS============================//
  
      /**
       * Sets the number of times the given node (and all of its children) will be
       * executed each time it is encountered in a depth first traversal of the
       * tree.
       *
       * @param repetition the repetition, an integer > 0.
       */
      public void setRepetition(SessionNode node, int repetition) {
          if (node == null) {
              throw new NullPointerException();
          }
  
          if (repetition <= 0) {
              throw new IllegalArgumentException(
                      "Repeat must be > 0: " + repetition);
          }
  
          node.setRepetition(repetition);
          getSessionSupport().fireRepetitionChanged(node);
      }
  
      /**
       * Gets the repeition of the given node. If the repetition of a node has not
       * been set, it is assumed to be 1.
       *
       * @see #setRepetition
       */
      public static int getRepetition(SessionNode node) {
          if (node.getRepetition() < 1) {
              node.setRepetition(1);
          }
  
          return node.getRepetition();
      }
  
      /**
       * Executes the given node the specified number of times.
       *
       * @see #getRepetition
       */
      public void execute(SessionNode sessionNode, boolean overwrite) {
          if (!this.session.contains(sessionNode)) {
              throw new IllegalArgumentException("Session node not in the " +
                      "session: " + sessionNode.getDisplayName());
          }
  
          this.nodesToExecute = this.session.getNodes();
  //        this.nodesToExecute = nodesWithModels();
  
          // Begin the execution, making sure that each node's children are
          // executed in the order of the given tier ordering.
          LinkedList<SessionNode> tierOrdering = new LinkedList<>(getTierOrdering(sessionNode));
          notifyDownstreamOfStart(sessionNode);
  
          boolean doRepetition = true;
          boolean simulation = true;
  
          TetradLogger.getInstance().forceLogMessage("\n\n===STARTING SIMULATION STUDY===");
          long time1 = System.currentTimeMillis();
  
          execute(tierOrdering, doRepetition, simulation, overwrite);
  
          TetradLogger.getInstance().forceLogMessage("\n\n===FINISHING SIMULATION STUDY===");
          long time2 = System.currentTimeMillis();
          TetradLogger.getInstance().forceLogMessage("Elapsed time = " + (time2 - time1) / 1000. + " s");
      }
  
      public boolean createDescendantModels(final SessionNode sessionNode,
                                            boolean overwrite) {
          if (!session.contains(sessionNode)) {
              throw new IllegalArgumentException("Session node not in the " +
                      "session: " + sessionNode.getDisplayName());
          }
  
          this.nodesToExecute = this.session.getNodes();
  
          // Begin the execution, making sure that each node's children are
          // executed in the order of the given tier ordering.
          LinkedList<SessionNode> tierOrdering = getTierOrdering(sessionNode);
  
          if (sessionNode.getModel() != null) {
              tierOrdering.remove(sessionNode);
          }
  
          notifyDownstreamOfStart(sessionNode);
          boolean doRepetition = false;
          boolean simulation = true;
          return execute(tierOrdering, doRepetition, simulation, overwrite);
      }
  
      /**
       * Adds a session listener.
       */
      public void addSessionListener(SessionListener l) {
          getSessionSupport().addSessionListener(l);
      }
  
      //===========================PRIVATE METHODS===========================//
  
      private HashSet<SessionNode> nodesWithModels() {
          HashSet<SessionNode> nodesWithModels = new HashSet<>();
  
          for (SessionNode node : session.getNodes()) {
              if (node.getModel() != null) {
                  nodesWithModels.add(node);
              }
          }
  
          return nodesWithModels;
      }
  
      /**
       * Notify session nodes (and their parameters) downstream that a new
       * execution has begun of a simulation edu.cmu.tetrad.study.
       */
      private void notifyDownstreamOfStart(final SessionNode sessionNode) {
          SessionSupport sessionSupport = new SessionSupport(this);
          sessionSupport.addSessionListener(sessionNode.getSessionHandler());
          sessionSupport.fireExecutionStarted();
      }
  
      /**
       * Executes the given node the specified number of times. Executes the
       * children of the node. If the node has more than one child, the nodes are
       * executed in the order of the given tier ordering. This needs to be a tier
       * ordering over all of the nodes in the graph.
       *
       * @see #getRepetition
       */
      private boolean execute(LinkedList<SessionNode> tierOrdering, boolean doRepetition,
                              boolean simulation, boolean overwrite) {
          if (tierOrdering.isEmpty()) {
              return true;
          }
  
          SessionNode sessionNode = tierOrdering.getFirst();
  
          if (!session.contains(sessionNode)) {
              throw new IllegalArgumentException("Session node not in the " +
                      "session: " + sessionNode.getDisplayName());
          }
  
          // Only fill in nodes that were already filled in.
          if (!nodesToExecute.contains(sessionNode)) {
              return false;
          }
  
  
          // Assume a node is repeated n times. Each time, the node's
          // model has to be destroyed first, which causes all of the
          // node models downstream to be destroyed in cascade through
          // events. (Please don't mess with the event structure!!) Then
          // the node's model is created. Then each of the child nodes'
          // models is created in the order of the tier ordering, unless
          // the model for a particular node already exists, in which
          // cases it is not created again. (This avoids repetition.)
          // jdramsey 1/11/01
          int repetition = doRepetition ? getRepetition(sessionNode) : 1;
  
          Preferences.userRoot().putBoolean("errorFound", false);
  
          for (int i = 0; i < repetition; i++) {
              if (Preferences.userRoot().getBoolean("experimental", false) &&
                      Preferences.userRoot().getBoolean("errorFound", false)) {
  //                break;
              }
  
              if (!overwrite && sessionNode.getModel() != null) {
                  return false;
              }
  
              sessionNode.destroyModel();
  
              try {
  
                  if (repetition > 1) {
                      TetradLogger.getInstance().forceLogMessage("\nREPETITION #" + (i + 1) + " FOR "
                              + sessionNode.getDisplayName() + "\n");
                  }
  
                  boolean created = sessionNode.createModel(simulation);
  
                  if (created) {
                      SessionModel source = sessionNode.getModel();
                      Map<String, String> paramSettings = new LinkedHashMap<>();
                      collectParentParamSettings(sessionNode, paramSettings);
  
                      if (source instanceof SimulationParamsSource) {
                          ((SimulationParamsSource) source).setAllParamSettings(paramSettings);
                      }
                  }
  
                  if (!created) {
                      return false;
                  }
              } catch (RuntimeException e) {
                  e.printStackTrace();
                  return false;
              }
  
              LinkedList<SessionNode> _tierOrdering = new LinkedList<>(tierOrdering);
              _tierOrdering.removeFirst();
              boolean success =
                      execute(_tierOrdering, doRepetition, simulation, overwrite);
  
              if (!success) {
                  return false;
              }
          }
  
          return true;
      }
  
      private void collectParentParamSettings(SessionNode sessionNode,
                                              Map<String, String> paramSettings) {
          for (SessionNode parent : sessionNode.getParents()) {
              collectParentParamSettings(parent, paramSettings);
          }
  
  //        if (sessionNode.getModel() instanceof SimulationParamsSource) {
  //            paramSettings.putAll(((SimulationParamsSource) sessionNode.getModel()).getParamSettings());
  //        }
      }
  
      /**
       * Removes the repetition number for the given node. If it's still in the
       * graph, its repetition will be 1.
       */
      private static void removeRepetition(SessionNode sessionNode) {
          sessionNode.setRepetition(1);
      }
  
      /**
       * This method returns the nodes of a digraph in such an order that as one
       * iterates through the list, the parents of each node have already been
       * encountered in the list.
       *
       * @return a tier ordering for the nodes in this graph.
       */
      private LinkedList<SessionNode> getTierOrdering(SessionNode node) {
          Session session = this.session;
          Set<SessionNode> sessionNodes = session.getNodes();
  
          LinkedList<SessionNode> found = new LinkedList<>();
          Set<SessionNode> notFound = new HashSet<>();
  
          // The getVariableNodes() method already returns a copy, so there's no
          // need to make a new copy.
          notFound.addAll(sessionNodes);
  
          while (!notFound.isEmpty()) {
              for (Iterator<SessionNode> it = notFound.iterator(); it.hasNext(); ) {
                  SessionNode sessionNode = it.next();
  
                  if (found.containsAll(sessionNode.getParents())) {
                      found.add(sessionNode);
                      it.remove();
                  }
              }
          }
  
          found.retainAll(getDescendants(node));
          return found;
      }
  
      public static Set getDescendants(SessionNode node) {
          HashSet<SessionNode> descendants = new HashSet<>();
          doChildClosureVisit(node, descendants);
          return descendants;
      }
  
      /**
       * closure under the child relation
       */
      private static void doChildClosureVisit(SessionNode node, Set<SessionNode> closure) {
          if (!closure.contains(node)) {
              closure.add(node);
              Collection<SessionNode> children = node.getChildren();
  
              for (SessionNode child : children) {
                  doChildClosureVisit(child, closure);
              }
          }
      }
  
      private SessionSupport getSessionSupport() {
          if (this.sessionSupport == null) {
              this.sessionSupport = new SessionSupport(this);
          }
          return this.sessionSupport;
      }
  
      public Session getSession() {
          return session;
      }
  }
  
  
  
  
  
  