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