Skip to main content

osm_lump_ways/graph/
undirected_graph.rs

1use super::*;
2use crate::inter_store::InterStore;
3use haversine::haversine_m_fpair_ord;
4use ordered_float::OrderedFloat;
5use rand::prelude::*;
6use rayon::prelude::ParallelIterator;
7use smallvec::SmallVec;
8use sorted_slice_store::SortedSliceMap;
9use std::collections::BTreeMap;
10use std::collections::HashSet;
11use std::fmt::Debug;
12use utils::min_max;
13
14use kiddo::{KdTree, SquaredEuclidean};
15
16use itertools::Itertools;
17use smallvec::smallvec;
18use std::iter;
19
20type SmallVecIntermediates<V> = SmallVec<[V; 1]>;
21
22pub struct UndirectedAdjGraph<V, E> {
23    edges: BTreeMap<V, BTreeMap<V, (E, SmallVecIntermediates<V>)>>,
24}
25
26impl<V, E> Default for UndirectedAdjGraph<V, E>
27where
28    V: std::hash::Hash + Eq + Copy + Ord + Send + std::fmt::Debug + Default,
29    E: Copy
30        + PartialOrd
31        + Clone
32        + std::fmt::Debug
33        + std::ops::Add<Output = E>
34        + std::cmp::PartialEq
35        + Default,
36{
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl<V, E> UndirectedAdjGraph<V, E>
43where
44    V: std::hash::Hash + Eq + Copy + Ord + Send + std::fmt::Debug + Default,
45    E: Copy
46        + PartialOrd
47        + Clone
48        + std::fmt::Debug
49        + std::ops::Add<Output = E>
50        + std::cmp::PartialEq
51        + Default,
52{
53    pub fn new() -> Self {
54        Self {
55            edges: Default::default(),
56        }
57    }
58
59    pub fn set(&mut self, i: &V, j: &V, val: E) {
60        self.edges
61            .entry(*i)
62            .or_default()
63            .insert(*j, (val, Default::default()));
64        self.edges
65            .entry(*j)
66            .or_default()
67            .insert(*i, (val, Default::default()));
68    }
69
70    pub fn remove_vertex(&mut self, v: &V) {
71        while let Some((other_v, (_weight, _intermediaters))) =
72            self.edges.get_mut(v).unwrap().pop_last()
73        {
74            self.edges.get_mut(&other_v).unwrap().remove(v);
75        }
76        assert!(self.edges[v].is_empty());
77        self.edges.remove(v);
78    }
79
80    pub fn get(&self, i: &V, j: &V) -> Option<&E> {
81        self.edges
82            .get(i)
83            .and_then(|from_i| from_i.get(j).map(|(e, _intermediates)| e))
84    }
85
86    pub fn get_all(&self, i: &V, j: &V) -> Option<&(E, SmallVecIntermediates<V>)> {
87        self.edges.get(i).and_then(|from_i| from_i.get(j))
88    }
89
90    pub fn get_intermediates(&self, i: &V, j: &V) -> Option<&[V]> {
91        self.get_all(i, j)
92            .map(|(_e, intermediates)| intermediates.as_slice())
93    }
94
95    /// Return iterator over all the “contracted” edges.
96    /// Each element is 1: the edge weight for that “segment”, and 2: an iterator over the vertexes
97    /// of this segment (in order)
98    pub fn get_all_contracted_edges(&self) -> impl Iterator<Item = (&E, impl Iterator<Item = &V>)> {
99        self.edges
100            .iter()
101            .flat_map(|(a, from_a)| {
102                from_a
103                    .iter()
104                    // since edges are stored twice, only take the first one
105                    .filter(move |(b, _)| a < b)
106                    .map(move |(b, (edge_weight, inter))| ((a, inter, b), edge_weight))
107            })
108            .map(|((a, inter, b), edge_weight)| {
109                (
110                    edge_weight,
111                    iter::once(a).chain(inter.iter()).chain(iter::once(b)),
112                )
113            })
114    }
115
116    /// returns each vertex id and how many neighbours it has
117    pub fn iter_vertexes_num_neighbours(&self) -> impl Iterator<Item = (&V, usize)> {
118        self.edges.iter().map(|(vid, edges)| (vid, edges.len()))
119    }
120
121    pub fn contains_vertex(&self, v: &V) -> bool {
122        self.edges.contains_key(v)
123    }
124
125    /// All the neighbours of this vertex and the edge weight
126    pub fn neighbors(&self, i: &V) -> impl Iterator<Item = (&V, &E)> + use<'_, V, E> {
127        self.edges[i]
128            .iter()
129            .map(|(j, (edge_weight, _intermediates))| (j, edge_weight))
130    }
131
132    /// Number of neighbours for this vertex.
133    pub fn num_neighbors(&self, i: &V) -> usize {
134        self.edges.get(i).map_or(0, |es| es.len())
135    }
136
137    pub fn len(&self) -> usize {
138        self.edges
139            .values()
140            .map(|from_i| from_i.len())
141            .sum::<usize>()
142            / 2
143    }
144
145    pub fn num_edges(&self) -> usize {
146        self.edges
147            .values()
148            .map(|from_i| from_i.len())
149            .sum::<usize>()
150            / 2
151    }
152
153    pub fn is_empty(&self) -> bool {
154        self.edges.is_empty()
155    }
156    pub fn num_vertexes(&self) -> usize {
157        self.edges.len()
158    }
159
160    pub fn vertexes(&self) -> impl Iterator<Item = &V> {
161        self.edges.keys()
162    }
163
164    pub fn remove_edge(&mut self, i: &V, j: &V) {
165        if let Some(from_i) = self.edges.get_mut(i) {
166            from_i.remove(j);
167            if from_i.is_empty() {
168                self.edges.remove(i);
169            }
170        }
171        if let Some(from_j) = self.edges.get_mut(j) {
172            from_j.remove(i);
173            if from_j.is_empty() {
174                self.edges.remove(j);
175            }
176        }
177    }
178}
179
180pub type SmallNidVec = SmallVec<[i64; 1]>;
181pub type SmallI32Vec = SmallVec<[i32; 1]>;
182
183#[derive(Default, Debug, Clone)]
184pub struct Graph2 {
185    // key is vertex id
186    edges: BTreeMap<i64, SmallNidVec>,
187}
188
189impl Graph2 {
190    pub fn new() -> Self {
191        Default::default()
192    }
193
194    pub fn add_edge(&mut self, vertex1: i64, vertex2: i64) -> bool {
195        if vertex1 == vertex2 {
196            return false;
197        }
198        match self.edges.get_mut(&vertex1) {
199            Some(others) => {
200                if others.contains(&vertex2) {
201                    true
202                } else {
203                    others.push(vertex2);
204                    self.edges.entry(vertex2).or_default().push(vertex1);
205                    false
206                }
207            }
208            _ => {
209                self.edges.insert(vertex1, smallvec![vertex2]);
210                self.edges.entry(vertex2).or_default().push(vertex1);
211                false
212            }
213        }
214    }
215
216    pub fn vertexes(&self) -> impl ExactSizeIterator<Item = &i64> {
217        self.edges.keys()
218    }
219    pub fn vertexes_par_iter(&self) -> impl ParallelIterator<Item = &i64> {
220        self.edges.par_iter().map(|(k, _v)| k)
221    }
222
223    pub fn vertexes_w_num_neighbours(&self) -> impl Iterator<Item = (&i64, usize)> {
224        self.edges.iter().map(|(nid, neigh)| (nid, neigh.len()))
225    }
226
227    pub fn vertexes_w_num_neighbours_par(&self) -> impl ParallelIterator<Item = (&i64, usize)> {
228        self.edges.par_iter().map(|(nid, neigh)| (nid, neigh.len()))
229    }
230
231    pub fn edges_iter(&self) -> impl Iterator<Item = (&i64, &i64)> {
232        self.edges.iter().flat_map(|(nid, neighs)| {
233            neighs
234                .iter()
235                .filter(|other| *other > nid)
236                .map(move |other| (nid, other))
237        })
238    }
239    pub fn edges_par_iter(&self) -> impl ParallelIterator<Item = (&i64, &i64)> {
240        self.edges.par_iter().flat_map(|(nid, neighs)| {
241            neighs
242                .par_iter()
243                .filter(|other| *other > nid)
244                .map(move |other| (nid, other))
245        })
246    }
247
248    pub fn neighbours(&self, vertex: &i64) -> impl Iterator<Item = &i64> + use<'_> {
249        self.edges.get(vertex).into_iter().flat_map(|ns| ns.iter())
250    }
251
252    pub fn num_neighbors(&self, vertex: &i64) -> Option<usize> {
253        self.edges.get(vertex).map(|others| others.len())
254    }
255
256    pub fn add_edge_chain(&mut self, vertexes: &[i64]) -> bool {
257        // This could be speed up by keeping a reference to the current vertex edges and walking
258        // along. saved looking up the same vertex twice in the btreemap
259        if vertexes.len() < 2 {
260            return false;
261        }
262        let mut added = false;
263        for w in vertexes.windows(2) {
264            added |= self.add_edge(w[0], w[1]);
265        }
266        added
267    }
268
269    pub fn num_vertexes(&self) -> usize {
270        self.edges.len()
271    }
272    pub fn num_edges(&self) -> usize {
273        self.edges
274            .values()
275            .map(|from_i| from_i.len())
276            .sum::<usize>()
277            / 2
278    }
279    pub fn is_empty(&self) -> bool {
280        self.edges.is_empty()
281    }
282
283    pub fn first_vertex(&self) -> Option<&i64> {
284        self.edges.first_key_value().map(|(k, _v)| k)
285    }
286    pub fn contains_vertex(&self, vertex: i64) -> bool {
287        self.edges.contains_key(&vertex)
288    }
289    pub fn contains_edge(&self, v1: i64, v2: i64) -> bool {
290        self.edges
291            .get(&v1)
292            .is_some_and(|neighs| neighs.contains(&v2))
293    }
294
295    pub fn remove_vertex(&mut self, vertex: i64) -> Option<SmallNidVec> {
296        let others = self.edges.remove(&vertex);
297        if let Some(ref others) = others {
298            for o in others {
299                let oo = self.edges.get_mut(o).unwrap();
300                oo.retain(|n| *n != vertex);
301                if oo.is_empty() {
302                    self.edges.remove(o);
303                }
304            }
305        }
306
307        others
308    }
309    pub fn remove_edge(&mut self, vertex1: i64, vertex2: i64) -> Option<(i64, i64)> {
310        if self
311            .edges
312            .get(&vertex1)
313            .is_some_and(|v1_neighs| v1_neighs.contains(&vertex2))
314        {
315            self.edges
316                .get_mut(&vertex1)
317                .unwrap()
318                .retain(|n| *n != vertex2);
319            self.edges
320                .get_mut(&vertex2)
321                .unwrap()
322                .retain(|n| *n != vertex1);
323            if self.edges.get(&vertex1).unwrap().is_empty() {
324                self.edges.remove(&vertex1);
325            }
326            if self.edges.get(&vertex2).unwrap().is_empty() {
327                self.edges.remove(&vertex2);
328            }
329
330            Some((vertex1, vertex2))
331        } else {
332            None
333        }
334    }
335
336    pub fn into_disconnected_graphs(
337        mut self,
338        progress_bar: impl Into<Option<ProgressBar>>,
339    ) -> impl Iterator<Item = Self> {
340        let mut frontier = Vec::new();
341        let progress_bar: Option<ProgressBar> = progress_bar.into();
342
343        std::iter::from_fn(move || {
344            if self.is_empty() {
345                return None;
346            }
347
348            let mut new_graph = Graph2::new();
349
350            frontier.truncate(0);
351            frontier.push(*self.first_vertex().unwrap());
352
353            while let Some(nid) = frontier.pop() {
354                // might have been already removed when removing another
355                if let Some(others) = self.remove_vertex(nid) {
356                    if let Some(progress_bar) = &progress_bar {
357                        progress_bar.inc(1);
358                    }
359                    for o in others {
360                        if !new_graph.contains_vertex(o) {
361                            frontier.push(o);
362                        }
363                        new_graph.add_edge(nid, o);
364                    }
365                }
366            }
367
368            Some(new_graph)
369        })
370    }
371
372    pub fn into_lines_random(self) -> impl Iterator<Item = Box<[i64]>> {
373        let mut graph = self;
374
375        std::iter::from_fn(move || {
376            if graph.is_empty() {
377                return None;
378            }
379
380            let mut curr_path: Vec<i64> = Vec::new();
381            // Try to start “at an end”, not in the middle.
382            // i.e. look for a point with only 1 neighbour.
383            // Only check a few vertexes, to prevent long generation time.
384            // This results in nicer (& smaller) data.
385            curr_path.push(
386                graph
387                    .vertexes_w_num_neighbours()
388                    .take(100)
389                    .filter(|(_nid, nn)| *nn == 1)
390                    .map(|(nid, _)| *nid)
391                    .next()
392                    .unwrap_or_else(|| *graph.first_vertex().unwrap()),
393            );
394            let mut next: Option<i64>;
395            let mut last: &i64;
396
397            loop {
398                last = curr_path.last().unwrap();
399                next = graph
400                    .neighbours(last)
401                    .find(|v| !curr_path.contains(*v))
402                    .copied();
403                match next {
404                    None => {
405                        break;
406                    }
407                    Some(next) => {
408                        graph.remove_edge(*last, next).unwrap();
409                        curr_path.push(next);
410                    }
411                }
412            }
413
414            Some(curr_path.into_boxed_slice())
415        })
416    }
417
418    pub fn into_lines_as_crow_flies(
419        self,
420        nodeid_pos: &impl NodeIdPosition,
421    ) -> impl Iterator<Item = Box<[i64]>> + '_ {
422        let mut graphs = vec![self];
423
424        std::iter::from_fn(move || {
425            if graphs.is_empty() {
426                return None;
427            }
428            let mut graph = graphs.pop().unwrap();
429            assert!(!graph.is_empty());
430
431            let target_pair = graph
432                .edges
433                .keys()
434                .par_bridge()
435                .map(|n1| (n1, nodeid_pos.get(n1).unwrap()))
436                .flat_map(|(n1, p1)| {
437                    graph
438                        .edges
439                        .keys()
440                        .par_bridge()
441                        .filter(|n2| *n2 > n1)
442                        .map(move |n2| ((n1, p1), (n2, nodeid_pos.get(n2).unwrap())))
443                })
444                .max_by_key(|((_n1, p1), (_n2, p2))| haversine_m_fpair_ord(*p1, *p2));
445            let target_pair = target_pair.unwrap();
446
447            let src = target_pair.0;
448            let dest = target_pair.1;
449            let dest_nid = dest.0;
450            let dest_p = dest.1;
451
452            let res = dij::path_one_to_one(
453                (*src.0, (OrderedFloat(src.1.0), OrderedFloat(src.1.1))),
454                (*dest_nid, (OrderedFloat(dest_p.0), OrderedFloat(dest_p.1))),
455                nodeid_pos,
456                &graph,
457                None,
458            );
459            let path = res.1;
460
461            for edge in path.iter().tuple_windows::<(_, _)>() {
462                graph.remove_edge(*edge.0, *edge.1).unwrap();
463            }
464
465            if !graph.is_empty() {
466                let _old_num_graphs = graphs.len();
467                graphs.extend(graph.into_disconnected_graphs(None));
468            }
469
470            Some(path.into_boxed_slice())
471        })
472    }
473
474    /// Return a random sample of points in this graph, somewhat spread out.
475    #[allow(clippy::type_complexity)]
476    pub fn random_sample_vertexes(
477        &self,
478        num: usize,
479        nodeid_pos: &impl NodeIdPosition,
480    ) -> Box<[i64]> {
481        if num >= self.num_vertexes() {
482            let all_nodes = self.vertexes().copied().collect::<Vec<_>>();
483            return all_nodes.into_boxed_slice();
484        }
485
486        let all_nodes = self
487            .vertexes()
488            .copied()
489            .collect::<Vec<i64>>()
490            .into_boxed_slice();
491        assert!(!all_nodes.is_empty());
492
493        // In each iteration, how many to
494        let k = 100;
495
496        // Need to quickly check existing nodes, so keep as a hashmap
497        let mut new_nodes = HashSet::with_capacity(num);
498
499        let mut kdtree: KdTree<f64, 2> = KdTree::with_capacity(num);
500        let mut rng = &mut rand::rng();
501
502        let first = *all_nodes.choose(&mut rng).unwrap();
503        let pos = nodeid_pos.get_arr(&first).unwrap();
504        new_nodes.insert(first);
505        kdtree.add(&pos, first.try_into().unwrap());
506
507        // Buffer of possible nodes for each iteration.
508        let mut possible_nodes = Vec::with_capacity(k);
509
510        while new_nodes.len() < num {
511            // We need to exclude nodes we
512            // It's quicker to repidly call choose_multiple rather than multiple .choose.
513            possible_nodes.truncate(0);
514            while possible_nodes.len() < k {
515                possible_nodes.extend(
516                    all_nodes
517                        .sample(&mut rng, k - possible_nodes.len() + 1)
518                        .filter(|nid| !new_nodes.contains(nid))
519                        .map(|nid| {
520                            let pos = nodeid_pos.get_arr(nid).unwrap();
521                            let dist = kdtree.nearest_one::<SquaredEuclidean>(&pos).distance;
522
523                            // dist is minus → largest dist is first
524                            (OrderedFloat(-dist), *nid, pos)
525                        }),
526                );
527            }
528
529            // Take the node with the largest distance
530            possible_nodes.par_sort_by_key(|(dist, _nid, _pos)| *dist);
531            let (_dist, nid, pos) = possible_nodes[0];
532
533            // save it
534            kdtree.add(&pos, nid.try_into().unwrap());
535            new_nodes.insert(nid);
536        }
537
538        let new_nodes = new_nodes.into_iter().collect::<Vec<_>>();
539        new_nodes.into_boxed_slice()
540    }
541
542    pub fn betweenness_centrality(
543        &self,
544        nodes: &[i64],
545        nodeid_pos: &impl NodeIdPosition,
546        inter_store: &inter_store::InterStore,
547        progress_bar: impl Into<Option<ProgressBar>>,
548    ) -> SortedSliceMap<(i64, i64), u64> {
549        let progress_bar: Option<ProgressBar> = progress_bar.into();
550
551        let edge_lengths = SortedSliceMap::from_iter(self.edges_iter().map(|(nid1, nid2)| {
552            let edge_len = inter_store
553                .expand_undirected(*nid1, *nid2)
554                .map(|nid| nodeid_pos.get(&nid).unwrap())
555                .tuple_windows::<(_, _)>()
556                .par_bridge()
557                .map(|(p1, p2)| haversine::haversine_m_fpair(p1, p2))
558                .sum::<f64>();
559
560            let edge_len = (edge_len * 100.).round() as u64;
561
562            ((*nid1, *nid2), edge_len)
563        }));
564
565        // The results of the Betweenness Centrality. key = segment (nid1↭nid2), value = the BC
566        // value
567        let bc_res =
568            SortedSliceMap::from_iter(self.edges_iter().map(|(&nid1, &nid2)| ((nid1, nid2), 0)));
569        let bc_res = Arc::new(Mutex::new(bc_res));
570
571        nodes.par_iter().enumerate().for_each_with(
572            (bc_res.clone(), HashMap::new()),
573            |(bc_res, prev_dist), (i, nid0)| {
574                let target_nodes = &nodes[(i + 1)..];
575
576                // ↓ Calculate the shortest path to everywhere
577                dij::dij_single(*nid0, self, &edge_lengths, prev_dist);
578
579                // Now generate all the paths
580                let mut new_segs: Vec<((i64, i64), u64)> = Vec::with_capacity(target_nodes.len());
581
582                // Keep track of all the
583                let mut buf_segs: Vec<(u64, i64, _)> = Vec::with_capacity(target_nodes.len());
584
585                buf_segs.extend(
586                    target_nodes
587                        .iter()
588                        .copied()
589                        .map(|nid_n| (prev_dist[&nid_n].1, nid_n, 1)),
590                );
591                buf_segs.par_sort_by_key(|(dist, nid, _acc)| (*dist, *nid));
592
593                while let Some((_dist, nid_b, acc)) = buf_segs.pop() {
594                    if &nid_b == nid0 {
595                        continue;
596                    }
597                    let (nid_a, new_dist) = prev_dist[&nid_b];
598
599                    // save this segment
600                    new_segs.push((min_max(nid_a, nid_b), acc));
601
602                    let k = buf_segs.partition_point(|(thisdist, nid, _acc)| {
603                        (*thisdist, *nid).le(&(new_dist, nid_a))
604                    });
605                    if k >= buf_segs.len() {
606                        // put it on the end
607                        buf_segs.push((new_dist, nid_a, acc));
608                    } else if buf_segs[k].1 == nid_a {
609                        // we update this item
610                        buf_segs[k].2 += acc;
611                    } else {
612                        // new item, put it here
613                        buf_segs.insert(k, (new_dist, nid_a, acc));
614                    }
615                }
616
617                let mut bc_res = bc_res.lock().unwrap();
618                for ((nid_a, nid_b), val) in new_segs.into_iter() {
619                    *bc_res.get_mut(&(nid_a, nid_b)).unwrap() += val;
620                }
621                if let Some(progress_bar) = &progress_bar {
622                    progress_bar.inc(target_nodes.len() as u64);
623                }
624            },
625        );
626
627        Arc::try_unwrap(bc_res).unwrap().into_inner().unwrap()
628    }
629
630    pub fn compress_graph(
631        &mut self,
632        inter_store: &mut Arc<Mutex<&mut InterStore>>,
633        remove_old_inters: bool,
634        never_remove_vertexes: impl Fn(i64) -> bool + Sync,
635    ) {
636        let num_orig_vertexes = self.num_vertexes();
637
638        // these nodes have 2 neighbours, but shouldn't be removed.
639        let mut vertexes_already_excluded = HashSet::new();
640
641        // temp needed buffers
642        let mut vertex_queue: Vec<i64> = Vec::new();
643        let mut tmp_inters = Vec::new();
644
645        loop {
646            vertex_queue.par_extend(self.vertexes_w_num_neighbours_par().filter_map(
647                |(nid, nneigh)| {
648                    if nneigh == 2
649                        && !vertexes_already_excluded.contains(nid)
650                        && !never_remove_vertexes(*nid)
651                    {
652                        Some(*nid)
653                    } else {
654                        None
655                    }
656                },
657            ));
658            if vertex_queue.is_empty() {
659                break;
660            }
661
662            while let Some(nid) = vertex_queue.pop() {
663                if self.num_neighbors(&nid) != Some(2) || never_remove_vertexes(nid) {
664                    continue;
665                }
666                let mut others = self.remove_vertex(nid).unwrap();
667                let nid_b = others.pop().unwrap();
668                let nid_a = others.pop().unwrap();
669
670                if self.contains_edge(nid_a, nid_b) {
671                    // There's already an edge from a ↔ b, so don't another.
672                    // we need to undo what we did here
673                    self.add_edge(nid, nid_a);
674                    self.add_edge(nid, nid_b);
675
676                    // don't look at this node again
677                    vertexes_already_excluded.insert(nid);
678                    continue;
679                }
680
681                let mut inter_store = inter_store.lock().unwrap();
682                tmp_inters.truncate(0);
683                tmp_inters.extend(inter_store.inters_undirected(&nid_a, &nid));
684                tmp_inters.push(nid);
685                tmp_inters.extend(inter_store.inters_undirected(&nid, &nid_b));
686
687                if remove_old_inters {
688                    inter_store.remove_undirected(&nid_a, &nid);
689                    inter_store.remove_undirected(&nid, &nid_b);
690                }
691                inter_store.insert_undirected((nid_a, nid_b), &tmp_inters);
692
693                self.add_edge(nid_a, nid_b);
694
695                // potential shortcut
696                vertex_queue.push(nid_a);
697                vertex_queue.push(nid_b);
698            }
699        }
700
701        debug!(
702            "Removed {} vertexes ({}%)",
703            num_orig_vertexes - self.num_vertexes(),
704            (num_orig_vertexes - self.num_vertexes()) * 100 / num_orig_vertexes,
705        );
706    }
707
708    pub fn remove_spikes(&mut self, never_remove_vertexes: impl Fn(i64) -> bool + Sync) {
709        let num_orig_vertexes = self.num_vertexes();
710
711        // temp needed buffers
712        let mut vertex_queue: Vec<i64> = Vec::new();
713
714        loop {
715            vertex_queue.par_extend(self.vertexes_w_num_neighbours_par().filter_map(
716                |(nid, nneigh)| {
717                    if nneigh == 1 && !never_remove_vertexes(*nid) {
718                        Some(*nid)
719                    } else {
720                        None
721                    }
722                },
723            ));
724            if vertex_queue.is_empty() {
725                break;
726            }
727
728            while let Some(nid) = vertex_queue.pop() {
729                if self.num_neighbors(&nid) != Some(1) || never_remove_vertexes(nid) {
730                    continue;
731                }
732                let mut others = self.remove_vertex(nid).unwrap();
733                let nid_a = others.pop().unwrap();
734
735                vertex_queue.push(nid_a);
736            }
737        }
738
739        debug!(
740            "Removed {} vertexes ({}%)",
741            num_orig_vertexes - self.num_vertexes(),
742            (num_orig_vertexes - self.num_vertexes()) * 100 / num_orig_vertexes,
743        );
744    }
745}