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::{OptimizationProblem, Problem};
9use crate::types::{Direction, One, SolutionSize, 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_best(&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 Metric = SolutionSize<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]) -> SolutionSize<W::Sum> {
133        if !is_independent_set_config(&self.graph, config) {
134            return SolutionSize::Invalid;
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        SolutionSize::Valid(total)
143    }
144}
145
146impl<G, W> OptimizationProblem for MaximumIndependentSet<G, W>
147where
148    G: Graph + crate::variant::VariantParam,
149    W: WeightElement + crate::variant::VariantParam,
150{
151    type Value = W::Sum;
152
153    fn direction(&self) -> Direction {
154        Direction::Maximize
155    }
156}
157
158/// Check if a configuration forms a valid independent set.
159fn is_independent_set_config<G: Graph>(graph: &G, config: &[usize]) -> bool {
160    for (u, v) in graph.edges() {
161        if config.get(u).copied().unwrap_or(0) == 1 && config.get(v).copied().unwrap_or(0) == 1 {
162            return false;
163        }
164    }
165    true
166}
167
168crate::declare_variants! {
169    opt MaximumIndependentSet<SimpleGraph, i32>        => "1.1996^num_vertices",
170    default opt MaximumIndependentSet<SimpleGraph, One>         => "1.1996^num_vertices",
171    opt MaximumIndependentSet<KingsSubgraph, i32>      => "2^sqrt(num_vertices)",
172    opt MaximumIndependentSet<KingsSubgraph, One>       => "2^sqrt(num_vertices)",
173    opt MaximumIndependentSet<TriangularSubgraph, i32> => "2^sqrt(num_vertices)",
174    opt MaximumIndependentSet<UnitDiskGraph, i32>      => "2^sqrt(num_vertices)",
175    opt MaximumIndependentSet<UnitDiskGraph, One>       => "2^sqrt(num_vertices)",
176}
177
178#[cfg(feature = "example-db")]
179pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
180    vec![
181        crate::example_db::specs::ModelExampleSpec {
182            id: "maximum_independent_set_simplegraph_one",
183            instance: Box::new(MaximumIndependentSet::new(
184                SimpleGraph::new(
185                    10,
186                    vec![
187                        (0, 1),
188                        (1, 2),
189                        (2, 3),
190                        (3, 4),
191                        (4, 0),
192                        (5, 7),
193                        (7, 9),
194                        (9, 6),
195                        (6, 8),
196                        (8, 5),
197                        (0, 5),
198                        (1, 6),
199                        (2, 7),
200                        (3, 8),
201                        (4, 9),
202                    ],
203                ),
204                vec![One; 10],
205            )),
206            optimal_config: vec![1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
207            optimal_value: serde_json::json!({"Valid": 4}),
208        },
209        crate::example_db::specs::ModelExampleSpec {
210            id: "maximum_independent_set_simplegraph_i32",
211            instance: Box::new(MaximumIndependentSet::new(
212                SimpleGraph::new(
213                    10,
214                    vec![
215                        (0, 1),
216                        (1, 2),
217                        (2, 3),
218                        (3, 4),
219                        (4, 0),
220                        (5, 7),
221                        (7, 9),
222                        (9, 6),
223                        (6, 8),
224                        (8, 5),
225                        (0, 5),
226                        (1, 6),
227                        (2, 7),
228                        (3, 8),
229                        (4, 9),
230                    ],
231                ),
232                vec![5, 1, 1, 1, 1, 3, 1, 1, 1, 3],
233            )),
234            optimal_config: vec![1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
235            optimal_value: serde_json::json!({"Valid": 10}),
236        },
237    ]
238}
239
240/// Check if a set of vertices forms an independent set.
241///
242/// # Arguments
243/// * `graph` - The graph
244/// * `selected` - Boolean slice indicating which vertices are selected
245///
246/// # Panics
247/// Panics if `selected.len() != graph.num_vertices()`.
248#[cfg(test)]
249pub(crate) fn is_independent_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
250    assert_eq!(
251        selected.len(),
252        graph.num_vertices(),
253        "selected length must match num_vertices"
254    );
255    for (u, v) in graph.edges() {
256        if selected[u] && selected[v] {
257            return false;
258        }
259    }
260    true
261}
262
263#[cfg(test)]
264#[path = "../../unit_tests/models/graph/maximum_independent_set.rs"]
265mod tests;