1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct MaximumIndependentSet<G, W> {
63 graph: G,
65 weights: Vec<W>,
67}
68
69impl<G: Graph, W: Clone + Default> MaximumIndependentSet<G, W> {
70 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 pub fn graph(&self) -> &G {
82 &self.graph
83 }
84
85 pub fn weights(&self) -> &[W] {
87 &self.weights
88 }
89
90 pub fn is_weighted(&self) -> bool
92 where
93 W: WeightElement,
94 {
95 !W::IS_UNIT
96 }
97
98 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 pub fn num_vertices(&self) -> usize {
107 self.graph().num_vertices()
108 }
109
110 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
158fn 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#[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;