Skip to content

Commit fb9c608

Browse files
GiggleLiuclaude
andauthored
feat: Decision wrapper for optimization→decision conversion (#1014)
* docs: add Decision wrapper design spec and implementation plan Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add OptimizationValue trait for Min/Max decision conversion * feat: add Decision<P> generic wrapper with Problem impl Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add ReduceToAggregate<P> impl for Decision<P> 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> * fix: extract_type_name handles Decision<T> nested generics * feat: register Decision variants for MVC and MDS * refactor: remove hand-written VertexCover, replaced by Decision<MinimumVertexCover> * feat: add golden-section search solver via Decision queries * docs: migrate VertexCover paper entry and example_db to DecisionMinimumVertexCover * chore: integration fixups for Decision wrapper * fix: address PR #1014 review comments and quality issues - 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> * fix: relax flaky kclique_ilp assertion (ILP may return larger valid clique) 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> * feat: add Turing (multi-query) reduction edges for Optimization → Decision 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> * refactor: parameterize register_decision_variant! size fields 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> * docs: update CLAUDE.md and add-model skill for Decision wrapper - 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> * fix: CLI compat for Decision types (--k alias, MDS example) - Map --k to --bound for Decision types in schema_field_flag_keys so `pred create VC --k 2` works (backward compat with old VertexCover) - Add canonical model example for DecisionMinimumDominatingSet so `pred create --example DecisionMinimumDominatingSet` works Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove backward-compat aliases (VertexCover/VC/--k) Breaking changes are allowed at this stage. Remove: - VertexCover and VC aliases from DecisionMinimumVertexCover schema - --k → --bound mapping for Decision types - Replace with DMVC alias Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Typst syntax errors in paper completeness filter - Use if/else instead of `not ... and` (Typst precedence issue) - Fix RR_ge → RR_(gt.eq 0) in DecisionMVC math notation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 07caf14 commit fb9c608

File tree

30 files changed

+1260
-326
lines changed

30 files changed

+1260
-326
lines changed

.claude/CLAUDE.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ make release V=x.y.z # Tag and push a new release (CI publishes to crates.io)
9191
- `misc/` - Unique input structures
9292
- Run `pred list` for the full catalog of problems, variants, and reductions; `pred show <name>` for details on a specific problem
9393
- `src/rules/` - Reduction rules + inventory registration
94-
- `src/solvers/` - BruteForce solver for aggregate values plus witness recovery when supported, ILP solver (feature-gated, witness-only). To check if a problem supports ILP solving via a witness-capable reduction path, run `pred path <ProblemName> ILP`
94+
- `src/models/decision.rs` - Generic `Decision<P>` wrapper converting optimization problems to decision problems
95+
- `src/solvers/` - BruteForce solver for aggregate values plus witness recovery when supported, ILP solver (feature-gated, witness-only), decision search (binary search via Decision queries). To check if a problem supports ILP solving via a witness-capable reduction path, run `pred path <ProblemName> ILP`
9596
- `src/traits.rs` - `Problem` trait
9697
- `src/rules/traits.rs` - `ReduceTo<T>`, `ReduceToAggregate<T>`, `ReductionResult`, `AggregateReductionResult` traits
9798
- `src/registry/` - Compile-time reduction metadata collection
@@ -122,14 +123,22 @@ Problem (core trait — all problems must implement)
122123

123124
**Aggregate-only problems** use fold values such as `Sum<W>` or `And`; these solve to a value but have no representative witness configuration.
124125

126+
**Decision problems** wrap an optimization problem with a bound: `Decision<P>` where `P::Value: OptimizationValue`. Evaluates to `Or(true)` when the inner objective meets the bound (≤ for Min, ≥ for Max).
127+
125128
Common aggregate wrappers live in `src/types.rs`:
126129
```rust
127130
Max<V>, Min<V>, Sum<W>, Or, And, Extremum<V>, ExtremumSense
128131
```
129132

133+
`OptimizationValue` trait (in `src/types.rs`) enables generic Decision conversion:
134+
- `Min<V>`: meets bound when value ≤ bound
135+
- `Max<V>`: meets bound when value ≥ bound
136+
130137
### Key Patterns
131138
- `variant_params!` macro implements `Problem::variant()` — e.g., `crate::variant_params![G, W]` for two type params, `crate::variant_params![]` for none (see `src/variant.rs`)
132139
- `declare_variants!` proc macro registers concrete type instantiations with best-known complexity and registry-backed load/serialize/value-solve/witness-solve metadata. One entry per problem may be marked `default`, and variable names in complexity strings are validated at compile time against actual getter methods.
140+
- `decision_problem_meta!` macro registers `DecisionProblemMeta` for a concrete inner type, providing the `DECISION_NAME` constant.
141+
- `register_decision_variant!` macro generates `declare_variants!`, `ProblemSchemaEntry`, and both `ReductionEntry` submissions (aggregate Decision→Opt + Turing Opt→Decision) for a `Decision<P>` variant. Callers must define inherent getters (`num_vertices()`, `num_edges()`, `k()`) on `Decision<P>` before invoking. Accepts `dims`, `fields`, and `size_getters` parameters for problem-specific size fields.
133142
- Problems parameterized by graph type `G` and optionally weight type `W` (problem-dependent)
134143
- `Solver::solve()` computes the aggregate value for any `Problem` whose `Value` implements `Aggregate`
135144
- `BruteForce::find_witness()` / `find_all_witnesses()` recover witnesses only when `P::Value::supports_witnesses()`
@@ -171,14 +180,16 @@ Reduction graph nodes use variant key-value pairs from `Problem::variant()`:
171180
- Default variant ranking: `SimpleGraph`, `One`, `KN` are considered default values; variants with the most default values sort first
172181
- Nodes come exclusively from `#[reduction]` registrations; natural edges between same-name variants are inferred from the graph/weight subtype partial order
173182
- Each primitive reduction is determined by the exact `(source_variant, target_variant)` endpoint pair
174-
- Reduction edges carry `EdgeCapabilities { witness, aggregate }`; graph search defaults to witness mode, and aggregate mode is available through `ReductionMode::Aggregate`
175-
- `#[reduction]` accepts only `overhead = { ... }` and currently registers witness/config reductions; aggregate-only edges require manual `ReductionEntry` registration with `reduce_aggregate_fn`
183+
- Reduction edges carry `EdgeCapabilities { witness, aggregate, turing }`; graph search defaults to witness mode, aggregate mode is available through `ReductionMode::Aggregate`, and Turing (multi-query) mode via `ReductionMode::Turing`
184+
- `#[reduction]` accepts only `overhead = { ... }` and currently registers witness/config reductions; aggregate-only and Turing edges require manual `ReductionEntry` registration
185+
- `Decision<P> → P` is an aggregate-only edge (solve optimization, compare to bound); `P → Decision<P>` is a Turing edge (binary search over decision bound)
176186

177187
### Extension Points
178188
- New models register dynamic load/serialize/brute-force dispatch through `declare_variants!` in the model file, not by adding manual match arms in the CLI
179189
- **CLI creation is schema-driven:** `pred create` automatically maps `ProblemSchemaEntry` fields to CLI flags via `snake_case → kebab-case` convention. New models need only: (1) matching CLI flags in `CreateArgs` + `flag_map()`, and (2) type parser support in `parse_field_value()` if using a new field type. No match arm in `create.rs` is needed.
180190
- **CLI flag names must match schema field names.** The canonical name for a CLI flag is the schema field name in kebab-case (e.g., schema field `universe_size``--universe-size`, field `subsets``--subsets`). Old aliases (e.g., `--universe`, `--sets`) may exist as clap `alias` for backward compatibility at the clap level, but `flag_map()`, help text, error messages, and documentation must use the schema-derived name. Do not add new backward-compat aliases; if a field is renamed in the schema, update the CLI flag name to match.
181-
- Aggregate-only models are first-class in `declare_variants!`; aggregate-only reduction edges still need manual `ReductionEntry` wiring because `#[reduction]` only registers witness/config reductions today
191+
- **Decision variants** of optimization problems use `Decision<P>` wrapper. Add via: (1) `decision_problem_meta!` for the inner type, (2) inherent methods on `Decision<Inner>`, (3) `register_decision_variant!` with `dims`, `fields`, `size_getters`. Schema-driven CLI creation auto-restructures flat JSON into `{inner: {...}, bound}`.
192+
- Aggregate-only models are first-class in `declare_variants!`; aggregate-only and Turing reduction edges still need manual `ReductionEntry` wiring because `#[reduction]` only registers witness/config reductions today
182193
- Exact registry dispatch lives in `src/registry/`; alias resolution and partial/default variant resolution live in `problemreductions-cli/src/problem_name.rs`
183194
- `pred create` schema-driven dispatch lives in `problemreductions-cli/src/commands/create.rs` (`create_schema_driven()`)
184195
- Canonical paper and CLI examples live in `src/example_db/model_builders.rs` and `src/example_db/rule_builders.rs`
@@ -195,8 +206,8 @@ Reduction graph nodes use variant key-value pairs from `Problem::variant()`:
195206
### Paper (docs/paper/reductions.typ)
196207
- `problem-def(name)[def][body]` — defines a problem with auto-generated schema, reductions list, and label `<def:ProblemName>`. Title comes from `display-name` dict.
197208
- `reduction-rule(source, target, example: bool, ...)[rule][proof]` — generates a theorem with label `<thm:Source-to-Target>` and registers in `covered-rules` state. Overhead auto-derived from JSON edge data.
198-
- Every directed reduction needs its own `reduction-rule` entry
199-
- Completeness warnings auto-check that all JSON graph nodes/edges are covered in the paper
209+
- Every directed reduction needs its own `reduction-rule` entry (except trivial Decision↔Optimization pairs which are auto-filtered)
210+
- Completeness warnings auto-check that all JSON graph nodes/edges are covered in the paper; `Decision<P> ↔ P` edges are excluded since they are trivial solve-and-compare reductions
200211
- `display-name` dict maps `ProblemName` to display text
201212

202213
## Testing Requirements

.claude/skills/add-model/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ Structural and quality review is handled by the `review-pipeline` stage, not her
311311
| Wrong aggregate wrapper | Use `Max` / `Min` / `Extremum` for objective problems, `Or` for existential witness problems, and `Sum` / `And` (or a custom aggregate) for value-only folds |
312312
| Wrong `declare_variants!` syntax | Entries no longer use `opt` / `sat`; one entry per problem may be marked `default` |
313313
| Forgetting CLI alias | Must add lowercase entry in `problem_name.rs` `resolve_alias()` |
314+
| Adding a hand-written decision model | Use `Decision<P>` wrapper instead — see `decision_problem_meta!` + `register_decision_variant!` in `src/models/graph/minimum_vertex_cover.rs` for the pattern |
314315
| Inventing short aliases | Only use well-established literature abbreviations (MIS, SAT, TSP); do NOT invent new ones |
315316
| Forgetting CLI flags | Schema-driven create needs matching CLI flags in `CreateArgs` for each `ProblemSchemaEntry` field (snake_case → kebab-case). Also add to `flag_map()`. |
316317
| Missing type parser | If the problem uses a new field type, add a handler in `parse_field_value()` in `create.rs` |

docs/paper/reductions.typ

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,18 @@
6262
}
6363
}
6464

65-
#let graph-num-vertices(instance) = instance.graph.num_vertices
66-
#let graph-num-edges(instance) = instance.graph.edges.len()
65+
#let graph-instance(instance) = {
66+
if "graph" in instance {
67+
instance
68+
} else if "inner" in instance and "graph" in instance.inner {
69+
instance.inner
70+
} else {
71+
instance
72+
}
73+
}
74+
75+
#let graph-num-vertices(instance) = graph-instance(instance).graph.num_vertices
76+
#let graph-num-edges(instance) = graph-instance(instance).graph.edges.len()
6777
#let spin-num-spins(instance) = instance.fields.len()
6878
#let sat-num-clauses(instance) = instance.clauses.len()
6979
#let subsetsum-num-elements(instance) = instance.sizes.len()
@@ -234,7 +244,7 @@
234244
"MinimumDisjunctiveNormalForm": [Minimum Disjunctive Normal Form],
235245
"MinimumGraphBandwidth": [Minimum Graph Bandwidth],
236246
"MinimumMetricDimension": [Minimum Metric Dimension],
237-
"VertexCover": [Vertex Cover],
247+
"DecisionMinimumVertexCover": [Decision Minimum Vertex Cover],
238248
"MinimumCodeGenerationUnlimitedRegisters": [Minimum Code Generation (Unlimited Registers)],
239249
"RegisterSufficiency": [Register Sufficiency],
240250
"ResourceConstrainedScheduling": [Resource Constrained Scheduling],
@@ -649,22 +659,23 @@ In all graph problems below, $G = (V, E)$ denotes an undirected graph with $|V|
649659
}
650660

651661
#{
652-
let x = load-model-example("VertexCover")
662+
let x = load-model-example("DecisionMinimumVertexCover")
663+
let inner = x.instance.inner
653664
let nv = graph-num-vertices(x.instance)
654665
let ne = graph-num-edges(x.instance)
655-
let k = x.instance.k
666+
let k = x.instance.bound
656667
let sol = x.optimal_config
657668
let cover = sol.enumerate().filter(((i, v)) => v == 1).map(((i, _)) => i)
658669
[
659-
#problem-def("VertexCover")[
660-
Given an undirected graph $G = (V, E)$ and a positive integer $k <= |V|$, determine whether there exists a vertex cover of size at most $k$: a subset $V' subset.eq V$ with $|V'| <= k$ such that for each edge ${u, v} in E$, at least one of $u, v$ belongs to $V'$.
670+
#problem-def("DecisionMinimumVertexCover")[
671+
Given an undirected graph $G = (V, E)$ with vertex weights $w: V -> RR_(gt.eq 0)$ and an integer bound $k$, determine whether there exists a vertex cover $S subset.eq V$ with $sum_(v in S) w(v) <= k$ such that every edge has at least one endpoint in $S$.
661672
][
662-
Vertex Cover is one of Karp's 21 NP-complete problems @karp1972 and the decision version of Minimum Vertex Cover @garey1979. The best known exact algorithm runs in $O^*(1.1996^n)$ time (Chen, Kanj, and Xia, 2010).
673+
Decision Minimum Vertex Cover is the decision version of Minimum Vertex Cover and one of Karp's 21 NP-complete problems @karp1972 @garey1979. It asks whether the optimization objective can be achieved within a prescribed budget rather than minimizing the cover weight directly.
663674

664-
*Example.* Consider a graph on $n = #nv$ vertices and $|E| = #ne$ edges with threshold $k = #k$. The cover $V' = {#cover.map(i => $v_#i$).join(", ")}$ with $|V'| = #cover.len() <= k$ is a valid vertex cover.
675+
*Example.* Consider a graph on $n = #nv$ vertices and $|E| = #ne$ edges with threshold $k = #k$. The cover $S = {#cover.map(i => $v_#i$).join(", ")}$ has total weight $2 <= #k$ and therefore certifies a yes-instance.
665676

666677
#pred-commands(
667-
"pred create --example VertexCover -o vc.json",
678+
"pred create --example DecisionMinimumVertexCover -o vc.json",
668679
"pred solve vc.json",
669680
"pred evaluate vc.json --config " + sol.map(str).join(","),
670681
)
@@ -13086,8 +13097,13 @@ See #link("https://github.com/CodingThrust/problem-reductions/blob/main/examples
1308613097
}
1308713098
unique
1308813099
}
13100+
// Skip trivial Decision<P> ↔ P edges (solve-and-compare, no interesting proof)
13101+
let is-decision-opt-pair(src, tgt) = {
13102+
src == "Decision" + tgt or tgt == "Decision" + src
13103+
}
1308913104
let missing = json-edges.filter(e => {
13090-
covered.find(c => c.at(0) == e.at(0) and c.at(1) == e.at(1)) == none
13105+
if is-decision-opt-pair(e.at(0), e.at(1)) { false }
13106+
else { covered.find(c => c.at(0) == e.at(0) and c.at(1) == e.at(1)) == none }
1309113107
})
1309213108
if missing.len() > 0 {
1309313109
block(width: 100%, inset: (x: 1em, y: 0.5em), fill: rgb("#fff3cd"), stroke: (left: 3pt + rgb("#ffc107")))[

problemreductions-cli/src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ Flags by problem type:
230230
SpinGlass --graph, --couplings, --fields
231231
KColoring --graph, --k
232232
KClique --graph, --k
233-
VertexCover (VC) --graph, --k
233+
DecisionMinimumVertexCover --graph, --weights, --bound
234234
MinimumMultiwayCut --graph, --terminals, --edge-weights
235235
MonochromaticTriangle --graph
236236
PartitionIntoTriangles --graph

problemreductions-cli/src/commands/create.rs

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ use problemreductions::models::graph::{
1717
GeneralizedHex, HamiltonianCircuit, HamiltonianPath, HamiltonianPathBetweenTwoVertices,
1818
LengthBoundedDisjointPaths, LongestCircuit, MinimumCutIntoBoundedSets,
1919
MinimumDummyActivitiesPert, MinimumMaximalMatching, RootedTreeArrangement, SteinerTree,
20-
SteinerTreeInGraphs, VertexCover,
20+
SteinerTreeInGraphs,
2121
};
2222
use problemreductions::models::misc::{
2323
CbqRelation, FrequencyTable, KnownValue, QueryArg, SchedulingWithIndividualDeadlines,
2424
ThreePartition,
2525
};
26+
use problemreductions::models::Decision;
2627
use problemreductions::prelude::*;
2728
use problemreductions::registry::collect_schemas;
2829
use problemreductions::topology::{
@@ -672,6 +673,19 @@ fn ser_vertex_weight_problem_with<G: Graph + Serialize>(
672673
}
673674
}
674675

676+
fn ser_decision_minimum_vertex_cover_with<
677+
G: Graph + Serialize + problemreductions::variant::VariantParam,
678+
>(
679+
graph: G,
680+
weights: Vec<i32>,
681+
bound: i32,
682+
) -> Result<serde_json::Value> {
683+
ser(Decision::new(
684+
MinimumVertexCover::new(graph, weights),
685+
bound,
686+
))
687+
}
688+
675689
fn ser<T: Serialize>(problem: T) -> Result<serde_json::Value> {
676690
util::ser(problem)
677691
}
@@ -1787,6 +1801,63 @@ fn create_random(
17871801
let graph_type = resolved_graph_type(resolved_variant);
17881802

17891803
let (data, variant) = match canonical {
1804+
"DecisionMinimumVertexCover" => {
1805+
let raw_bound = args.bound.ok_or_else(|| {
1806+
anyhow::anyhow!(
1807+
"DecisionMinimumVertexCover requires --bound\n\n\
1808+
Usage: pred create DecisionMinimumVertexCover --random --num-vertices 5 [--edge-prob 0.5] [--seed 42] --bound 3"
1809+
)
1810+
})?;
1811+
anyhow::ensure!(
1812+
raw_bound >= 0,
1813+
"DecisionMinimumVertexCover: --bound must be non-negative"
1814+
);
1815+
let bound = i32::try_from(raw_bound).map_err(|_| {
1816+
anyhow::anyhow!(
1817+
"DecisionMinimumVertexCover: --bound must fit in a 32-bit signed integer, got {raw_bound}"
1818+
)
1819+
})?;
1820+
let weights = vec![1i32; num_vertices];
1821+
match graph_type {
1822+
"KingsSubgraph" => {
1823+
let positions = util::create_random_int_positions(num_vertices, args.seed);
1824+
let graph = KingsSubgraph::new(positions);
1825+
(
1826+
ser_decision_minimum_vertex_cover_with(graph, weights, bound)?,
1827+
resolved_variant.clone(),
1828+
)
1829+
}
1830+
"TriangularSubgraph" => {
1831+
let positions = util::create_random_int_positions(num_vertices, args.seed);
1832+
let graph = TriangularSubgraph::new(positions);
1833+
(
1834+
ser_decision_minimum_vertex_cover_with(graph, weights, bound)?,
1835+
resolved_variant.clone(),
1836+
)
1837+
}
1838+
"UnitDiskGraph" => {
1839+
let positions = util::create_random_float_positions(num_vertices, args.seed);
1840+
let radius = args.radius.unwrap_or(1.5);
1841+
let graph = UnitDiskGraph::new(positions, radius);
1842+
(
1843+
ser_decision_minimum_vertex_cover_with(graph, weights, bound)?,
1844+
resolved_variant.clone(),
1845+
)
1846+
}
1847+
_ => {
1848+
let edge_prob = args.edge_prob.unwrap_or(0.5);
1849+
if !(0.0..=1.0).contains(&edge_prob) {
1850+
bail!("--edge-prob must be between 0.0 and 1.0");
1851+
}
1852+
let graph = util::create_random_graph(num_vertices, edge_prob, args.seed);
1853+
(
1854+
ser_decision_minimum_vertex_cover_with(graph, weights, bound)?,
1855+
resolved_variant.clone(),
1856+
)
1857+
}
1858+
}
1859+
}
1860+
17901861
// Graph problems with vertex weights
17911862
"MaximumIndependentSet"
17921863
| "MinimumVertexCover"
@@ -1848,29 +1919,6 @@ fn create_random(
18481919
)
18491920
}
18501921

1851-
"VertexCover" => {
1852-
let edge_prob = args.edge_prob.unwrap_or(0.5);
1853-
if !(0.0..=1.0).contains(&edge_prob) {
1854-
bail!("--edge-prob must be between 0.0 and 1.0");
1855-
}
1856-
let graph = util::create_random_graph(num_vertices, edge_prob, args.seed);
1857-
let usage =
1858-
"Usage: pred create VertexCover --random --num-vertices 5 [--edge-prob 0.5] [--seed 42] --k 3";
1859-
let k = args
1860-
.k
1861-
.ok_or_else(|| anyhow::anyhow!("VertexCover requires --k\n\n{usage}"))?;
1862-
if k == 0 {
1863-
bail!("VertexCover: --k must be positive");
1864-
}
1865-
if k > graph.num_vertices() {
1866-
bail!("VertexCover: k must be <= graph num_vertices");
1867-
}
1868-
(
1869-
ser(VertexCover::new(graph, k))?,
1870-
variant_map(&[("graph", "SimpleGraph")]),
1871-
)
1872-
}
1873-
18741922
// MinimumCutIntoBoundedSets (graph + edge weights + s/t/B/K)
18751923
"MinimumCutIntoBoundedSets" => {
18761924
let edge_prob = args.edge_prob.unwrap_or(0.5);
@@ -2232,7 +2280,7 @@ fn create_random(
22322280
_ => bail!(
22332281
"Random generation is not supported for {canonical}. \
22342282
Supported: graph-based problems (MIS, MVC, MaxCut, MaxClique, \
2235-
MaximumMatching, MinimumDominatingSet, SpinGlass, KColoring, KClique, VertexCover, TravelingSalesman, \
2283+
MaximumMatching, MinimumDominatingSet, SpinGlass, KColoring, KClique, DecisionMinimumVertexCover, TravelingSalesman, \
22362284
BottleneckTravelingSalesman, SteinerTreeInGraphs, HamiltonianCircuit, MaximumLeafSpanningTree, SteinerTree, \
22372285
OptimalLinearArrangement, RootedTreeArrangement, HamiltonianPath, LongestCircuit, GeneralizedHex)"
22382286
),

0 commit comments

Comments
 (0)