Skip to content

[tpc] Save edge-rejection diagnostics to file in tskeleton (sepset path sanity check + test stats) #161

Description

@felixleopoldo

Summary

When tskeleton removes an edge x–y (i.e. a CI test returns pval >= alpha), no diagnostic information is currently persisted. This makes it hard to audit why an edge was rejected — especially in the presence of missing data where the MCAR/MAR/MNAR mechanism can cause spurious separations.

Proposed Feature

At the moment an edge is removed inside the repeat loop of tskeleton (currently workflow/rules/structure_learning_algorithms/tpc/R/tskeleton.R, around the pval >= alpha branch), write a row to a CSV file with the following columns:

Column Description
x Label of node x
y Label of node y
sepset Comma-separated labels of the separating set S
pval P-value of the CI test that triggered removal
n_complete Number of complete-case observations used in the test
sepset_on_path Boolean — whether all elements of S satisfy the path condition (see below)

This logging should only be performed when verbose = TRUE.

Path condition for sepset_on_path

For each element z ∈ S, check whether z lies on some indirect simple path between x and y in the current skeleton G:

  • Empty S → always TRUE (no elements to check)
  • Each zTRUE iff x can reach z in G \ {y} and y can reach z in G \ {x} (BFS/DFS on the undirected adjacency matrix)

sepset_on_path is TRUE only if every element of S passes. A FALSE value is a sanity-check warning: the CI test found independence via a node that isn't even on an indirect route between x and y, which may indicate a spurious test result (e.g. due to missing data artefacts).

Rationale: the direct edge x–y is not a path that can be "broken" by conditioning, so only indirect paths (length ≥ 2, no repeated vertices) are relevant. Checking reachability in G \ {y} and G \ {x} respectively is sufficient for this sanity check.

Acceptance Criteria

  • A file path for the output CSV is passed into tskeleton (e.g. as an optional edge_log parameter; NULL disables logging)
  • Logging only occurs when verbose = TRUE
  • One row is appended per removed edge, at the point of removal (i.e. inside the pval >= alpha block)
  • n_complete reuses the value already computed at the complete.cases call
  • The path check handles the empty-S case correctly (returns TRUE)
  • Writing is append-based (write.table(..., append = TRUE)) so partial runs remain readable
  • script.R for the tpc module passes the log path through to tskeleton

Context / Motivation

  • Debugging missing-data artefacts in the tpc algorithm where CI tests can misfire
  • Understanding which edges were removed and whether the separating set is structurally plausible
  • The n_complete and pval columns allow filtering out tests based on low sample size or marginal significance

Relevant Code

  • workflow/rules/structure_learning_algorithms/tpc/R/tskeleton.Rpval >= alpha branch (edge removal + sepset assignment, ~line 527–529)
  • workflow/rules/structure_learning_algorithms/tpc/script.R — entry point that calls tskeleton

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions