Skip to content

Commit 38e377d

Browse files
GiggleLiuclaude
andcommitted
fix: model_specs_are_optimal stuck on DecisionMVC→HC→QA→ILP chain
The new DecisionMVC→HamiltonianCircuit reduction created a path DecisionMVC → HC(50 verts) → QA(2500 vars) → ILP that HiGHS can't solve in reasonable time. Fix: try brute force first for small instances (≤2^20 configs) before falling back to ILP via reduction. DecisionMVC with 4 vertices has only 16 configs — instant via brute force. Also add CLAUDE.md note: overhead expressions describe scaling, not exact sizes — read reduce_to() code for actual sizes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 06b4c1a commit 38e377d

2 files changed

Lines changed: 15 additions & 14 deletions

File tree

.claude/CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl ReduceTo<Target> for Source { ... }
162162
- Expression strings are parsed at compile time by a Pratt parser in the proc macro crate
163163
- Variable names are validated against actual getter methods on the source type — typos cause compile errors
164164
- Each problem type provides inherent getter methods (e.g., `num_vertices()`, `num_edges()`) that the overhead expressions reference
165+
- **Overhead expressions describe scaling (asymptotic upper bounds), not exact sizes.** To determine the actual target problem size for a specific instance, read the `reduce_to()` construction code and count the actual variables/constraints/vertices built.
165166
- `ReductionOverhead` stores `Vec<(&'static str, Expr)>` — field name to symbolic expression mappings
166167
- `ReductionEntry` has both symbolic (`overhead_fn`) and compiled (`overhead_eval_fn`) evaluation — the compiled version calls getters directly
167168
- `VariantEntry` has both a complexity string and compiled `complexity_eval_fn` — same pattern

src/unit_tests/example_db.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -517,20 +517,20 @@ fn model_specs_are_optimal() {
517517
for spec in specs {
518518
let name = spec.instance.problem_name();
519519
let variant = spec.instance.variant_map();
520-
// Try ILP (direct or via reduction), fall back to brute force for small instances
521-
let best_config = ilp_solver
522-
.solve_via_reduction(name, &variant, spec.instance.as_any())
523-
.or_else(|| {
524-
// Only brute-force if search space is small (≤ 2^20 configs)
525-
let dims = spec.instance.dims_dyn();
526-
let log_space: f64 = dims.iter().map(|&d| (d as f64).log2()).sum();
527-
if log_space > 20.0 {
528-
return None;
529-
}
530-
let entry = find_variant_entry(name, &variant)?;
531-
let (config, _) = (entry.solve_witness_fn)(spec.instance.as_any())?;
532-
Some(config)
533-
});
520+
// Try brute force first for small instances (fast, avoids expensive ILP chains)
521+
let dims = spec.instance.dims_dyn();
522+
let log_space: f64 = dims.iter().map(|&d| (d as f64).log2()).sum();
523+
let best_config = if log_space <= 20.0 {
524+
find_variant_entry(name, &variant)
525+
.and_then(|entry| (entry.solve_witness_fn)(spec.instance.as_any()))
526+
.map(|(config, _)| config)
527+
.or_else(|| {
528+
ilp_solver.solve_via_reduction(name, &variant, spec.instance.as_any())
529+
})
530+
} else {
531+
ilp_solver
532+
.solve_via_reduction(name, &variant, spec.instance.as_any())
533+
};
534534

535535
if let Some(best_config) = best_config {
536536
let best_value = spec.instance.evaluate_json(&best_config);

0 commit comments

Comments
 (0)