graph_algo_ptas/algorithm/
ptas.rs1use super::dynamic_programming::solve::{dp_solve_hashmap_graph, DpObjective, DpProblem};
13use crate::utils::convert::UndirectedGraph;
14use arboretum_td::graph::{HashMapGraph, MutableGraph};
15use petgraph::{algo::kosaraju_scc, stable_graph::NodeIndex, visit::EdgeRef};
16use std::collections::{HashSet, VecDeque};
17
18pub fn ptas(graph: &UndirectedGraph, prob: &DpProblem, eps: f64) -> HashSet<usize> {
24 let mut sols: Vec<HashSet<usize>> = vec![];
25
26 for ring_decomposition in get_ring_decompositions(&mut graph.clone(), eps) {
27 let mut sol: HashSet<usize> = HashSet::new();
28
29 for ring in get_component_graphs(&ring_decomposition.rings) {
30 let ring_sol = dp_solve_hashmap_graph(&ring, None, prob);
31 sol.extend(ring_sol.iter());
32 }
33
34 if prob.objective == DpObjective::Minimize {
35 let vertices_deleted = ring_decomposition
36 .vertices_deleted
37 .iter()
38 .map(|v| v.index());
39 sol.extend(vertices_deleted);
40 }
41
42 sols.push(sol);
43 }
44
45 let best_sol = match prob.objective {
46 DpObjective::Minimize => sols.iter().min_by(|s1, s2| s1.len().cmp(&s2.len())),
47 DpObjective::Maximize => sols.iter().max_by(|s1, s2| s1.len().cmp(&s2.len())),
48 };
49
50 best_sol.unwrap().clone()
51}
52
53fn get_component_graphs(graph: &UndirectedGraph) -> Vec<HashMapGraph> {
54 let mut component_graphs = vec![];
55
56 for component in &kosaraju_scc(&graph) {
57 let mut component_graph = HashMapGraph::new();
58
59 for v in component {
60 component_graph.add_vertex(v.index());
61 }
62
63 for v in component {
64 for e in graph.edges(*v) {
65 component_graph.add_edge(e.source().index(), e.target().index());
66 }
67 }
68
69 component_graphs.push(component_graph);
70 }
71
72 component_graphs
73}
74
75struct RingDecomposition {
76 rings: UndirectedGraph,
77 vertices_deleted: HashSet<NodeIndex>,
78}
79
80fn get_ring_decompositions(graph: &mut UndirectedGraph, eps: f64) -> Vec<RingDecomposition> {
81 let k = (1.0 / eps).ceil() as usize;
82 assert!(kosaraju_scc(&graph.clone()).len() == 1);
83 assert!(graph.node_count() > 0);
84
85 let mut ring_decompositions: Vec<RingDecomposition> = vec![];
86
87 for i in 0..k {
88 let mut rings = graph.clone();
89 let mut vertices_deleted = HashSet::new();
90 let mut level = 1;
91 let start = graph.node_indices().next().unwrap();
92 let mut visited: HashSet<NodeIndex<u32>> = HashSet::new();
93 let mut queue: VecDeque<NodeIndex<u32>> = VecDeque::new();
94 let sep = NodeIndex::new(usize::max_value());
95 queue.push_back(start);
96 queue.push_back(sep);
97
98 while !queue.is_empty() {
99 let current = queue.pop_front().unwrap();
100
101 if current == sep {
102 level += 1;
103
104 if !queue.is_empty() {
105 queue.push_back(sep);
106 }
107
108 continue;
109 }
110
111 if visited.contains(¤t) {
112 continue;
113 }
114 visited.insert(current);
115
116 if level % k == i {
117 vertices_deleted.insert(current);
118 rings.remove_node(current);
119 }
120
121 for n in graph.neighbors(current) {
122 queue.push_back(n);
123 }
124 }
125
126 ring_decompositions.push(RingDecomposition {
127 rings,
128 vertices_deleted,
129 });
130 }
131
132 ring_decompositions
133}
134
135#[cfg(test)]
136mod tests {
137 use super::get_ring_decompositions;
138 use crate::{
139 algorithm::{dynamic_programming::solve::DpProblem, ptas::ptas},
140 generation::{erdos_renyi::generate_petgraph, planar::generate},
141 utils::{
142 convert::{to_hash_map_graph, UndirectedGraph},
143 max_independent_set::{brute_force_max_independent_set, is_independent_set},
144 min_vertex_cover::{brute_force_min_vertex_cover, is_vertex_cover},
145 },
146 };
147 use petgraph::algo::kosaraju_scc;
148 use rand::{rngs::StdRng, Rng, SeedableRng};
149 use std::collections::HashSet;
150
151 #[test]
152 fn approximation_ratio() {
153 let seed = [2; 32];
154 let mut rng = StdRng::from_seed(seed);
155
156 let mut i = 0;
157 while i < 100 {
158 let graph = generate_petgraph(
159 rng.gen_range(1..100),
160 rng.gen_range(0.1..1.0),
161 Some(i as u64),
162 );
163
164 if kosaraju_scc(&graph).len() != 1 {
165 continue;
166 }
167
168 i += 1;
169
170 let eps = rng.gen_range(0.05..0.5) as f64;
171 let ring_decompositions = get_ring_decompositions(&mut graph.clone(), eps);
172 let mut vertices = HashSet::new();
173
174 for ring_decomposition in &ring_decompositions {
175 vertices.extend(ring_decomposition.vertices_deleted.iter());
176 }
177
178 assert!(vertices == graph.node_indices().collect());
179 assert!(ring_decompositions
180 .iter()
181 .any(|rd| { rd.vertices_deleted.len() as f64 <= eps * graph.node_count() as f64 }));
182 }
183 }
184
185 #[test]
186 fn max_independent_set_single_vertex() {
187 let mut graph = UndirectedGraph::default();
188 let v0 = graph.add_node(());
189 let sol = ptas(&graph, &DpProblem::max_independent_set(), 0.5);
190
191 assert!(sol.len() == 1);
192 assert!(sol.contains(&v0.index()));
193 }
194
195 #[test]
196 fn max_independent_set_single_edge() {
197 let mut graph = UndirectedGraph::default();
198 let v0 = graph.add_node(());
199 let v1 = graph.add_node(());
200 graph.add_edge(v0, v1, ());
201 let sol = ptas(&graph, &DpProblem::max_independent_set(), 0.5);
202 assert!(sol.len() == 1);
203 assert!(sol.contains(&v0.index()) || sol.contains(&v1.index()));
204 }
205
206 #[test]
207 fn max_independent_set_random() {
208 for n in 2..30 {
209 let graph: UndirectedGraph = generate(n, Some(n as u64)).to_pet_graph();
210 let eps = 0.5;
211 let sol = ptas(&graph, &DpProblem::max_independent_set(), eps);
212
213 assert!(is_independent_set(&to_hash_map_graph(&graph), &sol));
214
215 if n <= 15 {
217 let sol2 = brute_force_max_independent_set(&to_hash_map_graph(&graph));
218
219 assert!(sol.len() as f64 >= (1.0 - eps) * sol2.len() as f64);
220 }
221 }
222 }
223
224 #[test]
225 fn min_vertex_cover_single_vertex() {
226 let mut graph = UndirectedGraph::default();
227 graph.add_node(());
228 let sol = ptas(&graph, &DpProblem::min_vertex_cover(), 0.5);
229
230 assert!(sol.is_empty());
231 }
232
233 #[test]
234 fn min_vertex_cover_single_edge() {
235 let mut graph = UndirectedGraph::default();
236 let v0 = graph.add_node(());
237 let v1 = graph.add_node(());
238 graph.add_edge(v0, v1, ());
239 let sol = ptas(&graph, &DpProblem::min_vertex_cover(), 0.5);
240 assert!(sol.len() == 1);
241 assert!(sol.contains(&v0.index()) || sol.contains(&v1.index()));
242 }
243
244 #[test]
245 fn min_vertex_cover_random() {
246 for n in 2..30 {
247 let graph: UndirectedGraph = generate(n, Some(n as u64)).to_pet_graph();
248 let eps = 0.5;
249 let sol = ptas(&graph, &DpProblem::min_vertex_cover(), eps);
250
251 assert!(is_vertex_cover(&to_hash_map_graph(&graph), &sol));
252
253 if n <= 15 {
255 let sol2 = brute_force_min_vertex_cover(&to_hash_map_graph(&graph));
256
257 assert!(sol.len() as f64 <= (1.0 + 0.5 * eps) * sol2.len() as f64);
258 }
259 }
260 }
261}