Skip to content

Pair search over every candidate, up to a pair budget - #16

Merged
Autoplectic merged 1 commit into
mainfrom
pair-pool-coverage
Nov 14, 2026
Merged

Autoplectic merged 1 commit into
mainfrom
pair-pool-coverage

Conversation

@Autoplectic

Copy link
Copy Markdown
Member

Problem

The synergy-aware pair search finds parents that inform the target only jointly, as in an XOR. Its pool took the remaining candidates in (lag, process) order and stopped at the first 20. In larger networks, XOR parents at longer lags were therefore never searched. For example, with 8 processes and parents (5, lag 3) and (6, lag 2), the pair sits beyond the 20th candidate and is missed entirely.

Change

  • SkeletonSettings.max_pairs (default 5000 pairs, i.e. 100 candidates) replaces the 20-candidate cap. max_pair_candidates now defaults to None and still caps the pool when set. The order is unchanged and does not look at the target, so the maximum statistic stays a valid test.
  • TargetSkeleton.pair_pool_dropped lists the candidates left out, and a single warning says so.
  • The pair test is curtailed for every estimator unless curtail=False. It reports no p-value, and curtailment never changes a decision.
  • Joint codes are built pair by pair instead of all at once. A 100-candidate pool has 4950 pairs, each a column as long as the data.

Results

From validation/pair_pool.py, with 4000 samples, the default permutation counts, and 10 XOR plus 10 null targets per size:

Processes Pool XOR recovered False parents per target Seconds per target
8 first 20 5/10 0.00 2.3
8 5000 pairs 10/10 0.10 3.8
12 first 20 2/10 0.05 2.7
12 5000 pairs 10/10 0.05 7.8
20 first 20 0/10 0.05 3.7
20 5000 pairs 10/10 0.25 25.6

The pair test keeps its level with the larger pool. On null targets it rejected 5 of 100 with 12 processes (1540 pairs) and 6 of 100 with 20 processes (4560 pairs). At 20 processes, the false parents were one greedy addition and two spurious pairs across 20 targets, which is consistent with the per-search level.

Tests

  • test_pair_pool_order_and_budget.
  • test_pair_search_reaches_candidates_beyond_twenty, which also checks the warning and pair_pool_dropped.
  • test_pair_search_curtailment_keeps_the_decision.
  • Full suite: 278 passed, 13 skipped. ruff, ty and sphinx -W are clean.

Motivated by Hourican & Quax, "Operationalising Weaker Faithfulness" (arXiv:2610.31724).

The synergy-aware pair search took the remaining candidates in (lag, process)
order and stopped at the first 20, so jointly informative (XOR) parents at
longer lags or in larger networks were never searched. With 12 processes the old
pool recovered 2 of 10 XOR pairs; with 20 processes it recovered none.

- SkeletonSettings.max_pairs (default 5000 pairs, i.e. 100 candidates) replaces
  the 20-candidate cap. max_pair_candidates now defaults to None and still caps
  the pool if set. The order is unchanged and does not look at the target, so
  the maximum statistic stays valid: on null targets the pair test rejected 5 of
  100 with 1540 pairs and 6 of 100 with 4560.
- Candidates left out are listed in TargetSkeleton.pair_pool_dropped, with one
  warning.
- The pair test is curtailed for every estimator unless curtail=False: it
  reports no p-value, and curtailment never changes a decision.
- Joint codes are built pair by pair; materialising them all would mean up to
  4950 columns as long as the data.
- validation/pair_pool.py and a table in the validation docs: 10 of 10 XOR
  pairs recovered with 8, 12 and 20 processes, at 1.7 to 7 times the time per
  target.
@Autoplectic
Autoplectic marked this pull request as ready for review November 14, 2026 19:03
@Autoplectic
Autoplectic merged commit 8b85678 into main Nov 14, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant