Skip to content

Make MIS → KingsSubgraph reduction deterministic (#1061)#1063

Open
isPANN wants to merge 2 commits intomainfrom
fix/1061-deterministic-pathwidth
Open

Make MIS → KingsSubgraph reduction deterministic (#1061)#1063
isPANN wants to merge 2 commits intomainfrom
fix/1061-deterministic-pathwidth

Conversation

@isPANN
Copy link
Copy Markdown
Collaborator

@isPANN isPANN commented Apr 23, 2026

Summary

Fixes #1061. pred reduce output for MIS<SimpleGraph, One> → MIS<KingsSubgraph, One> now produces byte-identical bundles across repeated invocations on the same input. Three pred reduce runs on a 64-vertex MIS used to vary ~15–20 % in atom/edge count; they now match exactly.

Root cause

Two compounding sources of non-determinism in src/rules/unitdiskmapping/pathdecomposition.rs:

  1. Unseeded RNGgreedy_step used rand::rng() (thread-local, OS-seeded) to pick among tied candidates. Compounded by pathwidth's 10 random restarts for >30-vertex inputs.
  2. HashSet iteration orderAdjList was Vec<HashSet<usize>>. When vsep_updated_neighbors did for &w in &adj[v] { nbs.push(w); }, per-process HashSet iteration order leaked into layout.neighbors, which then fed the next greedy step. Seeding the RNG alone was not enough (I hit this: same seed, different output — which is how I found this second source).

Fix

  • Change AdjList from Vec<HashSet<usize>> to Vec<BTreeSet<usize>> so iteration is deterministic (sorted).
  • Thread a SmallRng through greedy_step / greedy_decompose / pathwidth, seeded from new DEFAULT_PATHWIDTH_SEED = 0 so the public API is reproducible by default.
  • Add pathwidth_with_seed(.., seed) as an escape hatch for callers that want to sample the layout space. Single SmallRng threads through all 10 restarts so each restart remains diverse (RNG state progresses) while overall output is reproducible given the seed.
  • reduce_to() stays a pure function — no env vars, no global state, no trait changes.

Non-goals (tracked separately)

Tests

Three new tests (all passing):

  • test_pathwidth_greedy_is_deterministic — 64-vertex grid graph, 4 pathwidth calls must yield identical vertex orderings and vsep.
  • test_pathwidth_with_seed_varies_output — confirms seed actually threads into tie-breaking (same seed → same output; at least one seed in 1..=10 → different output).
  • test_mis_simple_one_to_kings_one_is_deterministic_on_large_graph — end-to-end regression for the reporter's 64-vertex scenario; 4 reduce_to calls must agree on target num_vertices and num_edges.

Full suite: 4960 library tests + 316 CLI tests pass. cargo clippy and cargo fmt --check clean.

Test plan

  • cargo test --lib (4960 pass, 0 fail)
  • cargo test -p problemreductions-cli (316 pass, 0 fail)
  • cargo clippy --lib --all-features clean
  • cargo fmt --check clean
  • Manual CLI repro: 3× pred reduce on 64-vertex MIS produce byte-identical diff-empty bundles

🤖 Generated with Claude Code

isPANN and others added 2 commits April 23, 2026 19:52
Two compounding sources of non-determinism in the greedy path decomposition
were making `pred reduce` vary ~15-20% in target-graph size across runs:

- `greedy_step` used `rand::rng()` (unseeded thread-local RNG) for tie-breaking.
- `AdjList` was `Vec<HashSet<usize>>`; per-process HashSet iteration order
  leaked into `layout.neighbors` via pushes in `vsep_updated_neighbors`,
  so even a fixed RNG seed would still drift.

Fix:

- Switch `AdjList` to `Vec<BTreeSet<usize>>` so iteration is sorted.
- Thread a seeded `SmallRng` through `greedy_step` / `greedy_decompose` /
  `pathwidth`, using `DEFAULT_PATHWIDTH_SEED = 0` for the public entry point.
- Expose `pathwidth_with_seed(.., seed)` as an escape hatch for callers that
  want to sample the layout space (e.g. future minimum-atom search in #1062).

Three CLI invocations of `pred reduce` on a 64-vertex MIS now produce
byte-identical bundles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The triangular mapping shares the same `pathwidth` call as KSG, so it had
the same non-determinism before this PR. Pin the behavior with an
equivalent regression test so future changes to pathdecomposition can't
silently regress this mapping either.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.92%. Comparing base (b3b18f1) to head (84c67d4).

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1063    +/-   ##
========================================
  Coverage   97.92%   97.92%            
========================================
  Files         966      966            
  Lines      100043   100153   +110     
========================================
+ Hits        97967    98076   +109     
- Misses       2076     2077     +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

pred reduce output is non-deterministic (MIS → KingsSubgraph varies 10-20% in atom count)

1 participant