Add XGES (Extremely Greedy Equivalence Search) - #274
Open
smellslikeml wants to merge 1 commit into
Open
smellslikeml wants to merge 1 commit into
smellslikeml wants to merge 1 commit into
Conversation
Adds `XGES` to causallearn/search/ScoreBased/ — a score-based causal-discovery algorithm (Extremely Greedy Equivalence Search; Nazaret & Blei, UAI 2024, arXiv:2502.19551). It learns a CPDAG by greedily applying the single best-scoring edit across insert / reverse / delete operators with incremental candidate maintenance and an extended-search local-optima escape, returning a Record whose `G` is a GeneralGraph CPDAG (matching the `ges(...)` contract). Reuses causal-learn's own BIC LocalScoreClass, pdag2dag/dag2cpdag, and GeneralGraph. Clean-room from the paper only: the reference implementation (ANazaret/XGES) is unlicensed, so no reference code was read or vendored; the `xges` PyPI package is used solely as an output parity oracle in the tests. Requested in py-why#257. Adds tests/TestXGES.py (SHD correctness floor mirroring TestGES, XGES-equivalence, operator-vs-brute-force, and a guarded xges parity oracle) and docs (XGES.rst + index entry). Co-authored-by: remyx-ai[bot] <289541483+remyx-ai[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: smellslikeml <smellslikeml@users.noreply.github.com>
smellslikeml
force-pushed
the
feat/xges
branch
from
September 28, 2026 15:32
93da4d0 to
7d0efbe
Compare
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.
Add XGES (Extremely Greedy Equivalence Search)
Adds
XGEStocausallearn/search/ScoreBased/— a score-based causal-discovery algorithm (Extremely Greedy Equivalence Search; Nazaret & Blei, UAI 2024, arXiv:2502.19551). It learns a CPDAG by greedily applying the single best-scoring edit across insert / reverse / delete operators with incremental candidate maintenance, plus an extended-search local-optima escape (extended_searchselects XGES-0 vs full XGES) — a distinct, faster engine than GES's phase-separated passes. Requested in #257.Provenance / license
Clean-room from the paper. The reference implementation (
ANazaret/XGES) is unlicensed (all-rights-reserved), so no reference code was read or vendored — the algorithm is implemented from arXiv:2502.19551 only. ThexgesPyPI package is used solely as an output parity oracle in the tests (its outputs are compared; its code never enters the diff). This mirrors house practice — causal-learn's owngesis a from-algorithm implementation.Design
causallearn/search/ScoreBased/XGES.py: the three-queue greedy engine (insert / reverse / delete, with the Reverse operator GES lacks and its semi-directed-path validity check) + the extended-search escape, returning aRecordwhoseGis aGeneralGraphCPDAG — matching theges(...)contract.LocalScoreClass,pdag2dag/dag2cpdag, andGeneralGraph. No new dependencies (pure NumPy/SciPy).alpha(default 2) maps to causal-learn'slambda_valueaslambda_value = alpha / 2(paper α=2 → λ=1.0), documented so the SHD test and thexgesparity comparison are meaningful.Tests (
tests/TestXGES.py)TestGES::test_ges_simulate_linear_gaussian_with_local_score_BIC: recovers a known 5-node CPDAG atSHD <= 1(both XGES-0 and extended).SHD(GES, XGES) == 0on the same data (same Markov equivalence class as causal-learn's own GES — validates against a trusted in-repo reference).xgesPyPI parity oracle — guarded (skips ifxgesisn't installed); compares the learned CPDAG against the reference solver on the same data with the α↔λ-mapped penalty.All four pass locally: the SHD floor / equivalence / operator tests need only causal-learn, and the parity oracle was run against
xges0.1.6 on PyPI —SHD(xges, causal-learn XGES) = 0on the test graph.SHD <= 1on a 5-node graph is a correctness floor; benchmark-scale parity across larger/denser graphs is a reasonable follow-up, and the (skip-guarded) oracle test is the harness for it.Scope
4 files:
XGES.py,tests/TestXGES.py,docs/.../XGES.rst, and theindex.rstentry (mirroring the DGES layout). No changes to GES/DGES or shared machinery.🤖 Drafted with Claude Code, human-reviewed. Implemented clean-room from the paper.