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};
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        module_path: module_path!(),
17        description: "Find maximum weight independent set in a graph",
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 Independent Set problem.
26///
27/// Given a graph G = (V, E) and weights w_v for each vertex,
28/// find a subset S ⊆ V such that:
29/// - No two vertices in S are adjacent (independent set constraint)
30/// - The total weight Σ_{v ∈ S} w_v is maximized
31///
32/// # Type Parameters
33///
34/// * `G` - The graph type (e.g., `SimpleGraph`, `KingsSubgraph`, `UnitDiskGraph`)
35/// * `W` - The weight type (e.g., `i32`, `f64`, `One`)
36///
37/// # Example
38///
39/// ```
40/// use problemreductions::models::graph::MaximumIndependentSet;
41/// use problemreductions::topology::SimpleGraph;
42/// use problemreductions::{Problem, Solver, BruteForce};
43///
44/// // Create a triangle graph (3 vertices, 3 edges)
45/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]);
46/// let problem = MaximumIndependentSet::new(graph, vec![1; 3]);
47///
48/// // Solve with brute force
49/// let solver = BruteForce::new();
50/// let solutions = solver.find_all_best(&problem);
51///
52/// // Maximum independent set in a triangle has size 1
53/// assert!(solutions.iter().all(|s| s.iter().sum::<usize>() == 1));
54/// ```
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct MaximumIndependentSet<G, W> {
57    /// The underlying graph.
58    graph: G,
59    /// Weights for each vertex.
60    weights: Vec<W>,
61}
62
63impl<G: Graph, W: Clone + Default> MaximumIndependentSet<G, W> {
64    /// Create an Independent Set problem from a graph with given weights.
65    pub fn new(graph: G, weights: Vec<W>) -> Self {
66        assert_eq!(
67            weights.len(),
68            graph.num_vertices(),
69            "weights length must match graph num_vertices"
70        );
71        Self { graph, weights }
72    }
73
74    /// Get a reference to the underlying graph.
75    pub fn graph(&self) -> &G {
76        &self.graph
77    }
78
79    /// Get a reference to the weights.
80    pub fn weights(&self) -> &[W] {
81        &self.weights
82    }
83
84    /// Check if the problem uses a non-unit weight type.
85    pub fn is_weighted(&self) -> bool
86    where
87        W: WeightElement,
88    {
89        !W::IS_UNIT
90    }
91
92    /// Check if a configuration is a valid independent set.
93    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
94        is_independent_set_config(&self.graph, config)
95    }
96}
97
98impl<G: Graph, W: WeightElement> MaximumIndependentSet<G, W> {
99    /// Get the number of vertices in the underlying graph.
100    pub fn num_vertices(&self) -> usize {
101        self.graph().num_vertices()
102    }
103
104    /// Get the number of edges in the underlying graph.
105    pub fn num_edges(&self) -> usize {
106        self.graph().num_edges()
107    }
108}
109
110impl<G, W> Problem for MaximumIndependentSet<G, W>
111where
112    G: Graph + crate::variant::VariantParam,
113    W: WeightElement + crate::variant::VariantParam,
114{
115    const NAME: &'static str = "MaximumIndependentSet";
116    type Metric = SolutionSize<W::Sum>;
117
118    fn variant() -> Vec<(&'static str, &'static str)> {
119        crate::variant_params![G, W]
120    }
121
122    fn dims(&self) -> Vec<usize> {
123        vec![2; self.graph.num_vertices()]
124    }
125
126    fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
127        if !is_independent_set_config(&self.graph, config) {
128            return SolutionSize::Invalid;
129        }
130        let mut total = W::Sum::zero();
131        for (i, &selected) in config.iter().enumerate() {
132            if selected == 1 {
133                total += self.weights[i].to_sum();
134            }
135        }
136        SolutionSize::Valid(total)
137    }
138}
139
140impl<G, W> OptimizationProblem for MaximumIndependentSet<G, W>
141where
142    G: Graph + crate::variant::VariantParam,
143    W: WeightElement + crate::variant::VariantParam,
144{
145    type Value = W::Sum;
146
147    fn direction(&self) -> Direction {
148        Direction::Maximize
149    }
150}
151
152/// Check if a configuration forms a valid independent set.
153fn is_independent_set_config<G: Graph>(graph: &G, config: &[usize]) -> bool {
154    for (u, v) in graph.edges() {
155        if config.get(u).copied().unwrap_or(0) == 1 && config.get(v).copied().unwrap_or(0) == 1 {
156            return false;
157        }
158    }
159    true
160}
161
162crate::declare_variants! {
163    MaximumIndependentSet<SimpleGraph, i32>        => "1.1996^num_vertices",
164    MaximumIndependentSet<SimpleGraph, One>         => "1.1996^num_vertices",
165    MaximumIndependentSet<KingsSubgraph, i32>      => "2^sqrt(num_vertices)",
166    MaximumIndependentSet<KingsSubgraph, One>       => "2^sqrt(num_vertices)",
167    MaximumIndependentSet<TriangularSubgraph, i32> => "2^sqrt(num_vertices)",
168    MaximumIndependentSet<UnitDiskGraph, i32>      => "2^sqrt(num_vertices)",
169    MaximumIndependentSet<UnitDiskGraph, One>       => "2^sqrt(num_vertices)",
170}
171
172/// Check if a set of vertices forms an independent set.
173///
174/// # Arguments
175/// * `graph` - The graph
176/// * `selected` - Boolean slice indicating which vertices are selected
177///
178/// # Panics
179/// Panics if `selected.len() != graph.num_vertices()`.
180#[cfg(test)]
181pub(crate) fn is_independent_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
182    assert_eq!(
183        selected.len(),
184        graph.num_vertices(),
185        "selected length must match num_vertices"
186    );
187    for (u, v) in graph.edges() {
188        if selected[u] && selected[v] {
189            return false;
190        }
191    }
192    true
193}
194
195#[cfg(test)]
196#[path = "../../unit_tests/models/graph/maximum_independent_set.rs"]
197mod tests;