Skip to main content

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