gt:                     _y = edge.getNode2();
context: ///////////////////////////////////////////////////////////////////////////////
  // 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.DataSet;
  import edu.cmu.tetrad.data.IKnowledge;
  import edu.cmu.tetrad.data.Knowledge2;
  import edu.cmu.tetrad.graph.*;
  import edu.cmu.tetrad.sem.DagScorer;
  import edu.cmu.tetrad.sem.Scorer;
  import edu.cmu.tetrad.sem.SemIm;
  import edu.cmu.tetrad.util.TetradLogger;
  
  import java.text.DecimalFormat;
  import java.text.NumberFormat;
  import java.util.*;
  
  /**
   * Best Fit Finder using the GES algorithm.
   * </p>
   * Improves the P value of a SEM IM by adding, removing, or reversing single edges.
   *
   * @author Joseph Ramsey
   */
  
  public final class BffGes implements Bff {
      private IKnowledge knowledge = new Knowledge2();
      private Graph graph;
      private double alpha = 0.05;
      private double highPValueAlpha = 0.05;
      private final NumberFormat nf = new DecimalFormat("0.0#########");
      private Set<GraphWithPValue> significantModels = new HashSet<>();
      private SemIm originalSemIm;
      private SemIm newSemIm;
      private Scorer scorer;
      private Graph newDag;
  
      public BffGes(Graph graph, DataSet data) {
          if (graph == null) throw new NullPointerException("Graph not specified.");
  
          boolean allowArbitraryOrientations = true;
          boolean allowNewColliders = true;
          DagInPatternIterator iterator = new DagInPatternIterator(graph, getKnowledge(), allowArbitraryOrientations,
                  allowNewColliders);
          graph = iterator.next();
          graph = SearchGraphUtils.patternForDag(graph);
  
          if (GraphUtils.containsBidirectedEdge(graph)) {
              throw new IllegalArgumentException("Contains bidirected edge.");
          }
  
          this.graph = graph;
          this.scorer = new DagScorer(data);
      }
  
      private void saveModelIfSignificant(Graph graph) {
          double pValue = scoreGraph(graph).getPValue();
  
          if (pValue > alpha) {
              getSignificantModels().add(new GraphWithPValue(graph, pValue));
          }
      }
  
      public void setNewDag(Graph newDag) {
          this.newDag = newDag;
      }
  
      public Graph getNewDag() {
          return newDag;
      }
  
      public static class GraphWithPValue {
          private Graph graph;
          private double pValue;
  
          public GraphWithPValue(Graph graph, double pValue) {
              this.graph = graph;
              this.pValue = pValue;
          }
  
          public Graph getGraph() {
              return graph;
          }
  
          public double getPValue() {
              return pValue;
          }
  
          public int hashCode() {
              return 17 * graph.hashCode();
          }
  
          public boolean equals(Object o) {
              if (o == null) return false;
              if (!(o instanceof GraphWithPValue)) return false;
              GraphWithPValue p = (GraphWithPValue) o;
              return (p.graph.equals(graph));
          }
      }
  
      public Score scoreGraph(Graph graph) {
          Graph dag = SearchGraphUtils.dagFromPattern(graph, getKnowledge());
  
          if (dag == null) {
              return Score.negativeInfinity();
          }
  
          scorer.score(dag);
          return new Score(scorer);
      }
  
      public Graph getGraph() {
          return graph;
      }
  
      public SemIm getOriginalSemIm() {
          return originalSemIm;
      }
  
      public SemIm getNewSemIm() {
          return newSemIm;
      }
  
      public void setHighPValueAlpha(double highPValueAlpha) {
          this.highPValueAlpha = highPValueAlpha;
      }
  
      public Score scoreDag(Graph dag) {
  //        SemPm semPm = new SemPm(dag);
  //        SemEstimator semEstimator = new SemEstimator(dataSet, semPm, new SemOptimizerEm());
  //        semEstimator.estimate();
  //        SemIm estimatedSem = semEstimator.getEstimatedSem();
  //        return new Score(estimatedSem);
  
          scorer.score(dag);
          return new Score(scorer);
      }
  
  //    private void removeHighPValueEdges(Graph bestGraph) {
  //        boolean changed = true;
  //
  //        while (changed) {
  //            changed = false;
  //            Score score = scoreGraph(bestGraph);
  //            SemIm estSem = score.getEstimatedSem();
  //
  //            for (Parameter param : estSem.getSemPm().getParameters()) {
  //                if (param.getType() != ParamType.COEF) {
  //                    continue;
  //                }
  //
  //                double p = estSem.getScore(param, 10000);
  //                Edge edge = bestGraph.getEdge(param.getNodeA(), param.getNodeB());
  //
  //                if (p > getHighPValueAlpha()) {
  //                    System.out.println("Removing edge " + edge + " because it has p = " + p);
  //                    bestGraph.removeEdge(edge);
  //                    changed = true;
  //                }
  //            }
  //        }
  //    }
  
      public Graph search() {
          Score score1 = scoreGraph(getGraph());
          double score = score1.getScore();
          System.out.println(getGraph());
          System.out.println(score);
  
          originalSemIm = score1.getEstimatedSem();
  
          saveModelIfSignificant(getGraph());
  
  //        removeHighPValueEdges(getGraph());
  
          // Do forward search.
          score = fes(getGraph(), score);
  
          // Do backward search.
          bes(getGraph(), score);
  
  //        removeHighPValueEdges(getGraph());
  
          setNewDag(SearchGraphUtils.dagFromPattern(getGraph()));
  
          Score _score = scoreGraph(getGraph());
          newSemIm = _score.getEstimatedSem();
  
          return new EdgeListGraph(getGraph());
      }
  
      private double fes(Graph graph, double score) {
          TetradLogger.getInstance().log("info", "** FORWARD EQUIVALENCE SEARCH");
          double bestScore = score;
          TetradLogger.getInstance().log("info", "Initial Score = " + nf.format(bestScore));
  
          Node x, y;
          Set<Node> t = new HashSet<>();
  
          do {
              x = y = null;
              List<Node> nodes = graph.getNodes();
              Collections.shuffle(nodes);
  
              for (int i = 0; i < nodes.size(); i++) {
                  Node _x = nodes.get(i);
  
                  for (Node _y : nodes) {
                      if (_x == _y) {
                          continue;
                      }
  
                      if (graph.isAdjacentTo(_x, _y)) {
                          continue;
                      }
  
                      if (getKnowledge().isForbidden(_x.getName(),
                              _y.getName())) {
                          continue;
                      }
  
                      List<Node> tNeighbors = getTNeighbors(_x, _y, graph);
                      List<Set<Node>> tSubsets = powerSet(tNeighbors);
  
                      for (Set<Node> tSubset : tSubsets) {
  
                          if (!validSetByKnowledge(_x, _y, tSubset, true)) {
                              continue;
                          }
  
                          Graph graph2 = new EdgeListGraph(graph);
  
                          tryInsert(_x, _y, tSubset, graph2, true);
  
                          if (graph2.existsDirectedCycle()) {
                              continue;
                          }
  
                          double evalScore = scoreGraph(graph2).getScore();
  
                          TetradLogger.getInstance().log("edgeEvaluations", "Trying to add " + _x + "-->" + _y + " evalScore = " +
                                  evalScore);
  
                          if (!(evalScore > bestScore && evalScore > score)) {
                              continue;
                          }
  
                          if (!validInsert(_x, _y, tSubset, graph)) {
                              continue;
                          }
  
                          bestScore = evalScore;
                          x = _x;
                          y = _y;
                          t = tSubset;
                      }
                  }
              }
  
              if (x != null) {
                  score = bestScore;
                  insert(x, y, t, graph, true);
                  rebuildPattern(graph);
  
                  saveModelIfSignificant(graph);
  
                  if (scoreGraph(graph).getPValue() > alpha) {
                      return score;
                  }
              }
          } while (x != null);
          return score;
      }
  
      private double bes(Graph graph, double initialScore) {
          TetradLogger.getInstance().log("info", "** BACKWARD ELIMINATION SEARCH");
          TetradLogger.getInstance().log("info", "Initial Score = " + nf.format(initialScore));
          double score = initialScore;
          double bestScore = score;
          Node x, y;
          Set<Node> t = new HashSet<>();
          do {
              x = y = null;
              List<Edge> graphEdges = new ArrayList<>(graph.getEdges());
              Collections.shuffle(graphEdges);
  
              for (Edge edge : graphEdges) {
                  Node _x, _y;
  
                  if (Edges.isUndirectedEdge(edge)) {
                      _x = edge.getNode1();
  