problemreductions/models/graph/
maximal_is.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::{OptimizationProblem, Problem};
9use crate::types::{Direction, SolutionSize, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaximalIS",
16 module_path: module_path!(),
17 description: "Find maximum weight maximal independent set",
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#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct MaximalIS<G, W> {
54 graph: G,
56 weights: Vec<W>,
58}
59
60impl<G: Graph, W: Clone + Default> MaximalIS<G, W> {
61 pub fn new(graph: G, weights: Vec<W>) -> Self {
63 assert_eq!(
64 weights.len(),
65 graph.num_vertices(),
66 "weights length must match graph num_vertices"
67 );
68 Self { graph, weights }
69 }
70
71 pub fn graph(&self) -> &G {
73 &self.graph
74 }
75
76 pub fn weights(&self) -> &[W] {
78 &self.weights
79 }
80
81 pub fn is_weighted(&self) -> bool
83 where
84 W: WeightElement,
85 {
86 !W::IS_UNIT
87 }
88
89 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
91 self.is_maximal(config)
92 }
93
94 fn is_independent(&self, config: &[usize]) -> bool {
96 for (u, v) in self.graph.edges() {
97 if config.get(u).copied().unwrap_or(0) == 1 && config.get(v).copied().unwrap_or(0) == 1
98 {
99 return false;
100 }
101 }
102 true
103 }
104
105 fn is_maximal(&self, config: &[usize]) -> bool {
107 if !self.is_independent(config) {
108 return false;
109 }
110
111 let n = self.graph.num_vertices();
112 for v in 0..n {
113 if config.get(v).copied().unwrap_or(0) == 1 {
114 continue; }
116
117 let neighbors = self.graph.neighbors(v);
119 let can_add = neighbors
120 .iter()
121 .all(|&u| config.get(u).copied().unwrap_or(0) == 0);
122
123 if can_add {
124 return false; }
126 }
127
128 true
129 }
130}
131
132impl<G: Graph, W: WeightElement> MaximalIS<G, W> {
133 pub fn num_vertices(&self) -> usize {
135 self.graph().num_vertices()
136 }
137
138 pub fn num_edges(&self) -> usize {
140 self.graph().num_edges()
141 }
142}
143
144impl<G, W> Problem for MaximalIS<G, W>
145where
146 G: Graph + crate::variant::VariantParam,
147 W: WeightElement + crate::variant::VariantParam,
148{
149 const NAME: &'static str = "MaximalIS";
150 type Metric = SolutionSize<W::Sum>;
151
152 fn variant() -> Vec<(&'static str, &'static str)> {
153 crate::variant_params![G, W]
154 }
155
156 fn dims(&self) -> Vec<usize> {
157 vec![2; self.graph.num_vertices()]
158 }
159
160 fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
161 if !self.is_maximal(config) {
162 return SolutionSize::Invalid;
163 }
164 let mut total = W::Sum::zero();
165 for (i, &selected) in config.iter().enumerate() {
166 if selected == 1 {
167 total += self.weights[i].to_sum();
168 }
169 }
170 SolutionSize::Valid(total)
171 }
172}
173
174impl<G, W> OptimizationProblem for MaximalIS<G, W>
175where
176 G: Graph + crate::variant::VariantParam,
177 W: WeightElement + crate::variant::VariantParam,
178{
179 type Value = W::Sum;
180
181 fn direction(&self) -> Direction {
182 Direction::Maximize
183 }
184}
185
186#[cfg(test)]
191pub(crate) fn is_maximal_independent_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
192 assert_eq!(
193 selected.len(),
194 graph.num_vertices(),
195 "selected length must match num_vertices"
196 );
197
198 for (u, v) in graph.edges() {
200 if selected[u] && selected[v] {
201 return false;
202 }
203 }
204
205 for v in 0..graph.num_vertices() {
207 if selected[v] {
208 continue;
209 }
210 if graph.neighbors(v).iter().all(|&u| !selected[u]) {
211 return false;
212 }
213 }
214
215 true
216}
217
218crate::declare_variants! {
219 MaximalIS<SimpleGraph, i32> => "3^(num_vertices / 3)",
220}
221
222#[cfg(test)]
223#[path = "../../unit_tests/models/graph/maximal_is.rs"]
224mod tests;