edu.cmu.tetrad.search.LvLite Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below. //i
// 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.*;
import edu.cmu.tetrad.search.score.Score;
import edu.cmu.tetrad.search.test.MsepTest;
import edu.cmu.tetrad.search.utils.*;
import edu.cmu.tetrad.util.MillisecondTimes;
import edu.cmu.tetrad.util.TetradLogger;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* The LV-Lite algorithm implements a search algorithm for learning the structure of a graphical model from
* observational data with latent variables. The algorithm uses the BOSS or GRaSP algorithm to get an initial CPDAG.
* Then it uses scoring steps to infer some unshielded colliders in the graph, then finishes with a testing step to
* remove extra edges and orient more unshielded colliders. Finally, the final FCI orientation is applied to the graph.
*
* @author josephramsey
*/
public final class LvLite implements IGraphSearch {
/**
* The independence test.
*/
private final IndependenceTest test;
/**
* The score.
*/
private final Score score;
/**
* The background knowledge.
*/
private Knowledge knowledge = new Knowledge();
/**
* The algorithm to use to get the initial CPDAG.
*/
private START_WITH startWith = START_WITH.BOSS;
/**
* Flag indicating the output should be guaranteed to be a PAG.
*/
private boolean guaranteePag = false;
/**
* The number of starts for GRaSP.
*/
private int numStarts = 1;
/**
* The depth of the GRaSP if it is used.
*/
private int recursionDepth = 10;
/**
* The maximum path length for blocking paths.
*/
private int maxBlockingPathLength = -1;
/**
* The maximum size of any conditioning set.
*/
private int depth = -1;
/**
* Flag for the complete rule set, true if one should use the complete rule set, false otherwise.
*/
private boolean completeRuleSetUsed = true;
/**
* Flag indicating whether to use data order.
*/
private boolean useDataOrder = true;
/**
* This flag represents whether the Bes algorithm should be used in the search.
*
* If set to true, the Bes algorithm will be used. If set to false, the Bes algorithm will not be used.
*
* By default, the value of this flag is false.
*/
private boolean useBes = false;
/**
* This variable represents whether the discriminating path rule is used in the LV-Lite class.
*
* The discriminating path rule is a rule used in the search algorithm. It determines whether the algorithm
* considers discriminating paths when searching for patterns in the data.
*
* By default, the value of this variable is set to true, indicating that the discriminating path rule is used.
*/
private boolean doDiscriminatingPathTailRule = true;
/**
* Indicates whether the discriminating path collider rule is turned on or off.
*
* If set to true, the discriminating path collider rule is enabled. If set to false, the discriminating path
* collider rule is disabled.
*/
private boolean doDiscriminatingPathColliderRule = true;
/**
* True iff verbose output should be printed.
*/
private boolean verbose = false;
/**
* This private boolean variable determines whether the ablation leave-out scoring step is enabled or disabled. If
* this variable is set to true, the ablation leave-out scoring step is enabled. If this variable is set to false,
* the ablation leave-out-scoring step is disabled.
*
* Note that if the scoring step (BOSS or GRaSP) is left out, the algorithm will start with an initial complete
* connected graph with all edges oriented a nondirected (o-o), since the subsequent steps require an initial graph
* that is Markov.
*/
private boolean ablationLeaveOutScoringStep;
/**
* This variable represents whether the ablation leave out testing step is enabled or disabled. Ablation leave out
* testing step is a part of a software development process where certain components or features are temporarily
* removed or disabled for the purpose of testing or evaluating their impact on the overall system. By default, the
* ablation leave-out-testing step is disabled (false).
*/
private boolean ablationLeaveOutTestingStep = false;
/**
* The maximum length of any discriminating path.
*/
private int maxDdpPathLength = -1;
/**
* The style for removing extra edges.
*/
private ExtraEdgeRemovalStyle extraEdgeRemovalStyle = ExtraEdgeRemovalStyle.PARALLEL;
/**
* The timeout for the testing steps, for the extra edge removal steps and the discriminating path steps.
*/
private long testTimeout = 500;
/**
* LV-Lite constructor. Initializes a new object of LvLite search algorithm with the given IndependenceTest and
* Score object.
*
* @param test The IndependenceTest object to be used for testing independence between variables.
* @param score The Score object to be used for scoring DAGs.
* @throws NullPointerException if the score is null.
*/
public LvLite(IndependenceTest test, Score score) {
if (test == null) {
throw new NullPointerException();
}
if (score == null) {
throw new NullPointerException();
}
this.test = test;
this.score = score;
if (test instanceof MsepTest) {
this.startWith = START_WITH.GRASP;
}
}
/**
* Run the search and return s a PAG.
*
* @return The PAG.
*/
public Graph search() {
List nodes = new ArrayList<>(this.score.getVariables());
if (verbose) {
TetradLogger.getInstance().log("===Starting LV-Lite===");
}
Graph pag;
Graph dag;
List best;
if (ablationLeaveOutScoringStep) {
if (verbose) {
TetradLogger.getInstance().log("Ablation: Leave out scoring step.");
}
pag = new EdgeListGraph(nodes);
pag.fullyConnect(Endpoint.CIRCLE);
best = new ArrayList<>(nodes);
Collections.shuffle(best);
TeyssierScorer scorer = new TeyssierScorer(test, score);
scorer.score(best);
dag = scorer.getGraph(false);
} else {
if (startWith == START_WITH.BOSS) {
if (verbose) {
TetradLogger.getInstance().log("Running BOSS...");
}
long start = MillisecondTimes.wallTimeMillis();
var permutationSearch = getBossSearch();
dag = permutationSearch.search(false);
best = permutationSearch.getOrder();
best = dag.paths().getValidOrder(best, true);
long stop = MillisecondTimes.wallTimeMillis();
if (verbose) {
TetradLogger.getInstance().log("BOSS took " + (stop - start) + " ms.");
}
if (verbose) {
TetradLogger.getInstance().log("Initializing PAG to BOSS CPDAG.");
TetradLogger.getInstance().log("Initializing scorer with BOSS best order.");
}
} else if (startWith == START_WITH.GRASP) {
if (verbose) {
TetradLogger.getInstance().log("Running GRaSP...");
}
long start = MillisecondTimes.wallTimeMillis();
Grasp grasp = getGraspSearch();
best = grasp.bestOrder(nodes);
dag = grasp.getGraph(false);
long stop = MillisecondTimes.wallTimeMillis();
if (verbose) {
TetradLogger.getInstance().log("GRaSP took " + (stop - start) + " ms.");
}
if (verbose) {
TetradLogger.getInstance().log("Initializing PAG to GRaSP CPDAG.");
TetradLogger.getInstance().log("Initializing scorer with GRaSP best order.");
}
} else {
throw new IllegalArgumentException("Unknown startWith algorithm: " + startWith);
}
if (verbose) {
TetradLogger.getInstance().log("Best order: " + best);
}
}
var scorer = new TeyssierScorer(test, score);
scorer.score(best);
scorer.setKnowledge(knowledge);
scorer.bookmark();
// We initialize the estimated PAG to the BOSS/GRaSP CPDAG.
pag = new EdgeListGraph(dag);
if (verbose) {
TetradLogger.getInstance().log("Initializing PAG to BOSS CPDAG.");
TetradLogger.getInstance().log("Initializing scorer with BOSS best order.");
}
R0R4Strategy strategy = R0R4StrategyTestBased.specialConfiguration(test, knowledge, doDiscriminatingPathTailRule, doDiscriminatingPathColliderRule, false);
FciOrient fciOrient = new FciOrient(strategy);
fciOrient.setMaxPathLength(maxDdpPathLength);
fciOrient.setCompleteRuleSetUsed(completeRuleSetUsed);
fciOrient.setTestTimeout(testTimeout);
fciOrient.setVerbose(verbose);
if (verbose) {
TetradLogger.getInstance().log("Collider orientation and edge removal.");
}
// The main procedure.
Set unshieldedColliders = new HashSet<>();
Set checked = new HashSet<>();
scorer.score(best);
GraphUtils.reorientWithCircles(pag, verbose);
// We're looking for unshielded colliders in these next steps that we can detect without using only
// the scorer. We do this by looking at the structure of the DAG implied by the BOSS graph and nearby graphs
// that can be reached by constrained tucking. The BOSS graph should be edge minimal, so should have the
// highest number of unshielded colliders to copy to the PAG. Nearby graphs should have fewer unshielded
// colliders, though like the BOSS graph, they should be Markov, so their unshielded colliders should be
// valid. From sample, because of unfaithfulness, the quality may fall off depending on the difference in
// score between the best order and a tucked order.
for (Node b : best) {
var adj = pag.getAdjacentNodes(b);
for (Node x : adj) {
for (Node y : adj) {
if (GraphUtils.distinct(x, b, y) && !checked.contains(new Triple(x, b, y))) {
checkUntucked(x, b, y, pag, dag, scorer, unshieldedColliders, checked);
}
}
}
}
// These are the unshielded colliders copied from BOSS. BOSS by itself is not the cause of almost
// cycles; it's the subsequent testing steps that cause them. So we do not need to remove any
// unshielded colliders that are in this set to resolve almost-cycles.
// These will be the unshielded colliders that are found in the subsequent steps.
Set subsequentUnshieldedColliders = new HashSet<>();
GraphUtils.reorientWithCircles(pag, verbose);
GraphUtils.doRequiredOrientations(fciOrient, pag, best, knowledge, false);
GraphUtils.recallUnshieldedTriples(pag, unshieldedColliders, knowledge);
Map> extraSepsets;
if (ablationLeaveOutTestingStep) {
fciOrient.setDoDiscriminatingPathColliderRule(false);
fciOrient.setDoDiscriminatingPathTailRule(false);
} else {
// Remove extra edges using a test by examining paths in the BOSS/GRaSP DAG. The goal of this is to find a
// sufficient set of sepsets to test for extra edges in the PAG that is small, preferably just one test
// per edge.
extraSepsets = removeExtraEdges(pag, subsequentUnshieldedColliders);
unshieldedColliders.addAll(subsequentUnshieldedColliders);
if (verbose) {
TetradLogger.getInstance().log("Doing implied orientation after extra sepsets found");
}
GraphUtils.reorientWithCircles(pag, verbose);
GraphUtils.doRequiredOrientations(fciOrient, pag, best, knowledge, verbose);
GraphUtils.recallUnshieldedTriples(pag, unshieldedColliders, knowledge);
if (verbose) {
TetradLogger.getInstance().log("Finished implied orientation after extra sepsets found");
}
if (verbose) {
TetradLogger.getInstance().log("Orienting common adjacents");
}
for (Edge edge : extraSepsets.keySet()) {
orientCommonAdjacents(edge, pag, unshieldedColliders, extraSepsets);
}
if (verbose) {
TetradLogger.getInstance().log("Done orienting common adjacents");
}
}
// Final FCI orientation.
if (verbose) {
TetradLogger.getInstance().log("Doing implied orientation, grabbing unshielded colliders from FciOrient.");
}
fciOrient.setInitialAllowedColliders(new HashSet<>());
fciOrient.finalOrientation(pag);
if (verbose) {
TetradLogger.getInstance().log("Finished implied orientation.");
}
if (guaranteePag) {
pag = GraphUtils.guaranteePag(pag, fciOrient, knowledge, unshieldedColliders, false, verbose);
}
if (verbose) {
TetradLogger.getInstance().log("LV-Lite finished.");
}
return GraphUtils.replaceNodes(pag, this.score.getVariables());
}
/**
* Try adding an unshielded collider by checking the BOSS/GRaSP DAG.
*
* @param x Node - The first node.
* @param b Node - The second node.
* @param y Node - The third node.
* @param pag Graph - The graph to operate on.
* @param scorer The scorer to use for scoring the colliders.
* @param unshieldedColliders The set to store unshielded colliders.
* @param checked The set to store already checked nodes.
*/
private void checkUntucked(Node x, Node b, Node y, Graph pag, Graph cpdag, TeyssierScorer scorer, Set unshieldedColliders, Set checked) {
tryAddingCollider(x, b, y, pag, cpdag, scorer, unshieldedColliders, checked, knowledge, verbose);
}
/**
* Parameterizes and returns a new BOSS search.
*
* @return A new BOSS search.
*/
private @NotNull PermutationSearch getBossSearch() {
var suborderSearch = new Boss(score);
suborderSearch.setResetAfterBM(true);
suborderSearch.setResetAfterRS(true);
suborderSearch.setVerbose(false);
suborderSearch.setUseBes(useBes);
suborderSearch.setUseDataOrder(useDataOrder);
suborderSearch.setNumStarts(numStarts);
suborderSearch.setVerbose(verbose);
var permutationSearch = new PermutationSearch(suborderSearch);
permutationSearch.setKnowledge(knowledge);
permutationSearch.search();
return permutationSearch;
}
/**
* Parameterizes and returns a new GRaSP search.
*
* @return A new GRaSP search.
*/
private @NotNull Grasp getGraspSearch() {
Grasp grasp = new Grasp(test, score);
grasp.setSeed(-1);
grasp.setDepth(recursionDepth);
grasp.setUncoveredDepth(1);
grasp.setNonSingularDepth(1);
grasp.setOrdered(true);
grasp.setUseScore(true);
grasp.setUseRaskuttiUhler(false);
grasp.setUseDataOrder(useDataOrder);
grasp.setAllowInternalRandomness(true);
grasp.setVerbose(false);
grasp.setNumStarts(numStarts);
grasp.setKnowledge(this.knowledge);
return grasp;
}
/**
* Sets the maximum length of any discriminating path.
*
* @param maxBlockingPathLength the maximum length of any discriminating path, or -1 if unlimited.
*/
public void setMaxBlockingPathLength(int maxBlockingPathLength) {
if (maxBlockingPathLength < -1) {
throw new IllegalArgumentException("Max path length must be -1 (unlimited) or >= 0: " + maxBlockingPathLength);
}
this.maxBlockingPathLength = maxBlockingPathLength;
}
/**
* Sets the depth of the GRaSP if it is used.
*
* @param recursionDepth The depth of the GRaSP.
*/
public void setRecursionDepth(int recursionDepth) {
this.recursionDepth = recursionDepth;
}
/**
* Sets whether to guarantee a PAG output by repairing a faulty PAG.
*
* @param guaranteePag true if a faulty PAGs should be repaired, false otherwise
*/
public void setGuaranteePag(boolean guaranteePag) {
this.guaranteePag = guaranteePag;
}
/**
* Sets the algorithm to use to obtain the initial CPDAG.
*
* @param startWith the algorithm to use to obtain the initial CPDAG.
*/
public void setStartWith(START_WITH startWith) {
this.startWith = startWith;
}
/**
* Sets the knowledge used in search.
*
* @param knowledge This knowledge.
*/
public void setKnowledge(Knowledge knowledge) {
this.knowledge = new Knowledge(knowledge);
}
/**
* Sets whether the complete rule set should be used during the search algorithm. By default, the complete rule set
* is not used.
*
* @param completeRuleSetUsed true if the complete rule set should be used, false otherwise
*/
public void setCompleteRuleSetUsed(boolean completeRuleSetUsed) {
this.completeRuleSetUsed = completeRuleSetUsed;
}
/**
* Sets the verbosity level of the search algorithm.
*
* @param verbose true to enable verbose mode, false to disable it
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Sets the number of starts for BOSS.
*
* @param numStarts The number of starts.
*/
public void setNumStarts(int numStarts) {
this.numStarts = numStarts;
}
/**
* 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;
}
/**
* Sets whether to use the BES (Backward Elimination Search) algorithm during the search.
*
* @param useBes true to use the BES algorithm, false otherwise
*/
public void setUseBes(boolean useBes) {
this.useBes = useBes;
}
/**
* Sets the flag indicating whether to use data order.
*
* @param useDataOrder {@code true} if the data order should be used, {@code false} otherwise.
*/
public void setUseDataOrder(boolean useDataOrder) {
this.useDataOrder = useDataOrder;
}
/**
* Tries removing extra edges from the PAG using a test with sepsets obtained by examining the BOSS/GRaSP DAG.
*
* @param pag The graph in which to remove extra edges.
* @param unshieldedColliders A set to store the unshielded colliders found during the removal process.
* @return A map of edges to remove to sepsets used to remove them. The sepsets are the conditioning sets used to
* remove the edges. These can be used to do orientation of common adjacents, as x *->: b <-* y just in case b
* is not in this sepset.
*/
private Map> removeExtraEdges(Graph pag, Set unshieldedColliders) {
if (verbose) {
TetradLogger.getInstance().log("Checking for additional sepsets:");
}
// Note that we can use the MAG here instead of the DAG.
Map> extraSepsets = new ConcurrentHashMap<>();
// TODO: Explore the speed and accuracy implications for doing the extra edge removal in parallel or
// in serial.
if (extraEdgeRemovalStyle == ExtraEdgeRemovalStyle.PARALLEL) {
List>>> tasks = new ArrayList<>();
for (Edge edge : pag.getEdges()) {
tasks.add(() -> {
Set sepset = SepsetFinder.getSepsetPathBlockingOutOfX(pag, edge.getNode1(), edge.getNode2(), test, maxBlockingPathLength, depth, true, new HashSet<>());
return Pair.of(edge, sepset);
});
}
List>> results;
if (testTimeout == -1) {
results = tasks.parallelStream().map(task -> {
try {
return task.call();
} catch (Exception e) {
return null;
}
}).toList();
} else if (testTimeout > 0) {
results = tasks.parallelStream().map(task -> GraphSearchUtils.runWithTimeout(task, testTimeout, TimeUnit.MILLISECONDS)).toList();
} else {
throw new IllegalArgumentException("Test timeout must be -1 (unlimited) or > 0: " + testTimeout);
}
for (Pair> _edge : results) {
if (_edge != null && _edge.getRight() != null) {
extraSepsets.put(_edge.getLeft(), _edge.getRight());
}
}
for (Pair> _edge : results) {
if (_edge != null && _edge.getRight() != null) {
orientCommonAdjacents(_edge.getLeft(), pag, unshieldedColliders, extraSepsets);
}
}
} else if (extraEdgeRemovalStyle == ExtraEdgeRemovalStyle.SERIAL) {
Set edges = new HashSet<>(pag.getEdges());
Set visited = new HashSet<>();
Deque toVisit = new LinkedList<>(edges);
// Sort edges x *-* y in toVisit by |adj(x)| + |adj(y)|.
toVisit = toVisit.stream().sorted(Comparator.comparingInt(edge -> pag.getAdjacentNodes(edge.getNode1()).size() + pag.getAdjacentNodes(edge.getNode2()).size())).collect(Collectors.toCollection(LinkedList::new));
while (!toVisit.isEmpty()) {
Edge edge = toVisit.removeFirst();
visited.add(edge);
Set sepset = SepsetFinder.getSepsetPathBlockingOutOfX(pag, edge.getNode1(), edge.getNode2(), test, maxBlockingPathLength, depth, true, new HashSet<>());
if (verbose) {
TetradLogger.getInstance().log("For edge " + edge + " sepset: " + sepset);
}
if (sepset != null) {
extraSepsets.put(edge, sepset);
pag.removeEdge(edge.getNode1(), edge.getNode2());
orientCommonAdjacents(edge, pag, unshieldedColliders, extraSepsets);
for (Node node : pag.getAdjacentNodes(edge.getNode1())) {
Edge adjacentEdge = pag.getEdge(node, edge.getNode1());
if (!visited.contains(adjacentEdge)) {
toVisit.remove(adjacentEdge);
toVisit.addFirst(adjacentEdge);
}
}
for (Node node : pag.getAdjacentNodes(edge.getNode2())) {
Edge adjacentEdge = pag.getEdge(node, edge.getNode2());
if (!visited.contains(adjacentEdge)) {
toVisit.remove(adjacentEdge);
toVisit.addFirst(adjacentEdge);
}
}
}
}
}
if (verbose) {
TetradLogger.getInstance().log("Done checking for additional sepsets max length = " + maxBlockingPathLength + ".");
}
return extraSepsets;
}
/**
* Orients an unshielded collider in a graph based on a sepset from a test and adds the unshielded collider to the
* set of unshielded colliders.
*
* @param edge The edge to remove the adjacency for.
* @param pag The graph in which to orient the unshielded collider.
* @param unshieldedColliders The set of unshielded colliders to add the new unshielded collider to.
* @param extraSepsets The map of edges to sepsets used to remove them.
*/
private void orientCommonAdjacents(Edge edge, Graph pag, Set unshieldedColliders, Map> extraSepsets) {
List common = pag.getAdjacentNodes(edge.getNode1());
common.retainAll(pag.getAdjacentNodes(edge.getNode2()));
pag.removeEdge(edge.getNode1(), edge.getNode2());
for (Node node : common) {
if (!extraSepsets.get(edge).contains(node)) {
pag.setEndpoint(edge.getNode1(), node, Endpoint.ARROW);
pag.setEndpoint(edge.getNode2(), node, Endpoint.ARROW);
if (verbose) {
TetradLogger.getInstance().log("Oriented " + edge.getNode1() + " *-> " + node + " <-* " + edge.getNode2() + " in PAG.");
}
unshieldedColliders.add(new Triple(edge.getNode1(), node, edge.getNode2()));
}
}
}
/**
* Adds a collider if it's a collider in the current scorer and knowledge permits it in the current PAG.
*
* @param x The first node of the unshielded collider.
* @param b The second node of the unshielded collider.
* @param y The third node of the unshielded collider.
* @param pag The graph in which to add the unshielded collider.
* @param scorer The scorer to use for scoring the unshielded collider.
* @param unshieldedColliders The set of unshielded colliders to add the new unshielded collider to.
* @param checked The set of checked unshielded colliders.
* @param knowledge The knowledge object.
* @param verbose A boolean flag indicating whether verbose output should be printed.
*/
private void tryAddingCollider(Node x, Node b, Node y, Graph pag, Graph cpdag, TeyssierScorer scorer, Set unshieldedColliders, Set checked, Knowledge knowledge, boolean verbose) {
if (cpdag != null) {
if (cpdag.isDefCollider(x, b, y) && !cpdag.isAdjacentTo(x, y)) {
unshieldedColliders.add(new Triple(x, b, y));
checked.add(new Triple(x, b, y));
if (verbose) {
TetradLogger.getInstance().log("Copied " + x + " *-> " + b + " <-* " + y + " from CPDAG to PAG.");
}
}
} else if (GraphUtils.colliderAllowed(pag, x, b, y, knowledge)) {
if (scorer.unshieldedCollider(x, b, y)) {
unshieldedColliders.add(new Triple(x, b, y));
checked.add(new Triple(x, b, y));
if (verbose) {
TetradLogger.getInstance().log("Copied " + x + " *-> " + b + " <-* " + y + " from CPDAG to PAG.");
}
}
}
}
/**
* Sets the maximum size of the separating set used in the graph search algorithm.
*
* @param depth the maximum size of the separating set
*/
public void setDepth(int depth) {
this.depth = depth;
}
/**
* Sets whether the scoring step (BOSS or GRASP) should be left out during ablation. If this step is left out, the
* algorithm will start with a completely connected nondirected (o-o) graph, since the subsequent steps require an
* initial graph that is Markov. This will make the algorithm slow.
*
* One cannot leave out both the testing and scoring steps of the algorithm; one or the other must be enabled.
*
* @param ablationLeaveOutScoringStep True iff the scoring step should be left out.
*/
public void setAblationLeaveOutScoringStep(boolean ablationLeaveOutScoringStep) {
if (this.ablationLeaveOutTestingStep && ablationLeaveOutScoringStep) {
throw new IllegalArgumentException("Cannot leave out both the testing and scoring steps of the algorithm.");
}
this.ablationLeaveOutScoringStep = ablationLeaveOutScoringStep;
}
/**
* Sets whether to the testing steps (extra edge removal and discriminating path steps) should be left out during
* ablation. If these stepw are left out, the algorithm will not remove extra edges or do discriminating path
* steps.
*
* One cannot leave out both the testing and scoring steps of the algorithm; one or the other must be enabled.
*
* @param ablationLeaveOutTestingStep the flag indicating whether to enable the ablation leave-out testing step.
*/
public void setAblationLeaveOutTestingStep(boolean ablationLeaveOutTestingStep) {
if (this.ablationLeaveOutScoringStep && ablationLeaveOutTestingStep) {
throw new IllegalArgumentException("Cannot leave out both the testing and scoring steps of the algorithm.");
}
this.ablationLeaveOutTestingStep = ablationLeaveOutTestingStep;
}
/**
* Sets the maximum DDP path length.
*
* @param maxDdpPathLength the maximum DDP path length to set
*/
public void setMaxDdpPathLength(int maxDdpPathLength) {
this.maxDdpPathLength = maxDdpPathLength;
}
/**
* Sets the style for removing extra edges.
*
* @param extraEdgeRemovalStyle the style for removing extra edges
*/
public void setExtraEdgeRemovalStyle(ExtraEdgeRemovalStyle extraEdgeRemovalStyle) {
this.extraEdgeRemovalStyle = extraEdgeRemovalStyle;
}
/**
* Sets the timeout for the testing steps, for the extra edge removal steps and the discriminating path steps.
*
* @param testTimeout the timeout for the testing steps, for the extra edge removal steps and the discriminating
* path steps.
*/
public void setTestTimeout(long testTimeout) {
this.testTimeout = testTimeout;
}
/**
* Enumeration representing different start options.
*/
public enum START_WITH {
/**
* Start with BOSS.
*/
BOSS,
/**
* Start with GRaSP.
*/
GRASP
}
/**
* The ExtraEdgeRemovalStyle enum specifies the styles for removing extra edges.
*/
public enum ExtraEdgeRemovalStyle {
/**
* Remove extra edges in parallel.
*/
PARALLEL,
/**
* Remove extra edges in serial.
*/
SERIAL,
}
}