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

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

The newest version!
///////////////////////////////////////////////////////////////////////////////
// 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, } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy