problemreductions/models/graph/
highly_connected_deletion.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
20use crate::topology::{Graph, SimpleGraph};
21use crate::traits::Problem;
22use crate::types::Min;
23use crate::variant::VariantParam;
24use serde::{Deserialize, Serialize};
25use std::collections::{HashSet, VecDeque};
26
27inventory::submit! {
28 ProblemSchemaEntry {
29 name: "HighlyConnectedDeletion",
30 display_name: "Highly Connected Deletion",
31 aliases: &[],
32 dimensions: &[
33 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
34 ],
35 category: crate::registry::ProblemCategory::Graph,
36 module_path: module_path!(),
37 description: "Minimum number of edge deletions so every component is an isolated vertex or a highly connected graph on >=3 vertices",
38 fields: &[
39 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
40 ],
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
76pub struct HighlyConnectedDeletion<G> {
77 graph: G,
79}
80
81impl<G: Graph> HighlyConnectedDeletion<G> {
82 pub fn new(graph: G) -> Self {
84 Self { graph }
85 }
86
87 pub fn graph(&self) -> &G {
89 &self.graph
90 }
91
92 pub fn num_vertices(&self) -> usize {
94 self.graph.num_vertices()
95 }
96
97 pub fn num_edges(&self) -> usize {
99 self.graph.num_edges()
100 }
101
102 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
105 is_feasible_deletion(&self.graph, config)
106 }
107}
108
109impl<G> Problem for HighlyConnectedDeletion<G>
110where
111 G: Graph + VariantParam,
112{
113 const NAME: &'static str = "HighlyConnectedDeletion";
114 type Solution = Vec<bool>;
115 type Value = Min<i64>;
116
117 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
118
119 fn variant() -> Vec<(&'static str, &'static str)> {
120 crate::variant_params![G]
121 }
122
123 fn evaluate(
124 &self,
125 config: &Self::Solution,
126 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
127 if config.len() != self.graph.num_edges() {
128 return Err(crate::traits::EvaluationError::InvalidConfiguration(
129 "edge-selection length does not match the graph".into(),
130 ));
131 }
132 Ok({
133 if !is_feasible_deletion(&self.graph, config) {
134 return Ok(Min(None));
135 }
136 let deleted = i64::try_from(config.iter().filter(|&&deleted| deleted).count())
137 .map_err(|_| {
138 crate::traits::EvaluationError::IntegerOverflow(
139 "converting deleted-edge count to i64".into(),
140 )
141 })?;
142 Min(Some(deleted))
143 })
144 }
145}
146
147impl<G> crate::solvers::BruteForceProblem for HighlyConnectedDeletion<G>
148where
149 G: Graph + VariantParam,
150{
151 fn dimensions(&self) -> Vec<usize> {
152 vec![2; self.graph.num_edges()]
153 }
154}
155
156fn is_feasible_deletion<G: Graph>(graph: &G, config: &[bool]) -> bool {
162 let n = graph.num_vertices();
163 let edges = graph.edges();
164 if config.len() != edges.len() {
165 return false;
166 }
167
168 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
170 for (i, &(u, v)) in edges.iter().enumerate() {
171 if !config.get(i).copied().unwrap_or(false) {
172 adj[u].push(v);
173 adj[v].push(u);
174 }
175 }
176
177 let mut visited = vec![false; n];
179 for start in 0..n {
180 if visited[start] {
181 continue;
182 }
183 let mut component: Vec<usize> = Vec::new();
184 let mut queue: VecDeque<usize> = VecDeque::new();
185 queue.push_back(start);
186 visited[start] = true;
187 while let Some(u) = queue.pop_front() {
188 component.push(u);
189 for &w in &adj[u] {
190 if !visited[w] {
191 visited[w] = true;
192 queue.push_back(w);
193 }
194 }
195 }
196 let size = component.len();
197 if size == 1 {
198 continue; }
200 if size == 2 {
201 return false; }
203 let lambda = edge_connectivity(&component, &adj);
205 if 2 * lambda <= size {
207 return false;
208 }
209 }
210 true
211}
212
213fn edge_connectivity(vertices: &[usize], adj: &[Vec<usize>]) -> usize {
226 let size = vertices.len();
227 if size <= 1 {
228 return 0;
229 }
230 let mut local: std::collections::HashMap<usize, usize> =
232 std::collections::HashMap::with_capacity(size);
233 for (i, &v) in vertices.iter().enumerate() {
234 local.insert(v, i);
235 }
236
237 let in_component: HashSet<usize> = vertices.iter().copied().collect();
242 let mut head: Vec<usize> = Vec::new();
243 let mut cap: Vec<u8> = Vec::new();
244 let mut out: Vec<Vec<usize>> = vec![Vec::new(); size];
245
246 let mut seen_edges: HashSet<(usize, usize)> = HashSet::new();
247 for &u in vertices {
248 let lu = local[&u];
249 for &v in &adj[u] {
250 if !in_component.contains(&v) {
251 continue;
252 }
253 let key = if u < v { (u, v) } else { (v, u) };
254 if !seen_edges.insert(key) {
255 continue;
256 }
257 let lv = local[&v];
258 let a = head.len();
260 head.push(lv);
261 cap.push(1);
262 head.push(lu);
264 cap.push(1);
265 out[lu].push(a);
266 out[lv].push(a + 1);
267 }
268 }
269
270 let mut best = usize::MAX;
271 let s = 0;
272 for t in 1..size {
273 for c in cap.iter_mut() {
275 *c = 1;
276 }
277 let mut flow = 0usize;
278 loop {
279 let mut parent_arc: Vec<Option<usize>> = vec![None; size];
281 let mut visited = vec![false; size];
282 visited[s] = true;
283 let mut queue: VecDeque<usize> = VecDeque::new();
284 queue.push_back(s);
285 while let Some(u) = queue.pop_front() {
286 if u == t {
287 break;
288 }
289 for &a in &out[u] {
290 let v = head[a];
291 if !visited[v] && cap[a] > 0 {
292 visited[v] = true;
293 parent_arc[v] = Some(a);
294 queue.push_back(v);
295 }
296 }
297 }
298 if !visited[t] {
299 break;
300 }
301 let mut cur = t;
303 while cur != s {
304 let a = parent_arc[cur].expect("visited vertex has a BFS parent arc");
305 cap[a] -= 1;
306 cap[a ^ 1] += 1;
307 cur = head[a ^ 1];
309 }
310 flow += 1;
311 }
312 if flow < best {
313 best = flow;
314 if best == 0 {
315 return 0;
316 }
317 }
318 }
319 if best == usize::MAX {
320 0
321 } else {
322 best
323 }
324}
325
326crate::declare_variants! {
327 default HighlyConnectedDeletion<SimpleGraph> => "2^num_edges",
328}
329
330crate::register_brute_force! {
331 HighlyConnectedDeletion<SimpleGraph> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
332}
333
334#[cfg(feature = "example-db")]
335pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
336 vec![crate::example_db::specs::ModelExampleSpec {
337 id: "highly_connected_deletion_simplegraph",
338 instance: Box::new(HighlyConnectedDeletion::new(SimpleGraph::new(
339 4,
340 vec![(0, 1), (0, 2), (1, 2), (2, 3)],
341 ))),
342 optimal_config: serde_json::json!(vec![false, false, false, true]),
344 optimal_value: serde_json::json!(1),
345 }]
346}
347
348pub(crate) fn is_feasible_cluster<G: Graph>(graph: &G, vertices: &[usize]) -> bool {
357 let size = vertices.len();
358 if size == 0 {
359 return false;
360 }
361 if size == 1 {
362 return true;
363 }
364 if size == 2 {
365 return false;
366 }
367
368 let n = graph.num_vertices();
370 let in_subset: HashSet<usize> = vertices.iter().copied().collect();
371 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
372 for (u, v) in graph.edges() {
373 if in_subset.contains(&u) && in_subset.contains(&v) {
374 adj[u].push(v);
375 adj[v].push(u);
376 }
377 }
378
379 let mut visited: HashSet<usize> = HashSet::new();
381 let start = vertices[0];
382 let mut queue: VecDeque<usize> = VecDeque::new();
383 queue.push_back(start);
384 visited.insert(start);
385 while let Some(u) = queue.pop_front() {
386 for &w in &adj[u] {
387 if !visited.contains(&w) {
388 visited.insert(w);
389 queue.push_back(w);
390 }
391 }
392 }
393 if visited.len() != size {
394 return false;
395 }
396
397 let lambda = edge_connectivity(vertices, &adj);
399 2 * lambda > size
400}
401
402pub(crate) fn induced_edge_count<G: Graph>(graph: &G, vertices: &[usize]) -> usize {
405 let in_subset: HashSet<usize> = vertices.iter().copied().collect();
406 graph
407 .edges()
408 .into_iter()
409 .filter(|(u, v)| in_subset.contains(u) && in_subset.contains(v))
410 .count()
411}
412
413#[cfg(test)]
414#[path = "../../unit_tests/models/graph/highly_connected_deletion.rs"]
415mod tests;
416
417#[cfg(test)]
418pub(crate) fn edge_connectivity_for_tests(vertices: &[usize], adj: &[Vec<usize>]) -> usize {
419 edge_connectivity(vertices, adj)
420}