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::graph_types::SimpleGraph;
7use crate::traits::{ConstraintSatisfactionProblem, Problem};
8use crate::types::{EnergyMode, LocalConstraint, LocalSolutionSize, ProblemSize, SolutionSize};
9use petgraph::graph::{NodeIndex, UnGraph};
10use petgraph::visit::EdgeRef;
11use serde::{Deserialize, Serialize};
12
13/// The Maximal Independent Set problem.
14///
15/// Given a graph G = (V, E), find an independent set S that is maximal,
16/// meaning no vertex can be added to S while keeping it independent.
17///
18/// This is different from Maximum Independent Set - maximal means locally
19/// optimal (cannot extend), while maximum means globally optimal (largest).
20///
21/// # Example
22///
23/// ```
24/// use problemreductions::models::graph::MaximalIS;
25/// use problemreductions::{Problem, Solver, BruteForce};
26///
27/// // Path graph 0-1-2
28/// let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
29///
30/// let solver = BruteForce::new();
31/// let solutions = solver.find_best(&problem);
32///
33/// // Maximal independent sets: {0, 2} or {1}
34/// for sol in &solutions {
35///     assert!(problem.solution_size(sol).is_valid);
36/// }
37/// ```
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct MaximalIS {
40    /// The underlying graph.
41    graph: UnGraph<(), ()>,
42}
43
44impl MaximalIS {
45    /// Create a new Maximal Independent Set problem.
46    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self {
47        let mut graph = UnGraph::new_undirected();
48        for _ in 0..num_vertices {
49            graph.add_node(());
50        }
51        for (u, v) in edges {
52            graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
53        }
54        Self { graph }
55    }
56
57    /// Get the number of vertices.
58    pub fn num_vertices(&self) -> usize {
59        self.graph.node_count()
60    }
61
62    /// Get the number of edges.
63    pub fn num_edges(&self) -> usize {
64        self.graph.edge_count()
65    }
66
67    /// Get neighbors of a vertex.
68    fn neighbors(&self, v: usize) -> Vec<usize> {
69        self.graph
70            .neighbors(NodeIndex::new(v))
71            .map(|n| n.index())
72            .collect()
73    }
74
75    /// Check if a configuration is an independent set.
76    fn is_independent(&self, config: &[usize]) -> bool {
77        for edge in self.graph.edge_references() {
78            let u = edge.source().index();
79            let v = edge.target().index();
80            if config.get(u).copied().unwrap_or(0) == 1
81                && config.get(v).copied().unwrap_or(0) == 1
82            {
83                return false;
84            }
85        }
86        true
87    }
88
89    /// Check if an independent set is maximal (cannot be extended).
90    fn is_maximal(&self, config: &[usize]) -> bool {
91        if !self.is_independent(config) {
92            return false;
93        }
94
95        let n = self.graph.node_count();
96        for v in 0..n {
97            if config.get(v).copied().unwrap_or(0) == 1 {
98                continue; // Already in set
99            }
100
101            // Check if v can be added
102            let can_add = self.neighbors(v).iter().all(|&u| {
103                config.get(u).copied().unwrap_or(0) == 0
104            });
105
106            if can_add {
107                return false; // Set is not maximal
108            }
109        }
110
111        true
112    }
113}
114
115impl Problem for MaximalIS {
116    const NAME: &'static str = "MaximalIS";
117    type GraphType = SimpleGraph;
118    type Weight = i32;
119    type Size = i32;
120
121    fn num_variables(&self) -> usize {
122        self.graph.node_count()
123    }
124
125    fn num_flavors(&self) -> usize {
126        2
127    }
128
129    fn problem_size(&self) -> ProblemSize {
130        ProblemSize::new(vec![
131            ("num_vertices", self.graph.node_count()),
132            ("num_edges", self.graph.edge_count()),
133        ])
134    }
135
136    fn energy_mode(&self) -> EnergyMode {
137        // We want any maximal IS, so minimize "non-maximality"
138        // Size = number of vertices in the set (larger is better among valid)
139        EnergyMode::LargerSizeIsBetter
140    }
141
142    fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
143        let is_valid = self.is_maximal(config);
144        let size: i32 = config.iter().sum::<usize>() as i32;
145        SolutionSize::new(size, is_valid)
146    }
147}
148
149impl ConstraintSatisfactionProblem for MaximalIS {
150    fn constraints(&self) -> Vec<LocalConstraint> {
151        let mut constraints = Vec::new();
152
153        // Independent set constraints: for each edge, at most one endpoint
154        for edge in self.graph.edge_references() {
155            let u = edge.source().index();
156            let v = edge.target().index();
157            constraints.push(LocalConstraint::new(
158                2,
159                vec![u, v],
160                vec![true, true, true, false],
161            ));
162        }
163
164        // Maximality constraints: for each vertex v, either v is selected
165        // or at least one neighbor is selected
166        let n = self.graph.node_count();
167        for v in 0..n {
168            let mut vars = vec![v];
169            vars.extend(self.neighbors(v));
170
171            let num_vars = vars.len();
172            let num_configs = 2usize.pow(num_vars as u32);
173
174            // Valid if: v is selected (first bit = 1) OR
175            //           at least one neighbor is selected (not all others are 0)
176            let spec: Vec<bool> = (0..num_configs)
177                .map(|config_idx| {
178                    let v_selected = (config_idx & 1) == 1;
179                    let any_neighbor_selected = (config_idx >> 1) > 0;
180                    v_selected || any_neighbor_selected
181                })
182                .collect();
183
184            constraints.push(LocalConstraint::new(2, vars, spec));
185        }
186
187        constraints
188    }
189
190    fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>> {
191        // Maximize the size of the independent set
192        (0..self.graph.node_count())
193            .map(|i| LocalSolutionSize::new(2, vec![i], vec![0, 1]))
194            .collect()
195    }
196
197    fn weights(&self) -> Vec<Self::Size> {
198        vec![1; self.graph.node_count()]
199    }
200
201    fn set_weights(&mut self, _weights: Vec<Self::Size>) {}
202
203    fn is_weighted(&self) -> bool {
204        false
205    }
206}
207
208/// Check if a set is a maximal independent set.
209pub fn is_maximal_independent_set(
210    num_vertices: usize,
211    edges: &[(usize, usize)],
212    selected: &[bool],
213) -> bool {
214    if selected.len() != num_vertices {
215        return false;
216    }
217
218    // Build adjacency
219    let mut adj: Vec<Vec<usize>> = vec![vec![]; num_vertices];
220    for &(u, v) in edges {
221        if u < num_vertices && v < num_vertices {
222            adj[u].push(v);
223            adj[v].push(u);
224        }
225    }
226
227    // Check independence
228    for &(u, v) in edges {
229        if u < selected.len() && v < selected.len() && selected[u] && selected[v] {
230            return false;
231        }
232    }
233
234    // Check maximality
235    for v in 0..num_vertices {
236        if selected[v] {
237            continue;
238        }
239        let can_add = adj[v].iter().all(|&u| !selected[u]);
240        if can_add {
241            return false;
242        }
243    }
244
245    true
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use crate::solvers::{BruteForce, Solver};
252
253    #[test]
254    fn test_maximal_is_creation() {
255        let problem = MaximalIS::new(4, vec![(0, 1), (1, 2), (2, 3)]);
256        assert_eq!(problem.num_vertices(), 4);
257        assert_eq!(problem.num_edges(), 3);
258    }
259
260    #[test]
261    fn test_is_independent() {
262        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
263
264        assert!(problem.is_independent(&[1, 0, 1]));
265        assert!(problem.is_independent(&[0, 1, 0]));
266        assert!(!problem.is_independent(&[1, 1, 0]));
267    }
268
269    #[test]
270    fn test_is_maximal() {
271        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
272
273        // {0, 2} is maximal (cannot add 1)
274        assert!(problem.is_maximal(&[1, 0, 1]));
275
276        // {1} is maximal (cannot add 0 or 2)
277        assert!(problem.is_maximal(&[0, 1, 0]));
278
279        // {0} is not maximal (can add 2)
280        assert!(!problem.is_maximal(&[1, 0, 0]));
281
282        // {} is not maximal (can add any vertex)
283        assert!(!problem.is_maximal(&[0, 0, 0]));
284    }
285
286    #[test]
287    fn test_solution_size() {
288        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
289
290        // Maximal: {0, 2}
291        let sol = problem.solution_size(&[1, 0, 1]);
292        assert!(sol.is_valid);
293        assert_eq!(sol.size, 2);
294
295        // Maximal: {1}
296        let sol = problem.solution_size(&[0, 1, 0]);
297        assert!(sol.is_valid);
298        assert_eq!(sol.size, 1);
299
300        // Not maximal: {0}
301        let sol = problem.solution_size(&[1, 0, 0]);
302        assert!(!sol.is_valid);
303    }
304
305    #[test]
306    fn test_brute_force_path() {
307        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
308        let solver = BruteForce::new();
309
310        let solutions = solver.find_best(&problem);
311        // Largest maximal IS is {0, 2} with size 2
312        assert_eq!(solutions.len(), 1);
313        assert_eq!(solutions[0], vec![1, 0, 1]);
314    }
315
316    #[test]
317    fn test_brute_force_triangle() {
318        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2), (0, 2)]);
319        let solver = BruteForce::new();
320
321        let solutions = solver.find_best(&problem);
322        // All maximal IS have size 1 (any single vertex)
323        assert_eq!(solutions.len(), 3);
324        for sol in &solutions {
325            assert_eq!(sol.iter().sum::<usize>(), 1);
326            assert!(problem.solution_size(sol).is_valid);
327        }
328    }
329
330    #[test]
331    fn test_is_maximal_independent_set_function() {
332        let edges = vec![(0, 1), (1, 2)];
333
334        assert!(is_maximal_independent_set(3, &edges, &[true, false, true]));
335        assert!(is_maximal_independent_set(3, &edges, &[false, true, false]));
336        assert!(!is_maximal_independent_set(3, &edges, &[true, false, false])); // Can add 2
337        assert!(!is_maximal_independent_set(3, &edges, &[true, true, false])); // Not independent
338    }
339
340    #[test]
341    fn test_energy_mode() {
342        let problem = MaximalIS::new(2, vec![(0, 1)]);
343        assert!(problem.energy_mode().is_maximization());
344    }
345
346    #[test]
347    fn test_empty_graph() {
348        let problem = MaximalIS::new(3, vec![]);
349        let solver = BruteForce::new();
350
351        let solutions = solver.find_best(&problem);
352        // Only maximal IS is all vertices
353        assert_eq!(solutions.len(), 1);
354        assert_eq!(solutions[0], vec![1, 1, 1]);
355    }
356
357    #[test]
358    fn test_constraints() {
359        let problem = MaximalIS::new(3, vec![(0, 1)]);
360        let constraints = problem.constraints();
361        // 1 edge constraint + 3 maximality constraints
362        assert_eq!(constraints.len(), 4);
363    }
364
365    #[test]
366    fn test_is_satisfied() {
367        let problem = MaximalIS::new(3, vec![(0, 1), (1, 2)]);
368
369        assert!(problem.is_satisfied(&[1, 0, 1])); // Maximal
370        assert!(problem.is_satisfied(&[0, 1, 0])); // Maximal
371        // Note: is_satisfied checks constraints, which may be more complex
372    }
373
374    #[test]
375    fn test_objectives() {
376        let problem = MaximalIS::new(3, vec![(0, 1)]);
377        let objectives = problem.objectives();
378        assert_eq!(objectives.len(), 3); // One per vertex
379    }
380
381    #[test]
382    fn test_weights() {
383        let problem = MaximalIS::new(3, vec![(0, 1)]);
384        let weights = problem.weights();
385        assert_eq!(weights, vec![1, 1, 1]); // Unit weights
386    }
387
388    #[test]
389    fn test_set_weights() {
390        let mut problem = MaximalIS::new(3, vec![(0, 1)]);
391        // MaximalIS doesn't support weighted - set_weights is a no-op
392        problem.set_weights(vec![1, 2, 3]);
393        // Weights remain uniform
394        assert_eq!(problem.weights(), vec![1, 1, 1]);
395    }
396
397    #[test]
398    fn test_is_weighted() {
399        let problem = MaximalIS::new(3, vec![(0, 1)]);
400        assert!(!problem.is_weighted()); // Initially uniform
401    }
402
403    #[test]
404    fn test_is_weighted_empty() {
405        let problem = MaximalIS::new(0, vec![]);
406        assert!(!problem.is_weighted());
407    }
408
409    #[test]
410    fn test_is_maximal_independent_set_wrong_len() {
411        assert!(!is_maximal_independent_set(3, &[(0, 1)], &[true, false]));
412    }
413
414    #[test]
415    fn test_problem_size() {
416        let problem = MaximalIS::new(5, vec![(0, 1), (1, 2), (2, 3)]);
417        let size = problem.problem_size();
418        assert_eq!(size.get("num_vertices"), Some(5));
419        assert_eq!(size.get("num_edges"), Some(3));
420    }
421}