fusion_blossom/
complete_graph.rs

1use super::dual_module::EdgeWeightModifier;
2use super::util::*;
3use crate::priority_queue::PriorityQueue;
4use crate::rayon::prelude::*;
5use std::collections::BTreeMap;
6
7/// build complete graph out of skeleton graph using Dijkstra's algorithm
8#[derive(Debug, Clone)]
9pub struct CompleteGraph {
10    /// number of vertices
11    pub vertex_num: VertexNum,
12    /// the vertices to run Dijkstra's algorithm
13    pub vertices: Vec<CompleteGraphVertex>,
14    /// timestamp to invalidate all vertices without iterating them; only invalidating all vertices individually when active_timestamp is usize::MAX
15    active_timestamp: FastClearTimestamp,
16    /// remember the edges that's modified by erasures
17    pub edge_modifier: EdgeWeightModifier,
18    /// original edge weights
19    pub weighted_edges: Vec<(VertexIndex, VertexIndex, Weight)>,
20}
21
22#[derive(Debug, Clone)]
23pub struct CompleteGraphVertex {
24    /// all skeleton graph edges connected to this vertex
25    pub edges: BTreeMap<VertexIndex, Weight>,
26    /// timestamp for Dijkstra's algorithm
27    timestamp: FastClearTimestamp,
28}
29
30impl CompleteGraph {
31    /// create complete graph given skeleton graph
32    #[allow(clippy::unnecessary_cast)]
33    pub fn new(vertex_num: VertexNum, weighted_edges: &[(VertexIndex, VertexIndex, Weight)]) -> Self {
34        let mut vertices: Vec<CompleteGraphVertex> = (0..vertex_num)
35            .map(|_| CompleteGraphVertex {
36                edges: BTreeMap::new(),
37                timestamp: 0,
38            })
39            .collect();
40        for &(i, j, weight) in weighted_edges.iter() {
41            vertices[i as usize].edges.insert(j, weight);
42            vertices[j as usize].edges.insert(i, weight);
43        }
44        Self {
45            vertex_num,
46            vertices,
47            active_timestamp: 0,
48            edge_modifier: EdgeWeightModifier::new(),
49            weighted_edges: weighted_edges.to_owned(),
50        }
51    }
52
53    /// reset any temporary changes like erasure edges
54    #[allow(clippy::unnecessary_cast)]
55    pub fn reset(&mut self) {
56        // recover erasure edges
57        while self.edge_modifier.has_modified_edges() {
58            let (edge_index, original_weight) = self.edge_modifier.pop_modified_edge();
59            let (vertex_idx_1, vertex_idx_2, _) = &self.weighted_edges[edge_index as usize];
60            let vertex_1 = &mut self.vertices[*vertex_idx_1 as usize];
61            vertex_1.edges.insert(*vertex_idx_2, original_weight);
62            let vertex_2 = &mut self.vertices[*vertex_idx_2 as usize];
63            vertex_2.edges.insert(*vertex_idx_1, original_weight);
64            self.weighted_edges[edge_index as usize] = (*vertex_idx_1, *vertex_idx_2, original_weight);
65        }
66    }
67
68    #[allow(clippy::unnecessary_cast)]
69    fn load_edge_modifier(&mut self, edge_modifier: &[(EdgeIndex, Weight)]) {
70        assert!(
71            !self.edge_modifier.has_modified_edges(),
72            "the current erasure modifier is not clean, probably forget to clean the state?"
73        );
74        for (edge_index, target_weight) in edge_modifier.iter() {
75            let (vertex_idx_1, vertex_idx_2, original_weight) = &self.weighted_edges[*edge_index as usize];
76            let vertex_1 = &mut self.vertices[*vertex_idx_1 as usize];
77            vertex_1.edges.insert(*vertex_idx_2, *target_weight);
78            let vertex_2 = &mut self.vertices[*vertex_idx_2 as usize];
79            vertex_2.edges.insert(*vertex_idx_1, *target_weight);
80            self.edge_modifier.push_modified_edge(*edge_index, *original_weight);
81            self.weighted_edges[*edge_index as usize] = (*vertex_idx_1, *vertex_idx_2, *target_weight);
82        }
83    }
84
85    /// temporarily set some edges to 0 weight, and when it resets, those edges will be reverted back to the original weight
86    pub fn load_erasures(&mut self, erasures: &[EdgeIndex]) {
87        let edge_modifier: Vec<_> = erasures.iter().map(|edge_index| (*edge_index, 0)).collect();
88        self.load_edge_modifier(&edge_modifier);
89    }
90
91    pub fn load_dynamic_weights(&mut self, dynamic_weights: &[(EdgeIndex, Weight)]) {
92        let edge_modifier = dynamic_weights.to_vec();
93        self.load_edge_modifier(&edge_modifier);
94    }
95
96    /// invalidate Dijkstra's algorithm state from previous call
97    #[allow(clippy::unnecessary_cast)]
98    pub fn invalidate_previous_dijkstra(&mut self) -> usize {
99        if self.active_timestamp == FastClearTimestamp::MAX {
100            // rarely happens
101            self.active_timestamp = 0;
102            for i in 0..self.vertex_num {
103                self.vertices[i as usize].timestamp = 0; // refresh all timestamps to avoid conflicts
104            }
105        }
106        self.active_timestamp += 1; // implicitly invalidate all vertices
107        self.active_timestamp
108    }
109
110    /// get all complete graph edges from the specific vertex, but will terminate if `terminate` vertex is found
111    #[allow(clippy::unnecessary_cast)]
112    pub fn all_edges_with_terminate(
113        &mut self,
114        vertex: VertexIndex,
115        terminate: VertexIndex,
116    ) -> BTreeMap<VertexIndex, (VertexIndex, Weight)> {
117        let active_timestamp = self.invalidate_previous_dijkstra();
118        let mut pq = PriorityQueue::<EdgeIndex, PriorityElement>::new();
119        pq.push(vertex, PriorityElement::new(0, vertex));
120        let mut computed_edges = BTreeMap::<VertexIndex, (VertexIndex, Weight)>::new(); // { peer: (previous, weight) }
121        loop {
122            // until no more elements
123            if pq.is_empty() {
124                break;
125            }
126            let (target, PriorityElement { weight, previous }) = pq.pop().unwrap();
127            // eprintln!("target: {}, weight: {}, next: {}", target, weight, next);
128            debug_assert!({
129                !computed_edges.contains_key(&target) // this entry shouldn't have been set
130            });
131            // update entry
132            self.vertices[target as usize].timestamp = active_timestamp; // mark as visited
133            if target != vertex {
134                computed_edges.insert(target, (previous, weight));
135                if target == terminate {
136                    break; // early terminate
137                }
138            }
139            // add its neighbors to priority queue
140            for (&neighbor, &neighbor_weight) in self.vertices[target as usize].edges.iter() {
141                let edge_weight = weight + neighbor_weight;
142                if let Some(PriorityElement {
143                    weight: existing_weight,
144                    previous: existing_previous,
145                }) = pq.get_priority(&neighbor)
146                {
147                    // update the priority if weight is smaller or weight is equal but distance is smaller
148                    // this is necessary if the graph has weight-0 edges, which could lead to cycles in the graph and cause deadlock
149                    let mut update = &edge_weight < existing_weight;
150                    if &edge_weight == existing_weight {
151                        let distance = if neighbor > previous {
152                            neighbor - previous
153                        } else {
154                            previous - neighbor
155                        };
156                        let existing_distance = if &neighbor > existing_previous {
157                            neighbor - existing_previous
158                        } else {
159                            existing_previous - neighbor
160                        };
161                        // prevent loop by enforcing strong non-descending
162                        if distance < existing_distance || (distance == existing_distance && &previous < existing_previous) {
163                            update = true;
164                        }
165                    }
166                    if update {
167                        pq.change_priority(&neighbor, PriorityElement::new(edge_weight, target));
168                    }
169                } else {
170                    // insert new entry only if neighbor has not been visited
171                    if self.vertices[neighbor as usize].timestamp != active_timestamp {
172                        pq.push(neighbor, PriorityElement::new(edge_weight, target));
173                    }
174                }
175            }
176        }
177        // println!("[debug] computed_edges: {:?}", computed_edges);
178        computed_edges
179    }
180
181    /// get all complete graph edges from the specific vertex
182    pub fn all_edges(&mut self, vertex: VertexIndex) -> BTreeMap<VertexIndex, (VertexIndex, Weight)> {
183        self.all_edges_with_terminate(vertex, VertexIndex::MAX)
184    }
185
186    /// get minimum-weight path between any two vertices `a` and `b`, in the order `a -> path[0].0 -> path[1].0 -> .... -> path[-1].0` and it's guaranteed that path[-1].0 = b
187    pub fn get_path(&mut self, a: VertexIndex, b: VertexIndex) -> (Vec<(VertexIndex, Weight)>, Weight) {
188        assert_ne!(a, b, "cannot get path between the same vertex");
189        let edges = self.all_edges_with_terminate(a, b);
190        // println!("edges: {:?}", edges);
191        let mut vertex = b;
192        let mut path = Vec::new();
193        loop {
194            if vertex == a {
195                break;
196            }
197            let &(previous, weight) = &edges[&vertex];
198            path.push((vertex, weight));
199            if path.len() > 1 {
200                let previous_index = path.len() - 2;
201                path[previous_index].1 -= weight;
202            }
203            vertex = previous;
204        }
205        path.reverse();
206        (path, edges[&b].1)
207    }
208}
209
210#[derive(Clone)]
211pub struct PrebuiltCompleteGraph {
212    /// number of vertices
213    pub vertex_num: VertexNum,
214    /// all edge weights, if set to Weight::MAX then this edge does not exist
215    pub edges: Vec<BTreeMap<VertexIndex, Weight>>,
216    /// the virtual boundary weight
217    pub virtual_boundary_weight: Vec<Option<(VertexIndex, Weight)>>,
218}
219
220impl PrebuiltCompleteGraph {
221    #[allow(clippy::unnecessary_cast)]
222    pub fn new_threaded(initializer: &SolverInitializer, thread_pool_size: usize) -> Self {
223        let mut thread_pool_builder = rayon::ThreadPoolBuilder::new();
224        if thread_pool_size != 0 {
225            thread_pool_builder = thread_pool_builder.num_threads(thread_pool_size);
226        }
227        let thread_pool = thread_pool_builder.build().expect("creating thread pool failed");
228        let vertex_num = initializer.vertex_num as usize;
229        // first collect virtual vertices and real vertices
230        let mut is_virtual = vec![false; vertex_num];
231        for &virtual_vertex in initializer.virtual_vertices.iter() {
232            is_virtual[virtual_vertex as usize] = true;
233        }
234        type Result = (BTreeMap<VertexIndex, Weight>, Option<(VertexIndex, Weight)>);
235        let mut results: Vec<Result> = vec![];
236        thread_pool.scope(|_| {
237            (0..vertex_num)
238                .into_par_iter()
239                .map(|vertex_index| {
240                    let mut complete_graph = CompleteGraph::new(initializer.vertex_num, &initializer.weighted_edges);
241                    let mut edges = BTreeMap::new();
242                    let mut virtual_boundary_weight = None;
243                    if !is_virtual[vertex_index] {
244                        // only build graph for non-virtual vertices
245                        let complete_graph_edges = complete_graph.all_edges(vertex_index as VertexIndex);
246                        let mut boundary: Option<(VertexIndex, Weight)> = None;
247                        for (&peer, &(_, weight)) in complete_graph_edges.iter() {
248                            if !is_virtual[peer as usize] {
249                                edges.insert(peer, weight);
250                            }
251                            if is_virtual[peer as usize] && (boundary.is_none() || weight < boundary.as_ref().unwrap().1) {
252                                boundary = Some((peer, weight));
253                            }
254                        }
255                        virtual_boundary_weight = boundary;
256                    }
257                    (edges, virtual_boundary_weight)
258                })
259                .collect_into_vec(&mut results);
260        });
261        // optimization: remove edges in the middle
262        type UnzipResult = (Vec<BTreeMap<VertexIndex, Weight>>, Vec<Option<(VertexIndex, Weight)>>);
263        let (mut edges, virtual_boundary_weight): UnzipResult = results.into_iter().unzip();
264        let mut to_be_removed_vec: Vec<Vec<VertexIndex>> = vec![];
265        thread_pool.scope(|_| {
266            (0..vertex_num)
267                .into_par_iter()
268                .map(|vertex_index| {
269                    let mut to_be_removed = vec![];
270                    if !is_virtual[vertex_index] {
271                        for (&peer, &weight) in edges[vertex_index].iter() {
272                            let boundary_weight = if let Some((_, weight)) = virtual_boundary_weight[vertex_index as usize] {
273                                weight
274                            } else {
275                                Weight::MAX
276                            };
277                            let boundary_weight_peer = if let Some((_, weight)) = virtual_boundary_weight[peer as usize] {
278                                weight
279                            } else {
280                                Weight::MAX
281                            };
282                            if boundary_weight != Weight::MAX
283                                && boundary_weight_peer != Weight::MAX
284                                && weight > boundary_weight + boundary_weight_peer
285                            {
286                                to_be_removed.push(peer);
287                            }
288                        }
289                    }
290                    to_be_removed
291                })
292                .collect_into_vec(&mut to_be_removed_vec);
293        });
294        for vertex_index in 0..vertex_num {
295            for peer in to_be_removed_vec[vertex_index].iter() {
296                edges[vertex_index].remove(peer);
297            }
298        }
299        Self {
300            vertex_num: initializer.vertex_num,
301            edges,
302            virtual_boundary_weight,
303        }
304    }
305
306    pub fn new(initializer: &SolverInitializer) -> Self {
307        Self::new_threaded(initializer, 1)
308    }
309
310    #[allow(clippy::unnecessary_cast)]
311    pub fn get_edge_weight(&self, vertex_1: VertexIndex, vertex_2: VertexIndex) -> Option<Weight> {
312        self.edges[vertex_1 as usize].get(&vertex_2).cloned()
313    }
314
315    #[allow(clippy::unnecessary_cast)]
316    pub fn get_boundary_weight(&self, vertex_index: VertexIndex) -> Option<(VertexIndex, Weight)> {
317        self.virtual_boundary_weight[vertex_index as usize]
318    }
319}
320
321#[derive(Eq, Debug)]
322pub struct PriorityElement {
323    pub weight: Weight,
324    pub previous: VertexIndex,
325}
326
327impl std::cmp::PartialEq for PriorityElement {
328    #[inline]
329    fn eq(&self, other: &PriorityElement) -> bool {
330        self.weight == other.weight
331    }
332}
333
334impl std::cmp::PartialOrd for PriorityElement {
335    #[inline]
336    fn partial_cmp(&self, other: &PriorityElement) -> Option<std::cmp::Ordering> {
337        Some(self.cmp(other))
338    }
339}
340
341impl std::cmp::Ord for PriorityElement {
342    #[inline]
343    fn cmp(&self, other: &PriorityElement) -> std::cmp::Ordering {
344        other.weight.cmp(&self.weight) // reverse `self` and `other` to prioritize smaller weight
345    }
346}
347
348impl PriorityElement {
349    pub fn new(weight: Weight, previous: VertexIndex) -> Self {
350        Self { weight, previous }
351    }
352}