Class RecursiveBlocking
This version replaces the mutual recursion between
findPathToTargetVisit and tryBlockAllContinuations with an
explicit stack, eliminating the risk of StackOverflowError on deep
graphs. All semantics are identical to the original recursive version.
Given distinct nodes x and y, the algorithm attempts to build a set Z that
blocks all blockable paths between x and y, starting from an optional seed set
of nodes to include. If such a set is found it is returned and may later be
tested against the distribution for conditional independence. If any path is
provably unblockable, or the analysis is interrupted or inconclusive, the
routine returns null, indicating that no valid graphical separating
set was found within the given constraints.
The eligible conditioning nodes are drawn from BFS shells of radius
maxRadius around x and/or y, controlled by
nearWhichEndpoint:
- 1 — pool drawn from shells around x only
- 2 — pool drawn from shells around y only
- 3 — pool drawn from shells around both x and y (union)
A node outside the pool that would otherwise be required to block a path
causes that branch to return INDETERMINATE rather than
UNBLOCKABLE, so the overall call returns null without
asserting that no separator exists at all — a wider radius might succeed.
When maxRadius is -1 the pool is unrestricted and all graph nodes
are eligible. A depth parameter additionally caps the total size of
Z; attempts to exceed it are likewise treated as INDETERMINATE.
Continuation memoization (clique optimization). Within a single
findPathToTargetVisit call, the verdict for a frame is, in the
common case, a pure function of the incoming pair (a, b) and the
conditioning set z in effect at frame entry: reachable
inspects only the triple (a, b, c) and z, so two frames that
arrive at the same b from the same a under the same entry
z explore identical continuations and reach the same verdict. This
recurs heavily inside cliques, where many distinct path prefixes converge on
the same node. We cache such verdicts keyed on (a, b, z) and reuse
them on later arrivals.
The cache is not a pure function of (a, b, z) in one
situation: a frame can return BLOCKED early because its node already
lies on the current path (a cycle hit). That verdict is an artifact
of where we came from, not an intrinsic property of (a, b, z),
so it must never be cached, and any ancestor frame whose verdict was computed
using such a child verdict must also not be cached. We track this with a
pathTainted flag that propagates from child to parent. Only untainted,
non-INDETERMINATE verdicts are stored. INDETERMINATE verdicts
are never cached because they reflect a search-limit (recursion depth, frame
cap) rather than a graph fact, and could resolve differently in another
context.
The cache lives for the duration of one findPathToTargetVisit
call. It is deliberately not shared across the outer fixed-point
iterations in blockPathsRecursivelyAdj, nor across first hops,
because z grows between those calls and a per-call cache keeps every
key consistent with the z actually in effect when the frame was
entered. The clique redundancy Bryan identified occurs entirely within a
single DFS call, so this scoping captures the win while keeping the
correctness argument simple.
Key features:
- Respects PAG semantics for colliders, non-colliders, and latent nodes.
- Uses an outer fixed-point loop to handle colliders activated by Z growth across branches.
- Supports recursion depth limits, depth limits, radius limits, and "do not follow" constraints.
- Background knowledge parameter is reserved for future extension.
- The presence of a direct edge x–y does not preempt construction, but such an edge may prevent a valid separator from existing.
- The returned set is always subject to a statistical independence test to confirm it functions as an actual separating set in the distribution.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumThree-valued result of path-blocking analysis.static final recordThe result of a blocking-set search.static final recordA single endpoint mark: the endpoint ataton the edge betweenfromandat.static enumSelects the search strategy used byblockPathsRecursively(Graph, Node, Node, Set, Set, int, int, int, int, boolean). -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic RecursiveBlocking.StrategyThe default strategy used for processing or executing tasks in the system. -
Method Summary
Modifier and TypeMethodDescriptionstatic voidBegins collecting load-bearing blocked-collider marks on this thread.blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge) Recursively blocks paths in a graph between two nodes based on specified parameters and constraints.blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge, long deadlineMs) Single canonical entry point for separating-set search.blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge, long deadlineMs, RecursiveBlocking.Strategy strategy) Blocks paths in a graph recursively based on the given parameters and strategy.static Set<RecursiveBlocking.Mark> Ends collection on this thread and returns the marks recorded sincebeginConflictCollection().
-
Field Details
-
DEFAULT_STRATEGY
The default strategy used for processing or executing tasks in the system. This is a globally accessible constant that indicates the predefined approach, which is set to a recursive strategy. It can be utilized wherever a default execution policy is required.
-
-
Method Details
-
beginConflictCollection
public static void beginConflictCollection()Begins collecting load-bearing blocked-collider marks on this thread. Every blocking call on this thread records into the same set untilendConflictCollection()is called. -
endConflictCollection
Ends collection on this thread and returns the marks recorded sincebeginConflictCollection().- Returns:
- the recorded marks, in first-encountered order; empty if none.
-
blockPathsRecursively
public static RecursiveBlocking.BlockingResult blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge) throws InterruptedException Recursively blocks paths in a graph between two nodes based on specified parameters and constraints.- Parameters:
graph- The graph in which the paths are to be blocked.x- The starting node of the path.y- The ending node of the path.containing- A set of nodes that must be included in the path.notFollowed- A set of nodes that must not be followed in the path.recursiveDepth- The maximum depth for recursion during the path blocking process.depth- The current depth of the recursive call.maxRadius- The maximum radius from the starting node to consider for path blocking.nearWhichEndpoint- Determines near which endpoint of the path the restriction is to be enforced.ignoreDirectEdge- A flag indicating whether to ignore direct edges between the starting node and ending node.- Returns:
- A BlockingResult object containing the results of the path blocking operation.
- Throws:
InterruptedException- If the operation is interrupted during execution.
-
blockPathsRecursively
public static RecursiveBlocking.BlockingResult blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge, long deadlineMs) throws InterruptedException Single canonical entry point for separating-set search.Dispatches to
blockPathsRecursivelyFull(Graph, Node, Node, Set, Set, int, int, int, int, boolean)(depth-first with backtracking) orblockPathsIterativeDeepening(Graph, Node, Node, Set, Set, int, int, int, int, boolean)(iterative deepening on recursion depth) based onstrategy, and returns a fullRecursiveBlocking.BlockingResultdistinguishing UNBLOCKABLE from INDETERMINATE.- Parameters:
graph- the graphx- first endpointy- second endpointcontaining- nodes forced into Z (the seed set)notFollowed- nodes not to be traversedrecursiveDepth- max recursion depth for RECURSIVE; ceiling for ITERATIVE_DEEPENING (-1 = graph.getNumNodes())depth- maximum size of Z (-1 = unlimited)maxRadius- BFS radius for the node pool (-1 = unlimited)nearWhichEndpoint- 1 = near x, 2 = near y, 3 = near bothignoreDirectEdge- whether to ignore the direct x–y edgedeadlineMs- deadline for the operation (in ms)- Returns:
- a
RecursiveBlocking.BlockingResultdescribing the outcome - Throws:
InterruptedException- if the thread is interrupted
-
blockPathsRecursively
public static RecursiveBlocking.BlockingResult blockPathsRecursively(Graph graph, Node x, Node y, Set<Node> containing, Set<Node> notFollowed, int recursiveDepth, int depth, int maxRadius, int nearWhichEndpoint, boolean ignoreDirectEdge, long deadlineMs, RecursiveBlocking.Strategy strategy) throws InterruptedException Blocks paths in a graph recursively based on the given parameters and strategy.- Parameters:
graph- the graph in which paths are to be blockedx- the starting node for path blockingy- the ending node for path blockingcontaining- the set of nodes that paths must containnotFollowed- the set of nodes that paths must not includerecursiveDepth- the current recursive depth of the processdepth- the depth of the searchmaxRadius- the maximum radius for path blocking considerationnearWhichEndpoint- specifies which endpoint (x or y) the operation is near, influencing behaviorignoreDirectEdge- whether to ignore the direct edge between x and ydeadlineMs- the time limit in milliseconds for the operationstrategy- the strategy to use for blocking paths (e.g., ITERATIVE_DEEPENING, SHALLOW_RECURSIVE, RECURSIVE)- Returns:
- a BlockingResult object containing the result of the blocking operation and associated metadata
- Throws:
InterruptedException- if the blocking operation is interrupted
-