Skip to main content

problemreductions/models/graph/
maximum_independent_set.rs

1//! Independent Set problem implementation.
2//!
3//! The Independent Set problem asks for a maximum weight subset of vertices
4//! such that no two vertices in the subset are adjacent.
5
6use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, KingsSubgraph, SimpleGraph, TriangularSubgraph, UnitDiskGraph};
8use crate::traits::Problem;
9use crate::types::{Max, One, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14    ProblemSchemaEntry {
15        name: "MaximumIndependentSet",
16        display_name: "Maximum Independent Set",
17        aliases: &["MIS"],
18        dimensions: &[
19            VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph", "KingsSubgraph", "TriangularSubgraph", "UnitDiskGraph"]),
20            VariantDimension::new("weight", "One", &["One", "i32"]),
21        ],
22        module_path: module_path!(),
23        description: "Find maximum weight independent set 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 Independent Set 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/// - No two vertices in S are adjacent (independent set constraint)
36/// - The total weight Σ_{v ∈ S} w_v is maximized
37///
38/// # Type Parameters
39///
40/// * `G` - The graph type (e.g., `SimpleGraph`, `KingsSubgraph`, `UnitDiskGraph`)
41/// * `W` - The weight type (e.g., `i32`, `f64`, `One`)
42///
43/// # Example
44///
45/// ```
46/// use problemreductions::models::graph::MaximumIndependentSet;
47/// use problemreductions::topology::SimpleGraph;
48/// use problemreductions::{Problem, Solver, BruteForce};
49///
50/// // Create a triangle graph (3 vertices, 3 edges)
51/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]);
52/// let problem = MaximumIndependentSet::new(graph, vec![1; 3]);
53///
54/// // Solve with brute force
55/// let solver = BruteForce::new();
56/// let solutions = solver.find_all_witnesses(&problem);
57///
58/// // Maximum independent set in a triangle has size 1
59/// assert!(solutions.iter().all(|s| s.iter().sum::<usize>() == 1));
60/// ```
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct MaximumIndependentSet<G, W> {
63    /// The underlying graph.
64    graph: G,
65    /// Weights for each vertex.
66    weights: Vec<W>,
67}
68
69impl<G: Graph, W: Clone + Default> MaximumIndependentSet<G, W> {
70    /// Create an Independent Set problem from a graph with given weights.
71    pub fn new(graph: G, weights: Vec<W>) -> Self {
72        assert_eq!(
73            weights.len(),
74            graph.num_vertices(),
75            "weights length must match graph num_vertices"
76        );
77        Self { graph, weights }
78    }
79
80    /// Get a reference to the underlying graph.
81    pub fn graph(&self) -> &G {
82        &self.graph
83    }
84
85    /// Get a reference to the weights.
86    pub fn weights(&self) -> &[W] {
87        &self.weights
88    }
89
90    /// Check if the problem uses a non-unit weight type.
91    pub fn is_weighted(&self) -> bool
92    where
93        W: WeightElement,
94    {
95        !W::IS_UNIT
96    }
97
98    /// Check if a configuration is a valid independent set.
99    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
100        is_independent_set_config(&self.graph, config)
101    }
102}
103
104impl<G: Graph, W: WeightElement> MaximumIndependentSet<G, W> {
105    /// Get the number of vertices in the underlying graph.
106    pub fn num_vertices(&self) -> usize {
107        self.graph().num_vertices()
108    }
109
110    /// Get the number of edges in the underlying graph.
111    pub fn num_edges(&self) -> usize {
112        self.graph().num_edges()
113    }
114}
115
116impl<G, W> Problem for MaximumIndependentSet<G, W>
117where
118    G: Graph + crate::variant::VariantParam,
119    W: WeightElement + crate::variant::VariantParam,
120{
121    const NAME: &'static str = "MaximumIndependentSet";
122    type Value = Max<W::Sum>;
123
124    fn variant() -> Vec<(&'static str, &'static str)> {
125        crate::variant_params![G, W]
126    }
127
128    fn dims(&self) -> Vec<usize> {
129        vec![2; self.graph.num_vertices()]
130    }
131
132    fn evaluate(&self, config: &[usize]) -> Max<W::Sum> {
133        if !is_independent_set_config(&self.graph, config) {
134            return Max(None);
135        }
136        let mut total = W::Sum::zero();
137        for (i, &selected) in config.iter().enumerate() {
138            if selected == 1 {
139                total += self.weights[i].to_sum();
140            }
141        }
142        Max(Some(total))
143    }
144}
145
146/// Check if a configuration forms a valid independent set.
147fn is_independent_set_config<G: Graph>(graph: &G, config: &[usize]) -> bool {
148    for (u, v) in graph.edges() {
149        if config.get(u).copied().unwrap_or(0) == 1 && config.get(v).copied().unwrap_or(0) == 1 {
150            return false;
151        }
152    }
153    true
154}
155
156crate::declare_variants! {
157    MaximumIndependentSet<SimpleGraph, i32>        => "1.1996^num_vertices",
158    default MaximumIndependentSet<SimpleGraph, One>         => "1.1996^num_vertices",
159    MaximumIndependentSet<KingsSubgraph, i32>      => "2^sqrt(num_vertices)",
160    MaximumIndependentSet<KingsSubgraph, One>       => "2^sqrt(num_vertices)",
161    MaximumIndependentSet<TriangularSubgraph, i32> => "2^sqrt(num_vertices)",
162    MaximumIndependentSet<UnitDiskGraph, i32>      => "2^sqrt(num_vertices)",
163    MaximumIndependentSet<UnitDiskGraph, One>       => "2^sqrt(num_vertices)",
164}
165
166impl<G, W> crate::models::decision::DecisionProblemMeta for MaximumIndependentSet<G, W>
167where
168    G: Graph + crate::variant::VariantParam,
169    W: WeightElement + crate::variant::VariantParam,
170    W::Sum: std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned,
171{
172    const DECISION_NAME: &'static str = "DecisionMaximumIndependentSet";
173}
174
175#[cfg(feature = "example-db")]
176pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
177    vec![
178        crate::example_db::specs::ModelExampleSpec {
179            id: "maximum_independent_set_simplegraph_one",
180            instance: Box::new(MaximumIndependentSet::new(
181                SimpleGraph::new(
182                    10,
183                    vec![
184                        (0, 1),
185                        (1, 2),
186                        (2, 3),
187                        (3, 4),
188                        (4, 0),
189                        (5, 7),
190                        (7, 9),
191                        (9, 6),
192                        (6, 8),
193                        (8, 5),
194                        (0, 5),
195                        (1, 6),
196                        (2, 7),
197                        (3, 8),
198                        (4, 9),
199                    ],
200                ),
201                vec![One; 10],
202            )),
203            optimal_config: vec![1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
204            optimal_value: serde_json::json!(4),
205        },
206        crate::example_db::specs::ModelExampleSpec {
207            id: "maximum_independent_set_simplegraph_i32",
208            instance: Box::new(MaximumIndependentSet::new(
209                SimpleGraph::new(
210                    10,
211                    vec![
212                        (0, 1),
213                        (1, 2),
214                        (2, 3),
215                        (3, 4),
216                        (4, 0),
217                        (5, 7),
218                        (7, 9),
219                        (9, 6),
220                        (6, 8),
221                        (8, 5),
222                        (0, 5),
223                        (1, 6),
224                        (2, 7),
225                        (3, 8),
226                        (4, 9),
227                    ],
228                ),
229                vec![5, 1, 1, 1, 1, 3, 1, 1, 1, 3],
230            )),
231            optimal_config: vec![1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
232            optimal_value: serde_json::json!(10),
233        },
234    ]
235}
236
237/// Check if a set of vertices forms an independent set.
238///
239/// # Arguments
240/// * `graph` - The graph
241/// * `selected` - Boolean slice indicating which vertices are selected
242///
243/// # Panics
244/// Panics if `selected.len() != graph.num_vertices()`.
245#[cfg(test)]
246pub(crate) fn is_independent_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
247    assert_eq!(
248        selected.len(),
249        graph.num_vertices(),
250        "selected length must match num_vertices"
251    );
252    for (u, v) in graph.edges() {
253        if selected[u] && selected[v] {
254            return false;
255        }
256    }
257    true
258}
259
260#[cfg(test)]
261#[path = "../../unit_tests/models/graph/maximum_independent_set.rs"]
262mod tests;