problemreductions/models/graph/
maximal_is.rs

1//! Maximal Independent Set problem implementation.
2//!
3//! The Maximal Independent Set problem asks for an independent set that
4//! cannot be extended by adding any other vertex.
5
6use crate::registry::{FieldInfo, ProblemSchemaEntry};
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: "MaximalIS",
16        module_path: module_path!(),
17        description: "Find maximum weight maximal independent set",
18        fields: &[
19            FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
20            FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
21        ],
22    }
23}
24
25/// The Maximal Independent Set problem.
26///
27/// Given a graph G = (V, E), find an independent set S that is maximal,
28/// meaning no vertex can be added to S while keeping it independent.
29///
30/// This is different from Maximum Independent Set - maximal means locally
31/// optimal (cannot extend), while maximum means globally optimal (largest).
32///
33/// # Example
34///
35/// ```
36/// use problemreductions::models::graph::MaximalIS;
37/// use problemreductions::topology::SimpleGraph;
38/// use problemreductions::{Problem, Solver, BruteForce};
39///
40/// // Path graph 0-1-2
41/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
42/// let problem = MaximalIS::new(graph, vec![1; 3]);
43///
44/// let solver = BruteForce::new();
45/// let solutions = solver.find_all_best(&problem);
46///
47/// // Maximal independent sets: {0, 2} or {1}
48/// for sol in &solutions {
49///     assert!(problem.evaluate(sol).is_valid());
50/// }
51/// ```
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct MaximalIS<G, W> {
54    /// The underlying graph.
55    graph: G,
56    /// Weights for each vertex.
57    weights: Vec<W>,
58}
59
60impl<G: Graph, W: Clone + Default> MaximalIS<G, W> {
61    /// Create a Maximal Independent Set problem from a graph with given weights.
62    pub fn new(graph: G, weights: Vec<W>) -> Self {
63        assert_eq!(
64            weights.len(),
65            graph.num_vertices(),
66            "weights length must match graph num_vertices"
67        );
68        Self { graph, weights }
69    }
70
71    /// Get a reference to the underlying graph.
72    pub fn graph(&self) -> &G {
73        &self.graph
74    }
75
76    /// Get a reference to the weights.
77    pub fn weights(&self) -> &[W] {
78        &self.weights
79    }
80
81    /// Check if the problem uses a non-unit weight type.
82    pub fn is_weighted(&self) -> bool
83    where
84        W: WeightElement,
85    {
86        !W::IS_UNIT
87    }
88
89    /// Check if a configuration is a valid maximal independent set.
90    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
91        self.is_maximal(config)
92    }
93
94    /// Check if a configuration is an independent set.
95    fn is_independent(&self, config: &[usize]) -> bool {
96        for (u, v) in self.graph.edges() {
97            if config.get(u).copied().unwrap_or(0) == 1 && config.get(v).copied().unwrap_or(0) == 1
98            {
99                return false;
100            }
101        }
102        true
103    }
104
105    /// Check if an independent set is maximal (cannot be extended).
106    fn is_maximal(&self, config: &[usize]) -> bool {
107        if !self.is_independent(config) {
108            return false;
109        }
110
111        let n = self.graph.num_vertices();
112        for v in 0..n {
113            if config.get(v).copied().unwrap_or(0) == 1 {
114                continue; // Already in set
115            }
116
117            // Check if v can be added
118            let neighbors = self.graph.neighbors(v);
119            let can_add = neighbors
120                .iter()
121                .all(|&u| config.get(u).copied().unwrap_or(0) == 0);
122
123            if can_add {
124                return false; // Set is not maximal
125            }
126        }
127
128        true
129    }
130}
131
132impl<G: Graph, W: WeightElement> MaximalIS<G, W> {
133    /// Get the number of vertices in the underlying graph.
134    pub fn num_vertices(&self) -> usize {
135        self.graph().num_vertices()
136    }
137
138    /// Get the number of edges in the underlying graph.
139    pub fn num_edges(&self) -> usize {
140        self.graph().num_edges()
141    }
142}
143
144impl<G, W> Problem for MaximalIS<G, W>
145where
146    G: Graph + crate::variant::VariantParam,
147    W: WeightElement + crate::variant::VariantParam,
148{
149    const NAME: &'static str = "MaximalIS";
150    type Metric = SolutionSize<W::Sum>;
151
152    fn variant() -> Vec<(&'static str, &'static str)> {
153        crate::variant_params![G, W]
154    }
155
156    fn dims(&self) -> Vec<usize> {
157        vec![2; self.graph.num_vertices()]
158    }
159
160    fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
161        if !self.is_maximal(config) {
162            return SolutionSize::Invalid;
163        }
164        let mut total = W::Sum::zero();
165        for (i, &selected) in config.iter().enumerate() {
166            if selected == 1 {
167                total += self.weights[i].to_sum();
168            }
169        }
170        SolutionSize::Valid(total)
171    }
172}
173
174impl<G, W> OptimizationProblem for MaximalIS<G, W>
175where
176    G: Graph + crate::variant::VariantParam,
177    W: WeightElement + crate::variant::VariantParam,
178{
179    type Value = W::Sum;
180
181    fn direction(&self) -> Direction {
182        Direction::Maximize
183    }
184}
185
186/// Check if a set is a maximal independent set.
187///
188/// # Panics
189/// Panics if `selected.len() != graph.num_vertices()`.
190#[cfg(test)]
191pub(crate) fn is_maximal_independent_set<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
198    // Check independence
199    for (u, v) in graph.edges() {
200        if selected[u] && selected[v] {
201            return false;
202        }
203    }
204
205    // Check maximality: no unselected vertex can be added
206    for v in 0..graph.num_vertices() {
207        if selected[v] {
208            continue;
209        }
210        if graph.neighbors(v).iter().all(|&u| !selected[u]) {
211            return false;
212        }
213    }
214
215    true
216}
217
218crate::declare_variants! {
219    MaximalIS<SimpleGraph, i32> => "3^(num_vertices / 3)",
220}
221
222#[cfg(test)]
223#[path = "../../unit_tests/models/graph/maximal_is.rs"]
224mod tests;