1use crate::models::decision::Decision;
7use crate::registry::{CreateSpec, FieldInfo, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::types::{Min, One, WeightElement};
11use num_traits::Zero;
12use serde::{Deserialize, Serialize};
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "MinimumVertexCover",
17 display_name: "Minimum Vertex Cover",
18 aliases: &["MVC"],
19 dimensions: &[
20 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
21 VariantDimension::new("weight", "i64", &["i64", "One"]),
22 ],
23 category: crate::registry::ProblemCategory::Graph,
24 module_path: module_path!(),
25 description: "Find minimum weight vertex cover in a graph",
26 fields: MinimumVertexCoverCreateSpec::<i64>::FIELDS,
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
56#[serde(try_from = "MinimumVertexCoverData<G, W>")]
57#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: Clone + Default + Deserialize<'de>"))]
58pub struct MinimumVertexCover<G, W> {
59 graph: G,
61 weights: Vec<W>,
63}
64
65#[derive(Deserialize)]
66struct MinimumVertexCoverData<G, W> {
67 graph: G,
68 weights: Vec<W>,
69}
70
71impl<G, W> TryFrom<MinimumVertexCoverData<G, W>> for MinimumVertexCover<G, W>
72where
73 G: Graph,
74 W: Clone + Default,
75{
76 type Error = crate::registry::ConstructionError;
77
78 fn try_from(data: MinimumVertexCoverData<G, W>) -> Result<Self, Self::Error> {
79 Self::try_new(data.graph, data.weights)
80 }
81}
82
83#[derive(Debug, Deserialize, crate::CreateSpec)]
84struct MinimumVertexCoverCreateSpec<W> {
85 graph: SimpleGraph,
87 weights: Option<Vec<W>>,
89}
90
91impl<W: WeightElement> TryFrom<MinimumVertexCoverCreateSpec<W>>
92 for MinimumVertexCover<SimpleGraph, W>
93{
94 type Error = crate::registry::ConstructionError;
95 fn try_from(spec: MinimumVertexCoverCreateSpec<W>) -> Result<Self, Self::Error> {
96 let weights = spec
97 .weights
98 .unwrap_or_else(|| vec![W::unit(); spec.graph.num_vertices()]);
99 Self::try_new(spec.graph, weights)
100 }
101}
102
103impl<G: Graph, W: Clone + Default> MinimumVertexCover<G, W> {
104 pub fn new(graph: G, weights: Vec<W>) -> Self {
106 Self::try_new(graph, weights).unwrap_or_else(|error| panic!("{error}"))
107 }
108
109 fn try_new(graph: G, weights: Vec<W>) -> Result<Self, crate::registry::ConstructionError> {
110 if weights.len() != graph.num_vertices() {
111 return Err(crate::registry::ConstructionError::length_mismatch(
112 "weights",
113 weights.len(),
114 graph.num_vertices(),
115 ));
116 }
117 Ok(Self { graph, weights })
118 }
119
120 pub fn graph(&self) -> &G {
122 &self.graph
123 }
124
125 pub fn weights(&self) -> &[W] {
127 &self.weights
128 }
129
130 pub fn is_weighted(&self) -> bool
132 where
133 W: WeightElement,
134 {
135 !W::IS_UNIT
136 }
137
138 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
140 is_vertex_cover_config(&self.graph, config)
141 }
142}
143
144impl<G: Graph, W: WeightElement> MinimumVertexCover<G, W> {
145 pub fn num_vertices(&self) -> usize {
147 self.graph().num_vertices()
148 }
149
150 pub fn num_edges(&self) -> usize {
152 self.graph().num_edges()
153 }
154}
155
156impl<G, W> Problem for MinimumVertexCover<G, W>
157where
158 G: Graph + crate::variant::VariantParam,
159 W: WeightElement + crate::variant::VariantParam,
160{
161 const NAME: &'static str = "MinimumVertexCover";
162 type Solution = Vec<bool>;
163 type Value = Min<W::Sum>;
164
165 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
166
167 fn variant() -> Vec<(&'static str, &'static str)> {
168 crate::variant_params![G, W]
169 }
170
171 fn evaluate(
172 &self,
173 config: &Self::Solution,
174 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
175 Ok({
176 if config.len() != self.graph.num_vertices() {
177 return Err(crate::traits::EvaluationError::InvalidConfiguration(
178 "vertex-selection length does not match the graph".into(),
179 ));
180 }
181 if !is_vertex_cover_config(&self.graph, config) {
182 return Ok(Min(None));
183 }
184 let mut total = W::Sum::zero();
185 for (i, &selected) in config.iter().enumerate() {
186 if selected {
187 total = W::checked_add_to_sum(
188 total,
189 self.weights[i].to_sum(),
190 "summing selected vertex-cover weights",
191 )?;
192 }
193 }
194 Min(Some(total))
195 })
196 }
197}
198
199impl<G, W> crate::solvers::BruteForceProblem for MinimumVertexCover<G, W>
200where
201 G: Graph + crate::variant::VariantParam,
202 W: WeightElement + crate::variant::VariantParam,
203{
204 fn dimensions(&self) -> Vec<usize> {
205 vec![2; self.graph.num_vertices()]
206 }
207}
208
209pub(crate) fn is_vertex_cover_config<G: Graph>(graph: &G, config: &[bool]) -> bool {
211 for (u, v) in graph.edges() {
212 let u_covered = config.get(u).copied().unwrap_or(false);
213 let v_covered = config.get(v).copied().unwrap_or(false);
214 if !u_covered && !v_covered {
215 return false;
216 }
217 }
218 true
219}
220
221crate::impl_random_generate!(MinimumVertexCover<SimpleGraph, i64>, crate::random::SimpleGraphRandomSpec, |spec| {
222 Ok(MinimumVertexCover::new(spec.graph()?, vec![1; spec.num_vertices]))
223});
224crate::impl_random_generate!(MinimumVertexCover<SimpleGraph, One>, crate::random::SimpleGraphRandomSpec, |spec| {
225 Ok(MinimumVertexCover::new(spec.graph()?, vec![One; spec.num_vertices]))
226});
227
228#[derive(Debug, Deserialize, crate::CreateSpec)]
229struct MinimumVertexCoverOneCreateSpec {
230 graph: SimpleGraph,
232}
233
234impl TryFrom<MinimumVertexCoverOneCreateSpec> for MinimumVertexCover<SimpleGraph, One> {
235 type Error = crate::registry::ConstructionError;
236 fn try_from(spec: MinimumVertexCoverOneCreateSpec) -> Result<Self, Self::Error> {
237 let weights = vec![One; spec.graph.num_vertices()];
238 Self::try_new(spec.graph, weights)
239 }
240}
241
242crate::declare_variants! {
243 default MinimumVertexCover<SimpleGraph, i64> => "1.1996^num_vertices" create MinimumVertexCoverCreateSpec<i64> random,
244 MinimumVertexCover<SimpleGraph, One> => "1.1996^num_vertices" create MinimumVertexCoverOneCreateSpec random,
245}
246
247crate::register_brute_force! {
248 MinimumVertexCover<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
249 MinimumVertexCover<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
250}
251
252impl<G, W> crate::models::decision::DecisionProblemMeta for MinimumVertexCover<G, W>
253where
254 G: Graph + crate::variant::VariantParam,
255 W: WeightElement + crate::variant::VariantParam,
256 W::Sum: std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned,
257{
258 const DECISION_NAME: &'static str = "DecisionMinimumVertexCover";
259}
260
261impl Decision<MinimumVertexCover<SimpleGraph, i64>> {
262 pub fn num_vertices(&self) -> usize {
264 self.inner().num_vertices()
265 }
266
267 pub fn num_edges(&self) -> usize {
269 self.inner().num_edges()
270 }
271
272 pub fn k(&self) -> usize {
274 (*self.bound()).try_into().unwrap_or(0)
275 }
276}
277
278#[derive(Debug, Deserialize, crate::CreateSpec)]
279struct DecisionMinimumVertexCoverRandomSpec {
280 num_vertices: usize,
282 edge_prob: Option<f64>,
284 seed: Option<i64>,
286 bound: i64,
288}
289
290crate::impl_random_generate!(
291 Decision<MinimumVertexCover<SimpleGraph, i64>>,
292 DecisionMinimumVertexCoverRandomSpec,
293 |spec| {
294 if spec.bound < 0 {
295 return Err("bound must be nonnegative".to_string().into());
296 }
297 let graph = crate::random::SimpleGraphRandomSpec {
298 num_vertices: spec.num_vertices,
299 edge_prob: spec.edge_prob,
300 seed: spec.seed,
301 }
302 .graph()?;
303 Ok(Decision::new(
304 MinimumVertexCover::new(graph, vec![1; spec.num_vertices]),
305 spec.bound,
306 ))
307 }
308);
309
310crate::register_decision_variant!(
311 MinimumVertexCover<SimpleGraph, i64>,
312 "DecisionMinimumVertexCover",
313 "1.1996^num_vertices",
314 &["DMVC", "VC", "VertexCover"],
315 "Decision version: does a vertex cover of cost <= bound exist?",
316 category: crate::registry::ProblemCategory::Graph,
317 dims: [
318 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
319 VariantDimension::new("weight", "i64", &["i64", "One"]),
320 ],
321 fields: [
322 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
323 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
324 FieldInfo { name: "bound", type_name: "W::Sum", description: "Decision bound (maximum allowed cover cost)" },
325 ],
326 additional: [MinimumVertexCover<SimpleGraph, One> => "1.1996^num_vertices"],
327 decode: |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
328 random
329);
330
331#[cfg(feature = "example-db")]
332pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
333 vec![crate::example_db::specs::ModelExampleSpec {
334 id: "minimum_vertex_cover_simplegraph",
335 instance: Box::new(MinimumVertexCover::new(
336 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
337 vec![1i64; 5],
338 )),
339 optimal_config: serde_json::json!(vec![true, false, false, true, true]),
340 optimal_value: serde_json::json!(3),
341 }]
342}
343
344#[cfg(feature = "example-db")]
345pub(crate) fn decision_canonical_model_example_specs(
346) -> Vec<crate::example_db::specs::ModelExampleSpec> {
347 vec![crate::example_db::specs::ModelExampleSpec {
348 id: "decision_minimum_vertex_cover_simplegraph",
349 instance: Box::new(crate::models::decision::Decision::new(
350 MinimumVertexCover::new(
351 SimpleGraph::new(4, vec![(0, 1), (1, 2), (0, 2), (2, 3)]),
352 vec![1i64; 4],
353 ),
354 2,
355 )),
356 optimal_config: serde_json::json!(vec![true, false, true, false]),
357 optimal_value: serde_json::json!(true),
358 }]
359}
360
361#[cfg(feature = "example-db")]
362pub(crate) fn decision_canonical_rule_example_specs(
363) -> Vec<crate::example_db::specs::RuleExampleSpec> {
364 use crate::example_db::specs::{rule_example_with_witness, RuleExampleSpec};
365 use crate::export::SolutionPair;
366 vec![
367 RuleExampleSpec {
368 id: "decision_minimum_vertex_cover_to_minimum_vertex_cover",
369 build: || {
370 rule_example_with_witness::<_, MinimumVertexCover<SimpleGraph, i64>>(
371 Decision::new(
372 MinimumVertexCover::new(
373 SimpleGraph::new(4, vec![(0, 1), (1, 2), (0, 2), (2, 3)]),
374 vec![1i64; 4],
375 ),
376 2,
377 ),
378 SolutionPair {
379 source_config: serde_json::json!([true, false, true, false]),
380 target_config: serde_json::json!([true, false, true, false]),
381 },
382 )
383 },
384 },
385 RuleExampleSpec {
386 id: "decision_minimum_vertex_cover_one_to_minimum_vertex_cover_one",
387 build: || {
388 rule_example_with_witness::<_, MinimumVertexCover<SimpleGraph, One>>(
389 Decision::new(
390 MinimumVertexCover::new(SimpleGraph::path(3), vec![One; 3]),
391 1,
392 ),
393 SolutionPair {
394 source_config: serde_json::json!([false, true, false]),
395 target_config: serde_json::json!([false, true, false]),
396 },
397 )
398 },
399 },
400 ]
401}
402
403#[cfg(test)]
412pub(crate) fn is_vertex_cover<G: Graph>(graph: &G, selected: &[bool]) -> bool {
413 assert_eq!(
414 selected.len(),
415 graph.num_vertices(),
416 "selected length must match num_vertices"
417 );
418 for (u, v) in graph.edges() {
419 if !selected[u] && !selected[v] {
420 return false;
421 }
422 }
423 true
424}
425
426#[cfg(test)]
427#[path = "../../unit_tests/models/graph/minimum_vertex_cover.rs"]
428mod tests;