All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.cmu.tetrad.search.Cfci Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
// 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, 2022 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.Knowledge;
import edu.cmu.tetrad.data.KnowledgeEdge;
import edu.cmu.tetrad.graph.*;
import edu.cmu.tetrad.search.utils.*;
import edu.cmu.tetrad.util.ChoiceGenerator;
import edu.cmu.tetrad.util.MillisecondTimes;
import edu.cmu.tetrad.util.TetradLogger;
import org.apache.commons.math3.util.FastMath;

import java.util.*;


/**
 * Adjusts FCI (see) to use conservative orientation as in CPC (see). Because the collider orientation is conservative,
 * there may be ambiguous triples; these may be retrieved using that accessor method.
 * 

* This class is configured to respect knowledge of forbidden and required edges, including knowledge of temporal * tiers. * * @author josephramsey * @version $Id: $Id * @see Fci * @see Cpc * @see #getAmbiguousTriples() * @see Knowledge */ public final class Cfci implements IGraphSearch { // The SepsetMap being constructed. private final SepsetMap sepsets = new SepsetMap(); // The variables to search over (optional) private final List variables = new ArrayList<>(); // The independence test. private final IndependenceTest independenceTest; // The PAG being constructed. private Graph graph; // The background knowledge. private Knowledge knowledge = new Knowledge(); // Flag for the complete rule set, true if you should use the complete rule set, false otherwise. private boolean completeRuleSetUsed = true; // True iff the possible msep search is done. private boolean possibleMsepSearchDone = true; // The maximum length for any discriminating path. -1 if unlimited; otherwise, a positive integer. private int maxReachablePathLength = -1; // Set of ambiguous unshielded triples. private Set ambiguousTriples; // The depth for the fast adjacency search. private int depth = -1; // Elapsed time of last search. private long elapsedTime; // Whether verbose output (about independencies) is output. private boolean verbose; // Whether to do the discriminating path rule. private boolean doDiscriminatingPathTailRule; private boolean doDiscriminatingPathColliderRule; private int maxPathLength = -1; /** * Whether to leave out the final orientation step. */ private boolean ablationLeaveOutFinalOrientation = false; /** * Constructs a new FCI search for the given independence test and background knowledge. * * @param independenceTest The independence to use as an oracle. */ public Cfci(IndependenceTest independenceTest) { if (independenceTest == null) { throw new NullPointerException(); } this.independenceTest = independenceTest; this.variables.addAll(independenceTest.getVariables()); } /** * Performs the search and returns the PAG. * * @return The search PAG. */ public Graph search() { long beginTime = MillisecondTimes.timeMillis(); if (this.verbose) { TetradLogger.getInstance().log("Starting CFCI algorithm."); TetradLogger.getInstance().log("Independence test = " + this.independenceTest + "."); } setMaxReachablePathLength(this.maxReachablePathLength); //List variables = independenceTest.getVariable(); - Robert Tillman 2008 List nodes = new LinkedList<>(this.variables); this.graph = new EdgeListGraph(nodes); this.graph.fullyConnect(Endpoint.TAIL); // // Step FCI B. (Zhang's step F2.) Fas adj = new Fas(this.independenceTest); adj.setKnowledge(this.knowledge); adj.setDepth(this.depth); adj.setVerbose(this.verbose); this.graph = adj.search(); this.graph.reorientAllWith(Endpoint.CIRCLE); // Note we don't use the sepsets from this search. // Optional step: Possible Msep. (Needed for correctness but very time-consuming.) if (isPossibleMsepSearchDone()) { long time1 = MillisecondTimes.timeMillis(); ruleR0(this.independenceTest, this.depth, this.sepsets); long time2 = MillisecondTimes.timeMillis(); if (this.verbose) { TetradLogger.getInstance().log("Step C: " + (time2 - time1) / 1000. + "s"); } // Step FCI D. long time3 = MillisecondTimes.timeMillis(); PossibleMsepFci possibleMSep = new PossibleMsepFci(this.graph, this.independenceTest); possibleMSep.setDepth(this.depth); possibleMSep.setKnowledge(this.knowledge); possibleMSep.setMaxPathLength(getMaxReachablePathLength()); // We use these sepsets though. this.sepsets.addAll(possibleMSep.search()); long time4 = MillisecondTimes.timeMillis(); if (this.verbose) { TetradLogger.getInstance().log("Step D: " + (time4 - time3) / 1000. + "s"); } // Reorient all edges as o-o. this.graph.reorientAllWith(Endpoint.CIRCLE); } // Step CI C (Zhang's step F3.) long time5 = MillisecondTimes.timeMillis(); fciOrientbk(this.knowledge, this.graph, this.variables); ruleR0(this.independenceTest, this.depth, this.sepsets); long time6 = MillisecondTimes.timeMillis(); if (this.verbose) { TetradLogger.getInstance().log("Step CI C: " + (time6 - time5) / 1000. + "s"); } // Step CI D. (Zhang's step F4.) FciOrient fciOrient = new FciOrient( R0R4StrategyTestBased.defaultConfiguration(independenceTest, new Knowledge())); if (!ablationLeaveOutFinalOrientation) { fciOrient.finalOrientation(this.graph); } long endTime = MillisecondTimes.timeMillis(); this.elapsedTime = endTime - beginTime; if (this.verbose) { TetradLogger.getInstance().log("Returning graph: " + this.graph); } return this.graph; } /** * Sets the depth--i.e., the maximum number of variables conditioned on in any test. * * @param depth This maximum. */ public void setDepth(int depth) { if (depth < -1) { throw new IllegalArgumentException( "Depth must be -1 (unlimited) or >= 0: " + depth); } this.depth = depth; } /** * Returns the elapsed time to the search. * * @return This time. */ public long getElapsedTime() { return this.elapsedTime; } /** * Returns the map from nodes to their sepsets. For x _||_ y | z1,...,zn, this would map {x, y} to {z1,...,zn}. * * @return This map. */ public SepsetMap getSepsets() { return this.sepsets; } /** * Set the knowledge used in the search. * * @param knowledge This knowledge. * @see Knowledge */ public void setKnowledge(Knowledge knowledge) { this.knowledge = new Knowledge(knowledge); } /** * Returns true if Zhang's complete rule set should be used, false if only R1-T1 (the rule set of the original FCI) * should be used. False by default. * * @return True for the complete rule set. */ public boolean isCompleteRuleSetUsed() { return this.completeRuleSetUsed; } /** * Sets whether the complete rule set should be used. * * @param completeRuleSetUsed set to true if Zhang's complete rule set should be used, false if only R1-T1 (the rule * set of the original FCI) should be used. False by default. */ public void setCompleteRuleSetUsed(boolean completeRuleSetUsed) { this.completeRuleSetUsed = completeRuleSetUsed; } /** * Returns the ambiguous triples found in the search. * * @return This set. * @see Cpc */ public Set getAmbiguousTriples() { return new HashSet<>(this.ambiguousTriples); } private Graph getGraph() { return this.graph; } private void ruleR0(IndependenceTest test, int depth, SepsetMap sepsets) { if (this.verbose) { TetradLogger.getInstance().log("Starting Collider Orientation:"); } this.ambiguousTriples = new HashSet<>(); for (Node y : getGraph().getNodes()) { List adjacentNodes = new ArrayList<>(getGraph().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]); if (this.getGraph().isAdjacentTo(x, z)) { continue; } TripleType type = getTripleType(x, y, z, test, depth); Set sepset = sepsets.get(x, z); if (type == TripleType.COLLIDER || (sepset != null && !sepset.contains(y))) { if (FciOrient.isArrowheadAllowed(x, y, graph, knowledge) && FciOrient.isArrowheadAllowed(z, y, graph, knowledge)) { getGraph().setEndpoint(x, y, Endpoint.ARROW); getGraph().setEndpoint(z, y, Endpoint.ARROW); if (this.verbose) { String message = "Collider: " + Triple.pathString(this.graph, x, y, z); TetradLogger.getInstance().log(message); } } } else { Triple triple = new Triple(x, y, z); this.ambiguousTriples.add(triple); getGraph().addAmbiguousTriple(triple.getX(), triple.getY(), triple.getZ()); if (this.verbose) { String message = "AmbiguousTriples: " + Triple.pathString(this.graph, x, y, z); TetradLogger.getInstance().log(message); } } } } if (this.verbose) { TetradLogger.getInstance().log("Finishing Collider Orientation."); } } private TripleType getTripleType(Node x, Node y, Node z, IndependenceTest test, int depth) { boolean existsSepsetContainingY = false; boolean existsSepsetNotContainingY = false; Set __nodes = new HashSet<>(this.getGraph().getAdjacentNodes(x)); __nodes.remove(z); List _nodes = new LinkedList<>(__nodes); int _depth = depth; if (_depth == -1) { _depth = 1000; } _depth = FastMath.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) { Set condSet = GraphUtils.asSet(choice, _nodes); if (test.checkIndependence(x, z, condSet).isIndependent()) { if (condSet.contains(y)) { existsSepsetContainingY = true; } else { existsSepsetNotContainingY = true; } } } } __nodes = new HashSet<>(this.getGraph().getAdjacentNodes(z)); __nodes.remove(x); _nodes = new LinkedList<>(__nodes); _depth = depth; if (_depth == -1) { _depth = 1000; } _depth = FastMath.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) { Set condSet = GraphUtils.asSet(choice, _nodes); if (test.checkIndependence(x, z, condSet).isIndependent()) { if (condSet.contains(y)) { existsSepsetContainingY = true; } else { existsSepsetNotContainingY = true; } } } } // Note: Unless sepsets are being collected during fas, most likely // this will be null. (Only sepsets found during possible msep search // will be here.) Set condSet = getSepsets().get(x, z); if (condSet != null) { if (condSet.contains(y)) { existsSepsetContainingY = true; } else { existsSepsetNotContainingY = true; } } if (existsSepsetContainingY == existsSepsetNotContainingY) { return TripleType.AMBIGUOUS; } else if (!existsSepsetNotContainingY) { return TripleType.NONCOLLIDER; } else { return TripleType.COLLIDER; } } /** * Whether verbose output (about independencies) is output. * * @return True iff verbose output (about independencies) is output. */ public boolean isVerbose() { return this.verbose; } /** * Whether verbose output (about independencies) is output. * * @param verbose True iff verbose output (about independencies) is output. */ public void setVerbose(boolean verbose) { this.verbose = verbose; } /** * Whether to do the discriminating path rule. * * @return True, iff the discriminating path rule is done. */ public boolean isPossibleMsepSearchDone() { return this.possibleMsepSearchDone; } /** * Whether to do the discriminating path rule. * * @param possibleMsepSearchDone True, iff the discriminating path rule is done. */ public void setPossibleMsepSearchDone(boolean possibleMsepSearchDone) { this.possibleMsepSearchDone = possibleMsepSearchDone; } /** * Returns the maximum length for any discriminating path. -1 if unlimited; otherwise, a positive integer. * * @return This length. */ public int getMaxReachablePathLength() { return this.maxReachablePathLength; } /** * Sets the maximum length for any discriminating path. -1 if unlimited; otherwise, a positive integer. * * @param maxReachablePathLength This length. */ public void setMaxReachablePathLength(int maxReachablePathLength) { this.maxReachablePathLength = maxReachablePathLength; } /** * Sets whether the discriminating path tail rule should be used. * * @param doDiscriminatingPathTailRule True, if so. */ public void setDoDiscriminatingPathTailRule(boolean doDiscriminatingPathTailRule) { this.doDiscriminatingPathTailRule = doDiscriminatingPathTailRule; } /** * Sets whether the discriminating path collider rule should be used. * * @param doDiscriminatingPathColliderRule True, if so. */ public void setDoDiscriminatingPathColliderRule(boolean doDiscriminatingPathColliderRule) { this.doDiscriminatingPathColliderRule = doDiscriminatingPathColliderRule; } /** * Orients according to background knowledge * * @param bk The background knowledge * @param graph The graph to orient * @param variables The variables in the graph */ private void fciOrientbk(Knowledge bk, Graph graph, List variables) { if (this.verbose) { TetradLogger.getInstance().log("Starting BK Orientation."); } for (Iterator it = bk.forbiddenEdgesIterator(); it.hasNext(); ) { KnowledgeEdge edge = it.next(); //match strings to variables in the graph. Node from = GraphSearchUtils.translate(edge.getFrom(), variables); Node to = GraphSearchUtils.translate(edge.getTo(), variables); if (from == null || to == null) { continue; } if (graph.getEdge(from, to) == null) { continue; } // Orient to*->from graph.setEndpoint(to, from, Endpoint.ARROW); if (this.verbose) { String message = LogUtilsSearch.edgeOrientedMsg("Knowledge", graph.getEdge(from, to)); TetradLogger.getInstance().log(message); } } for (Iterator it = bk.requiredEdgesIterator(); it.hasNext(); ) { KnowledgeEdge edge = it.next(); //match strings to variables in this graph Node from = GraphSearchUtils.translate(edge.getFrom(), variables); Node to = GraphSearchUtils.translate(edge.getTo(), variables); if (from == null || to == null) { continue; } if (graph.getEdge(from, to) == null) { continue; } // Orient from*->to (?) // Orient from-->to if (this.verbose) { System.out.println("Rule T3: Orienting " + from + "-->" + to); } graph.setEndpoint(to, from, Endpoint.TAIL); graph.setEndpoint(from, to, Endpoint.ARROW); if (this.verbose) { String message = LogUtilsSearch.edgeOrientedMsg("Knowledge", graph.getEdge(from, to)); TetradLogger.getInstance().log(message); } } if (this.verbose) { TetradLogger.getInstance().log("Finishing BK Orientation."); } } /** * Sets the maximum length of any discriminating path. * * @param maxPathLength the maximum length of any discriminating path, or -1 if unlimited. */ public void setMaxPathLength(int maxPathLength) { if (maxPathLength < -1) { throw new IllegalArgumentException("Max path length must be -1 (unlimited) or >= 0: " + maxPathLength); } this.maxPathLength = maxPathLength; } /** * Sets whether to leave out the final orientation in the search algorithm. * * @param ablationLeaveOutFinalOrientation True, if the final orientation should be left out; false otherwise. */ public void setLeaveOutFinalOrientation(boolean ablationLeaveOutFinalOrientation) { this.ablationLeaveOutFinalOrientation = ablationLeaveOutFinalOrientation; } /** * The type of an unshielded triple. */ private enum TripleType { COLLIDER, NONCOLLIDER, AMBIGUOUS } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy