Skip to content

Replace hardcoded canonical rule examples with ILPSolver calls#775

Merged
zazabap merged 4 commits intomainfrom
replace-hardcoded-ilp-examples-772-v2
Mar 26, 2026
Merged

Replace hardcoded canonical rule examples with ILPSolver calls#775
zazabap merged 4 commits intomainfrom
replace-hardcoded-ilp-examples-772-v2

Conversation

@isPANN
Copy link
Copy Markdown
Collaborator

@isPANN isPANN commented Mar 26, 2026

Summary

  • Convert 46 ILP reduction rules from hardcoded SolutionPair to dynamic ILPSolver::new().solve() via new rule_example_via_ilp<S, V>() helper
  • Add z upper bound constraint in MinMaxMulticenter ILP formulation to prevent HiGHS stalling
  • Extract shared helper in example_db::specs, eliminating ~700 lines of boilerplate

Convention change

New ILP rule examples should use:

crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)  // or i32

instead of hardcoding SolutionPair. See PR comment for details.

Test plan

  • cargo build --features example-db,ilp compiles cleanly
  • cargo test --features example-db,ilp — 3530 tests pass
  • rule_specs_solution_pairs_are_consistent validates all canonical examples

Closes #772

🤖 Generated with Claude Code

isPANN and others added 2 commits March 26, 2026 11:22
Convert 46 ILP reduction rules from hardcoded SolutionPair to dynamic
ILPSolver::new().solve() calls, ensuring canonical examples actually
verify the ILP solver can solve the reduction.

Also adds a z upper bound constraint in MinMaxMulticenter ILP formulation
to prevent HiGHS from stalling on unbounded [0, 2^31) domain.

Closes #772

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…plate

Add `rule_example_via_ilp<S, V>()` to `example_db::specs` that performs
reduce → ILP solve → extract → assemble in one call. This replaces
the 8-line block that was copy-pasted across 60 ILP rule files, and
also eliminates the double `reduce_to()` call (once explicit, once
inside `rule_example_with_witness`).

Also fixes stale docstring on `build_example_db()` which incorrectly
claimed no solver is called.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@isPANN
Copy link
Copy Markdown
Collaborator Author

isPANN commented Mar 26, 2026

Canonical rule example convention change

After this PR, the standard pattern for canonical_rule_example_specs() in *_ilp.rs files is:

#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
    vec![crate::example_db::specs::RuleExampleSpec {
        id: "<source>_to_ilp",
        build: || {
            let source = /* construct instance */;
            crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)  // or i32
        },
    }]
}

What changed:

  • rule_example_via_ilp<S, V>(source) replaces the old pattern of hardcoding SolutionPair { source_config: vec![...], target_config: vec![...] }
  • Internally it does: reduce_to()ILPSolver::new().solve()extract_solution() → assemble — in one call, with no double reduction
  • The use crate::export::SolutionPair import is no longer needed for ILP rules
  • rule_example_with_witness() still exists for non-ILP target rules that need a manually specified solution pair

For new ILP rules: just use rule_example_via_ilp. If the ILP solver hangs, it means the formulation needs tighter variable bounds (see minmaxmulticenter_ilp.rs for an example of adding a z upper bound).

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.79%. Comparing base (093c595) to head (49fc30f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #775      +/-   ##
==========================================
- Coverage   97.80%   97.79%   -0.01%     
==========================================
  Files         580      580              
  Lines       65742    65250     -492     
==========================================
- Hits        64297    63811     -486     
+ Misses       1445     1439       -6     

☔ 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.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the example database infrastructure so canonical ILP reduction rule examples are computed dynamically by running the ILP solver (instead of relying on hardcoded SolutionPair witnesses), and it adds an explicit upper bound constraint in the MinMaxMulticenter→ILP formulation to improve HiGHS performance.

Changes:

  • Introduce rule_example_via_ilp<S, V>() helper to reduce→solve→extract and assemble canonical ILP rule examples in a single pass.
  • Replace hardcoded canonical SolutionPair witnesses across many ILP rule example specs with calls to rule_example_via_ilp.
  • Add a z <= z_upper constraint to MinMaxMulticenter→ILP and update related unit tests.

Reviewed changes

Copilot reviewed 64 out of 64 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/example_db/specs.rs Adds rule_example_via_ilp helper for dynamic ILP-based canonical rule examples.
src/example_db/mod.rs Updates docs to reflect ILP examples are solved at build time (feature-gated).
src/rules/minmaxmulticenter_ilp.rs Adds z upper-bound constraint and switches canonical example to rule_example_via_ilp.
src/unit_tests/rules/minmaxmulticenter_ilp.rs Updates constraint-count expectation to include the new z bound.
src/unit_tests/example_db.rs Relaxes IntegralFlowBundles→ILP example assertions (now solver-produced).
src/rules/undirectedflowlowerbounds_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/travelingsalesman_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/timetabledesign_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/sumofsquarespartition_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/subgraphisomorphism_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/steinertreeingraphs_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/steinertree_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/stackercrane_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/shortestweightconstrainedpath_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/sequencingwithreleasetimesanddeadlines_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/sequencingtominimizeweightedtardiness_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/sequencingtominimizeweightedcompletiontime_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/sequencingtominimizemaximumcumulativecost_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/schedulingwithindividualdeadlines_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/ruralpostman_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/resourceconstrainedscheduling_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/rectilinearpicturecompression_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/qubo_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/quadraticassignment_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/precedenceconstrainedscheduling_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/pathconstrainednetworkflow_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/partitionintotriangles_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/partitionintopathsoflength2_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/partiallyorderedknapsack_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/optimallineararrangement_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/naesatisfiability_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/multiprocessorscheduling_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/multiplecopyfileallocation_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/mixedchinesepostman_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumtardinesssequencing_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumsummulticenter_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumsetcovering_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimummultiwaycut_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumhittingset_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumfeedbackvertexset_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumfeedbackarcset_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/minimumdominatingset_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/maximumsetpacking_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/maximummatching_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/maximumclique_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/maximalis_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/longestpath_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/longestcommonsubsequence_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/longestcircuit_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/lengthboundeddisjointpaths_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/integralflowwithmultipliers_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/integralflowhomologousarcs_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/integralflowbundles_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/hamiltonianpath_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/flowshopscheduling_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/factoring_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/disjointconnectingpaths_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/directedtwocommodityintegralflow_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/consistencyofdatabasefrequencytables_ilp.rs Uses rule_example_via_ilp for canonical example; adjusts dead-code gating on encoder helper.
src/rules/consecutiveblockminimization_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/capacityassignment_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/bottlenecktravelingsalesman_ilp.rs Switches canonical example to rule_example_via_ilp.
src/rules/bmf_ilp.rs Switches canonical example to rule_example_via_ilp.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 367 to 368
use crate::rules::ReduceTo as _;
use crate::topology::MixedGraph;
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use crate::rules::ReduceTo as _; import is no longer needed here (the example now uses rule_example_via_ilp) and will be unused under --features example-db. Please remove it to avoid warnings.

Copilot uses AI. Check for mistakes.
#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
use crate::export::SolutionPair;
use crate::rules::ReduceTo as _;
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use crate::rules::ReduceTo as _; is unused now that the canonical example calls rule_example_via_ilp and no longer calls reduce_to() directly. Please drop the import to avoid warnings when building the example DB.

Copilot uses AI. Check for mistakes.
Comment on lines 129 to 130
use crate::rules::ReduceTo as _;

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use crate::rules::ReduceTo as _; is unused in this example-spec function after moving to rule_example_via_ilp. Please remove it to avoid unused-import warnings under --features example-db.

Copilot uses AI. Check for mistakes.
Comment on lines +246 to +247
assert!(!example.solutions[0].source_config.is_empty());
assert!(!example.solutions[0].target_config.is_empty());
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions are very weak now: a non-empty config could still have the wrong length (which would make it invalid for the source problem / ILP). Consider asserting source_config.len() equals the number of arcs in the exported graph.arcs (for IntegralFlowBundles) and target_config.len() equals target.instance["num_vars"], which keeps the test solver-agnostic while still catching regressions in solution extraction/serialization.

Copilot uses AI. Check for mistakes.
#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
use crate::export::SolutionPair;
use crate::rules::ReduceTo as _;
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use crate::rules::ReduceTo as _; is no longer needed here (the example now delegates to rule_example_via_ilp), so this import becomes unused when example-db is enabled. Please remove it to avoid warning noise in cargo test --features "example-db ilp-highs".

Copilot uses AI. Check for mistakes.
Comment on lines 173 to 174
use crate::rules::ReduceTo as _;

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use crate::rules::ReduceTo as _; appears unused now that this canonical example uses rule_example_via_ilp instead of calling reduce_to() directly. Removing it will avoid an unused-import warning under --features example-db.

Suggested change
use crate::rules::ReduceTo as _;

Copilot uses AI. Check for mistakes.
Comment on lines 202 to 203
use crate::rules::ReduceTo as _;

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use crate::rules::ReduceTo as _; is unused in this function after switching the example to rule_example_via_ilp. Please remove it to keep example-db builds warning-free.

Suggested change
use crate::rules::ReduceTo as _;

Copilot uses AI. Check for mistakes.
@zazabap
Copy link
Copy Markdown
Collaborator

zazabap commented Mar 26, 2026

Agentic Review Report

Structural Check

# Check Status
1 New helper rule_example_via_ilp correctly implemented PASS
2 Feature gate #[cfg(feature = "ilp-solver")] on helper PASS
3 Conversion pattern consistent across files PASS
4 No leftover hardcoded configs in converted files PASS
5 Tests pass (cargo test) PASS
6 cargo test --features "example-db ilp-highs" PASS (28 tests)
7 minmaxmulticenter_ilp z-bound addition is sound PASS
8 minmaxmulticenter_ilp test updated for new constraint count PASS
9 cargo fmt --check FAIL — ~32 files have 8-space indentation instead of 4 in canonical_rule_example_specs() body

Issue 1 (Medium): Formatting — ~32 files fail cargo fmt --check
The vec![...] expression inside canonical_rule_example_specs() is indented with 8 spaces instead of 4 across ~32 files. Example: src/rules/maximumclique_ilp.rs:83. Must be fixed before merge.

Issue 2 (Medium): Incorrect overhead expression in src/rules/minmaxmulticenter_ilp.rs:119
The overhead num_constraints = "2 * num_vertices^2 + 4 * num_vertices + 2" should be "2 * num_vertices^2 + 3 * num_vertices + 2":

  • For n=3: 2(9) + 3(3) + 2 = 29 ✓ (matches test assertion on line 26)
  • Current formula: 2(9) + 4(3) + 2 = 32 ✗
    The Vec::with_capacity hint on line 142 also uses the wrong formula (2*n*n + 4*n + 1).

Issue 3 (Low): 6 unused use crate::rules::ReduceTo as _; imports
Files: stackercrane_ilp.rs:203, mixedchinesepostman_ilp.rs:367, steinertreeingraphs_ilp.rs:129, disjointconnectingpaths_ilp.rs:173, ruralpostman_ilp.rs:218, lengthboundeddisjointpaths_ilp.rs:202. Copilot review comments confirmed.

Copilot comment on unit_tests/example_db.rs assertions: Not actionable — existing tests are comprehensive with round-trip validation and feasibility checks.


Quality Check

DRY — Improvement with incomplete coverage. The rule_example_via_ilp() helper eliminates duplicated ILP boilerplate from ~60 files. However, ~22 ILP rule files still use rule_example_with_witness with hardcoded configs (e.g., coloring_ilp.rs, knapsack_ilp.rs, binpacking_ilp.rs, kclique_ilp.rs). If these can't use the helper, rationale should be documented.

KISS — OK. The helper is 15 lines, clear, does one thing. No over-engineering.

HC/LC — OK. Helper lives in src/example_db/specs.rs alongside rule_example_with_witness, correct cohesion. Feature gating correct.

Issues:

  1. (Medium) Weakened test assertionsrc/unit_tests/example_db.rs:246-247: Previously asserted exact solution values, now only checks !is_empty(). Should at minimum verify solution vector lengths or evaluate against the source problem.

  2. (Minor) MinMaxMulticenter reduction bugfix bundledsrc/rules/minmaxmulticenter_ilp.rs:119-189 adds a z upper-bound constraint and changes the overhead formula. This is a behavioral fix to the reduction itself, not just DRY cleanup — should be noted.

  3. (Minor) No dedicated unit test for rule_example_via_ilp — The helper is exercised indirectly through example_db, but a focused test would catch regressions faster.


Agentic Feature Tests

# Command Expected Actual Status
1 make cli CLI builds Built (7 compiler warnings — unused imports) PASS
2 cargo test All tests pass 80 passed, 0 failed, 5 ignored PASS
3 cargo test --features "example-db ilp-highs" -- example_db Example DB tests pass with ILP solver 28 passed, 0 failed PASS
4 cargo test --features "ilp-highs" -- minmaxmulticenter_ilp Minmax ILP tests pass 5 passed, 0 failed PASS
5 pred create --example MinimumCutIntoBoundedSets && pred solve End-to-end works Created example, solved to Min(6) PASS

7/7 feature tests pass. Minor: 7 compiler warnings (unused imports/variables).


Generated by review-pipeline

- Fix indentation in ~32 canonical_rule_example_specs functions (cargo fmt)
- Fix incorrect overhead in minmaxmulticenter_ilp: num_constraints
  formula was 2n²+4n+2, correct is 2n²+3n+2 (matching actual constraint
  count: 1 cardinality + n assignment + n² link + n x-bounds + n² y-bounds
  + 1 z-bound + n minimax)
- Fix Vec::with_capacity hint to match corrected formula
- Remove 6 unused `use crate::rules::ReduceTo as _;` imports in files
  that switched to rule_example_via_ilp

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@zazabap zazabap self-requested a review March 26, 2026 09:48
@zazabap
Copy link
Copy Markdown
Collaborator

zazabap commented Mar 26, 2026

All review findings addressed in 49fc30f:

  • Fixed indentation in ~32 files (cargo fmt)
  • Fixed incorrect overhead formula in minmaxmulticenter_ilp (2n²+4n+2 → 2n²+3n+2)
  • Removed 6 unused ReduceTo as _ imports

All CI checks pass.

Copy link
Copy Markdown
Collaborator

@zazabap zazabap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Collaborator

@zazabap zazabap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed: all review findings addressed, CI green, overhead formula corrected.

@zazabap zazabap merged commit 0980d5a into main Mar 26, 2026
5 checks passed
@zazabap zazabap deleted the replace-hardcoded-ilp-examples-772-v2 branch March 26, 2026 09:51
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.

Replace hardcoded canonical rule examples with ILPSolver calls

3 participants