problemreductions/models/graph/
minimum_vertex_cover.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::{OptimizationProblem, Problem};
9use crate::types::{Direction, SolutionSize, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinimumVertexCover",
16 display_name: "Minimum Vertex Cover",
17 aliases: &["MVC"],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i32", &["i32"]),
21 ],
22 module_path: module_path!(),
23 description: "Find minimum weight vertex cover in a graph",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
26 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MinimumVertexCover<G, W> {
58 graph: G,
60 weights: Vec<W>,
62}
63
64impl<G: Graph, W: Clone + Default> MinimumVertexCover<G, W> {
65 pub fn new(graph: G, weights: Vec<W>) -> Self {
67 assert_eq!(
68 weights.len(),
69 graph.num_vertices(),
70 "weights length must match graph num_vertices"
71 );
72 Self { graph, weights }
73 }
74
75 pub fn graph(&self) -> &G {
77 &self.graph
78 }
79
80 pub fn weights(&self) -> &[W] {
82 &self.weights
83 }
84
85 pub fn is_weighted(&self) -> bool
87 where
88 W: WeightElement,
89 {
90 !W::IS_UNIT
91 }
92
93 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
95 is_vertex_cover_config(&self.graph, config)
96 }
97}
98
99impl<G: Graph, W: WeightElement> MinimumVertexCover<G, W> {
100 pub fn num_vertices(&self) -> usize {
102 self.graph().num_vertices()
103 }
104
105 pub fn num_edges(&self) -> usize {
107 self.graph().num_edges()
108 }
109}
110
111impl<G, W> Problem for MinimumVertexCover<G, W>
112where
113 G: Graph + crate::variant::VariantParam,
114 W: WeightElement + crate::variant::VariantParam,
115{
116 const NAME: &'static str = "MinimumVertexCover";
117 type Metric = SolutionSize<W::Sum>;
118
119 fn variant() -> Vec<(&'static str, &'static str)> {
120 crate::variant_params![G, W]
121 }
122
123 fn dims(&self) -> Vec<usize> {
124 vec![2; self.graph.num_vertices()]
125 }
126
127 fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
128 if !is_vertex_cover_config(&self.graph, config) {
129 return SolutionSize::Invalid;
130 }
131 let mut total = W::Sum::zero();
132 for (i, &selected) in config.iter().enumerate() {
133 if selected == 1 {
134 total += self.weights[i].to_sum();
135 }
136 }
137 SolutionSize::Valid(total)
138 }
139}
140
141impl<G, W> OptimizationProblem for MinimumVertexCover<G, W>
142where
143 G: Graph + crate::variant::VariantParam,
144 W: WeightElement + crate::variant::VariantParam,
145{
146 type Value = W::Sum;
147
148 fn direction(&self) -> Direction {
149 Direction::Minimize
150 }
151}
152
153fn is_vertex_cover_config<G: Graph>(graph: &G, config: &[usize]) -> bool {
155 for (u, v) in graph.edges() {
156 let u_covered = config.get(u).copied().unwrap_or(0) == 1;
157 let v_covered = config.get(v).copied().unwrap_or(0) == 1;
158 if !u_covered && !v_covered {
159 return false;
160 }
161 }
162 true
163}
164
165crate::declare_variants! {
166 default opt MinimumVertexCover<SimpleGraph, i32> => "1.1996^num_vertices",
167}
168
169#[cfg(feature = "example-db")]
170pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
171 vec![crate::example_db::specs::ModelExampleSpec {
172 id: "minimum_vertex_cover_simplegraph_i32",
173 instance: Box::new(MinimumVertexCover::new(
174 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
175 vec![1i32; 5],
176 )),
177 optimal_config: vec![1, 0, 0, 1, 1],
178 optimal_value: serde_json::json!({"Valid": 3}),
179 }]
180}
181
182#[cfg(test)]
191pub(crate) fn is_vertex_cover<G: Graph>(graph: &G, selected: &[bool]) -> bool {
192 assert_eq!(
193 selected.len(),
194 graph.num_vertices(),
195 "selected length must match num_vertices"
196 );
197 for (u, v) in graph.edges() {
198 if !selected[u] && !selected[v] {
199 return false;
200 }
201 }
202 true
203}
204
205#[cfg(test)]
206#[path = "../../unit_tests/models/graph/minimum_vertex_cover.rs"]
207mod tests;