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.search;
  
  import edu.cmu.tetrad.data.CovarianceMatrixOnTheFly;
  import edu.cmu.tetrad.data.DataSet;
  import edu.cmu.tetrad.data.IKnowledge;
  import edu.cmu.tetrad.data.Knowledge2;
  import edu.cmu.tetrad.graph.Graph;
  import edu.cmu.tetrad.graph.Node;
  
  import java.util.Collections;
  
  /**
   * Fast adjacency search followed by robust skew orientation. Checks are done for adding
   * two-cycles. The two-cycle checks do not require non-Gaussianity. The robust skew
   * orientation of edges left or right does.
   *
   * @author Joseph Ramsey
   */
  public final class FasLofs implements GraphSearch {
  
      private final Lofs2.Rule rule;
      // Elapsed time of the search, in milliseconds.
      private long elapsed = 0;
  
      // The data sets being analyzed. They must all have the same variables and the same
      // number of records.
      private DataSet dataSet = null;
  
      // For the Fast Adjacency Search.
      private int depth = -1;
  
      // For the SEM BIC score, for the Fast Adjacency Search.
      private double penaltyDiscount = 1;
  
      // Knowledge the the search will obey, of forbidden and required edges.
      private IKnowledge knowledge = new Knowledge2();
  
      /**
       * @param dataSet These datasets to analyze.
       */
      public FasLofs(DataSet dataSet, Lofs2.Rule rule) {
          this.dataSet = dataSet;
          this.rule = rule;
      }
  
      //======================================== PUBLIC METHODS ====================================//
  
      /**
       * Runs the search on the concatenated data, returning a graph, possibly cyclic, possibly with
       * two-cycles. Runs the fast adjacency search (FAS, Spirtes et al., 2000) follows by a modification
       * of the robust skew rule (Pairwise Likelihood Ratios for Estimation of Non-Gaussian Structural
       * Equation Models, Smith and Hyvarinen), together with some heuristics for orienting two-cycles.
       *
       * @return the graph. Some of the edges may be undirected (though it shouldn't be many in most cases)
       * and some of the adjacencies may be two-cycles.
       */
      public Graph search() {
          long start = System.currentTimeMillis();
  
          SemBicScore score = new SemBicScore(new CovarianceMatrixOnTheFly(dataSet));
          score.setPenaltyDiscount(penaltyDiscount);
          IndependenceTest test = new IndTestScore(score, dataSet);
  
          System.out.println("FAS");
  
          FasStable fas = new FasStable(test);
          fas.setDepth(getDepth());
          fas.setVerbose(false);
          fas.setKnowledge(knowledge);
          Graph G0 = fas.search();
  
          System.out.println("LOFS orientation, rule " + rule);
  
          Lofs2 lofs2 = new Lofs2(G0, Collections.singletonList(dataSet));
          lofs2.setRule(rule);
          lofs2.setKnowledge(knowledge);
          Graph graph = lofs2.orient();
  
          System.out.println("Done");
  
          long stop = System.currentTimeMillis();
          this.elapsed = stop - start;
  
          return graph;
      }
  
      /**
       * @return The depth of search for the Fast Adjacency Search (FAS).
       */
      public int getDepth() {
          return depth;
      }
  
      /**
       * @param depth The depth of search for the Fast Adjacency Search (S). The default is -1.
       *              unlimited. Making this too high may results in statistical errors.
       */
      public void setDepth(int depth) {
          this.depth = depth;
      }
  
      /**
       * @return The elapsed time in milliseconds.
       */
      public long getElapsedTime() {
          return elapsed;
      }
  
      /**
       * @return Returns the penalty discount used for the adjacency search. The default is 1,
       * though a higher value is recommended, say, 2, 3, or 4.
       */
      public double getPenaltyDiscount() {
          return penaltyDiscount;
      }
  
      /**
       * @param penaltyDiscount Sets the penalty discount used for the adjacency search.
       *                        The default is 1, though a higher value is recommended, say,
       *                        2, 3, or 4.
       */
      public void setPenaltyDiscount(double penaltyDiscount) {
          this.penaltyDiscount = penaltyDiscount;
      }
  
      /**
       * @return the current knowledge.
       */
      public IKnowledge getKnowledge() {
          return knowledge;
      }
  
      /**
       * @param knowledge Knowledge of forbidden and required edges.
       */
      public void setKnowledge(IKnowledge knowledge) {
          this.knowledge = knowledge;
      }
  
      //======================================== PRIVATE METHODS ====================================//
  
      private boolean knowledgeOrients(Node left, Node right) {
          return knowledge.isForbidden(right.getName(), left.getName()) || knowledge.isRequired(left.getName(), right.getName());
      }
  
  }
  
  
  
  
  
  
  