Exact symbolic growth, unified reduction size contracts, and deterministic solver backends - #1137
Conversation
Introduce exact symbolic expressions, rule-owned size relations, strict extraction and numeric contracts, model-owned construction metadata, and deterministic solver selection as one compile-time contract. Migrate directly from the main-branch APIs without recording discarded intermediate designs.
Use the unified registry for creation, inspection, deterministic solver selection, reduction paths, and size reporting. Keep CLI and MCP outputs consistent and cover their user-facing error paths and round trips.
Update executable examples and integration scenarios for the final size, extraction, and registry contracts, including source-relative path assertions and exact variant handling.
Align design, CLI, MCP, paper, and maintainer workflow documentation with exact expressions, rule-owned size relations, registry-owned construction, and deterministic solver dispatch.
Use the required HiGHS backend, keep benchmark-only dependencies out of ordinary builds, add portable stack handling, and exercise macOS ARM64, Windows x86_64, and RISC-V targets in CI.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1137 +/- ##
==========================================
- Coverage 98.00% 95.93% -2.08%
==========================================
Files 1044 1074 +30
Lines 107380 132106 +24726
==========================================
+ Hits 105234 126730 +21496
- Misses 2146 5376 +3230 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Use the Rust 1.98 slice chunk API required by Clippy and remove the numeric-format sections from the problem and rule issue templates.
Derive factoring widths from the target and canonicalize factor witnesses. Model CVP without artificial coefficient bounds and register exact sphere enumeration. Make QUBO numeric variants and same-problem variant reductions explicit so the standard models remain executable.
Disambiguate infeasible and unbounded ILPs with a zero-objective solve. Map optimal target values through aggregate reductions before extracting decision witnesses, including HamiltonianCircuit and PartitionIntoCliques. Cover decision bounds and error propagation with regression tests.
Align model validation, flow and scheduling reductions with their mathematical contracts. Add the EnsembleComputation ILP rule and fixed solver pipeline, update proofs and regression tests, and propagate construction overflow through shared error APIs.
Repair reduction constructions, witness extraction, and boundary handling identified during corpus validation. Include the associated model and variant support, exact JSON replay, regression tests, and mathematical documentation. Reuse existing SAT allocation and big-integer APIs, remove redundant extraction state, and avoid allocating zero-coefficient sets. Validation: make check (6555 tests passed), make coverage, and git diff --cached --check. Known unresolved rules and resource limits remain tracked separately.
Keep ILP numerical semantics explicit, report unresolved decisions, check circulation overflow, and enumerate nearby CVP candidates first. Make unit weights implicit across One variants and share concrete construction inputs across CLI, MCP, and decision wrappers. Add regression coverage and document the contracts.
There was a problem hiding this comment.
Fixed all eight findings from the parallel review in e1a45a77d9552a43e47d15fe41e9b47f510f17a0. A fresh review of the fixes found no additional issues.
| Finding | Fix and regression evidence |
|---|---|
| CVP reported a suboptimal lattice point as optimal | Gram-Schmidt projections and sphere bounds now use exact rational arithmetic. The large translated target now returns coefficients [100000000000000,100000000000000] and Min(0), with integer/float, negative-translation, and nearly parallel basis regressions. Existing transport limits remain enforced. |
| Bundle solving labeled an unsatisfiable source optimal | Shared bundle extraction checks typed source feasibility and returns an extraction error for an infeasible witness. This covers CLI/MCP solving and external extraction. The contradictory 3-SAT/MVC bundle is rejected; the satisfiable counterpart still succeeds. Failure to extract a witness is not treated as proof of infeasibility. |
| CVP-to-QUBO determinant calculation took factorial time | Replaced recursive cofactor expansion with pivoted Bareiss elimination using the existing BigInt dependency. Added a 12-dimensional identity reduction and pivoting, singularity, cancellation, and overflow checks. |
| Valid schedule start times were rejected | Removed the task-count restriction on time slots. Deadline validation remains. A one-task instance with deadline 3 now accepts start time 1 and completes witness enumeration. |
| Schedule extraction rejected valid optimal target assignments | Added one equality fixing all slots beyond individual deadlines to zero and updated exact constraint metadata. Exhaustively checked all 16 target assignments for the two-task regression. |
| Printed symbolic expressions changed meaning | Fractional exponents and negative bases now retain required parentheses. Round-trip checks cover n^(1/3), (2/3)^n, (-2)^n, and negative fractional bases. |
| Intersection-basis solving overflowed machine-word masks | Replaced edge-mask dynamic programming with exact clique-cover search over boolean coverage vectors. K8 and K12 return optimum 1 without the overflowing masks or exponential edge table. |
| Factorial complexity callbacks panicked above 170 | Approximate complexity overflow now returns infinity, consistent with other approximate operators. Checked 5!, 170!, and 171!; typed runtime factorial evaluation still reports overflow. |
Validation: make check paper mcp-test and git diff --check passed. This includes 5,800 library tests, workspace integration and documentation tests, paper compilation, 53 MCP unit tests, and 2 MCP integration tests. The original CVP and unsatisfiable-bundle counterexamples were also rerun through the CLI.
The original review covered the main changed subsystems with three subagents and was risk-focused rather than exhaustive over all 1,183 files. The fix diff changes 18 files, adds no dependencies, and updates the affected paper descriptions. The independent follow-up review also checked 560 determinant matrices and 2,000 cover systems.
All nine checks passed on e1a45a77d9552a43e47d15fe41e9b47f510f17a0: Rustfmt, Clippy, Test, Code Coverage, macOS ARM64, Windows x86_64, RISC-V, codecov/patch, and codecov/project. PR #1137 was then squash-merged as 7dd5fcdf.
Closes #1075, #1078, #1076, #788, and #1090.
Systematic API changes
The main API changes are: problems accept their own solution types, solvers return a solution or explicit failure, and reductions report named input/output parameters. The cross-cutting changes come first; individual model and reduction fixes follow separately. This is a breaking API change.
1.
Problem::evaluate: pass a mathematical solutionWhy needed: A Boolean selection, a signed lattice coefficient, and a pair of factors are different kinds of solutions. Encoding all of them as
usizearrays makes callers understand solver-specific encodings and leaves malformed inputs indistinguishable from ordinary candidates.Previously every problem accepted
&[usize], andevaluate()returned a value directly. Now each problem definestype Solution, and evaluation returnsResult<Value, EvaluationError>.For a unit-weight path with edges
0–1–2–3:mis.evaluate(&vec![true, false, true, false])Ok(Max(Some(2))): vertices 0 and 2 are independentmis.evaluate(&vec![true, true, false, false])Ok(Max(None)): adjacent vertices were selectedmis.evaluate(&vec![true])Err(EvaluationError::InvalidConfiguration(...)): wrong solution lengthThis separates an infeasible candidate from a malformed input or arithmetic failure. The corresponding CLI input is now Boolean JSON, for example
--config '[true,false,true,false]'.Requiring every problem to expose a finite Cartesian search space also forces artificial restrictions on models such as unbounded CVP.
Problem::dims()is removed from the base trait. Models that support finite enumeration implementBruteForceProblem::dimensions()and register that solver separately.Numeric handling is updated because a wrapped objective or rounded integer can change which solution is optimal. Using one signed integer domain also avoids inconsistent model/reduction boundaries; a wider type alone does not replace checked arithmetic. Across models, signed integer fields change from
i32toi64. Overflow, non-finite float results, and inexact integer-to-float conversions return errors. Arbitrary precision remains available where the problem requires it, including factoring.2.
solve: return a witness, distinguish infeasibility from failureWhy needed: A reduction needs a target witness to reconstruct a source solution; an objective value alone is insufficient. Callers also need to distinguish “no solution exists” from “the solver failed,” rather than treating both as an absent witness.
BruteForce::solve(&problem)now returnsResult<Option<P::Solution>, SolveError>:Ok(Some(solution))Ok(None)Err(error)Deterministic
pred solvepipelinePreviously, automatic solving used the same reduction-path search as
pred pathto choose a route to ILP. This coupled solver behavior to path discovery: future optimizations topred pathcould change the reduction chain used bypred solve, even when the input and solver request stayed the same.pred solvenow uses an explicitly registered pipeline for each exact problem variant:--solverselects only that backend.BicliqueCoverpipeline isBicliqueCover -> BMF -> ILP<bool, i64> -> ILP<bool, f64>.The shared
solve(problem, SolverRequest)API does not search the reduction graph at solve time.pred pathremains a path-exploration command; changes to its search or ordering do not alter the registered solve pipeline. Changing a solve route requires explicitly updating its pipeline declaration. A graph route to ILP alone does not establish solver availability for a variant.pred inspect problem.jsonshows the registered capabilities and ILP path; solve results identify the selected backend and, for ILP, the executed reduction path.pred solve problem.json --solver brute-forceexplicitly selects brute force.Dynamic results contain
Optimal { solution, evaluation }orInfeasible; successful solves do not return a value without a solution.3. ILP: separate variable and coefficient types, check backend answers
Why needed: Variable integrality and coefficient representation are independent: an integer variable may appear in a row with fractional coefficients. Keeping these choices separate lets reductions express the intended domain and makes the conversion to the floating-point backend explicit.
ILP<bool, i64>describes Boolean variables with integer coefficients;ILP<i64, f64>describes integer variables with floating-point coefficients. Variables store explicit lower/upper bounds, including unbounded sides. Constraints and objectives use sparse coefficients.4. Reduction parameters: report sizes by name and compose formulas
Why needed: A number such as “size = 10” does not identify whether it counts vertices, edges, or variables. Path composition needs the same named quantities at both ends of every edge. It must also distinguish equalities from bounds: substituting
m <= n^2into10 - mas though it were equality would produce an unsound upper bound.Problem::parameters()reports namedu64values such asnum_verticesandnum_edges. Reduction declarations specify how those values change usingtransform = exact,upper_bound, orunavailable.ParameterTransform::evaluate()applies one relation;compose()combines consecutive relations:m = 4, thenk = 10 - mk = 6m <= n^2, thenk = 10 - mk <= 10Each target parameter must have a formula or a stated reason it is unavailable. Unknown parameter names are rejected when declarations compile, so a typo cannot silently disconnect a formula from its model. Runtime and macro expressions share exact integer/rational arithmetic to avoid parser disagreement and floating-point rounding in symbolic calculations; symbolic Big-O analysis reports unsupported cases explicitly instead of presenting an unjustified estimate.
ReductionGraph::path_parameter_transforms()returns the individual relations, andcompose_path_parameter_transform()returns their composition.5. CLI/MCP: construct the selected variant and inspect bounded path lists
Why needed: Separate model-specific parsers in CLI and MCP duplicate field definitions and validation. Selecting construction fields from the concrete variant prevents the two entry points from accepting different versions of the same instance.
Creation flags come from the selected model's construction fields. CLI and MCP call the same registered constructor, so they apply the same validation. For example:
Path output is bounded because the number of simple reduction paths can grow combinatorially; inspecting a route should not require listing or executing all of them. The
truncatedfield prevents a partial list from being mistaken for a complete one.The fourth command lists up to 50 reduction paths; the fifth also executes those paths on the instance and reports the constructed problems' parameters. The default limit is 20, the maximum is 999, and
--limit allmeans 999. JSON includespathsandtruncatedto indicate whether more paths exist.Saved-route replay, evaluation, and extraction carry the model's typed solution. Direct reduction extractors validate the target solution before decoding it, so malformed target data cannot become an apparently valid source witness through indexing or decoding.
Concrete model changes
Factoring and ClosestVectorProblem
Why needed: To factor 15, a caller should supply 15 without first choosing a bit encoding. For CVP, guessed coefficient bounds can exclude the true closest lattice point and thereby change the problem being solved. These constructors now take the mathematical input; explicit factoring widths remain available when the caller actually wants that restriction.
Factoring::new(2, 3, 15); solutions encode factor bitsFactoring::new(15)derives widths 2 and 3; the solution is the orderedBigUintpair(3, 5)Factoring::with_factor_bits(15, 2, 4)when widths are part of the requested instance; these widths also admit(1, 15)ClosestVectorProblem::new(basis, target); a solution directly contains signed integer coefficientsFor CVP with basis columns
[2,0,0],[1,2,0]and target[3,3,1], evaluating coefficients[1,1]gives distancesqrt(2). Coefficients[11,-12]are also valid candidates—there is no artificial enumeration box. A registered sphere-enumeration solver handles this model.Other model fixes
OpenShopSchedulingUndirectedTwoCommodityIntegralFlowBiconnectivityAugmentationMixedChinesePostmanKColoring<K, G>num_colorsvalues that disagree with fixedK.MinimumCostMaximumFlowSteinerTreeIsomorphicSpanningTreeDecisionMaximumIndependentSetandMinimumFeedbackVertexSetConcrete reduction-rule changes
These changes repair particular constructions or decoders, separately from the shared API migration above.
EnsembleComputation -> ILPMultipleChoiceBranching -> ILPPartitionIntoCliques -> ILPDecisionMaximumIndependentSet -> IntegralFlowBundlesDecisionMinimumDominatingSet -> MinimumSumMulticenterSteinerTree -> ILPedges = vertices - 1.BiconnectivityAugmentation -> ILPUndirectedTwoCommodityIntegralFlow -> ILPOpenShopScheduling -> ILPPartition -> OpenShopSchedulingHamiltonianCircuit -> LongestCircuitHamiltonianPathBetweenTwoVertices -> LongestPathKColoring -> QUBOCircuitSAT -> SpinGlassILP<bool> -> QUBOTravelingSalesman -> QUBOvertex * n + position.SpinGlass -> MaxCut+1before removing it.Factoring -> CircuitSATSubsetSum -> ClosestVectorProblemMinimumVertexCover -> EnsembleComputationPartitionIntoCliques -> MinimumCoveringByCliquesMinimumFeedbackArcSet -> MaximumLikelihoodRankingThreeDimensionalMatching -> ThreeMatroidIntersectionDocumentation and build changes
Verification
All seven checks passed at
0227c24788ec2aeebecd1b06704328599cfcbbf9: Rustfmt, Clippy, Test, Code Coverage, and macOS ARM64 / Windows x86_64 / RISC-V build-and-run jobs.Examples in this description were checked against the current implementation and existing tests. No local test suite was rerun for this body-only edit.