Skip to main content

problemreductions/models/graph/
minimum_vertex_cover.rs

1//! Vertex Covering problem implementation.
2//!
3//! The Vertex Cover problem asks for a minimum weight subset of vertices
4//! such that every edge has at least one endpoint in the subset.
5
6use 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/// The Vertex Covering problem.
32///
33/// Given a graph G = (V, E) and weights w_v for each vertex,
34/// find a subset S ⊆ V such that:
35/// - Every edge has at least one endpoint in S (covering constraint)
36/// - The total weight Σ_{v ∈ S} w_v is minimized
37///
38/// # Example
39///
40/// ```
41/// use problemreductions::models::graph::MinimumVertexCover;
42/// use problemreductions::topology::SimpleGraph;
43/// use problemreductions::{Problem, Solver, BruteForce};
44///
45/// // Create a path graph 0-1-2
46/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
47/// let problem = MinimumVertexCover::new(graph, vec![1; 3]);
48///
49/// // Solve with brute force
50/// let solver = BruteForce::new();
51/// let solutions = solver.find_all_best(&problem);
52///
53/// // Minimum vertex cover is just vertex 1
54/// assert!(solutions.contains(&vec![0, 1, 0]));
55/// ```
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MinimumVertexCover<G, W> {
58    /// The underlying graph.
59    graph: G,
60    /// Weights for each vertex.
61    weights: Vec<W>,
62}
63
64impl<G: Graph, W: Clone + Default> MinimumVertexCover<G, W> {
65    /// Create a Vertex Covering problem from a graph with given weights.
66    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    /// Get a reference to the underlying graph.
76    pub fn graph(&self) -> &G {
77        &self.graph
78    }
79
80    /// Get a reference to the weights.
81    pub fn weights(&self) -> &[W] {
82        &self.weights
83    }
84
85    /// Check if the problem uses a non-unit weight type.
86    pub fn is_weighted(&self) -> bool
87    where
88        W: WeightElement,
89    {
90        !W::IS_UNIT
91    }
92
93    /// Check if a configuration is a valid vertex cover.
94    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    /// Get the number of vertices in the underlying graph.
101    pub fn num_vertices(&self) -> usize {
102        self.graph().num_vertices()
103    }
104
105    /// Get the number of edges in the underlying graph.
106    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
153/// Check if a configuration forms a valid vertex cover.
154fn 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/// Check if a set of vertices forms a vertex cover.
183///
184/// # Arguments
185/// * `graph` - The graph
186/// * `selected` - Boolean slice indicating which vertices are selected
187///
188/// # Panics
189/// Panics if `selected.len() != graph.num_vertices()`.
190#[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;