Visit GST grow branches in descending score order (BOSS/GRaSP)#265
Merged
Conversation
BOSS uses a higher-is-better score, and TETRAD's GrowShrinkTree visits
grow branches in descending growScore order (GSTNode.compareTo() is
ascending, but grow() sorts with Collections.reverseOrder()).
causal-learn's GST sorted them ascending, so the trace followed the
lowest-scoring improving add first.
Because GST.trace descends into the first prefix-matching branch and
returns its shrink score, the sort direction changes the returned score
and parent set for a given prefix -- not just presentation order. With
the issue's mock scores, prefix [1, 2] returns {1} (2.0837) ascending vs
{2} (4.3568) descending. Visiting the best improving add first follows
the more promising path: a better local approximation that also prunes
more, matching TETRAD. This GST is shared by both BOSS and GRaSP.
Adds tests/TestGST.py, a focused regression test over the deterministic
mock scores from the issue.
Refs py-why#264
Signed-off-by: Sanghack Lee <sanghack.lee@gmail.com>
sanghack81
force-pushed
the
fix/gst-branch-sort-descending
branch
from
May 30, 2026 07:46
708cb8a to
140a858
Compare
kunwuz
approved these changes
Jun 4, 2026
Collaborator
|
Thanks so much for your contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the grow-branch sort direction in
causallearn/search/PermutationBased/gst.pyso the Grow-Shrink Tree visits higher-scoring grow branches first, matching TETRAD'sGrowShrinkTree(discussed in #264).Root cause
BOSS uses a higher-is-better score. In TETRAD's
edu.cmu.tetrad.search.utils.GrowShrinkTree,GSTNode.compareTo()orders by ascendinggrowScore, butgrow()sorts withthis.branches.sort(Collections.reverseOrder()), so branches are visited in descendinggrowScoreorder.In causal-learn,
GSTNode.__lt__is ascending andgrow()calledself.branches.sort()— i.e. ascending, the opposite direction.Why it matters (not just presentation order)
GST.tracedescends into the first prefix-matching branch and returns its shrink score, so the sort direction changes the returned score and parent set for a given prefix. With the deterministic mock scores from #264 and prefix[1, 2]:Visiting the best improving add first follows the more promising path — a better local approximation that also prunes more of the tree.
Change
I kept
__lt__ascending and reversed at the sort site, mirroring TETRAD'scompareTo+Collections.reverseOrder()(equivalent to inverting the comparator, the other form proposed in #264). ThisGSTis shared by bothbossandgrasp, so both now use the descending (TETRAD-style) traversal.Scope
This PR changes only the branch traversal order. Other existing behavior is intentionally left as-is: grow branches are still included on strict improvement (
score > self.grow_score; TETRAD uses>=), and background knowledge (forbidden/required) remains unimplemented as before. Aligning those with TETRAD is out of scope here.Performance (from #264; synthetic linear-Gaussian, BIC-from-cov, avg_degree=2, lambda=2)
p=50, n=1000:
Mean wall time 1.110s → 0.257s (4.32x).
p=100, n=1000:
Mean wall time 8.596s → 1.505s (5.71x). As noted in #264, in some seeds the returned CPDAG edge count also changes.
Test
Adds
tests/TestGST.py, a focused, deterministic regression test over the #264 mock scores: it asserts the root grow branches are ordered by descending score and that tracing prefix[1, 2]selects the higher-scoring parent{2}. The test fails on the previous ascending behavior and passes after this change.Closes #264