edu.cmu.tetrad.search.BFci Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
// 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.graph.EdgeListGraph;
import edu.cmu.tetrad.graph.Graph;
import edu.cmu.tetrad.graph.GraphUtils;
import edu.cmu.tetrad.graph.Node;
import edu.cmu.tetrad.search.score.Score;
import edu.cmu.tetrad.search.utils.FciOrient;
import edu.cmu.tetrad.search.utils.SepsetProducer;
import edu.cmu.tetrad.search.utils.SepsetsGreedy;
import edu.cmu.tetrad.util.TetradLogger;
import java.util.List;
import static edu.cmu.tetrad.graph.GraphUtils.gfciExtraEdgeRemovalStep;
/**
* Uses BOSS in place of FGES for the initial step in the GFCI algorithm.
* This tends to produce a accurate PAG than GFCI as a result, for the latent variables case. This is a simple
* substitution; the reference for GFCI is here:
*
* J.M. Ogarrio and P. Spirtes and J. Ramsey, "A Hybrid Causal Search Algorithm
* for Latent Variable Models," JMLR 2016. Here, BOSS has been substituted for FGES.
*
* BOSS is a an algorithm that is currently being written up for publication,
* so we don't yet have a reference for it.
*
* For BOSS only a score is needed, but there are steps in GFCI that require
* a test, so for this method, both a test and a score need to be given.
*
* This class is configured to respect knowledge of forbidden and required
* edges, including knowledge of temporal tiers.
*
* @author josephramsey
* @author bryan andrews
* @see Boss
* @see GFci
* @see GraspFci
* @see SpFci
* @see Fges
* @see Knowledge
*/
public final class BFci implements IGraphSearch {
// The conditional independence test.
private final IndependenceTest independenceTest;
// The logger to use.
private final TetradLogger logger = TetradLogger.getInstance();
// The score.
private final Score score;
// The sample size.
int sampleSize;
// The background knowledge.
private Knowledge knowledge = new Knowledge();
// Flag for the complete rule set, true if it should use the complete rule set, false otherwise.
private boolean completeRuleSetUsed = true;
// The maximum length for any discriminating path. -1 if unlimited; otherwise, a positive integer.
private int maxPathLength = -1;
// True iff verbose output should be printed.
private boolean verbose;
private int numStarts = 1;
private int depth = -1;
private boolean doDiscriminatingPathRule = true;
private boolean bossUseBes = false;
/**
* Constructor. The test and score should be for the same data.
*
* @param test The test to use.
* @param score The score to use.
* @see IndependenceTest
* @see Score
*/
public BFci(IndependenceTest test, Score score) {
if (score == null) {
throw new NullPointerException();
}
this.sampleSize = score.getSampleSize();
this.score = score;
this.independenceTest = test;
}
/**
* Does the search and returns a PAG.
*
* @return The discovered graph.
*/
public Graph search() {
List nodes = getIndependenceTest().getVariables();
this.logger.log("info", "Starting FCI algorithm.");
this.logger.log("info", "Independence test = " + getIndependenceTest() + ".");
// BOSS CPDAG learning step
Boss subAlg = new Boss(this.score);
subAlg.setUseBes(bossUseBes);
subAlg.setNumStarts(this.numStarts);
PermutationSearch alg = new PermutationSearch(subAlg);
alg.setKnowledge(this.knowledge);
Graph graph = alg.search();
Graph referenceDag = new EdgeListGraph(graph);
// GFCI extra edge removal step...
SepsetProducer sepsets = new SepsetsGreedy(graph, this.independenceTest, null, this.depth, knowledge);
gfciExtraEdgeRemovalStep(graph, referenceDag, nodes, sepsets);
GraphUtils.gfciR0(graph, referenceDag, sepsets, knowledge);
FciOrient fciOrient = new FciOrient(sepsets);
fciOrient.setCompleteRuleSetUsed(this.completeRuleSetUsed);
fciOrient.setMaxPathLength(this.maxPathLength);
fciOrient.setDoDiscriminatingPathColliderRule(this.doDiscriminatingPathRule);
fciOrient.setDoDiscriminatingPathTailRule(this.doDiscriminatingPathRule);
fciOrient.setVerbose(verbose);
fciOrient.setKnowledge(knowledge);
fciOrient.doFinalOrientation(graph);
GraphUtils.replaceNodes(graph, this.independenceTest.getVariables());
return graph;
}
/**
* Sets the knowledge to be used for the search.
*
* @param knowledge This knowledge.
*/
public void setKnowledge(Knowledge knowledge) {
this.knowledge = new Knowledge(knowledge);
}
/**
* Sets whether the complete (Zhang's) rule set should be used.
*
* @param completeRuleSetUsed True if Zhang's complete rule set should be used, false if only R1-R4 (the rule set of
* the original FCI) should be used. False by default.
*/
public void setCompleteRuleSetUsed(boolean completeRuleSetUsed) {
this.completeRuleSetUsed = completeRuleSetUsed;
}
/**
* Returns the maximum length of any discriminating path, or -1 if unlimited.
*
* @param maxPathLength This maximum.
*/
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 verbose output should be printed.
*
* @param verbose True iff the case
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* The independence test being used for some steps in final orientation.
*
* @return This test.
*/
public IndependenceTest getIndependenceTest() {
return this.independenceTest;
}
public void setNumStarts(int numStarts) {
this.numStarts = numStarts;
}
public void setDepth(int depth) {
this.depth = depth;
}
public void setDoDiscriminatingPathRule(boolean doDiscriminatingPathRule) {
this.doDiscriminatingPathRule = doDiscriminatingPathRule;
}
public void setBossUseBes(boolean useBes) {
this.bossUseBes = useBes;
}
}