Skip to content

Commit c40e943

Browse files
isPANNclaude
andauthored
Batch-add 13 Backlog Models + 26 Backlog Rules (#1067)
* Add /auto-pipeline orchestrator + strengthen rule-issue quality gates - New skill .claude/skills/auto-pipeline: orchestrator that drives one Backlog issue from quality gate to Final review via fresh-context subagents (check-issue, fix-issue, run-pipeline, review-pipeline). Substantive issue-body problems are routed to codex xhigh; fundamental flaws with no public reference park the issue on OnHold. - check-issue: add Rule Check 5 (Completeness, fail label "Incomplete"). Mandatory literature research + codebase corner-case enumeration + hand-tracing on >= 2 non-canonical instances for every [Rule] issue. - review-structural: add Step 4b (Round-trip Execution, mandatory for Rule reviews). Reviewer must run cargo test by name, paste the "test result: ok" line, and confirm the test exercises the four phases of a real round-trip. - review-quality: promote "closed-loop without round-trip verification" from a Minor flag to Critical, with explicit red flags (is_some-only, target-side-only asserts, unique-optimum instances). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MinimumDiscretePlanarInverseKinematics model (#994) Robotics inverse-kinematics problem: given link lengths l_j, target g in R^2, per-link sampled orientations Phi_j, and consecutive-pair admissibility sets A_j, pick indices a_j in {0..m_j-1} with (a_{j-1}, a_j) in A_j minimizing the squared end-effector distance ||sum_j l_j (cos phi_{j,a_j}, sin phi_{j,a_j}) - g||^2. - src/models/misc/minimum_discrete_planar_inverse_kinematics.rs: per-link dims (non-binary), Min<f64> objective, A_j feasibility returns Min(None), declare_variants! default entry, ProblemSchemaEntry + ProblemSizeFieldEntry, canonical example_db spec via inventory. - src/unit_tests/models/misc/...: creation, evaluate (feasible/ infeasible), brute-force solver, serialization roundtrip. - problemreductions-cli/: new (f64,f64) and Vec<Vec<(usize,usize)>> schema parsers; --link-lengths/--target-point/--orientation-samples/ --allowed-pairs flags via the schema-driven create path. - docs/paper: problem-def block + display-name + worked example; references.bib entries for Salloum2025 and DaiIzattTedrake2019. Reference: Salloum et al., "Quantum annealing for inverse kinematics in robotics", Scientific Reports 2025, doi:10.1038/s41598-025-34346-z. Closes #994 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix #994 model: complexity reflects total config product, not 2^n The brute-force search space is prod_{j=1}^n m_j, not 2^n — per-link sample counts m_j are arbitrary. Add a `total_configurations()` getter that returns the product, and rewrite the declare_variants! complexity as `num_links * total_configurations` (n vertices in evaluate cost times the iteration space). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Reformat total_configurations getter (cargo fmt) Trivial single-line rewrite to match rustfmt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MaximumCoKPlex model (#1015) Co-k-plex problem: given graph G=(V,E), vertex weights w, and integer k>=1, find max-weight subset S subseteq V such that the induced subgraph G[S] has maximum degree at most k-1 (i.e. every selected vertex has at most k-1 selected neighbours). Generalizes MaximumIndependentSet (the k=1 case) and is the complement-graph view of maximum k-plex from the clique-relaxation literature. - src/models/graph/maximum_co_k_plex.rs: MaximumCoKPlex<G,W,K> parameterized by graph type, weight type, and K-multiplier. Only the KN (runtime-k) variant registered initially per the issue's "initially KN, K1/K2/... later" plan. Max<W::Sum> objective, induced-degree feasibility, declare_variants! default + i32 variant, canonical example via inventory (5-cycle weights (5,1,4,1,3) k=2, optimum {0,2,4} value 12). - src/unit_tests/models/graph/maximum_co_k_plex.rs: creation, evaluate-feasible (issue optimum + smaller feasible), evaluate- infeasible (degree-2 violation), brute-force solver, serialization. - problemreductions-cli/src/commands/create/: schema-driven CLI maps schema field bound_k to existing --k flag with semantic validation. - docs/paper: problem-def block with C_5 worked example and k=1 -> MaximumIndependentSet equivalence note; references.bib gains Hernandez2016MolecularSimilarity and HosseinianButenko2022KDependent. References: arXiv:1601.06693 (Hernandez et al., 2016) for the molecular-similarity framing; doi:10.1016/j.dam.2021.10.015 (Hosseinian & Butenko, 2022) for the maximum k-dependent set view. Closes #1015 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MaximumCommonEdgeSubgraph model (#1018) MCES: given two directed edge-labelled graphs G1, G2, find a partial injective map f: U1 ⊆ V1 → V2 maximizing the number of preserved labelled arcs (u, λ, v) ∈ E1 with f(u), f(v) defined and (f(u), λ, f(v)) ∈ E2. Edge labels must match exactly; set semantics (no multiplicities); disconnected common subgraphs allowed; no secondary tie-break. - src/models/graph/maximum_common_edge_subgraph.rs: local LabelledArc + LabelledDigraph structs (does not extend the existing Graph trait hierarchy in this PR). dims = vec![|V2|+1; |V1|] with the +1 slot encoding ⊥. Max<i64> objective with injectivity feasibility on the matched slots. ProblemSchemaEntry + ProblemSizeFieldEntry for num_vertices_1/_2 and num_arcs_1/_2, declare_variants! default with complexity (num_vertices_2+1)^num_vertices_1. Canonical example via inventory from the issue's 5-vs-4-vertex instance with optimum value 5. - src/unit_tests/models/graph/maximum_common_edge_subgraph.rs: 12 tests covering creation, evaluate-feasible (optimum 5), evaluate-injectivity-violated, evaluate-fewer-preserved, brute-force solver, serialization. - problemreductions-cli/: new --graph-1 / --graph-2 flags with a LabelledDigraph parser; alias MCES. - docs/paper: problem-def block, display-name, MCES worked example. - docs/paper/references.bib: corrected per Crossref against the check-issue warning — Bahiense2012 first names (Laura/Gordana/Breno), Soule2021 author list (Soule/Reinharz/Sarrazin-Gendron/Denise/ Waldispuhl) and venue, Bokhari1981 volume (C-30). References: doi:10.1109/TC.1981.1675756 (Bokhari 1981), doi:10.1016/j.dam.2012.01.026 (Bahiense et al. 2012, polyhedral investigation), doi:10.1371/journal.pcbi.1008990 (Soule et al. 2021, RNA networks application). The direct `MaximumCommonEdgeSubgraph -> ILP` rule (#1019) is out of scope for this PR and will follow separately. Closes #1018 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MaximumEdgeWeightedKClique model (#1020) Exact-cardinality edge-weighted clique: given a simple undirected graph G=(V,E), edge weights w: E→R, and an integer k with 0≤k≤|V|, find a vertex subset S with |S|=k forming a clique that maximizes the sum of weights of edges induced by S. Edge weights may be negative; k=0 and k=1 are admitted with objective value 0. Distinct from the existing MaximumClique (vertex-weighted, no exact-k) and KClique (decision problem with threshold |S|>=k). - src/models/graph/maximum_edge_weighted_k_clique.rs: MaximumEdgeWeightedKClique<W: WeightElement> with SimpleGraph fixed; edge_weights vector aligned to graph.edges() order, runtime k field. dims = vec![2; |V|]. Max<W::Sum> objective; infeasible when |S|≠k or S is not a clique. declare_variants! default (SimpleGraph,i32) plus (SimpleGraph,f64). Canonical example via inventory from the issue's 4-vertex instance with negative weight (clique {0,1,2} value 8 beats {0,1,3} value 6). - src/unit_tests/models/graph/maximum_edge_weighted_k_clique.rs: 12 tests covering creation, evaluate-feasible (both optima), evaluate-infeasible-wrong-size, evaluate-infeasible-not-clique, brute-force solver, edge cases k=0 and k=1 (value 0), f64 variant, serialization roundtrip, panic guards. - docs/paper: problem-def block with worked example highlighting that the optimum includes a negative edge; display-name entry; cites Gouveia & Martins 2015 and Hunting/Faigle/Kern 2001. - docs/paper/references.bib: Crossref-verified Gouveia2015MEWC (author corrected to Pedro Martins, not Paulo as the issue body said) and HuntingFaigleKern2001EWC. References: doi:10.1007/s13675-014-0028-1 (Gouveia & Martins 2015, sparse-graph compact formulations); doi:10.1016/S0377-2217(99)00449-X (Hunting, Faigle & Kern 2001, Lagrangian relaxation). The direct `MaximumEdgeWeightedKClique -> ILP` rule (#1021) is out of scope for this PR and will follow separately. Closes #1020 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add HighlyConnectedDeletion model (#1022) Given a simple undirected graph G=(V,E), find a minimum-cardinality edge set F ⊆ E such that every connected component of G - F is either an isolated vertex or a highly connected graph on ≥3 vertices (edge connectivity λ(H) > |V(H)|/2, strict). Components of size 2 are explicitly invalid. Weaker than clique-deletion: every K_k for k≥3 is highly connected, but not every highly connected graph is a clique. - src/models/graph/highly_connected_deletion.rs: variables are EDGES (x_e=1 means delete edge e). Min<i64> objective counts deletions; infeasibility on any non-singleton component that is not highly connected (and any 2-vertex component). Private edge_connectivity helper computes λ via repeated max-flow with unit edge capacities (fine for small components in tests). ProblemSchemaEntry, ProblemSizeFieldEntry (num_vertices/num_edges), declare_variants! default with complexity 2^num_edges. Canonical example via inventory: K3 with leaf vertex 3 attached to 2 (4 vertices, 4 edges) — optimum deletes only (2,3), value 1. - src/unit_tests/models/graph/highly_connected_deletion.rs: 17 tests covering creation, evaluate-optimum, evaluate-zero-deletions- infeasible, evaluate-delete-all-feasible, evaluate-infeasible 2-vertex-component and infeasible path-component, wrong-length config guard, brute-force on canonical + a "double triangle" discriminator instance (two K3's joined at a bridge — optimum 1) to address the check-issue warning about example discriminatory power, serialization, variant, plus edge_connectivity helper tests (single vertex=0, single edge=1, P3=1, K3=2, K4=3). - docs/paper: problem-def block with the K3-with-leaf worked example, display-name entry; Crossref-verified BibTeX entries for Hüffner et al. 2014 (TCBB) and Hartuv & Shamir 2000 (IPL), with proper umlaut encoding H{"u}ffner per repo convention. References: doi:10.1109/TCBB.2013.177 (Hüffner et al. 2014, partitioning biological networks); doi:10.1016/S0020-0190(00)00142-3 (Hartuv & Shamir 2000, HCS clustering algorithm). The direct `HighlyConnectedDeletion -> ILP` rule (#1023) is out of scope for this PR and will follow separately. Closes #1022 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add EulerianPath model (#1024) Classical directed-multigraph satisfaction problem: given D=(V,A) with parallel arcs and loops allowed, decide whether a directed trail exists that uses every arc exactly once. No start or end vertex is fixed by the input. Empty-arc instance is accepted with the empty trail; isolated vertices are ignored. Polynomial-time solvable (O(num_vertices + num_arcs)) by the standard Eulerian criterion plus Hierholzer construction, so this widens the catalog beyond NP-hard problems. - src/models/graph/eulerian_path.rs: EulerianPath { graph: DirectedGraph }. dims = vec![m; m] where m = num_arcs (variable t picks which arc occurrence is the t-th trail step); the brute-force search space is m^m but the registry complexity reflects the linear-time best-known algorithm. Or-typed feasibility: configuration must be a permutation of {0..m-1} and consecutive arcs must chain (end of arc t equals start of arc t+1). declare_variants! default + ProblemSchemaEntry + ProblemSizeFieldEntry. Canonical example via inventory from the issue's 3-vertex 4-arc instance with parallel arcs (yes-instance, witness config [0,2,3,1]). - src/unit_tests/models/graph/eulerian_path.rs: 11 tests covering creation, evaluate-valid-witness, evaluate-not-permutation, evaluate-bad-trail, evaluate-out-of-range, evaluate-wrong-length, brute-force yes (canonical) + brute-force no (the issue's 2-vertex 4-arc imbalanced counterexample), empty-arcs edge case (Or(true) with the empty witness), serialization roundtrip, variant + name. - problemreductions-cli/src/commands/create/schema_support.rs: wire --graph (DirectedGraph) for EulerianPath via the existing parser. - docs/paper: problem-def block with both the yes-instance and the no-instance from the issue; display-name entry; references.bib gains Crossref-verified BangJensenGutin2009Digraphs (J{\o}rgen Bang-Jensen, o-slash) and Ebert1988ComputingEulerianTrails (J{"u}rgen Ebert, u-umlaut) — corrected from the issue body's mojibake. References: doi:10.1007/978-1-84800-998-1 (Bang-Jensen & Gutin 2009, digraphs); doi:10.1016/0020-0190(88)90170-6 (Ebert 1988, computing Eulerian trails). The direct `EulerianPath -> ILP` rule (#1025) is out of scope for this PR and will follow separately. Closes #1024 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add PrizeCollectingSteinerForest model (#1026) Biology-paper prize-collecting Steiner forest: given a network G with nonnegative vertex prizes p(v), nonnegative edge costs c(e), and tradeoff coefficients beta, omega, find a forest subgraph F = (V_F, E_F) minimizing beta * sum_{v notin V_F} p(v) + sum_{e in E_F} c(e) + omega * kappa(F) where kappa(F) is the number of tree components (singleton selected vertices count). Generalizes prize-collecting Steiner tree from one connected tree to a forest; the artificial-root trick is deliberately kept out of the base model and will live in the companion reduction rule. - src/models/graph/prize_collecting_steiner_forest.rs: PrizeCollectingSteinerForest<G, W> with dims = vec![2; n+m] (vertex bits then edge bits), Min<W::Sum> objective. Feasibility checks edges-incident-to-selected-vertices and forest acyclicity; infeasible → Min(None). Canonical example via inventory from the issue's 3-vertex path with optimum [1,1,1, 1,0] value 5 (cost 1 + omega*2 components). declare_variants! default (SimpleGraph,i32) plus (SimpleGraph,f64). Complexity 2^(num_vertices+num_edges). - src/unit_tests/models/graph/prize_collecting_steiner_forest.rs: 13 tests — creation, evaluate-optimum, evaluate-full-path (value 9), evaluate-three-singletons (value 6), evaluate-empty-forest (value 12), evaluate-edge-without-endpoint-infeasible, evaluate-cycle-infeasible (triangle selected entirely), brute-force solver, serialization, f64 variant, panic guards. - problemreductions-cli/: new --vertex-prizes / --edge-costs / --beta / --omega flags via schema-driven create; mapping and fixture updates. - docs/paper: problem-def block with worked example breakdown (omitted-prize / edge-cost / component terms summing to 5); display-name; Crossref-verified BibTeX for both Tuncbag et al. papers (JCB 2013 and RECOMB 2012). References: doi:10.1089/cmb.2012.0092 (Tuncbag et al. 2013, JCB); doi:10.1007/978-3-642-29627-7_31 (Tuncbag et al. 2012, RECOMB). The direct `PrizeCollectingSteinerForest -> SteinerTree` rule (#1027) is out of scope for this PR and will follow separately. Closes #1026 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MinimumCostMaximumFlow model (#1029) Lexicographic objective on a directed multigraph with source s, sink t, arc capacities u_e, and arc costs c_e: first maximize the s-t flow value |f|, then among maximum-value flows minimize total arc cost sum_e c_e f_e. Captures the CellRouter (Lummertz da Rocha et al. 2018) model directly. Architectural carve-out: the repo's Problem trait is discrete, so the implementation restricts to INTEGRAL flows with dims=[c_e+1; m]. This mirrors the existing MinimumEdgeCostFlow precedent and stays sound for any rational instance by scaling. Documented in the module doc. Lex encoding: Min<i64> with combined scalar score = M * (max_possible_flow - flow_value) + cost where M = sum_e c_e * u_e + 1 dominates any feasible cost — so lower scores always prefer higher flow value first, breaking ties by lower cost. Infeasible (capacity / conservation violations) → Min(None). - src/models/graph/minimum_cost_maximum_flow.rs: MinimumCostMaximumFlow { graph: DirectedGraph, source, sink, capacities: Vec<i64>, costs: Vec<i64> }. Inherent helpers flow_value(config) and total_cost(config) for tests. Canonical example via inventory: V={0,1,2,3}, arcs [(0,1),(0,2),(1,2), (1,3),(2,3)], capacities [2,1,1,1,2], costs [1,0,0,1,2] — optimum config [2,1,1,1,2] with value 3 and cost 7. ProblemSchemaEntry + ProblemSizeFieldEntry (num_vertices, num_arcs). declare_variants! default with complexity (num_vertices+num_arcs)^6 (a conservative polynomial placeholder justified by the LP formulation). - src/unit_tests/models/graph/minimum_cost_maximum_flow.rs: 9 tests covering creation, evaluate-optimum, evaluate-suboptimal-feasible, evaluate-capacity-exceeded (infeasible), evaluate-conservation- violated (infeasible), brute-force solver returning value 3 cost 7, serialization, and the lex-tiebreaker test on a 4-vertex bottleneck instance where two distinct max-value flows (value 1) exist with costs 1 and 5 — brute-force must pick the cheaper one. The tiebreaker test directly addresses the check-issue warning that the original example admits a unique max-flow. - problemreductions-cli/: new --source / --sink (usize) flags wired via schema-driven create; --graph (DirectedGraph), --capacities, --costs reused from the MECF wiring. - docs/paper: problem-def block explaining the lex objective and the integral-flow restriction, worked example with value/cost breakdown; display-name; Crossref-verified BibTeX for Lummertz da Rocha et al. 2018 (doi:10.1038/s41467-018-03214-y); MIT 6.854 min-cost-flow notes as a @misc entry with URL. References: doi:10.1038/s41467-018-03214-y (CellRouter, Nature Comms 2018); MIT 6.854 scribe notes for the standard min-cost-flow equivalence. The direct `MinimumCostMaximumFlow -> MinimumCostCirculation` rule (#1031) is out of scope for this PR and will follow separately. Closes #1029 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MinimumCostCirculation model (#1030) Classical minimum-cost circulation on a directed multigraph with finite arc capacities u_e ≥ 0 and signed arc costs a_e ∈ R: find g: E→R_{≥0} satisfying capacity bounds (0 ≤ g_e ≤ u_e) and flow conservation at every vertex, minimizing sum_e a_e g_e. This is the exact companion target for `MinimumCostMaximumFlow` (reduction #1031) — the standard MCMF → MCCirc reduction uses a sufficiently negative return arc from sink to source, which is why signed costs must be supported in the base model. Architectural carve-out (same as MinimumCostMaximumFlow #1029 and MinimumEdgeCostFlow): the discrete Problem trait restricts to INTEGRAL circulation with dims=[c_e+1; m]; sound for any rational instance by scaling. Documented in the module doc. - src/models/graph/minimum_cost_circulation.rs: MinimumCostCirculation { graph: DirectedGraph, capacities: Vec<i64>, costs: Vec<i64> } — no source/sink, conservation at every vertex. Min<i64> objective; capacity-or-conservation violations → Min(None). ProblemSchemaEntry + ProblemSizeFieldEntry (num_vertices, num_arcs). declare_variants! default with conservative polynomial placeholder (num_vertices+num_arcs)^6. Canonical example via inventory — a 3-vertex two-cycle instance discriminating between four feasible alternatives (zero, cycle-A-only -2, cycle-B-only -3, both at capacity -5) so round-trip tests have real discriminatory power, addressing the check-issue warning about the issue's 2-vertex example being too small. - src/unit_tests/models/graph/minimum_cost_circulation.rs: 11 tests covering creation, evaluate-optimum (config [2,2,1,1] → Min(-5)), evaluate-zero, evaluate-cycle-A-only (-2), evaluate-cycle-B-only (-3), evaluate-half-cycle-A (-4), evaluate-infeasible (capacity exceeded, conservation violated), brute-force solver, serialization, negative-cost-only-cycle smoke. - problemreductions-cli/: --graph (DirectedGraph), --capacities, --costs reused from MECF/MCMF wiring; new schema mapping for MCCirc. - docs/paper: problem-def block with the two-cycle worked example spelled out (per-unit costs, capacity bottlenecks), display-name entry; reuses the existing mit6854MinCostFlow @misc bib entry added with MCMF — no new references.bib changes. References: MIT 6.854 (S2021) min-cost flow algorithms notes (shared with #1029). The direct `MinimumCostMaximumFlow -> MinimumCostCirculation` rule (#1031) is out of scope for this PR and will follow separately. Closes #1030 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add ClosestString model (#1032) Consensus-string problem under Hamming distance: given an alphabet Σ = {0,...,q-1} and n equal-length strings s_1,...,s_n ∈ Σ^m, find a center string c ∈ Σ^m minimizing max_i d_H(c, s_i). NP-hard (Frances & Litman 1997, Lanctot et al. 1999), with extensive FPT and PTAS literature (Gramm & Niedermeier 2003, Ma & Sun 2009, Li/Ma/Wang 2002). Distinct from ClosestSubstring — every input string has the same length as the center, so there is no window-selection decision. - src/models/misc/closest_string.rs: ClosestString { alphabet_size, strings: Vec<Vec<usize>> }. Validating constructor panics on length mismatch or out-of-alphabet symbol. dims = vec![q; m]. Min<i64> objective (always feasible — every config in the cube is a syntactically valid center). Inherent getters alphabet_size, num_strings, string_length, total_length. ProblemSchemaEntry + ProblemSizeFieldEntry with all four size fields. declare_variants! default with complexity alphabet_size^string_length. Canonical example via inventory from the issue's 4-string binary length-3 instance (optimal center [0,0,0], radius 2). - src/unit_tests/models/misc/closest_string.rs: 11 tests covering creation, evaluate at three different centers (c=000 → 2, c=100 → 3, c=111 → 3), brute-force solver returning radius 2 over 8 candidates, three panic guards (empty input, length mismatch, out-of-alphabet symbol), a q=3 length-2 ternary smoke test (radius 2 over 9 candidates), and serialization. - problemreductions-cli/: schema-driven create wires --alphabet-size (usize) and --strings (Vec<Vec<usize>>) — reuses the existing Vec<Vec<usize>> parser added with #994. - docs/paper: problem-def block with all four Hamming distances spelled out for c=000; display-name entry; Crossref-verified Li/Ma/Wang 2002 (JACM) bib entry. Reference: doi:10.1145/506147.506150 (Li, Ma & Wang 2002, JACM). The direct `ClosestString -> ILP` rule (#1034) is out of scope for this PR and will follow separately. Closes #1032 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add ClosestSubstring model (#1033) Window-selection generalization of ClosestString (#1032): given an alphabet Σ, n strings (NOT necessarily equal length), and a substring length ℓ, find a center c ∈ Σ^ℓ and start positions p_i selecting length-ℓ windows s_i[p_i .. p_i+ℓ) minimizing max_i d_H(c, s_i[p_i .. p_i+ℓ)). Motif-discovery model — NP-hard, no PTAS in general (Li, Ma & Wang 2002 JACM; Marx 2008). ClosestString is the special case where every input string has length exactly ℓ (single window per string). - src/models/misc/closest_substring.rs: ClosestSubstring { alphabet_size, strings: Vec<Vec<usize>>, substring_length }. Validating constructor panics on empty input, substring_length > min |s_i|, or out-of-alphabet symbol. dims concatenates ℓ center slots (domain {0..q-1}) with n window-start slots (domain {0..W_i-1} where W_i = |s_i| - ℓ + 1). Min<i64> objective, always feasible since every config in the cube is syntactically valid. Inherent getters alphabet_size, num_strings, substring_length, total_length, total_num_windows, num_window_choice_product (with saturating multiplication). ProblemSchemaEntry + ProblemSizeFieldEntry. declare_variants! default with complexity alphabet_size^substring_length * num_window_choice_product. Canonical example via inventory from the issue's 3 binary strings with ℓ=3 — optimum center [0,1,0] with window picks (0,1,0), radius 1 over 216 candidate configs. - src/unit_tests/models/misc/closest_substring.rs: 11 tests covering creation, evaluate at optimum (radius 1), evaluate at center [0,0,0] with all-zero windows (radius 2), evaluate at center [1,1,1] (radius >=1), brute-force solver, ClosestString reduction validation (substring_length = string_length → matches the #1032 canonical's radius 2), three panic guards (empty input, length mismatch, out-of-alphabet symbol), and serialization roundtrip. - problemreductions-cli/: schema-driven create wires --alphabet-size + --strings (reused from #1032) plus the new --substring-length (usize) flag. - docs/paper: problem-def block with the worked example listing all three window picks and per-window Hamming distances; display-name entry. Reuses the existing Li/Ma/Wang 2002 JACM BibTeX entry added with #1032 — no references.bib changes. Reference: doi:10.1145/506147.506150 (Li, Ma & Wang 2002, JACM) shared with #1032. The direct `ClosestSubstring -> ILP` rule (#1035) is out of scope for this PR and will follow separately. Closes #1033 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add MaximumContactMapOverlap model (#1043) Classical protein-structure contact-map alignment: given two ordered contact graphs G_1=(V_1,E_1) and G_2=(V_2,E_2), find an order-preserving partial injective map f: V_1 → V_2 ∪ {unmatched} maximizing the number of contacts {i,k} ∈ E_1 such that both i, k are matched and {f(i), f(k)} ∈ E_2. Aliases: CMO, MaxCMO. NP-hard with substantial literature on exact algorithms and integer programming (Andonov, Malod-Dognin & Yanev 2011; Xie & Sahinidis 2007). - src/models/graph/maximum_contact_map_overlap.rs: MaximumContactMapOverlap { num_vertices_1, contacts_1, num_vertices_2, contacts_2 }. Validating constructor normalizes each pair to sorted form (u<v), rejects self-loops, duplicates, and out-of-range endpoints. dims = vec![num_vertices_2 + 1; num_vertices_1] (value 0 encodes unmatched; value j+1 maps to vertex j of G_2). Max<i64> objective; non-injective or non-order-preserving matched values → Max(None). ProblemSchemaEntry + ProblemSizeFieldEntry; inherent getters num_vertices_1/_2 and num_contacts_1/_2. declare_variants! default with complexity (num_vertices_2+1)^num_vertices_1. Canonical example via inventory: G_1 with 4 vertices and contacts {(0,2),(1,3)}, G_2 with 5 vertices and contacts {(0,2),(0,3),(1,4)} — optimum [1,2,4,5] preserves both contacts → Max(Some(2)). - src/unit_tests/models/graph/maximum_contact_map_overlap.rs: 17 tests covering creation, evaluate at optimum, all-unmatched, single-match, non-injective Max(None), non-order-preserving Max(None), suboptimal feasible (config [1,2,3,4] preserves 1 of 2 contacts), brute-force solver returning Max(2), wrong-length and out-of-range guards, serialization, alias resolution for CMO/MaxCMO, and three panic guards (self-loop, duplicate contact, endpoint out of range). - problemreductions-cli/: schema-driven create wires --num-vertices-1 / --num-vertices-2 / --contacts-1 / --contacts-2 (Vec<(usize,usize)> parser) via the existing CreateArgs + flag_map + tests fixture. - docs/paper: problem-def block with the alignment table and the two preserved-contact bullets; display-name; Crossref-verified BibTeX for both Andonov-Malod-Dognin-Yanev 2011 and Xie-Sahinidis 2007 JCB papers (with N{\"o}el encoded per repo umlaut convention). References: doi:10.1089/cmb.2009.0196 (Andonov, Malod-Dognin & Yanev 2011, JCB); doi:10.1089/cmb.2007.R007 (Xie & Sahinidis 2007, JCB). The direct `MaximumContactMapOverlap -> ILP` rule (#1044) is out of scope for this PR and will follow separately. Closes #1043 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Trim skill redundancies surfaced by self-review of #1067 Net -83 lines across the four skill files touched by 694ef0c8. auto-pipeline (-79): - Collapse Step 0a + 0b into a single picker block; the only difference was "filter by number" vs "sort and pick top" — now branched by whether the ISSUE env var is set. - Extract the boilerplate that was duplicated across all five subagent prompts (output-only-JSON-block contract, universal don'ts, malformed-JSON retry policy, severity vocabulary) into a single "Subagent Contract" section near the top. Each prompt now states only its scope and JSON shape. Drop the trailing "Reporting Contract" section (merged into the new one). - Trim the Board states table from 8 GraphQL IDs to the 3 columns the orchestrator actually writes (ready, on-hold, plus the Backlog it reads in Step 0). The IDs for In Progress / Review pool / Under review / Final review live in run-pipeline / review-pipeline where they are used. - Drop three rows from Common Mistakes that just echoed the spec (codex retry cap, increment SUBSTANTIVE_RETRIES, re-check after auto-fix); keep only the non-obvious cross-cutting traps. check-issue (-9 net): - Rule Check 5a no longer re-lists the literature fallback chain; one-line reference to Check 3c suffices. - Rule Check 5c verdict table drops the (severity: ...) annotations on Fail rows — severity classification is owned by auto-pipeline's Subagent Contract, not by check-issue itself. - Rule Check 5c drops the "cited reference does not contain the reduction" row that explicitly admitted overlap with Check 3c; add a one-line note that 3c handles that case. review-structural (-19): - Step 4b-4 (pred --via spot-check) was hedged out of its own purpose with a "fall back to 4b-2" escape hatch. Rewrite as a short focused step that uses pred --via when wired and is skipped (with a note) otherwise — no padding. review-quality (~2 net): - Replace the 4-criterion expansion of the round-trip rule (which was copy-pasted from review-structural Step 4b-3) with a single pointer to that source-of-truth section. Per the existing feedback_skill_no_duplication memory. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address final-review findings on #1067 - MinimumDiscretePlanarInverseKinematics: simplify complexity string from "num_links * total_configurations" to "total_configurations" so it matches issue #994's stated O(prod_{j=1}^n m_j) baseline literally (the extra num_links factor was per-config feasibility-check work, not configs). - MinimumCostCirculation: add test_minimum_cost_circulation_issue_example_1030 that constructs issue #1030's verbatim 2-vertex example (arcs 0->1 cap=2 cost=3 and 1->0 cap=1 cost=-5, optimum -2). The existing richer 3-vertex canonical instance is kept as the primary discriminator. * Add Partition -> IntegralFlowWithMultipliers reduction (#363) Adds Sahni's multiplier-flow gadget: each Partition element becomes an item vertex whose multiplier amplifies a binary source choice into either 0 or a_i units entering a relay. A single bottleneck arc of capacity S/2 converts the target's "net inflow at least R" condition into the exact equality needed by Partition. Odd-S inputs reduce to a fixed infeasible 3-vertex instance. - src/rules/partition_integralflowwithmultipliers.rs: reduction impl with odd-S/even-S branches and witness extraction from source arcs - src/unit_tests/rules/partition_integralflowwithmultipliers.rs: 5 tests (closed-loop, structure on even-total YES, even-total NO exercises bottleneck, odd-total fixed NO, witness extraction) - src/rules/mod.rs: register module and example specs - docs/paper/reductions.typ: full theorem with construction, correctness proof, and worked example using the canonical fixture Closes #363. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MinimumVertexCover -> ComparativeContainment reduction (#385) Plaisted (1976) reduction from Decision Minimum Vertex Cover to Comparative Containment (Garey & Johnson SP10). Given a unit-weight VC instance (G = (V, E), K), the universe X = V is encoded as: - For each vertex v, a reward set R_v = V \\ {v} with weight 1, so the total R-weight equals n - |Y|. - For each edge e = {u, v}, a penalty set S_e = V \\ {u, v} with weight n + 1 that dominates the maximum possible reward whenever the edge is uncovered. - One budget set S_0 = V with weight n - K, which encodes the bound through the resulting inequality K - |Y| >= (n + 1) * (#uncovered). Source assertions mirror decisionminimumvertexcover_hamiltoniancircuit.rs: unit weights are required, a negative bound emits a fixed unsatisfiable target, and K >= n is handled as a trivial-YES instance (empty universe, no R/S sets). Adds a ProblemSizeFieldEntry for ComparativeContainment since it had no declared size_fields, and a paper theorem entry. Eight unit tests cover structure counts, closed-loop YES/NO, witness extraction, both trivial-YES branches, the trivial-NO branch, and the weight-assertion guard. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MinimumDiscretePlanarInverseKinematics -> QUBO reduction (#995) Implements Salloum et al. 2025 quantum-annealing IK encoding: one-hot binary lifting of sampled orientations, quadratic position-error term, one-hot exactly-one penalty, and pair-feasibility penalty for forbidden adjacent (a,b) pairs. Penalty constants are chosen above the maximum possible position-error savings, so every QUBO minimizer decodes to a feasible source configuration. Also adds num_orientation_samples() getter on the source model. Closes #995. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add OptimalLinearArrangement -> SequencingToMinimizeWeightedCompletionTime reduction (#472) Implements Lawler 1978 precedence-constrained 1||sum w_j C_j encoding: each vertex becomes a unit-length task with weight 1, each edge becomes a zero-length task with weight 1, and the precedence constraints enforce the OLA ordering. The minimum weighted completion time recovers the OLA objective plus an additive shift d_max*n*(n+1)/2. Also relaxes the target model's validator to allow zero-length edge jobs needed by Lawler's construction. Closes #472. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add ThreeDimensionalMatching -> ThreeMatroidIntersection reduction (#857) Direct partition-matroid embedding: a 3DM matching is precisely the common independent set of three partition matroids, one over each coordinate axis (X, Y, Z). The reduction copies the triple set, builds the three partition matroids by grouping triples that share each coordinate, and uses identity solution extraction. Closes #857. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MinimumCoveringByCliques -> MinimumIntersectionGraphBasis reduction (#848) Identity instance mapping: a clique cover {C_1, ..., C_k} of G is a valid intersection-graph basis with the same cardinality k, since each vertex's labels are exactly the cliques it belongs to and each edge is witnessed by a shared label. The witness extraction labels each edge by a shared intersection slot. Closes #848. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add Numerical3DimensionalMatching -> NumericalMatchingWithTargetSums reduction (#827) Standard G&J transformation: a Numerical 3DM instance with triples (X, Y, Z, bound) maps to an NMTS instance where the X and Y triples form the pair sizes and each Z element becomes a target sum equal to bound + z. The witness is recovered by multiset matching against the source's third coordinate. Source u64 sizes are checked-cast to i64 with a documented overflow guard. Closes #827. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MaximumCoKPlex -> ILP reduction (#1016) Direct boolean ILP encoding for the Co-k-Plex problem: each vertex gets a binary variable, the objective maximizes the weighted sum, and a per-vertex constraint bounds the induced degree by k - 1 using the linear inequality sum_{u in N(v)} y_u <= (k - 1) + M * (1 - y_v). Closes #1016. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MaximumCommonEdgeSubgraph -> ILP reduction (#1019) Boolean ILP encoding finds a one-to-one vertex correspondence between G1 and G2 that maximizes the number of preserved edges. Variables x[i,k] encode whether v_i in G1 maps to v_k in G2, with row/column sum-at-most-1 constraints enforcing a partial injection, and edge-preservation variables y[(i,j),(k,l)] linearized via the standard product-with-binaries pattern. Closes #1019. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MaximumEdgeWeightedKClique -> ILP reduction (#1021) Boolean ILP encoding with vertex selectors x_v, edge selectors y_uv via McCormick linearization, an exact-cardinality constraint sum x_v = k, and non-edge clique constraints x_u + x_v <= 1 for every non-edge. The lower-bound McCormick inequality is essential because edge weights may be negative. Closes #1021. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add HighlyConnectedDeletion -> ILP reduction (#1023) Set-partitioning ILP encoding: each candidate cluster is a feasible highly-connected vertex subset (verified via edge-connectivity > n/2), and a partition constraint forces each vertex to belong to exactly one selected cluster. The objective minimizes the number of deleted edges (those whose endpoints are in different clusters). Also exposes pub(crate) helpers is_feasible_cluster and induced_edge_count on the source model. Closes #1023. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add EulerianPath -> ILP reduction (#1025) MTZ-style ILP encoding for EulerianPath: integer variables y_{a,b} encode compatible arc-pair successions, plus per-arc start/end/position variables. Constraints enforce in-degree/out-degree balance for the Eulerian trail, unique start/end arcs, and a position-strict ordering that prevents subtours. The empty-arc source instance maps to the empty ILP. Closes #1025. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MinimumCostMaximumFlow -> MinimumCostCirculation reduction (#1031) Textbook reduction: keep all original arcs unchanged, append a return arc (t, s) with capacity U = sum_{e in delta^+(s)} u_e and cost -(1 + sum_e c_e). The deeply negative return-arc cost makes maximizing flow on (t, s) the priority — i.e. the circulation must saturate s -> t flow before optimizing residual cost. Solution extraction discards the return arc. Closes #1031. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix #995 example-db test compile error Replace serde_json::json! comparisons with plain Vec<usize> values to match the SolutionPair.source_config/target_config field types. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add ClosestString -> ILP reduction (#1034) Position-character ILP encoding for ClosestString: binary variables x_{j,a} encode the consensus string character at position j (with one-hot assignment constraints), and a single integer radius variable R is bounded by per-string Hamming-distance constraints. The objective minimizes R. Closes #1034. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add ClosestSubstring -> ILP reduction (#1035) Position-character ILP encoding for ClosestSubstring: binary variables x_{r,a} for the center substring's character at each position, window choice indicators y_{i,p} (exactly one window per source string), and an integer radius R bounded by per-window Hamming-distance constraints plus a tight R <= ell upper-bound constraint critical for ILP solver performance. Closes #1035. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix clippy identity_op in closestsubstring_ilp test (#1035) Replace `6 + 0` and `6 + 6 + 0` with `6` and `6 + 6`. Surfaced during structural review of #363 commit (the lint blocks `cargo clippy --all-targets -- -D warnings`). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix incorrect expected value in zero-length-task test (#472 review) The relaxed-validator test asserted Min(14) but the actual weighted completion time for lengths [0,1,3], weights [3,5,1], schedule [0,1,2] is 3*0 + 5*1 + 1*4 = 9. Surfaced during structural review of #472. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add canonical example for ThreeDimensionalMatching -> ThreeMatroidIntersection (#857 review) Surfaced during structural review: rule was missing canonical_rule_example_specs() and was not extended into the rule example aggregator in mod.rs. Adds the q=3 5-triple feasible instance from the closed-loop test as the canonical example. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix missing canonical examples + bib keys (#1021 review) Surfaced during structural review of #1021: - MaximumEdgeWeightedKClique has both i32 and f64 ReduceTo impls but only an i32 canonical example was registered; the example-db coverage test failed. Add an f64 spec mirroring the i32 instance. - MaximumCoKPlex has both i32 and One ReduceTo impls but only an i32 canonical example existed (also flagged by the coverage test). Add a One-weighted spec on the same C5 graph. - Paper cited `@ParkLeePark1996EWClique` and `@GouveiaMartins2015EWClique` but neither bib key existed. Add both entries to references.bib while keeping the original `@GouveiaMartins2015MEWC` entry that the problem-def section still cites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Remove stale zero-length CLI tests after validator relaxation (#472) PR #472 (commit f13bbb3d) relaxed SequencingToMinimizeWeightedCompletionTime's validator to accept zero-length tasks for the Lawler OLA reduction. Two CLI tests still asserted the removed "task lengths must be positive" rejection, so CI broke on the workspace test job. The new accept-zero-length semantics are positively covered by unit tests in src/unit_tests/models/misc/sequencing_to_minimize_weighted_completion_time.rs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix paper compile errors: typst inter, missing/typo'd bib keys Three mechanical paper-build fixes: - Replace math-mode `intersect` with Typst's `inter` (set-intersection) in the MinimumCoveringByCliques → MinimumIntersectionGraphBasis proof (5 occurrences on lines 18087-18091). - Drop orphan citation @deGastinesKnippel2024MCES from MaximumCommonEdgeSubgraph → ILP (no matching bib entry; the rule still cites the valid @Bahiense2012MCES McCormick formulation). - Fix typo @lawler1978a → @lawler1978 on the OLA → SequencingToMinimizeWeightedCompletionTime rule. The first CI run was masked by the cli_tests failure (set -e); now that tests pass, the paper compile step exposed these issues. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add auto-pipeline integration gate (Step 2.5) PR #1067 hit two CI failures the per-item phases could not see: - Two CLI integration tests asserted a model-validator behaviour that the new OLA -> SequencingToMinimizeWeightedCompletionTime reduction intentionally relaxed; closed-loop and unit tests for the new rule all passed. - `make paper` blew up on `intersect` (Typst expected `inter`), an orphan bib key, and a typo'd key; no phase ran the Typst compile. Adds an orchestrator-owned Step 2.5 that runs `cargo test --workspace --features "ilp-highs example-db"` and `make paper` on PR HEAD between Phase 2 (run-pipeline) and Phase 3 (review-pipeline). Any failure parks the card on OnHold for human triage — codex rescue is not appropriate because the failing artefact lives outside the issue's files. In batch mode (multiple issues stacked on one branch with the PR opened at the end), this gate is the only thing that catches accumulated cross-item breakage before review. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Slim down auto-pipeline Step 2.5 CI-class failures (stale tests, typo'd bib keys, math-mode typos) are small and mechanical; hand them straight to codex-rescue with a one-line failure summary rather than walking the orchestrator through a long JSON contract, PR-comment template, and explicit OnHold dance. Re-run Step 2.5 once after codex; OnHold only if still failing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Renumber auto-pipeline integration gate as Step 3 (not 2.5) Bumps review-pipeline to Step 4. Diagram, intro paragraph, cross-step references, and Common Mistakes table updated accordingly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Revert review-structural Step 4b; let auto-pipeline Step 3 cover it Step 4b mixed three different concerns into review-structural (a read-only structural skill): - 4b-2 ran `cargo test --exact <closed_loop>` for the new rule — redundant with auto-pipeline Step 3's workspace-wide `make check`. - 4b-3 read the test source to score it against four criteria — that's a test-quality judgment, not a structural check; review-quality is the right home for that and it already covers test quality. - 4b-4 ran `pred solve` end-to-end via the new rule — overlaps with the agentic feature tests review-pipeline already runs. - 4b-1 (grep for the closed_loop test) is subsumed by Step 4's existing checklist of required rule artefacts. Restoring review-structural to its main version. Also removing the Phase 4 prompt's mandatory-Step-4b clause from auto-pipeline. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Revert review-quality bullet to main version The PR's edit added a Critical-severity rule for `[Rule]` PRs that deferred to review-structural Step 4b-3 — now a dead link since that section was reverted. Main's original bullet already captures the intent: verify the extracted solution is optimal via brute-force on both source and target. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop codex from auto-pipeline; use plain subagent dispatch Removes the codex:codex-rescue dependency from both substantive-rewrite (Step 1c-sub) and integration-gate-failure (Step 3) paths. Both now dispatch a general-purpose subagent with the same JSON contract. Wins: - Step 1c-sub prompt drops from ~45 lines of nested codex-exec / <<<BODY>>> / FUNDAMENTAL_FLAW sentinel escaping to a direct subagent prompt with the issue body and check report inlined. - Phase 3 failure path is consistent with the rest of the skill — one subagent, one JSON return, orchestrator owns side effects. No behaviour change to Step 2's stop-on-failure stance (run-pipeline still moves the card to OnHold itself; orchestrator just halts). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop batch-mode language from auto-pipeline Step 3 rationale The skill is scoped to one issue at a time; batching is an external concern. Step 3's rationale only needs to justify why catching workspace-wide breakage locally beats waiting for CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Render canonical examples in paper for 13 batch-added rules Adds `load-example` + `example: true` + `example-caption` + `extra:` (pred-commands + witness summary) to the paper entries that previously shipped without a rendered example, matching the existing `mvc_mis` pattern. Brings the 13 outstanding rules in PR #1067 in line with the already-rendered #363, #848, and #995. Rules updated: - DecisionMinimumVertexCover -> ComparativeContainment (#385) - OptimalLinearArrangement -> SequencingToMinimizeWeightedCompletionTime (#472) - Numerical3DimensionalMatching -> NumericalMatchingWithTargetSums (#827) - MinimumCoveringByCliques -> MinimumIntersectionGraphBasis (#848 - was using older inline style) - ThreeDimensionalMatching -> ThreeMatroidIntersection (#857, resolves sibling-inconsistency) - MaximumCoKPlex -> ILP (#1016, i32 variant) - MaximumCommonEdgeSubgraph -> ILP (#1019) - MaximumEdgeWeightedKClique -> ILP (#1021, i32 variant) - HighlyConnectedDeletion -> ILP (#1023) - EulerianPath -> ILP (#1025) - MinimumCostMaximumFlow -> MinimumCostCirculation (#1031) - ClosestString -> ILP (#1034) - ClosestSubstring -> ILP (#1035) Verified: `make paper` compiles cleanly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add rule: MinimumMaximalMatching -> MaximumAchromaticNumber (#846) Classical Yannakakis-Gavril (1980) reduction establishing NP-completeness of Achromatic Number (G&J GT5): for bipartite G, ach(complement(G)) = |V| - mm(G). Adds BipartiteGraph variant of MinimumMaximalMatching as a prerequisite, the complement-graph reduction with the inverse coloring -> maximal-matching extractor, a closed-loop test plus identity tests on several bipartite instances, the canonical P4 example (acknowledged check-issue warning that the P4 canonical example has only one suboptimal maximal matching; kept for tutorial clarity), and the matching paper entry under docs/paper/reductions.typ. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add rule: MinimumMaximalMatching -> MinimumMatrixDomination (#847) * Add rule: ExactCoverBy3Sets -> BoundedDiameterSpanningTree (#913) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix nits: MinimumMaximalMatching -> MaximumAchromaticNumber (#846) Address three review nits on commit 18268671: 1. Canonical example richness: swap the path P4 canonical example for a "T-tree" on 5 vertices (spider v0-v1-v2-v3 with extra leaf v1-v4), which exposes two strictly suboptimal maximal matchings besides the minimum (mm = 1 vs. two size-2 maximal matchings), comfortably passing the >=2-suboptimals rule-of-thumb. Update the canonical builder, the paper worked example, and the closed-loop test to match. 2. extract_solution: replace the per-call HashMap rebuild with a single pass over source_edges that checks whether each edge's endpoints share a color. For bipartite G all color classes have size <= 2, so this is equivalent and avoids any auxiliary allocation. 3. Unit test imports: drop the catch-all "use super::*" in favour of an explicit "use" list mirroring the MVC->MIS reference test. cargo test rules::minimummaximalmatching_maximumachromaticnumber: 5 passed (closed-loop, complement structure, known coloring, suboptimal recovery, identity); make paper builds cleanly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix nits: MinimumMaximalMatching -> MinimumMatrixDomination (#847) * Fix nits: ExactCoverBy3Sets -> BoundedDiameterSpanningTree (#913) - Add `q()` helper on ExactCoverBy3Sets and use it in the rule and tests instead of recomputing `universe_size / 3`. - Add a `debug_assert!` in `reduce_to` confirming that root-to-set edges occupy indices 2..2+m, so a future reordering will surface a failure in `extract_solution`. - Simplify the `no_instance` test: drop the duplicate assertion and the `unwrap_or_default` fallback; assert directly that `find_witness` is `None` and that the brute-force aggregate is `Or(false)` for an infeasible Or-valued target. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix nit: clarify invalid_source_solution intent (#848) * Fix nits: HighlyConnectedDeletion -> ILP debug_assert guards (#1023) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix nit: EulerianPath -> ILP extract_solution debug_assert (#1025) * Fix nit: MaximumCoKPlex bound_k load-time validation (#1015) * Fix nit: drop unused total_selected_vertices accumulator (#1026) * Fix nit: correct canonical-example route comment (#1029) * Add Planar3Satisfiability -> MinimumGeometricConnectedDominatingSet rule (#377) Implements Lichtenstein 1982 §6 Theorem 5: NP-hardness of Minimum Geometric Connected Dominating Set via Planar 3-SAT. Construction: - Phase A bipolarization via Lemma 1: introduce m_i copies per variable with cycle implication clauses forcing all copies equal; rewrite each source clause to use the per-occurrence copy. The bipolar formula B' has Sigma_i m_i = 3m copy variables and m_b = 4m clauses. - Phase B geometric embedding: emit per-copy-variable structures (top and bottom column rounds plus square forcers), a ground spine connecting them, and per-clause tripods. All points at radius = 1. - Bound K = NV + NC + NG + m_b per Lichtenstein p. 339. - Trivial corner case: num_clauses == 0 emits a 1-point target with K = 1. Closed-loop test on the trivial m = 0 case (brute-force solvable); structural tests verify radius, num_points overhead bound, K formula on non-trivial instances. Full round-trip solve on non-trivial instances requires a future MinimumGeometricConnectedDominatingSet -> ILP rule. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add closed-loop and structural tests for Planar3SAT -> GCD rule (#377) Forgotten in the previous commit: the unit test file linked via #[cfg(test)] #[path = ...] from the rule module. Without it, the rule compiles but has no tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add canonical example for Planar3Satisfiability -> MinimumGeometricConnectedDominatingSet (#377) * Fix fmt drift in minimummaximalmatching_minimummatrixdomination.rs * Add reduction-rule block for Planar3SAT -> GCD in paper (#377) * Add MaximumContactMapOverlap -> ILP rule (#1044) Direct ILP rendering of the Andonov-Malod-Dognin-Yanev / Xie-Sahinidis polyhedral formulation: binary x_(i,j) match variables, partial-injection row/column constraints, order-preservation (no-crossing, no-equal-image) constraints, and binary y_(i,k,j,l) contact-preservation variables linked via y <= x_(i,j) and y <= x_(k,l). Objective: maximize sum of y. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add closed-loop tests for MaximumContactMapOverlap -> ILP (#1044) Six unit tests covering: - ILP structure: variable + constraint counts and Maximize sense - closed-loop on the canonical issue instance (objective = 2) - trivial no-contacts instance - brute-force vs ILP optimum - order-preservation rejection of crossing alignments - extract_solution encoding (j -> j+1, unmatched -> 0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add reduction-rule block for MaximumContactMapOverlap -> ILP (#1044) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix clippy + fmt nits in MaximumContactMapOverlap -> ILP tests (#1044) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add KColoring -> BicliqueCover rule (#1058) Self-contained gadget construction that maps a `KColoring(G, q)` instance to a `BicliqueCover` instance on `4n` vertices with rank `n + q`. Each source vertex `v` contributes diagonal/guard/compat edges; guard-anchor edges force `n` bicliques to be spent on guards, leaving at most `q` bicliques to encode color classes under the sub-biclique semantics. Wires up `ReduceTo<BicliqueCover>` for `KColoring<KN, SimpleGraph>`, registers the module in `src/rules/mod.rs`, and adds a canonical example spec (P_2 with q=2) for the example-db. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add closed-loop and structure tests for KColoring -> BicliqueCover (#1058) Nine tests covering: - Closed-loop brute force on the trivial n=1, q=1 instance (8 binary vars). - Structural checks of `num_vertices = 4n`, `rank = n + q`, and the exact edge formula `2 n (n-1) - 4 m + 3 n` on a path and on K_4. - Explicit edge enumeration for n=2 P_2. - NO-instance check: K_4 with q=3 has no proper coloring. - Forward-witness construction on P_3, C_4, K_3: verifies the guard + color biclique cover is valid and that `extract_solution` recovers a proper q-coloring. - Adjacent-grouping rejection: confirms a witness merging adjacent source vertices into one color biclique fails the sub-biclique check. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add reduction-rule block for KColoring -> BicliqueCover (#1058) Adds the paper entry next to the existing KColoring -> Clustering rule, summarizing the gadget construction (diagonal, compatibility, guard-anchor, guard-compat edges), the bidirectional correctness argument, the edge count formula `2 n (n-1) - 4 m + 3 n`, and the diagonal-biclique solution extraction. References Karp 1972, Garey-Johnson 1979, Orlin 1977 as background. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix clippy dead-code on forward_witness helper (#1058) The forward_witness helper in src/rules/kcoloring_bicliquecover.rs was gated with cfg(any(test, feature = "example-db")), but its only caller (canonical_rule_example_specs) is gated with cfg(feature = "example-db"). Under cfg(test) without the example-db feature, the function was compiled but unused, tripping clippy's dead-code lint. Tighten the gate to cfg(feature = "example-db") to match its caller. The test file maintains its own build_forward_witness copy, so test coverage is unaffected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add KSatisfiability/K3 -> BicliqueCover rule (#1057) Implement the Chandran-Issac-Karrenbauer (IPEC 2016, Theorem 6) polynomial reduction from 3-SAT to BicliqueCover with logarithmic rank. The reduction normalizes the source formula (split each x_i into t_i, f_i with exactly-one clauses; pad to n = 2^ell normalized variables) and then assembles a bipartite gadget with crown H_n, clause matchings P_i, domino gadgets S_j, guard Q, and forcing matching Y. Solution extraction identifies the biclique B_1 covering s_{1,1}^u s_{1,1}^v (skipping Y-touching free-edge bicliques) and reads off the normalized assignment via h_i^u in B_1, then maps back to source variables. Includes a canonical rule example with a hand-built forward witness on the smallest case (1 source variable, 1 source clause, rank 18). Free-edge biclique enumeration follows Lemma 16 (H-S, P-P, P-Q, H-P, S_1-P sets). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add closed-loop and structure tests for KSAT/K3 -> BicliqueCover (#1057) Adds seven unit tests covering the new reduction: - Structural sizes for the smallest source (n=2, ell=1, m=3, rank=18). - Structural sizes for the four-variable example from the issue body (n=8, ell=3, m=10, rank=26) — the post-normalization rank is higher than the issue's 22 because we faithfully emit the exactly-one clauses. - Construction termination on a UNSAT formula. - extract_solution reading B_1 (positive-literal h-vertex) from a hand-built partial witness. - extract_solution skipping Y-touching free-edge bicliques. - Two-variable instance constructs without index-out-of-bounds. - Full closed-loop on the smallest source using the canonical forward witness from the example-db builder (gated on the example-db feature). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add paper entry + BibTeX for KSAT/K3 -> BicliqueCover (#1057) - Insert reduction-rule("KSatisfiability", "BicliqueCover", ...) block into docs/paper/reductions.typ with normalization, construction, correctness, and solution-extraction sections. - Add chandran_et_al:LIPIcs.IPEC.2016.11 BibTeX entry to references.bib. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix Phase 3 nits in KSatisfiability/K3 -> BicliqueCover (#1057) - Gate `use crate::traits::Problem;` behind `cfg(feature = "example-db")` in the unit test, matching the feature gate on the only test that uses `evaluate` (clippy `--features ilp-highs` flagged it as unused). - Fix Typst syntax error in the reduction-rule proof: stray `$` inside `$x_i = ($h_i^u in B_1)$` produced an unclosed delimiter; corrected to `$x_i = (h_i^u in B_1)$` so `make paper` compiles. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add ShortestCommonSuperstring model (#413) Implements ShortestCommonSuperstring (Garey & Johnson SR9, P157) as a minimization problem: given an alphabet Sigma and a set R of strings, find a shortest w in Sigma^* that contains every r in R as a contiguous substring. Uses Min<usize> value, fixed-length configuration with a sentinel padding symbol (mirroring sibling ShortestCommonSupersequence), and max_length derived in new() as the sum of input string lengths. Includes problem-def paper entry with CeTZ embedding diagram, canonical example_db instance, and unit tests covering the three issue examples (optima 9, 8, 7; the small one verified by brute force). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add QuadraticProgramming model (#528) Implements the bounded integer variant of Quadratic Programming (G&J MP2): minimize sum_i (c_i*y_i^2 + d_i*y_i) over y in {-K, ..., K}^m subject to linear inequality constraints x . y <= b. NP-hardness follows from Sahni (1974) PARTITION -> QP, whose construction lands on {0,1}^m. - Value = Min<f64>; infeasible configs return Min(None) - dims = vec![2*bound + 1; num_vars] with y_i = config_i - bound - Reuses LinearConstraint from the ILP model - Canonical example reproduces the Sahni PARTITION encoding for a=(1,1,2): optimum y=(1,1,0) with objective 0 - Paper entry + Vavasis (1990) NP-membership reference Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add Partition -> SumOfSquaresPartition rule (#393) Witness reduction specialising Garey & Johnson SP19 to K = 2: copy element sizes verbatim into a SumOfSquaresPartition with two groups. Source YES iff the optimal target witness is a balanced split, which Partition::evaluate accepts via identity solution extraction. Singleton sources take a sentinel path (target on two unit elements) that returns the all-zero source configuration, correctly classified as NO. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add MaxCut -> MinimumMatrixCover rule (#925) Implements the canonical Garey & Johnson MS13 reduction: given a MaxCut instance (G, w) on SimpleGraph with nonnegative i32 edge weights, take the weighted adjacency matrix A as the MinimumMatrixCover instance. The identity sum_{i,j} a_ij f(i) f(j) = 2W - 4 * cut(S) makes MaxCut and MinimumMatrixCover equivalent up to the constant 2W, and the binary encoding (config[i] = 1 iff i in S iff f(i) = +1) makes solution extraction the identity map. - Source variant: MaxCut/SimpleGraph/i32 with nonneg precondition; the reduction panics on any negative edge weight, mirroring the model's nonnegative-matrix invariant. - Overhead: num_rows = num_vertices. - Closed-loop tests cover C_4 (unit weights), weighted P_3, K_3, plus the algebraic identity check on every sign assignment and a negative-weight precondition test. - Canonical example_db builder uses C_4 with unit weights. - Paper entry under docs/paper/reductions.typ with construction, correctness proof, precondition note, and a worked C_4 walk-through. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add PrizeCollectingSteinerForest -> SteinerTree rule (#1027) * Add ThreeDimensionalMatching -> MinimumWeightDecoding rule (#916) Berlekamp-McEliece-van Tilborg (1978) / G&J MS7 construction: each triple becomes a column of a 3q x m binary parity-check matrix with all-ones syndrome; minimum weight codeword = q iff a perfect 3DM matching exists. Witness reduction (Or -> Min<usize>) with a 1x1 sentinel for q=0 or empty triple set. Tests cap at q=3, 5 triples (2^5 target search space). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add Decision<OptimalLinearArrangement> -> ConsecutiveOnesMatrixAugmentation rule (#434) Register the Decision<OptimalLinearArrangement> variant (prerequisite) and implement the reduction to ConsecutiveOnesMatrixAugmentation via the edge-vertex incidence matrix with augmentation bound k - |E|, including the m=0 always-YES sentinel and the k<m genuine-NO cyclic-overlap sentinel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove unused Solver import in PCSF->SteinerTree example builder (#1027) Surfaced under --features example-db; would fail clippy -D warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add Decision<OptimalLinearArrangement> aggregate-edge example + bib cleanup (#434) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Apply rustfmt to OLA->C1MA rule files (#434) Fixes inline-comment spacing flagged by cargo fmt --check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Replace deprecated sect with inter in PCSF->SteinerTree proof (#1027) Typst deprecated the `sect` set-intersection operator in favor of `inter`; silences the only remaining make paper warning. Co-Authored-By: Claude Opus 4.7 (1M contex…
1 parent 7068560 commit c40e943

103 files changed

Lines changed: 17835 additions & 144 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/auto-pipeline/SKILL.md

Lines changed: 389 additions & 0 deletions
Large diffs are not rendered by default.

.claude/skills/check-issue/SKILL.md

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,82 @@ If the algorithm is a high-level sketch rather than an implementable procedure
199199

200200
---
201201

202+
## Rule Check 5: Completeness (fail label: `Incomplete`)
203+
204+
**Goal:** Does the proposed mapping work for *every* instance of the source problem, not just the canonical case in the example?
205+
206+
A reduction that only handles a subset of source instances (e.g., "assumes connected graph", "assumes no duplicate elements", "works only when k is even") is **not a valid polynomial-time reduction** unless the issue explicitly:
207+
- restricts the source to a sub-variant that the codebase actually exposes as a distinct model, AND
208+
- the restriction is part of the algorithm statement, not a hidden assumption.
209+
210+
This check is **mandatory** for every `[Rule]` issue and must be backed by **explicit literature and codebase research** — not vibes.
211+
212+
### 5a: Literature research (mandatory)
213+
214+
For the cited construction(s):
215+
216+
1. Find the original paper or textbook section that defines the reduction.
217+
2. Read the **statement of the theorem**: what does it claim the reduction handles? Look for phrases like:
218+
- "for any instance of P" → covers all instances (good)
219+
- "for a P-instance such that ..." → has a precondition (must be flagged)
220+
- "in the special case where ..." → only a special case (must be flagged)
221+
3. Read the **proof**: are there steps that silently assume something about the source (no isolated vertices, no zero weights, integer-valued capacities, ...)?
222+
223+
Use the same fallback chain as Check 3c.
224+
225+
If the cited paper is **not actually a reduction from the full source problem** but from a restricted variant → **Fail** with the precise restriction quoted from the paper. The fix is one of: (a) add a preprocessing step that reduces the full source to the restricted variant, (b) split into a `[Rule]` issue from the actual restricted source, or (c) drop the reduction.
226+
227+
### 5b: Codebase corner-case research (mandatory)
228+
229+
Check the actual codebase to see what shape the source problem can take:
230+
231+
```bash
232+
pred show <Source> --json
233+
```
234+
235+
Read the `size_fields` and any variant getters, then enumerate corner cases the issue's algorithm must handle:
236+
237+
| Class | Example corner cases the reduction must accept |
238+
|---|---|
239+
| Graph-input problems | empty graph, single vertex, isolated vertices, self-loops if the model allows them, parallel edges if allowed, disconnected components, complete graph |
240+
| Weighted problems | all weights equal, all weights zero, mixed signs (if the weight type allows), one weight dominating the rest |
241+
| Formula/circuit | empty clause set, single-literal clauses, tautological clauses, repeated variables in a clause |
242+
| Set systems | empty universe, empty subsets, identical subsets, universe element appearing in no subset |
243+
| Algebraic | zero matrix, identity, singular matrix |
244+
245+
Then trace the **issue's** algorithm by hand against at least 2 corner cases that are not the worked example:
246+
247+
1. Pick a corner case from the table above that the source model actually allows.
248+
2. Simulate the issue's construction step by step.
249+
3. Check: is the target problem well-defined? Does solution extraction still work?
250+
251+
Also grep the codebase for any existing rule whose source has the same problem name — if it already handles certain corner cases, the new rule should at least match that coverage:
252+
253+
```bash
254+
grep -rl "impl.*ReduceTo.*for <Source>" src/rules/
255+
```
256+
257+
Read 1–2 of those existing rules for how they handle edge inputs.
258+
259+
If the issue's algorithm **crashes, produces an invalid target instance, or loses information** on a legitimate corner case → **Fail** with the corner case spelled out.
260+
261+
If the issue's algorithm appears to handle corner cases correctly but the issue body doesn't *state* this explicitly → **Warn** ("works on tested corner cases, but the algorithm description does not address edge inputs — please document").
262+
263+
### 5c: Verdict
264+
265+
| Finding | Verdict |
266+
|---|---|
267+
| Literature explicitly covers all instances AND traced corner cases work | **Pass** |
268+
| Literature explicitly covers all instances but issue is silent on corner cases | **Warn** |
269+
| Literature has a precondition the issue ignores | **Fail** |
270+
| Traced corner case breaks the algorithm | **Fail** |
271+
272+
(If the cited reference doesn't actually contain the reduction at all, Check 3c already catches it — don't double-flag here.)
273+
274+
Report the literature evidence and the corner cases you traced in the comment — this is the most expensive check and reviewers will want to see your work.
275+
276+
---
277+
202278
# Part B: Model Issue Checks
203279

204280
Applies when the title contains `[Model]`.
@@ -359,9 +435,10 @@ Post a single GitHub comment. The table adapts to the issue type:
359435
| Usefulness | ✅ Pass | No existing direct reduction Source → Target |
360436
| Non-trivial | ✅ Pass | Gadget construction with penalty terms |
361437
| Correctness | ❌ Fail | Paper "Smith 2020" not found on arxiv or Semantic Scholar |
438+
| Completeness | ⚠️ Warn | Algorithm correct on traced corner cases but issue body silent on edge inputs |
362439
| Well-written | ⚠️ Warn | Symbol `m` used in overhead table but not defined in algorithm |
363440

364-
**Overall: 2 passed, 1 failed, 1 warning**
441+
**Overall: 2 passed, 1 failed, 2 warnings**
365442

366443
---
367444

@@ -374,6 +451,9 @@ Post a single GitHub comment. The table adapts to the issue type:
374451
### Correctness
375452
[Per-reference verification results, any better algorithms found]
376453

454+
### Completeness
455+
[Literature passages cited (with quote + section/theorem number) showing whether the construction covers all source instances, and the corner cases you traced by hand with the algorithm — including any that broke or any preconditions you discovered]
456+
377457
### Well-written
378458
[Specific items to fix]
379459

@@ -423,13 +503,14 @@ gh issue edit <NUMBER> --add-label "Useless" # if Check 1 failed
423503
gh issue edit <NUMBER> --add-label "Trivial" # if Check 2 failed
424504
gh issue edit <NUMBER> --add-label "Wrong" # if Check 3 failed
425505
gh issue edit <NUMBER> --add-label "PoorWritten" # if Check 4 failed
506+
gh issue edit <NUMBER> --add-label "Incomplete" # if Rule Check 5 failed
426507

427-
# "Good" label requires: zero failures AND zero warnings on Usefulness or Correctness.
508+
# "Good" label requires: zero failures AND zero warnings on Usefulness, Correctness, or Completeness.
428509
# Warnings on Non-trivial or Well-written alone do NOT block "Good".
429510
gh issue edit <NUMBER> --add-label "Good"
430511

431512
# If re-checking after fixes, remove stale failure labels and add "Good" if now passing
432-
gh issue edit <NUMBER> --remove-label "Useless,Trivial,Wrong,PoorWritten" 2>/dev/null
513+
gh issue edit <NUMBER> --remove-label "Useless,Trivial,Wrong,PoorWritten,Incomplete" 2>/dev/null
433514
gh issue edit <NUMBER> --add-label "Good"
434515
```
435516

.config/nextest.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# nextest configuration.
2+
#
3+
# Project policy is that no single test exceeds 5s (see .claude/CLAUDE.md).
4+
# slow-timeout is a CI safety net far above that budget: a test is flagged slow
5+
# every 60s and forcibly terminated after 5 periods (300s). This bounds a truly
6+
# hung test (cf. issue #1069, where big_o expansion looped indefinitely and the
7+
# whole job was SIGTERM-killed at the runner level) — nextest reports it as a
8+
# clean per-test timeout failure instead of stalling the job.
9+
#
10+
# The large headroom (300s, not a tight ~10s) is deliberate. The subprocess
11+
# example tests in tests/suites/examples.rs shell out to `cargo run --example
12+
# … --features ilp-highs`:
13+
# - In the Test job, CI pre-builds those examples (see ci.yml) so the
14+
# subprocess reuses artifacts and each test runs in well under a second.
15+
# - In the Code Coverage job, the subprocess inherits llvm-cov's
16+
# `-C instrument-coverage` RUSTFLAGS, so it recompiles the examples
17+
# *instrumented* (a non-instrumented pre-build would not match its
18+
# fingerprint, so pre-building there is pointless). That instrumented
19+
# recompile legitimately takes >120s, hence the 300s bound.
20+
[profile.default]
21+
slow-timeout = { period = "60s", terminate-after = 5 }

.github/workflows/ci.yml

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,62 +6,106 @@ on:
66
pull_request:
77
branches: [ main, develop ]
88

9+
# Cancel superseded runs on the same ref (e.g. rapid pushes to a PR).
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
# Least privilege: jobs only read the repo; codecov auth is via token secret.
15+
permissions:
16+
contents: read
17+
918
env:
1019
CARGO_TERM_COLOR: always
1120
RUST_BACKTRACE: 1
21+
# Incremental compilation hurts cold CI builds and bloats the cache; disable it
22+
# (recommended by Swatinem/rust-cache).
23+
CARGO_INCREMENTAL: 0
24+
# Tolerate transient registry/network blips.
25+
CARGO_NET_RETRY: 10
26+
RUSTUP_MAX_RETRIES: 10
1227

1328
jobs:
14-
# Run clippy
29+
# Formatting — cheap, fails fast, no build/cache needed.
30+
fmt:
31+
name: Rustfmt
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v5
35+
- uses: dtolnay/rust-toolchain@stable
36+
with:
37+
components: rustfmt
38+
- name: Check formatting
39+
run: cargo fmt --all --check
40+
41+
# Lints.
1542
clippy:
1643
name: Clippy
1744
runs-on: ubuntu-latest
1845
steps:
19-
- uses: actions/checkout@v4
46+
- uses: actions/checkout@v5
2047
- uses: dtolnay/rust-toolchain@stable
2148
with:
2249
components: clippy
2350
- uses: Swatinem/rust-cache@v2
2451
- name: Run clippy
2552
run: cargo clippy --all-targets --features ilp-highs -- -D warnings
2653

27-
# Build and test
54+
# Build, test (nextest), doc tests, and paper.
2855
test:
2956
name: Test
3057
runs-on: ubuntu-latest
58+
# Single feature set across compile + test + doctest so artifacts are reused
59+
# (no redundant full recompile between steps).
60+
env:
61+
FEATURES: "ilp-highs example-db"
3162
steps:
32-
- uses: actions/checkout@v4
63+
- uses: actions/checkout@v5
3364
- uses: dtolnay/rust-toolchain@stable
65+
- uses: taiki-e/install-action@v2
66+
with:
67+
tool: nextest
3468
- uses: Swatinem/rust-cache@v2
69+
- name: Install typst
70+
uses: typst-community/setup-typst@v5
71+
72+
- name: Compile tests
73+
run: cargo nextest run --no-run --workspace --features "$FEATURES"
3574

36-
- name: Build
37-
run: cargo build --features ilp-highs --verbose
75+
# The subprocess example tests (tests/suites/examples.rs) shell out to
76+
# `cargo run --example … --features ilp-highs`. Pre-build those example
77+
# binaries with that exact feature set so the subprocess reuses artifacts
78+
# instead of recompiling the whole crate mid-test (which otherwise adds
79+
# 60s+ to a single test's wall-clock and would trip the nextest timeout).
80+
- name: Build examples (for subprocess tests)
81+
run: cargo build --examples --features ilp-highs
3882

3983
- name: Run tests
40-
run: cargo test --features "ilp-highs example-db" --workspace --verbose
84+
run: cargo nextest run --workspace --features "$FEATURES"
4185

86+
# nextest does not run doc tests; run them separately (reuses the build).
4287
- name: Run doc tests
43-
run: cargo test --doc --features ilp-highs --verbose
44-
45-
- name: Install typst
46-
uses: typst-community/setup-typst@v4
88+
run: cargo test --doc --features "$FEATURES" --verbose
4789

4890
- name: Build paper
4991
run: make paper
5092

51-
# Code coverage
93+
# Coverage. Feature set intentionally matches the historical coverage gate
94+
# (ilp-highs only) to keep the codecov baseline stable.
5295
coverage:
5396
name: Code Coverage
5497
runs-on: ubuntu-latest
5598
steps:
56-
- uses: actions/checkout@v4
99+
- uses: actions/checkout@v5
57100
- uses: dtolnay/rust-toolchain@stable
58101
with:
59102
components: llvm-tools-preview
103+
- uses: taiki-e/install-action@v2
104+
with:
105+
tool: cargo-llvm-cov,nextest
60106
- uses: Swatinem/rust-cache@v2
61-
- name: Install cargo-llvm-cov
62-
uses: taiki-e/install-action@cargo-llvm-cov
63107
- name: Generate coverage
64-
run: cargo llvm-cov --features ilp-highs --workspace --lcov --output-path lcov.info
108+
run: cargo llvm-cov nextest --features ilp-highs --workspace --lcov --output-path lcov.info
65109
- name: Upload to codecov.io
66110
uses: codecov/codecov-action@v5
67111
with:

0 commit comments

Comments
 (0)