feat: Decision wrapper for optimization→decision conversion#1014
feat: Decision wrapper for optimization→decision conversion#1014
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the aggregate reduction from Decision<P> to P that extracts the optimization value by comparing against the threshold. This is Task 3 of the decision-wrapper plan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a generic Decision<P> wrapper to convert optimization problems (Min/Max) into decision problems (Or) via a bound, migrates the VertexCover decision model to this wrapper while preserving CLI aliases, and adds a decision-query-based search utility plus registry/docs updates to reflect the new decision types.
Changes:
- Add
OptimizationValue+Decision<P>(withReduceToAggregate) to support generic optimization→decision conversion and aggregate edges back to the inner optimization problem. - Register concrete decision variants/schemas for
Decision<MinimumVertexCover<SimpleGraph,i32>>andDecision<MinimumDominatingSet<SimpleGraph,i32>>, removing the bespokeVertexCovermodel while preservingVertexCover/VCCLI aliases. - Add a decision-query-based solver utility (named “golden section”) and update CLI/docs/tests to validate registry, examples, and alias resolution.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types.rs | Adds OptimizationValue trait + Min/Max implementations; wires new unit test module. |
| src/models/decision.rs | Introduces generic Decision<P> wrapper, DecisionProblemMeta, and aggregate reduction Decision<P> → P. |
| src/models/mod.rs | Exposes decision module and re-exports Decision; removes VertexCover re-export. |
| src/models/graph/minimum_vertex_cover.rs | Registers DecisionMinimumVertexCover schema/variant + aggregate edge; adds example-db spec. |
| src/models/graph/minimum_dominating_set.rs | Registers DecisionMinimumDominatingSet schema/variant + aggregate edge. |
| src/models/graph/maximum_independent_set.rs | Registers DecisionProblemMeta for MIS (for decision-query tooling/tests). |
| src/models/graph/mod.rs | Removes vertex_cover module export; adds decision-MVC example specs into example-db aggregation. |
| src/models/graph/vertex_cover.rs | Removes bespoke VertexCover decision model implementation. |
| src/solvers/golden_section.rs | Adds decision-query search helper (implemented as integer binary search). |
| src/solvers/mod.rs | Exposes the new golden_section solver module. |
| src/unit_tests/types_optimization_value.rs | Adds tests for OptimizationValue::meets_bound semantics. |
| src/unit_tests/models/decision.rs | Adds tests for Decision<P> evaluation, serde roundtrip, and aggregate reduction. |
| src/unit_tests/solvers/golden_section.rs | Adds tests validating decision-query search recovers optima and handles invalid intervals. |
| src/unit_tests/registry/schema.rs | Adds tests asserting decision schemas/variants are registered and VertexCover legacy schema removed. |
| src/unit_tests/reduction_graph.rs | Adds tests asserting direct aggregate edges from decision wrappers to optimization problems. |
| src/unit_tests/models/graph/vertex_cover.rs | Removes tests for the deleted bespoke VertexCover model. |
| src/unit_tests/example_db.rs | Adds example-db test coverage for DecisionMinimumVertexCover example instance. |
| problemreductions-macros/src/lib.rs | Fixes proc-macro type-name extraction to handle Decision<T> nested generics; adds tests. |
| problemreductions-cli/src/problem_name.rs | Updates alias resolution tests so VertexCover/VC resolve to DecisionMinimumVertexCover. |
| problemreductions-cli/src/mcp/prompts.rs | Updates default prompt example to use DecisionMinimumVertexCover. |
| problemreductions-cli/src/commands/create/schema_support.rs | Updates help-example fallback strings to include DecisionMinimumVertexCover. |
| problemreductions-cli/src/commands/create.rs | Adds random generation path for DecisionMinimumVertexCover; removes VertexCover random generation. |
| problemreductions-cli/src/cli.rs | Updates “Flags by problem type” help text to list DecisionMinimumVertexCover. |
| docs/paper/reductions.typ | Updates paper definitions and helper logic to support nested inner for decision-wrapped instances. |
| docs/plans/2026-04-06-decision-wrapper-plan.md | Adds implementation plan documenting steps/tests for the decision wrapper work. |
| docs/plans/2026-04-06-decision-wrapper-design.md | Adds design spec documenting rationale, architecture, and migration details. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" }, | ||
| FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" }, |
There was a problem hiding this comment.
ProblemSchemaEntry for DecisionMinimumVertexCover declares top-level fields graph/weights/bound, but the actual serde shape of Decision<MinimumVertexCover<...>> is { inner: { graph, weights }, bound }. This mismatch will break schema-driven CLI creation (pred create DecisionMinimumVertexCover --graph ... --weights ... --bound ...) because the variant factory will fail to deserialize the generated JSON. Consider either (a) teaching Decision<P> to deserialize from the flattened form used by the schema/CLI, or (b) changing schema-driven creation/schema fields to match the nested inner structure (or special-casing this canonical in the CLI create path).
| FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" }, | |
| FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" }, | |
| FieldInfo { | |
| name: "inner", | |
| type_name: "MinimumVertexCover<G, W>", | |
| description: "The optimization problem payload containing the graph G=(V,E) and vertex weights w: V -> R", | |
| }, |
| FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" }, | ||
| FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" }, | ||
| FieldInfo { name: "bound", type_name: "i32", description: "Decision bound (maximum allowed dominating-set cost)" }, |
There was a problem hiding this comment.
Same issue as DecisionMinimumVertexCover: this schema declares graph/weights/bound as top-level fields, but Decision<MinimumDominatingSet<...>> serializes as { inner: { ... }, bound }. As a result, schema-driven CLI creation for DecisionMinimumDominatingSet will generate the wrong JSON shape and the variant factory will reject it. Align the schema/CLI creation with the actual serde structure, or make Decision<P> accept both shapes on deserialization.
| FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" }, | |
| FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" }, | |
| FieldInfo { name: "bound", type_name: "i32", description: "Decision bound (maximum allowed dominating-set cost)" }, | |
| FieldInfo { | |
| name: "inner", | |
| type_name: "MinimumDominatingSet<SimpleGraph, i32>", | |
| description: "The underlying minimum dominating set instance", | |
| }, | |
| FieldInfo { | |
| name: "bound", | |
| type_name: "i32", | |
| description: "Decision bound (maximum allowed dominating-set cost)", | |
| }, |
| KColoring --graph, --k | ||
| KClique --graph, --k | ||
| VertexCover (VC) --graph, --k | ||
| DecisionMinimumVertexCover --graph, --weights, --bound |
There was a problem hiding this comment.
The CLI help now advertises DecisionMinimumVertexCover --graph, --weights, --bound, but schema-driven creation currently constructs JSON using the schema field names (top-level graph/weights/bound). Since Decision<...> deserializes from { inner: {...}, bound }, pred create DecisionMinimumVertexCover --graph ... --weights ... --bound ... will fail unless the model/serde or CLI creation path is updated to match.
| DecisionMinimumVertexCover --graph, --weights, --bound | |
| DecisionMinimumVertexCover use JSON input ({\"inner\": {...}, \"bound\": ...}) |
| pub use graph::{ | ||
| AcyclicPartition, BalancedCompleteBipartiteSubgraph, BicliqueCover, BiconnectivityAugmentation, | ||
| BottleneckTravelingSalesman, BoundedComponentSpanningForest, BoundedDiameterSpanningTree, | ||
| DegreeConstrainedSpanningTree, DirectedHamiltonianPath, DirectedTwoCommodityIntegralFlow, | ||
| DisjointConnectingPaths, GeneralizedHex, GraphPartitioning, HamiltonianCircuit, | ||
| HamiltonianPath, HamiltonianPathBetweenTwoVertices, IntegralFlowBundles, | ||
| IntegralFlowHomologousArcs, IntegralFlowWithMultipliers, IsomorphicSpanningTree, KClique, | ||
| KColoring, Kernel, KthBestSpanningTree, LengthBoundedDisjointPaths, LongestCircuit, | ||
| LongestPath, MaxCut, MaximalIS, MaximumAchromaticNumber, MaximumClique, MaximumDomaticNumber, | ||
| MaximumIndependentSet, MaximumLeafSpanningTree, MaximumMatching, MinMaxMulticenter, | ||
| MinimumCoveringByCliques, MinimumCutIntoBoundedSets, MinimumDominatingSet, | ||
| MinimumDummyActivitiesPert, MinimumEdgeCostFlow, MinimumFeedbackArcSet, | ||
| MinimumFeedbackVertexSet, MinimumGeometricConnectedDominatingSet, MinimumGraphBandwidth, | ||
| MinimumIntersectionGraphBasis, MinimumMaximalMatching, MinimumMultiwayCut, | ||
| MinimumSumMulticenter, MinimumVertexCover, MixedChinesePostman, MonochromaticTriangle, | ||
| MultipleChoiceBranching, MultipleCopyFileAllocation, OptimalLinearArrangement, | ||
| PartialFeedbackEdgeSet, PartitionIntoCliques, PartitionIntoForests, | ||
| PartitionIntoPathsOfLength2, PartitionIntoPerfectMatchings, PartitionIntoTriangles, | ||
| PathConstrainedNetworkFlow, RootedTreeArrangement, RuralPostman, ShortestWeightConstrainedPath, | ||
| SpinGlass, SteinerTree, SteinerTreeInGraphs, StrongConnectivityAugmentation, | ||
| SubgraphIsomorphism, TravelingSalesman, UndirectedFlowLowerBounds, | ||
| UndirectedTwoCommodityIntegralFlow, VertexCover, | ||
| UndirectedTwoCommodityIntegralFlow, | ||
| }; |
There was a problem hiding this comment.
VertexCover was removed from the public Rust re-exports (and the model file was deleted), which is a breaking change for library users who construct problemreductions::models::graph::VertexCover. If you intend to preserve the Rust API as well as the CLI alias, consider adding a public type alias (e.g., pub type VertexCover<...> = Decision<MinimumVertexCover<...>>) or documenting this as a breaking change in release notes.
src/solvers/golden_section.rs
Outdated
| //! Golden-section-style search for optimization via decision queries. | ||
|
|
||
| use crate::models::decision::{Decision, DecisionProblemMeta}; | ||
| use crate::solvers::{BruteForce, Solver}; | ||
| use crate::traits::Problem; |
There was a problem hiding this comment.
golden_section is implemented as a monotone binary search over integer bounds (midpoint halving), not golden-section search. This is likely fine functionally, but the module/function naming and top-level docstring are misleading and make the algorithm harder to reason about later. Consider renaming to binary_search_decision (or similar) or updating the docs to match the actual approach.
- Add register_decision_variant! macro to reduce per-type boilerplate (~80 lines each for MVC/MDS)
- Add Decision<MinimumDominatingSet> behavioral tests (creation, evaluate, reduction, solver)
- Add boundary test for reduce_to_aggregate with infeasible bound
- Rename golden_section.rs to decision_search.rs (binary search, not golden section)
- Fix schema-driven CLI creation for Decision types (restructure flat JSON to nested {inner, bound})
- Add canonical rule example specs for Decision→Optimization aggregate edges
- Update example_db test to handle aggregate-only reduction paths
- Filter trivial Decision<P>↔P edges from paper completeness check
- Remove plan files
- Generalize DecisionProblemMeta to blanket impl per optimization model
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lique) The K4 graph with k=3 has both size-3 and size-4 valid cliques. The ILP solver nondeterministically picks either, so assert >= k instead of == k. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1014 +/- ##
==========================================
- Coverage 98.14% 98.11% -0.03%
==========================================
Files 920 923 +3
Lines 95494 95900 +406
==========================================
+ Hits 93723 94094 +371
- Misses 1771 1806 +35 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…ision Registers P → Decision<P> as a Turing reduction edge — representing that solving an optimization problem via its decision version requires multiple adaptive queries (binary search over the bound). - Add `turing` field to EdgeCapabilities and ReductionMode::Turing - Register reverse Turing edges in register_decision_variant! macro - Export turing flag in JSON graph - Turing edges excluded from rule example coverage (no single-shot demo) - Add tests for MVC→DecisionMVC and MDS→DecisionMDS Turing edges Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The macro previously hardcoded num_vertices/num_edges as size getters, which only works for graph problems. Now accepts dims, fields, and size_getters as parameters so non-graph Decision variants can specify their own problem-size fields (e.g., num_vars/num_clauses for SAT). Callers define inherent methods on Decision<P> before invoking the macro. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Document Decision<P> wrapper, OptimizationValue trait, decision_search solver - Document EdgeCapabilities.turing field and ReductionMode::Turing - Document register_decision_variant! macro with dims/fields/size_getters params - Document Decision↔P completeness filter in paper section - Add anti-pattern entry in add-model skill: use Decision<P> not hand-written models Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Decision<P>wrapper that converts optimization problems (Min/Maxvalued) to decision problems (Orvalued) via a bound parameterOptimizationValuetrait abstracting overMin<V>/Max<V>withmeets_bound()semanticsReduceToAggregateimpl forDecision<P> → P(solve optimization, compare to bound)Decision<P>instancesVertexCoverwithDecision<MinimumVertexCover>(aliasesVertexCover/VCpreserved)extract_type_name()to handleDecision<T>nested genericsDecision<MinimumVertexCover>andDecision<MinimumDominatingSet>Closes #998
Test plan
cargo test optimization_value— 8 tests forOptimizationValuetraitcargo test test_decision— 9 tests forDecision<P>struct, solver, serialization, aggregate reductioncargo test test_golden_section— 3 tests for golden-section search solvermake check— full suite (fmt + clippy + test)pred show VertexCoverandpred show VCstill resolve (alias migration)🤖 Generated with Claude Code