edu.cmu.tetrad.search.SearchGraphUtils.java Source code

Java tutorial

Introduction

Here is the source code for edu.cmu.tetrad.search.SearchGraphUtils.java

Source

///////////////////////////////////////////////////////////////////////////////
// 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 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 java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.map.MultiKeyMap;

import edu.cmu.tetrad.data.IKnowledge;
import edu.cmu.tetrad.data.Knowledge;
import edu.cmu.tetrad.data.KnowledgeEdge;
import edu.cmu.tetrad.graph.Dag;
import edu.cmu.tetrad.graph.Edge;
import edu.cmu.tetrad.graph.EdgeListGraph;
import edu.cmu.tetrad.graph.Edges;
import edu.cmu.tetrad.graph.Endpoint;
import edu.cmu.tetrad.graph.Graph;
import edu.cmu.tetrad.graph.GraphUtils;
import edu.cmu.tetrad.graph.Node;
import edu.cmu.tetrad.util.ChoiceGenerator;
import edu.cmu.tetrad.util.CombinationGenerator;
import edu.cmu.tetrad.util.TetradLogger;

/**
 * Graph utilities for search algorithms. Lots of orientation method, for instance.
 *
 * @author Joseph Ramsey
 */
public final class SearchGraphUtils {

    /**
     * Orients according to background knowledge.
     */
    public static void pcOrientbk(IKnowledge bk, Graph graph, List<Node> nodes) {
        TetradLogger.getInstance().log("details", "Staring BK Orientation.");
        for (Iterator<KnowledgeEdge> it = bk.forbiddenEdgesIterator(); it.hasNext();) {
            KnowledgeEdge edge = it.next();

            //match strings to variables in the graph.
            Node from = translate(edge.getFrom(), nodes);
            Node to = translate(edge.getTo(), nodes);

            if (from == null || to == null) {
                continue;
            }

            if (graph.getEdge(from, to) == null) {
                continue;
            }

            // Orient to-->from
            graph.removeEdge(from, to);
            graph.addDirectedEdge(to, from);
            //            graph.setEndpoint(from, to, Endpoint.TAIL);
            //            graph.setEndpoint(to, from, Endpoint.ARROW);

            TetradLogger.getInstance().log("knowledgeOrientations",
                    SearchLogUtils.edgeOrientedMsg("IKnowledge", graph.getEdge(to, from)));
        }

        for (Iterator<KnowledgeEdge> it = bk.requiredEdgesIterator(); it.hasNext();) {
            KnowledgeEdge edge = it.next();

            //match strings to variables in this graph
            Node from = translate(edge.getFrom(), nodes);
            Node to = translate(edge.getTo(), nodes);

            if (from == null || to == null) {
                continue;
            }

            if (graph.getEdge(from, to) == null) {
                continue;
            }

            // Orient from-->to
            graph.removeEdge(from, to);
            graph.addDirectedEdge(from, to);

            //            graph.setEndpoint(to, from, Endpoint.TAIL);
            //            graph.setEndpoint(from, to, Endpoint.ARROW);
            TetradLogger.getInstance().log("knowledgeOrientations",
                    SearchLogUtils.edgeOrientedMsg("IKnowledge", graph.getEdge(from, to)));
        }

        TetradLogger.getInstance().log("details", "Finishing BK Orientation.");
    }

    /**
     * Performs step C of the algorithm, as indicated on page xxx of CPS, with the modification that X--W--Y is oriented
     * as X-->W<--Y if W is *determined by* the sepset of (X, Y), rather than W just being *in* the sepset of (X, Y).
     */
    public static void pcdOrientC(SepsetMap set, IndependenceTest test, IKnowledge knowledge, Graph graph) {
        TetradLogger.getInstance().log("info", "Starting Collider Orientation:");

        List<Node> nodes = graph.getNodes();

        for (Node y : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(y);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node x = adjacentNodes.get(combination[0]);
                Node z = adjacentNodes.get(combination[1]);

                // Skip triples that are shielded.
                if (graph.isAdjacentTo(x, z)) {
                    continue;
                }

                List<Node> sepset = set.get(x, z);

                if (sepset == null) {
                    continue;
                }

                List<Node> augmentedSet = new LinkedList<Node>(sepset);
                augmentedSet.add(y);

                //                if (test.splitDetermines(Collections.singletonList(y), x, z)) {
                //                    continue;
                //                }

                if (test.determines(sepset, y)) {
                    TetradLogger.getInstance().log("info", sepset + " determines " + y);
                    continue;
                }

                boolean splitDeterminexSepsetXZ = test.determines(sepset, x) || test.determines(sepset, z);
                boolean splitDeterminesAugmentedSetXZ = test.determines(augmentedSet, x)
                        || test.determines(augmentedSet, z);

                if (!splitDeterminexSepsetXZ && splitDeterminesAugmentedSetXZ) {
                    TetradLogger.getInstance().log("info", sepset + " split determines " + x + " and " + z);
                    continue;
                }

                if (!isArrowpointAllowed(x, y, knowledge) || !isArrowpointAllowed(z, y, knowledge)) {
                    continue;
                }

                graph.setEndpoint(x, y, Endpoint.ARROW);
                graph.setEndpoint(z, y, Endpoint.ARROW);

                TetradLogger.getInstance().log("colliderOrientations", SearchLogUtils.colliderOrientedMsg(x, y, z));
            }
        }

        TetradLogger.getInstance().log("info", "Finishing Collider Orientation.");
    }

    //    /**
    //     * Performs step D of the algorithm, as indicated on page xxx of CPS. This
    //     * method should be called again if it returns true.
    //     *
    //     * <pre>
    //     * Meek-Orient(G, t)
    //     * 1.while orientations can be made, for arbitrary a, b, c, and d:
    //     * 2.    If a --> b, b --> c, a not in adj(c), and Is-Noncollider(a, b, c) then orient b --> c.
    //     * 3.    If a --> b, b --> c, a --- c, then orient a --> c.
    //     * 4.    If a --- b, a --- c, a --- d, c --> b, d --> b, then orient a --> b.
    //     * 5.    If a --> b, b in adj(d) a in adj(c), a --- d, b --> c, c --> d, then orient a --> d.
    //     * </pre>
    //     */
    //    public static void orientUsingMeekRules(IKnowledge knowledge, Graph graph) {
    //        LogUtils.getInstance().info("Starting Orientation Step D.");
    //        boolean changed;
    //
    //        do {
    //            changed = meekR1(graph, knowledge) || meekR2(graph, knowledge) ||
    //                    meekR3(graph, knowledge) || meekR4(graph, knowledge);
    //        } while (changed);
    //
    //        LogUtils.getInstance().info("Finishing Orientation Step D.");
    //    }

    /**
     * Orients using Meek rules, double checking noncolliders locally.
     */
    public static void orientUsingMeekRulesLocally(IKnowledge knowledge, Graph graph, IndependenceTest test,
            int depth) {
        TetradLogger.getInstance().log("info", "Starting Orientation Step D.");
        boolean changed;

        do {
            changed = meekR1Locally(graph, knowledge, test, depth) || meekR2(graph, knowledge)
                    || meekR3(graph, knowledge) || meekR4(graph, knowledge);
        } while (changed);

        TetradLogger.getInstance().log("info", "Finishing Orientation Step D.");
    }

    public static void orientUsingMeekRulesLocally2(IKnowledge knowledge, Graph graph, IndependenceTest test,
            int depth) {
        TetradLogger.getInstance().log("info", "Starting Orientation Step D.");
        boolean changed;

        do {
            changed = meekR1Locally2(graph, knowledge, test, depth) || meekR2(graph, knowledge)
                    || meekR3(graph, knowledge) || meekR4(graph, knowledge);
        } while (changed);

        TetradLogger.getInstance().log("info", "Finishing Orientation Step D.");
    }

    /**
     * Step C of PC; orients colliders using specified sepset. That is, orients x *-* y *-* z as x *-> y <-* z just in
     * case y is in Sepset({x, z}).
     */
    public static void orientCollidersUsingSepsets(SepsetMap set, IKnowledge knowledge, Graph graph) {
        TetradLogger.getInstance().log("details", "Starting Collider Orientation:");

        //        verifySepsetIntegrity(set, graph);

        List<Node> nodes = graph.getNodes();

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node b = adjacentNodes.get(combination[0]);
                Node c = adjacentNodes.get(combination[1]);

                // Skip triples that are shielded.
                if (graph.isAdjacentTo(b, c)) {
                    continue;
                }

                List<Node> sepset = set.get(b, c);

                if (sepset != null && !sepset.contains(a) && isArrowpointAllowed(b, a, knowledge)
                        && isArrowpointAllowed(c, a, knowledge)) {
                    //                    System.out.println("Collider orientation <" + b + ", " + a + ", " + c + "> sepset = " + sepset);

                    graph.setEndpoint(b, a, Endpoint.ARROW);
                    graph.setEndpoint(c, a, Endpoint.ARROW);
                    //                    TetradLogger.getInstance().log("colliderOrientations", SearchLogUtils.colliderOrientedMsg(b, a, c, sepset));
                }
            }
        }

        TetradLogger.getInstance().log("details", "Finishing Collider Orientation.");
    }

    public static void orientCollidersUsingSepsets2(SepsetMap set, IKnowledge knowledge, Graph graph,
            Graph trueGraph) {
        TetradLogger.getInstance().log("details", "Starting Collider Orientation:");

        //        verifySepsetIntegrity(set, graph);

        List<Node> nodes = graph.getNodes();

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node b = adjacentNodes.get(combination[0]);
                Node c = adjacentNodes.get(combination[1]);

                //                System.out.println("Collider orientation <b, a, c> = <" + b + ", " + a + ", " + c + ">");

                // Skip triples that are shielded.
                if (graph.isAdjacentTo(b, c)) {
                    continue;
                }

                List<Node> sepset = set.get(b, c);

                if (graph.getEdge(b, a).getProximalEndpoint(a) == Endpoint.ARROW
                        && graph.getEdge(c, a).getProximalEndpoint(a) == Endpoint.ARROW) {
                    Edge edge1 = trueGraph.getEdge(b, a);
                    Edge edge2 = trueGraph.getEdge(c, a);

                    System.out.println(edge1 + " " + edge2);

                    if (edge1 == null || edge2 == null) {
                        System.out.println(
                                "*** " + graph.getEdge(b, a) + " " + graph.getEdge(c, a) + " " + trueGraph);
                    }

                    if (!(edge1.getProximalEndpoint(a) == Endpoint.ARROW
                            && edge2.getProximalEndpoint(a) == Endpoint.ARROW)) {
                        System.out.println("Sepset(" + b + ", " + c + ") = " + sepset + " a = " + a);
                    }
                }

                if (sepset != null && !sepset.contains(a) && isArrowpointAllowed(b, a, knowledge)
                        && isArrowpointAllowed(c, a, knowledge)) {

                    graph.setEndpoint(b, a, Endpoint.ARROW);
                    graph.setEndpoint(c, a, Endpoint.ARROW);
                    TetradLogger.getInstance().log("colliderOrientations",
                            SearchLogUtils.colliderOrientedMsg(b, a, c, sepset));
                }
            }
        }

        TetradLogger.getInstance().log("details", "Finishing Collider Orientation.");
    }

    public static void orientCollidersLocally(IKnowledge knowledge, Graph graph, IndependenceTest test, int depth) {
        orientCollidersLocally(knowledge, graph, test, depth, null);
    }

    public static void orientCollidersLocally(IKnowledge knowledge, Graph graph, IndependenceTest test, int depth,
            Set<Node> nodesToVisit) {
        TetradLogger.getInstance().log("details", "Starting Collider Orientation:");

        if (nodesToVisit == null) {
            nodesToVisit = new HashSet<Node>(graph.getNodes());
        }

        for (Node a : nodesToVisit) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node b = adjacentNodes.get(combination[0]);
                Node c = adjacentNodes.get(combination[1]);

                // Skip triples that are shielded.
                if (graph.isAdjacentTo(b, c)) {
                    continue;
                }

                if (isArrowpointAllowed1(b, a, knowledge) && isArrowpointAllowed1(c, a, knowledge)) {
                    if (!existsLocalSepsetWith(b, a, c, test, graph, depth)) {
                        graph.setEndpoint(b, a, Endpoint.ARROW);
                        graph.setEndpoint(c, a, Endpoint.ARROW);
                        TetradLogger.getInstance().log("colliderOrientations",
                                SearchLogUtils.colliderOrientedMsg(b, a, c));
                    }
                }
            }
        }

        TetradLogger.getInstance().log("details", "Finishing Collider Orientation.");
    }

    /**
     * Performs step C of the algorithm, as indicated on page xxx of CPS
     */
    public static void orientCollidersLocallyDet(IKnowledge knowledge, Graph graph, IndependenceTest test,
            int depth) {
        TetradLogger.getInstance().log("info", "Starting Collider Orientation:");

        List<Node> nodes = graph.getNodes();

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node b = adjacentNodes.get(combination[0]);
                Node c = adjacentNodes.get(combination[1]);

                // Skip triples that are shielded.
                if (graph.isAdjacentTo(b, c)) {
                    continue;
                }

                if (isArrowpointAllowed1(b, a, knowledge) && isArrowpointAllowed1(c, a, knowledge)) {
                    if (existsLocalSepsetWithoutDet(b, a, c, test, graph, depth)) {
                        graph.setEndpoint(b, a, Endpoint.ARROW);
                        graph.setEndpoint(c, a, Endpoint.ARROW);
                        TetradLogger.getInstance().log("colliderOrientations",
                                SearchLogUtils.colliderOrientedMsg(b, a, c));
                    }
                }
            }
        }

        TetradLogger.getInstance().log("info", "Finishing Collider Orientation.");
    }

    public static boolean existsLocalSepsetWith(Node x, Node y, Node z, IndependenceTest test, Graph graph,
            int depth) {
        Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
        __nodes.addAll(graph.getAdjacentNodes(z));
        __nodes.remove(x);
        __nodes.remove(z);

        List<Node> _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        int _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 1; d <= _depth; d++) {
            if (_nodes.size() >= d) {
                ChoiceGenerator cg2 = new ChoiceGenerator(_nodes.size(), d);
                int[] choice;

                while ((choice = cg2.next()) != null) {
                    List<Node> condSet = GraphUtils.asList(choice, _nodes);

                    if (!condSet.contains(y)) {
                        continue;
                    }

                    //                    LogUtils.getInstance().finest("Trying " + condSet);

                    if (test.isIndependent(x, z, condSet)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public static boolean existsLocalSepsetWithout(Node x, Node y, Node z, IndependenceTest test, Graph graph,
            int depth) {
        Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
        __nodes.addAll(graph.getAdjacentNodes(z));
        __nodes.remove(x);
        __nodes.remove(z);
        List<Node> _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        int _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            if (_nodes.size() >= d) {
                ChoiceGenerator cg2 = new ChoiceGenerator(_nodes.size(), d);
                int[] choice;

                while ((choice = cg2.next()) != null) {
                    List<Node> condSet = GraphUtils.asList(choice, _nodes);

                    if (condSet.contains(y)) {
                        continue;
                    }

                    //            LogUtils.getInstance().finest("Trying " + condSet);

                    if (test.isIndependent(x, z, condSet)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public static boolean existsLocalSepsetWithoutDet(Node x, Node y, Node z, IndependenceTest test, Graph graph,
            int depth) {
        Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
        __nodes.addAll(graph.getAdjacentNodes(z));
        __nodes.remove(x);
        __nodes.remove(z);
        List<Node> _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        int _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            if (_nodes.size() >= d) {
                ChoiceGenerator cg2 = new ChoiceGenerator(_nodes.size(), d);
                int[] choice;

                while ((choice = cg2.next()) != null) {
                    List<Node> condSet = GraphUtils.asList(choice, _nodes);

                    if (condSet.contains(y)) {
                        continue;
                    }

                    if (test.determines(condSet, y)) {
                        continue;
                    }

                    //        LogUtils.getInstance().finest("Trying " + condSet);

                    if (test.isIndependent(x, z, condSet)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    //    /**
    //     * Meek's rule R1 (same as CPS).
    //     */
    //    public static boolean meekR1(Graph graph, IKnowledge knowledge) {
    //        List<Node> nodes = graph.getNodes();
    //        boolean changed = true;
    //
    //        while (changed) {
    //            changed = false;
    //
    //            for (Node a : nodes) {
    //                List<Node> adjacentNodes = graph.getAdjacentNodes(a);
    //
    //                if (adjacentNodes.size() < 2) {
    //                    continue;
    //                }
    //
    //                ChoiceGenerator cg =
    //                        new ChoiceGenerator(adjacentNodes.size(), 2);
    //                int[] combination;
    //
    //                while ((combination = cg.next()) != null) {
    //                    Node b = adjacentNodes.get(combination[0]);
    //                    Node c = adjacentNodes.get(combination[1]);
    //
    //                    // Skip triples that are shielded.
    //                    if (graph.isAdjacentTo(b, c)) {
    //                        continue;
    //                    }
    //
    //                    if (graph.getEndpoint(b, a) == Endpoint.ARROW &&
    //                            graph.isUndirectedFromTo(a, c)) {
    //                        if (isArrowpointAllowed(a, c, knowledge)) {
    //                            graph.setEndpoint(a, c, Endpoint.ARROW);
    //                            SearchLogUtils.logEdgeOriented("Meek R1",
    //                                    graph.getEdge(a, c), LOGGER);
    //                            changed = true;
    //                        }
    //                    }
    //                    else if (graph.getEndpoint(c, a) == Endpoint.ARROW &&
    //                            graph.isUndirectedFromTo(a, b)) {
    //                        if (isArrowpointAllowed(a, b, knowledge)) {
    //                            graph.setEndpoint(a, b, Endpoint.ARROW);
    //                            SearchLogUtils.logEdgeOriented("Meek R1",
    //                                    graph.getEdge(a, b), LOGGER);
    //                            changed = true;
    //                        }
    //                    }
    //                }
    //            }
    //        }
    //
    //        return changed;
    //    }

    /**
     * Orient away from collider.
     */
    public static boolean meekR1Locally(Graph graph, IKnowledge knowledge, IndependenceTest test, int depth) {
        List<Node> nodes = graph.getNodes();
        boolean changed = true;

        while (changed) {
            changed = false;

            for (Node a : nodes) {
                List<Node> adjacentNodes = graph.getAdjacentNodes(a);

                if (adjacentNodes.size() < 2) {
                    continue;
                }

                ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
                int[] combination;

                while ((combination = cg.next()) != null) {
                    Node b = adjacentNodes.get(combination[0]);
                    Node c = adjacentNodes.get(combination[1]);

                    // Skip triples that are shielded.
                    if (graph.isAdjacentTo(b, c)) {
                        continue;
                    }

                    if (graph.getEndpoint(b, a) == Endpoint.ARROW && graph.isUndirectedFromTo(a, c)) {
                        if (existsLocalSepsetWithout(b, a, c, test, graph, depth)) {
                            continue;
                        }

                        if (isArrowpointAllowed(a, c, knowledge)) {
                            graph.setEndpoint(a, c, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R1", graph.getEdge(a, c)));
                            changed = true;
                        }
                    } else if (graph.getEndpoint(c, a) == Endpoint.ARROW && graph.isUndirectedFromTo(a, b)) {
                        if (existsLocalSepsetWithout(b, a, c, test, graph, depth)) {
                            continue;
                        }

                        if (isArrowpointAllowed(a, b, knowledge)) {
                            graph.setEndpoint(a, b, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R1", graph.getEdge(a, b)));
                            changed = true;
                        }
                    }
                }
            }
        }

        return changed;
    }

    public static boolean meekR1Locally2(Graph graph, IKnowledge knowledge, IndependenceTest test, int depth) {
        List<Node> nodes = graph.getNodes();
        boolean changed = true;

        while (changed) {
            changed = false;

            for (Node a : nodes) {
                List<Node> adjacentNodes = graph.getAdjacentNodes(a);

                if (adjacentNodes.size() < 2) {
                    continue;
                }

                ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
                int[] combination;

                while ((combination = cg.next()) != null) {
                    Node b = adjacentNodes.get(combination[0]);
                    Node c = adjacentNodes.get(combination[1]);

                    // Skip triples that are shielded.
                    if (graph.isAdjacentTo(b, c)) {
                        continue;
                    }

                    if (graph.getEndpoint(b, a) == Endpoint.ARROW && graph.isUndirectedFromTo(a, c)) {
                        if (existsLocalSepsetWithoutDet(b, a, c, test, graph, depth)) {
                            continue;
                        }

                        if (isArrowpointAllowed(a, c, knowledge)) {
                            graph.setEndpoint(a, c, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R1", graph.getEdge(a, c)));
                            changed = true;
                        }
                    } else if (graph.getEndpoint(c, a) == Endpoint.ARROW && graph.isUndirectedFromTo(a, b)) {
                        if (existsLocalSepsetWithoutDet(b, a, c, test, graph, depth)) {
                            continue;
                        }

                        if (isArrowpointAllowed(a, b, knowledge)) {
                            graph.setEndpoint(a, b, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R1", graph.getEdge(a, b)));
                            changed = true;
                        }
                    }
                }
            }
        }

        return changed;
    }

    /**
     * If
     */
    public static boolean meekR2(Graph graph, IKnowledge knowledge) {
        List<Node> nodes = graph.getNodes();
        boolean changed = false;

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 2) {
                continue;
            }

            ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
            int[] combination;

            while ((combination = cg.next()) != null) {
                Node b = adjacentNodes.get(combination[0]);
                Node c = adjacentNodes.get(combination[1]);

                if (graph.isDirectedFromTo(b, a) && graph.isDirectedFromTo(a, c)
                        && graph.isUndirectedFromTo(b, c)) {
                    if (isArrowpointAllowed(b, c, knowledge)) {
                        graph.setEndpoint(b, c, Endpoint.ARROW);
                        TetradLogger.getInstance().log("impliedOrientation",
                                SearchLogUtils.edgeOrientedMsg("Meek R2", graph.getEdge(b, c)));
                    }
                } else if (graph.isDirectedFromTo(c, a) && graph.isDirectedFromTo(a, b)
                        && graph.isUndirectedFromTo(c, b)) {
                    if (isArrowpointAllowed(c, b, knowledge)) {
                        graph.setEndpoint(c, b, Endpoint.ARROW);
                        TetradLogger.getInstance().log("impliedOrientation",
                                SearchLogUtils.edgeOrientedMsg("Meek R2", graph.getEdge(c, b)));
                    }
                }
            }
        }

        return changed;
    }

    /**
     * Meek's rule R3. If a--b, a--c, a--d, c-->b, c-->b, then orient a-->b.
     */
    public static boolean meekR3(Graph graph, IKnowledge knowledge) {

        List<Node> nodes = graph.getNodes();
        boolean changed = false;

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 3) {
                continue;
            }

            for (Node b : adjacentNodes) {
                List<Node> otherAdjacents = new LinkedList<Node>(adjacentNodes);
                otherAdjacents.remove(b);

                if (!graph.isUndirectedFromTo(a, b)) {
                    continue;
                }

                ChoiceGenerator cg = new ChoiceGenerator(otherAdjacents.size(), 2);
                int[] combination;

                while ((combination = cg.next()) != null) {
                    Node c = otherAdjacents.get(combination[0]);
                    Node d = otherAdjacents.get(combination[1]);

                    if (graph.isAdjacentTo(c, d)) {
                        continue;
                    }

                    if (!graph.isUndirectedFromTo(a, c)) {
                        continue;
                    }

                    if (!graph.isUndirectedFromTo(a, d)) {
                        continue;
                    }

                    if (graph.isDirectedFromTo(c, b) && graph.isDirectedFromTo(d, b)) {
                        if (isArrowpointAllowed(a, b, knowledge)) {
                            graph.setEndpoint(a, b, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R3", graph.getEdge(a, b)));
                            changed = true;
                            break;
                        }
                    }
                }
            }
        }

        return changed;
    }

    public static boolean meekR4(Graph graph, IKnowledge knowledge) {
        if (knowledge == null) {
            return false;
        }

        List<Node> nodes = graph.getNodes();
        boolean changed = false;

        for (Node a : nodes) {
            List<Node> adjacentNodes = graph.getAdjacentNodes(a);

            if (adjacentNodes.size() < 3) {
                continue;
            }

            for (Node d : adjacentNodes) {
                if (!graph.isAdjacentTo(a, d)) {
                    continue;
                }

                List<Node> otherAdjacents = new LinkedList<Node>(adjacentNodes);
                otherAdjacents.remove(d);

                ChoiceGenerator cg = new ChoiceGenerator(otherAdjacents.size(), 2);
                int[] combination;

                while ((combination = cg.next()) != null) {
                    Node b = otherAdjacents.get(combination[0]);
                    Node c = otherAdjacents.get(combination[1]);

                    if (!graph.isUndirectedFromTo(a, b)) {
                        continue;
                    }

                    if (!graph.isUndirectedFromTo(a, c)) {
                        continue;
                    }

                    //                    if (!isUnshieldedNoncollider(c, a, b, graph)) {
                    //                        continue;
                    //                    }

                    if (graph.isDirectedFromTo(b, c) && graph.isDirectedFromTo(d, c)) {
                        if (isArrowpointAllowed(a, c, knowledge)) {
                            graph.setEndpoint(a, c, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R4", graph.getEdge(a, c)));
                            changed = true;
                            break;
                        }
                    } else if (graph.isDirectedFromTo(c, d) && graph.isDirectedFromTo(d, b)) {
                        if (isArrowpointAllowed(a, b, knowledge)) {
                            graph.setEndpoint(a, b, Endpoint.ARROW);
                            TetradLogger.getInstance().log("impliedOrientation",
                                    SearchLogUtils.edgeOrientedMsg("Meek R4", graph.getEdge(a, b)));
                            changed = true;
                            break;
                        }
                    }
                }
            }
        }

        return changed;
    }

    //    /**
    //     *
    //     */
    //    public static boolean meekR4(Graph graph, IKnowledge knowledge) {
    //        if (knowledge == null) {
    //            return false;
    //        }
    //
    //        List<Node> nodes = graph.getNodes();
    //        boolean changed = false;
    //
    //        for (Node a : nodes) {
    //            List<Node> adjacentNodes = graph.getAdjacentNodes(a);
    //
    //            if (adjacentNodes.size() < 3) {
    //                continue;
    //            }
    //
    //            for (Node d: adjacentNodes) {
    //                if (!graph.isUndirectedFromTo(a, d)) {
    //                    continue;
    //                }
    //
    //                List<Node> otherAdjacents = new LinkedList<Node>(adjacentNodes);
    //                otherAdjacents.remove(d);
    //
    //                ChoiceGenerator cg =
    //                        new ChoiceGenerator(otherAdjacents.size(), 2);
    //                int[] combination;
    //
    //                while ((combination = cg.next()) != null) {
    //                    Node b = otherAdjacents.get(combination[0]);
    //                    Node c = otherAdjacents.get(combination[1]);
    //
    //                    if (graph.isAdjacentTo(b, d)) {
    //                        continue;
    //                    }
    //
    //                    if (!graph.isUndirectedFromTo(a, b)) {
    //                        continue;
    //                    }
    //
    //                    if (!graph.isAdjacentTo(a, c)) {
    //                        continue;
    //                    }
    //
    ////                    if (!(graph.isUndirectedFromTo(a, c) ||
    ////                            graph.isDirectedFromTo(a, c) ||
    ////                            graph.isDirectedFromTo(c, a))) {
    ////                        continue;
    ////                    }
    //
    //                    if (graph.isDirectedFromTo(b, c) &&
    //                            graph.isDirectedFromTo(c, d)) {
    //                        if (isArrowpointAllowed(a, d, knowledge)) {
    //                            graph.setEndpoint(a, d, Endpoint.ARROW);
    //                            SearchLogUtils.logEdgeOriented("Meek R4",
    //                                    graph.getEdge(a, d), LOGGER);
    //                            changed = true;
    //                            break;
    //                        }
    //                    }
    //                    else if (graph.isDirectedFromTo(d, c) &&
    //                            graph.isDirectedFromTo(c, b)) {
    //                        if (isArrowpointAllowed(a, b, knowledge)) {
    //                            graph.setEndpoint(a, b, Endpoint.ARROW);
    //                            SearchLogUtils.logEdgeOriented("Meek R4",
    //                                    graph.getEdge(a, b), LOGGER);
    //                            changed = true;
    //                            break;
    //                        }
    //                    }
    //                }
    //            }
    //        }
    //
    //        return changed;
    //    }

    /**
     * Checks if an arrowpoint is allowed by background knowledge.
     */
    public static boolean isArrowpointAllowed(Object from, Object to, IKnowledge knowledge) {
        if (knowledge == null) {
            return true;
        }
        return !knowledge.edgeRequired(to.toString(), from.toString())
                && !knowledge.edgeForbidden(from.toString(), to.toString());
    }

    /**
     * Transforms a maximally directed pattern (PDAG) represented in graph <code>g</code> into an arbitrary DAG by
     * modifying <code>g</code> itself. Based on the algorithm described in </p> Chickering (2002) "Optimal structure
     * identification with greedy search" Journal of Machine Learning Research. </p> R. Silva, June 2004
     */
    public static void pdagToDag(Graph g) {
        Graph p = new EdgeListGraph(g);
        List<Edge> undirectedEdges = new ArrayList<Edge>();

        for (Edge edge : g.getEdges()) {
            if (edge.getEndpoint1() == Endpoint.TAIL && edge.getEndpoint2() == Endpoint.TAIL
                    && !undirectedEdges.contains(edge)) {
                undirectedEdges.add(edge);
            }
        }
        g.removeEdges(undirectedEdges);
        List<Node> pNodes = p.getNodes();

        do {
            Node x = null;

            for (Node pNode : pNodes) {
                x = pNode;

                if (p.getChildren(x).size() > 0) {
                    continue;
                }

                Set<Node> neighbors = new HashSet<Node>();

                for (Edge edge : p.getEdges()) {
                    if (edge.getNode1() == x || edge.getNode2() == x) {
                        if (edge.getEndpoint1() == Endpoint.TAIL && edge.getEndpoint2() == Endpoint.TAIL) {
                            if (edge.getNode1() == x) {
                                neighbors.add(edge.getNode2());
                            } else {
                                neighbors.add(edge.getNode1());
                            }
                        }
                    }
                }
                if (neighbors.size() > 0) {
                    Collection<Node> parents = p.getParents(x);
                    Set<Node> all = new HashSet<Node>(neighbors);
                    all.addAll(parents);
                    if (!GraphUtils.isClique(all, p)) {
                        continue;
                    }
                }

                for (Node neighbor : neighbors) {
                    Node node1 = g.getNode(neighbor.getName());
                    Node node2 = g.getNode(x.getName());

                    g.addDirectedEdge(node1, node2);
                }
                p.removeNode(x);
                break;
            }
            pNodes.remove(x);
        } while (pNodes.size() > 0);
    }

    /**
     * Get a graph and direct only the unshielded colliders.
     */
    public static void basicPattern(Graph graph) {
        Set<Edge> undirectedEdges = new HashSet<Edge>();

        NEXT_EDGE: for (Edge edge : graph.getEdges()) {
            Node head = null, tail = null;

            if (edge.getEndpoint1() == Endpoint.ARROW && edge.getEndpoint2() == Endpoint.TAIL) {
                head = edge.getNode1();
                tail = edge.getNode2();
            } else if (edge.getEndpoint2() == Endpoint.ARROW && edge.getEndpoint1() == Endpoint.TAIL) {
                head = edge.getNode2();
                tail = edge.getNode1();
            }

            if (head != null) {

                NEXT_PARENT: for (Node parent : graph.getParents(head)) {
                    if (parent != tail) {
                        if (graph.isAdjacentTo(parent, tail)) {
                            continue NEXT_PARENT;
                        }

                        continue NEXT_EDGE;
                    }
                }

                undirectedEdges.add(edge);
            }
        }

        for (Edge nextUndirected : undirectedEdges) {
            Node node1 = nextUndirected.getNode1();
            Node node2 = nextUndirected.getNode2();

            graph.removeEdge(nextUndirected);
            graph.addUndirectedEdge(node1, node2);
        }
    }

    /**
     * Transforms a DAG represented in graph <code>graph</code> into a maximally directed pattern (PDAG) by modifying
     * <code>g</code> itself. Based on the algorithm described in </p> Chickering (2002) "Optimal structure
     * identification with greedy search" Journal of Machine Learning Research. It works for both BayesNets and SEMs.
     * </p> R. Silva, June 2004
     */
    public static void dagToPdag(Graph graph) {
        //do topological sort on the nodes
        Graph graphCopy = new EdgeListGraph(graph);
        Node orderedNodes[] = new Node[graphCopy.getNodes().size()];
        int count = 0;
        while (graphCopy.getNodes().size() > 0) {
            Set<Node> exogenousNodes = new HashSet<Node>();

            for (Node next : graphCopy.getNodes()) {
                if (graphCopy.isExogenous(next)) {
                    exogenousNodes.add(next);
                    orderedNodes[count++] = graph.getNode(next.getName());
                }
            }

            graphCopy.removeNodes(new ArrayList<Node>(exogenousNodes));
        }
        //ordered edges - improvised, inefficient implementation
        count = 0;
        Edge edges[] = new Edge[graph.getNumEdges()];
        boolean edgeOrdered[] = new boolean[graph.getNumEdges()];
        Edge orderedEdges[] = new Edge[graph.getNumEdges()];

        for (Edge edge : graph.getEdges()) {
            edges[count++] = edge;
        }

        for (int i = 0; i < edges.length; i++) {
            edgeOrdered[i] = false;
        }

        while (count > 0) {
            for (Node orderedNode : orderedNodes) {
                for (int k = orderedNodes.length - 1; k >= 0; k--) {
                    for (int q = 0; q < edges.length; q++) {
                        if (!edgeOrdered[q] && edges[q].getNode1() == orderedNodes[k]
                                && edges[q].getNode2() == orderedNode) {
                            edgeOrdered[q] = true;
                            orderedEdges[orderedEdges.length - count] = edges[q];
                            count--;
                        }
                    }
                }
            }
        }

        //label edges
        boolean compelledEdges[] = new boolean[graph.getNumEdges()];
        boolean reversibleEdges[] = new boolean[graph.getNumEdges()];
        for (int i = 0; i < graph.getNumEdges(); i++) {
            compelledEdges[i] = false;
            reversibleEdges[i] = false;
        }
        for (int i = 0; i < graph.getNumEdges(); i++) {
            if (compelledEdges[i] || reversibleEdges[i]) {
                continue;
            }
            Node x = orderedEdges[i].getNode1();
            Node y = orderedEdges[i].getNode2();
            for (int j = 0; j < orderedEdges.length; j++) {
                if (orderedEdges[j].getNode2() == x && compelledEdges[j]) {
                    Node w = orderedEdges[j].getNode1();
                    if (!graph.isParentOf(w, y)) {
                        for (int k = 0; k < orderedEdges.length; k++) {
                            if (orderedEdges[k].getNode2() == y) {
                                compelledEdges[k] = true;
                                break;
                            }
                        }
                    } else {
                        for (int k = 0; k < orderedEdges.length; k++) {
                            if (orderedEdges[k].getNode1() == w && orderedEdges[k].getNode2() == y) {
                                compelledEdges[k] = true;
                                break;
                            }
                        }
                    }
                }
                if (compelledEdges[i]) {
                    break;
                }
            }
            if (compelledEdges[i]) {
                continue;
            }
            boolean foundZ = false;

            for (Edge orderedEdge : orderedEdges) {
                Node z = orderedEdge.getNode1();
                if (z != x && orderedEdge.getNode2() == y && !graph.isParentOf(z, x)) {
                    compelledEdges[i] = true;
                    for (int k = i + 1; k < graph.getNumEdges(); k++) {
                        if (orderedEdges[k].getNode2() == y && !reversibleEdges[k]) {
                            compelledEdges[k] = true;
                        }
                    }
                    foundZ = true;
                    break;
                }
            }

            if (!foundZ) {
                reversibleEdges[i] = true;

                for (int j = i + 1; j < orderedEdges.length; j++) {
                    if (!compelledEdges[j] && orderedEdges[j].getNode2() == y) {
                        reversibleEdges[j] = true;
                    }
                }
            }
        }

        //undirect edges that are reversible
        for (int i = 0; i < reversibleEdges.length; i++) {
            if (reversibleEdges[i]) {
                graph.setEndpoint(orderedEdges[i].getNode1(), orderedEdges[i].getNode2(), Endpoint.TAIL);
                graph.setEndpoint(orderedEdges[i].getNode2(), orderedEdges[i].getNode1(), Endpoint.TAIL);
            }
        }
    }

    /**
     * Returns the pattern to which the given DAG belongs.
     */
    public static Graph patternFromDag(Graph dag) {
        Graph graph = new EdgeListGraph(dag);
        SearchGraphUtils.basicPattern(graph);
        MeekRules rules = new MeekRules();
        rules.orientImplied(graph);
        return graph;
    }

    public static Graph dagFromPattern(Graph pattern) {

        pattern = new EdgeListGraph(pattern);
        for (Edge edge : pattern.getEdges()) {
            if (Edges.isBidirectedEdge(edge)) {
                edge.setEndpoint1(Endpoint.TAIL);
                edge.setEndpoint2(Endpoint.TAIL);
            }
        }
        //
        DagInPatternIterator dags = new DagInPatternIterator(pattern);
        return dags.next();

        //        return GraphUtils.dagFromPattern(pattern, new IKnowledge());
        //
        //        Graph graph = new EdgeListGraph(pattern);
        //        pdagToDag(graph);
        //        return graph;
    }

    public static void arrangeByKnowledgeTiers(Graph graph, IKnowledge knowledge) {
        if (knowledge.getNumTiers() == 0) {
            throw new IllegalArgumentException("There are no Tiers to arrange.");
        }

        List<Node> nodes = graph.getNodes();
        List<String> varNames = new ArrayList<String>();
        int ySpace = 500 / knowledge.getNumTiers();
        ySpace = ySpace < 50 ? 50 : ySpace;

        for (Node node1 : nodes) {
            varNames.add(node1.getName());
        }

        List<String> notInTier = knowledge.getVarsNotInTier();

        int x = 0;
        int y = 50 - ySpace;

        if (notInTier.size() > 0) {
            y += ySpace;

            for (String name : notInTier) {
                x += 90;
                Node node = graph.getNode(name);

                if (node != null) {
                    node.setCenterX(x);
                    node.setCenterY(y);
                }
            }
        }

        for (int i = 0; i < knowledge.getNumTiers(); i++) {
            List<String> tier = knowledge.getTier(i);
            y += ySpace;
            x = -25;

            for (String name : tier) {
                x += 90;
                Node node = graph.getNode(name);

                if (node != null) {
                    node.setCenterX(x);
                    node.setCenterY(y);
                }
            }
        }
    }

    /**
     * Double checks a sepset map against a pattern to make sure that X is adjacent to Y in the pattern iff {X, Y} is
     * not in the domain of the sepset map.
     *
     * @param sepset  a sepset map, over variables V.
     * @param pattern a pattern over variables W, V subset of W.
     * @return true if the sepset map is consistent with the pattern.
     */
    public static boolean verifySepsetIntegrity(SepsetMap sepset, Graph pattern) {
        for (Node x : pattern.getNodes()) {
            for (Node y : pattern.getNodes()) {
                if (x == y) {
                    continue;
                }

                if ((pattern.isAdjacentTo(y, x)) != (sepset.get(x, y) == null)) {
                    System.out.println("Sepset not consistent with graph for {" + x + ", " + y + "}");
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Returns the set of nodes reachable from the given set of initial nodes in the given graph according to the
     * criteria in the given legal pairs object.
     * <p/>
     * A variable V is reachable from initialNodes iff for some variable X in initialNodes thers is a path U [X, Y1,
     * ..., V] such that legalPairs.isLegalFirstNode(X, Y1) and for each [H1, H2, H3] as subpaths of U,
     * legalPairs.isLegalPairs(H1, H2, H3).
     * <p/>
     * The algorithm used is a variant of Algorithm 1 from Geiger, Verma, & Pearl (1990).
     *
     * @param initialNodes  The nodes that reachability paths start from.
     * @param legalPairs    Specifies initial edges (given initial nodes) and legal edge pairs.
     * @param c             a set of vertices (intuitively, the set of variables to be conditioned on.
     * @param d             a set of vertices (intuitively to be used in tests of legality, for example, the set of
     *                      ancestors of c).
     * @param graph         the graph with respect to which reachability is
     * @param maxPathLength
     */
    public static Set<Node> getReachableNodes(List<Node> initialNodes, LegalPairs legalPairs, List<Node> c,
            List<Node> d, Graph graph, int maxPathLength) {
        HashSet<Node> reachable = new HashSet<Node>();
        MultiKeyMap visited = new MultiKeyMap();
        List<ReachabilityEdge> nextEdges = new LinkedList<ReachabilityEdge>();

        for (Node x : initialNodes) {
            List<Node> adjX = graph.getAdjacentNodes(x);

            for (Node y : adjX) {
                if (legalPairs.isLegalFirstEdge(x, y)) {
                    reachable.add(y);
                    nextEdges.add(new ReachabilityEdge(x, y));
                    visited.put(x, y, Boolean.TRUE);
                }
            }
        }

        int pathLength = 1;

        while (nextEdges.size() > 0) {
            //            System.out.println("Path length = " + pathLength);
            if (++pathLength > maxPathLength)
                return reachable;

            List<ReachabilityEdge> currEdges = nextEdges;
            nextEdges = new LinkedList<ReachabilityEdge>();

            for (ReachabilityEdge edge : currEdges) {
                Node x = edge.getFrom();
                Node y = edge.getTo();
                List<Node> adjY = graph.getAdjacentNodes(y);

                for (Node z : adjY) {
                    if ((visited.get(y, z)) == Boolean.TRUE) {
                        continue;
                    }

                    if (legalPairs.isLegalPair(x, y, z, c, d)) {
                        reachable.add(z);

                        nextEdges.add(new ReachabilityEdge(y, z));
                        visited.put(y, z, Boolean.TRUE);
                    }
                }
            }
        }

        return reachable;
    }

    /**
     * Returns the string in nodelist which matches string in BK.
     */
    public static Node translate(String a, List<Node> nodes) {
        for (Node node : nodes) {
            if ((node.getName()).equals(a)) {
                return node;
            }
        }

        return null;
    }

    public static List<Set<Node>> powerSet(List<Node> nodes) {
        List<Set<Node>> subsets = new ArrayList<Set<Node>>();
        int total = (int) Math.pow(2, nodes.size());
        for (int i = 0; i < total; i++) {
            Set<Node> newSet = new HashSet<Node>();
            String selection = Integer.toBinaryString(i);
            for (int j = selection.length() - 1; j >= 0; j--) {
                if (selection.charAt(j) == '1') {
                    newSet.add(nodes.get(selection.length() - j - 1));
                }
            }
            subsets.add(newSet);
        }
        return subsets;
    }

    /**
     * Checks if an arrowpoint is allowed by background knowledge.
     */
    public static boolean isArrowpointAllowed1(Node from, Node to, IKnowledge knowledge) {
        if (knowledge == null) {
            return true;
        }

        return !knowledge.edgeRequired(to.toString(), from.toString())
                && !knowledge.edgeForbidden(from.toString(), to.toString());
    }

    public static boolean isArrowpointAllowed2(Node from, Node to, IKnowledge knowledge, Graph graph) {
        if (knowledge == null) {
            return true;
        }

        if (!graph.getNodesInTo(to, Endpoint.ARROW).isEmpty()) {
            return false;
        }

        return !knowledge.edgeRequired(to.toString(), from.toString())
                && !knowledge.edgeForbidden(from.toString(), to.toString());
    }

    /**
     * Generates the list of DAGs in the given pattern.
     */
    public static List<Graph> generatePatternDags(Graph pattern, boolean orientBidirectedEdges) {
        if (orientBidirectedEdges) {
            pattern = GraphUtils.removeBidirectedOrientations(pattern);
        }

        return getDagsInPatternMeek(pattern, new Knowledge());
    }

    public static List<Graph> getDagsInPatternMeek(Graph pattern, IKnowledge knowledge) {
        DagInPatternIterator iterator = new DagInPatternIterator(pattern, knowledge);
        List<Graph> dags = new ArrayList<Graph>();

        while (iterator.hasNext()) {
            Graph graph = iterator.next();

            try {
                if (knowledge.isViolatedBy(graph)) {
                    continue;
                }

                dags.add(graph);
            } catch (IllegalArgumentException e) {
                System.out.println("Found a non-DAG: " + graph);
            }
        }

        return dags;
    }

    public static List<Dag> getAllDagsInUndirectedGraph(Graph graph) {
        Graph undirected = GraphUtils.undirectedGraph(graph);

        DagIterator iterator = new DagIterator(undirected);
        List<Dag> dags = new ArrayList<Dag>();

        while (iterator.hasNext()) {
            Graph _graph = iterator.next();

            try {
                Dag dag = new Dag(_graph);
                dags.add(dag);
            } catch (IllegalArgumentException e) {
                //
            }
        }

        return dags;
    }

    public static List<Dag> getAllDagsByDirectingUndirectedEdges(Graph graph) {
        //        Graph undirected = undirectedGraph(graph);

        DagIterator iterator = new DagIterator(graph);
        List<Dag> dags = new ArrayList<Dag>();

        while (iterator.hasNext()) {
            Graph _graph = iterator.next();

            try {
                Dag dag = new Dag(_graph);
                dags.add(dag);
            } catch (IllegalArgumentException e) {
                //
            }
        }

        return dags;
    }

    public static List<Graph> getAllGraphsByDirectingUndirectedEdges(Graph skeleton) {
        List<Graph> graphs = new ArrayList<Graph>();
        List<Edge> edges = new ArrayList<Edge>(skeleton.getEdges());

        List<Integer> undirectedIndices = new ArrayList<Integer>();

        for (int i = 0; i < edges.size(); i++) {
            if (Edges.isUndirectedEdge(edges.get(i))) {
                undirectedIndices.add(i);
            }
        }

        int[] dims = new int[undirectedIndices.size()];

        for (int i = 0; i < undirectedIndices.size(); i++) {
            dims[i] = 2;
        }

        CombinationGenerator gen = new CombinationGenerator(dims);
        int[] comb;

        while ((comb = gen.next()) != null) {
            Graph graph = new EdgeListGraph(skeleton.getNodes());

            for (Edge edge : edges) {
                if (!Edges.isUndirectedEdge(edge)) {
                    graph.addEdge(edge);
                }
            }

            for (int i = 0; i < undirectedIndices.size(); i++) {
                Edge edge = edges.get(undirectedIndices.get(i));
                Node node1 = edge.getNode1();
                Node node2 = edge.getNode2();

                if (comb[i] == 1) {
                    graph.addEdge(Edges.directedEdge(node1, node2));
                } else {
                    graph.addEdge(Edges.directedEdge(node2, node1));
                }
            }

            graphs.add(graph);
        }

        return graphs;
    }

    public static Graph bestGuessCycleOrientation(Graph graph, IndependenceTest test) {
        while (true) {
            List<Node> cycle = GraphUtils.directedCycle(graph);

            if (cycle == null) {
                break;
            }

            LinkedList<Node> _cycle = new LinkedList<Node>(cycle);

            Node first = _cycle.getFirst();
            Node last = _cycle.getLast();

            _cycle.addFirst(last);
            _cycle.addLast(first);

            int _j = -1;
            double minP = Double.POSITIVE_INFINITY;

            for (int j = 1; j < _cycle.size() - 1; j++) {
                int i = j - 1;
                int k = j + 1;

                Node x = test.getVariable(_cycle.get(i).getName());
                Node y = test.getVariable(_cycle.get(j).getName());
                Node z = test.getVariable(_cycle.get(k).getName());

                test.isIndependent(x, z, Collections.singletonList(y));

                double p = test.getPValue();

                if (p < minP) {
                    _j = j;
                    minP = p;
                }
            }

            Node x = _cycle.get(_j - 1);
            Node y = _cycle.get(_j);
            Node z = _cycle.get(_j + 1);

            graph.removeEdge(x, y);
            graph.removeEdge(z, y);
            graph.addDirectedEdge(x, y);
            graph.addDirectedEdge(z, y);
        }

        return graph;
    }

    public static CpcTripleType getCpcTripleType(Node x, Node y, Node z, IndependenceTest test, int depth,
            Graph graph) {
        boolean existsSepsetContainingY = false;
        boolean existsSepsetNotContainingY = false;
        //        Set<Set<Node>> sepsets = new HashSet<Set<Node>>();

        Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
        __nodes.remove(z);

        List<Node> _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        int _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
            int[] choice;

            //            CHOICE:
            while ((choice = cg.next()) != null) {
                List<Node> condSet = GraphUtils.asList(choice, _nodes);
                //                for (Set<Node> _sepset : sepsets) {
                //                    if (_sepset.equals(new HashSet<Node>(condSet))) {
                //                        continue CHOICE;
                //                    }
                //                }

                if (test.isIndependent(x, z, condSet)) {
                    //                    sepsets.add(new HashSet<Node>(condSet));

                    if (condSet.contains(y)) {
                        existsSepsetContainingY = true;
                    } else {
                        existsSepsetNotContainingY = true;
                    }
                }
            }
        }

        __nodes = new HashSet<Node>(graph.getAdjacentNodes(z));
        __nodes.remove(x);

        _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
            int[] choice;

            while ((choice = cg.next()) != null) {
                List<Node> condSet = GraphUtils.asList(choice, _nodes);

                if (test.isIndependent(x, z, condSet)) {
                    if (condSet.contains(y)) {
                        existsSepsetContainingY = true;
                    } else {
                        existsSepsetNotContainingY = true;
                    }
                }

                if (existsSepsetContainingY == true && existsSepsetNotContainingY == true) {
                    return CpcTripleType.AMBIGUOUS;
                }
            }
        }

        if (existsSepsetContainingY && !existsSepsetNotContainingY) {
            return CpcTripleType.NONCOLLIDER;
        } else if (!existsSepsetContainingY && existsSepsetNotContainingY) {
            return CpcTripleType.COLLIDER;
        } else {
            return CpcTripleType.AMBIGUOUS;
        }
    }

    //     for play..

    public static CpcTripleType getCpcTripleType2(Node x, Node y, Node z, IndependenceTest test, int depth,
            Graph graph) {
        int numSepsetsContainingY = 0;
        int numSepsetsNotContainingY = 0;

        Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
        __nodes.remove(z);

        List<Node> _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        int _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
            int[] choice;

            while ((choice = cg.next()) != null) {
                List<Node> condSet = GraphUtils.asList(choice, _nodes);

                if (test.isIndependent(x, z, condSet)) {
                    if (condSet.contains(y)) {
                        numSepsetsContainingY++;
                    } else {
                        numSepsetsNotContainingY++;
                    }
                }
            }
        }

        __nodes = new HashSet<Node>(graph.getAdjacentNodes(z));
        __nodes.remove(x);

        _nodes = new LinkedList<Node>(__nodes);
        TetradLogger.getInstance().log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

        _depth = depth;
        if (_depth == -1) {
            _depth = 1000;
        }
        _depth = Math.min(_depth, _nodes.size());

        for (int d = 0; d <= _depth; d++) {
            ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
            int[] choice;

            while ((choice = cg.next()) != null) {
                List<Node> condSet = GraphUtils.asList(choice, _nodes);

                if (test.isIndependent(x, z, condSet)) {
                    if (condSet.contains(y)) {
                        numSepsetsContainingY++;
                    } else {
                        numSepsetsNotContainingY++;
                    }
                }
            }
        }

        int factor = 3;

        if (numSepsetsContainingY > factor * numSepsetsNotContainingY) {
            return CpcTripleType.NONCOLLIDER;
        } else if (numSepsetsNotContainingY > factor * numSepsetsContainingY) {
            return CpcTripleType.COLLIDER;
            //        } else if (numSepsetsContainingY != 0 && numSepsetsNotContainingY == 0) {
            //            return CpcTripleType.NONCOLLIDER;
            //        } else if (numSepsetsNotContainingY != 0 && numSepsetsContainingY == 0) {
            //            return CpcTripleType.COLLIDER;
        } else {
            return CpcTripleType.AMBIGUOUS;
        }

        //        if (numSepsetsContainingY == numSepsetsNotContainingY) {
        //            return CpcTripleType.AMBIGUOUS;
        //        }
        //        else if (!numSepsetsNotContainingY) {
        //            return CpcTripleType.NONCOLLIDER;
        //        }
        //        else {
        //            return CpcTripleType.COLLIDER;
        //        }
    }

    public static Graph chooseDagInPattern(Graph graph) {
        return new DagInPatternIterator(graph).next();

        //        Graph newGraph = new EdgeListGraph(graph);
        //
        //        for(Edge edge : newGraph.getEdges()){
        //            if(Edges.isBidirectedEdge(edge)){
        //                newGraph.removeEdge(edge);
        //            }
        //        }
        //
        //        PatternToDag search = new PatternToDag(new Pattern(newGraph));
        //        Graph dag = search.patternToDagMeekRules();
        //        GraphUtils.arrangeBySourceGraph(dag, graph);
        //        return dag;
    }

    public static Graph patternForDag(final Graph dag) {
        Graph pattern = new EdgeListGraph(dag);
        SearchGraphUtils.basicPattern(pattern);
        MeekRules rules = new MeekRules();
        rules.orientImplied(pattern);
        //        GraphUtils.replaceNodes(pattern, dag.getNodes());
        return pattern;
    }

    public static Graph patternForDag(final Graph dag, Knowledge knowledge) {
        Graph pattern = new EdgeListGraph(dag);
        SearchGraphUtils.basicPattern(pattern);
        orientRequired(knowledge, pattern, pattern.getNodes());
        MeekRules rules = new MeekRules();
        rules.setKnowledge(knowledge);
        rules.orientImplied(pattern);
        //        GraphUtils.replaceNodes(pattern, dag.getNodes());
        return pattern;
    }

    public static void orientRequired(IKnowledge bk, Graph graph, List<Node> nodes) {
        TetradLogger.getInstance().log("details", "Staring BK Orientation.");
        for (Iterator<KnowledgeEdge> it = bk.forbiddenEdgesIterator(); it.hasNext();) {
            KnowledgeEdge edge = it.next();

            //match strings to variables in the graph.
            Node from = translate(edge.getFrom(), nodes);
            Node to = translate(edge.getTo(), nodes);

            if (from == null || to == null) {
                continue;
            }

            if (graph.getEdge(from, to) == null) {
                continue;
            }

            // Orient to-->from
            graph.removeEdge(from, to);
            graph.addDirectedEdge(to, from);
            //            graph.setEndpoint(from, to, Endpoint.TAIL);
            //            graph.setEndpoint(to, from, Endpoint.ARROW);

            TetradLogger.getInstance().log("knowledgeOrientations",
                    SearchLogUtils.edgeOrientedMsg("IKnowledge", graph.getEdge(to, from)));
        }

        for (Iterator<KnowledgeEdge> it = bk.requiredEdgesIterator(); it.hasNext();) {
            KnowledgeEdge edge = it.next();

            //match strings to variables in this graph
            Node from = translate(edge.getFrom(), nodes);
            Node to = translate(edge.getTo(), nodes);

            if (from == null || to == null) {
                continue;
            }

            if (graph.getEdge(from, to) == null) {
                continue;
            }

            // Orient from-->to
            graph.removeEdge(from, to);
            graph.addDirectedEdge(from, to);

            //            graph.setEndpoint(to, from, Endpoint.TAIL);
            //            graph.setEndpoint(from, to, Endpoint.ARROW);
            TetradLogger.getInstance().log("knowledgeOrientations",
                    SearchLogUtils.edgeOrientedMsg("IKnowledge", graph.getEdge(from, to)));
        }

        TetradLogger.getInstance().log("details", "Finishing BK Orientation.");
    }

    //    /**
    //     * The recursive method used to list the MB DAGS consistent with an
    //     * Pattern (i.e. with the independence information available to the search.
    //     */
    //    public static Set<Graph> listPatternDags(Graph pattern,
    //                                             boolean orientBidirectedEdges) {
    //        Set<Graph> dags = new HashSet<Graph>();
    //        Graph graph = new EdgeListGraph(pattern);
    //
    //        MeekRules rules = new MeekRules();
    //        rules.orientImplied(graph);
    //
    //        for (Edge edge : rules.getChangedEdges().keySet()) {
    //            graph.setHighlighted(edge, true);
    //        }
    //
    //        List<Edge> edges = graph.getEdges();
    //        Edge edge = null;
    //
    //        for (Edge _edge : edges) {
    //            if (orientBidirectedEdges && Edges.isBidirectedEdge(_edge)) {
    //                edge = _edge;
    //                break;
    //            }
    //
    //            if (Edges.isUndirectedEdge(_edge)) {
    //                edge = _edge;
    //                break;
    //            }
    //        }
    //
    //        if (edge == null) {
    //            dags.add(graph);
    //            return dags;
    //        }
    //
    //        graph.setEndpoint(edge.getNode2(), edge.getNode1(), Endpoint.TAIL);
    //        graph.setEndpoint(edge.getNode1(), edge.getNode2(), Endpoint.ARROW);
    //        graph.setHighlighted(graph.getEdge(edge.getNode1(), edge.getNode2()), true);
    //        dags.addAll(listPatternDags(graph, orientBidirectedEdges));
    //
    //        graph.setEndpoint(edge.getNode1(), edge.getNode2(), Endpoint.TAIL);
    //        graph.setEndpoint(edge.getNode2(), edge.getNode1(), Endpoint.ARROW);
    //        graph.setHighlighted(graph.getEdge(edge.getNode1(), edge.getNode2()), true);
    //        dags.addAll(listPatternDags(graph, orientBidirectedEdges));
    //
    //        return dags;
    //    }

    public static Graph dagFromPattern(Graph graph, IKnowledge knowledge) {
        boolean allowArbitraryOrientations = true;
        boolean allowNewColliders = true;
        DagInPatternIterator iterator = new DagInPatternIterator(graph, knowledge, allowArbitraryOrientations,
                allowNewColliders);
        Graph dag = iterator.next();
        return dag;
    }

    /**
     * Simple class to store edges for the reachability search.
     *
     * @author Joseph Ramsey
     */
    private static class ReachabilityEdge {
        private Node from;
        private Node to;

        public ReachabilityEdge(Node from, Node to) {
            this.from = from;
            this.to = to;
        }

        @Override
        public int hashCode() {
            int hash = 17;
            hash += 63 * getFrom().hashCode();
            hash += 71 * getTo().hashCode();
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            ReachabilityEdge edge = (ReachabilityEdge) obj;

            if (!(edge.getFrom().equals(this.getFrom()))) {
                return false;
            }

            return edge.getTo().equals(this.getTo());
        }

        public Node getFrom() {
            return from;
        }

        public Node getTo() {
            return to;
        }
    }

    public enum CpcTripleType {
        COLLIDER, NONCOLLIDER, AMBIGUOUS
    }
}