Skip to main content

debruijn/
compression.rs

1// Copyright 2017 10x Genomics
2
3//! Create compressed DeBruijn graphs from uncompressed DeBruijn graphs, or a collection of disjoint DeBruijn graphs.
4use bit_set::BitSet;
5use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
6use log::debug;
7use std::collections::VecDeque;
8use std::fmt::Debug;
9use std::marker::PhantomData;
10use std::mem;
11use std::time::Instant;
12
13use crate::dna_string::DnaString;
14use crate::graph::{BaseGraph, DebruijnGraph};
15use crate::summarizer::SummaryData;
16use crate::{Dir, EdgeMap, EdgeMult, PROGRESS_STYLE, SingleDirEdgeMap, SingleDirEdgeMult};
17use crate::Exts;
18use crate::Kmer;
19use crate::Vmer;
20use boomphf::hashmap::BoomHashMap2;
21
22#[derive(Clone)]
23enum ExtMode<K: Kmer> {
24    Unique(K, Dir, Exts),
25    Terminal(TerminalExt),
26}
27
28#[derive(Clone)]
29enum ExtModeNode {
30    Unique(usize, Dir, Exts),
31    Terminal(TerminalExt),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
35struct TerminalExt {
36    exts: Exts,
37    edge_mults: Option<SingleDirEdgeMult>,
38    edge_maps: Option<SingleDirEdgeMap>
39}
40
41impl TerminalExt {
42    fn new(exts: Exts, edge_mults: Option<SingleDirEdgeMult>, edge_maps: Option<SingleDirEdgeMap>) -> Self {
43        TerminalExt { exts, edge_mults, edge_maps }
44    }
45}
46
47/// Customize the path-compression process. Implementing this trait lets the user
48/// control how the per-kmer data (of type `D`) is summarized into a per-path
49/// summary data (of type `DS`). It also let's the user introduce new breaks into
50/// paths by inspecting in the per-kmer data of a proposed with `join_test_kmer`
51/// function.
52pub trait CompressionSpec<D> {
53    /// combine the data of two nodes
54    fn reduce(&self, path_object: D, kmer_object: &D) -> D;
55    /// check if the data of two nodes can be combined
56    fn join_test(&self, d1: &D, d2: &D) -> bool;
57}
58
59/// Simple implementation of `CompressionSpec` that lets you provide that data reduction function as a closure
60pub struct SimpleCompress<D, F> {
61    func: F,
62    d: PhantomData<D>,
63}
64
65impl<D, F> SimpleCompress<D, F> {
66    pub fn new(func: F) -> SimpleCompress<D, F> {
67        SimpleCompress {
68            func,
69            d: PhantomData,
70        }
71    }
72}
73
74impl<D, F> CompressionSpec<D> for SimpleCompress<D, F>
75where
76    for<'r> F: Fn(D, &'r D) -> D,
77{
78    fn reduce(&self, d: D, other: &D) -> D {
79        (self.func)(d, other)
80    }
81
82    fn join_test(&self, _: &D, _: &D) -> bool {
83        true
84    }
85}
86
87/// Extending trait CompressionSpec for compression
88pub struct ScmapCompress<D> {
89    d: PhantomData<D>,
90}
91
92impl<D> ScmapCompress<D> {
93    pub fn new() -> ScmapCompress<D> {
94        ScmapCompress { d: PhantomData }
95    }
96}
97
98impl<D> Default for ScmapCompress<D> {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl<D: PartialEq> CompressionSpec<D> for ScmapCompress<D>
105where
106    D: Debug,
107{
108    fn reduce(&self, d: D, other: &D) -> D {
109        if d != *other {
110            panic!("{:?} != {:?}, Should not happen", d, *other);
111        }
112        d
113    }
114
115    fn join_test(&self, d1: &D, d2: &D) -> bool {
116        d1 == d2
117    }
118}
119
120/// CompressionSpec with custom check and function
121pub struct CheckCompress<D, F1, F2> {
122    reduce_func: F1,
123    join_func: F2,
124    d: PhantomData<D>
125} 
126
127impl<D, F1, F2> CheckCompress<D, F1, F2>
128where
129    for<'r> F1: Fn(D, &'r D) -> D,
130    for<'r> F2: Fn(&'r D, &'r D) -> bool
131{
132    /// make a new [`CheckCompress`]
133    /// 
134    /// ### Arguments
135    /// * `reduce_func`: closure taking graph data of two nodes and combining them into one
136    /// * `join_func`: closure taking graph data of two nodes and returning true if they can be combined
137    pub fn new(reduce_func: F1, join_func: F2) -> Self {
138        CheckCompress {
139            reduce_func,
140            join_func,
141            d: PhantomData,
142        }
143    }
144}
145
146impl<D, F1, F2> CompressionSpec<D> for CheckCompress<D, F1, F2>
147where
148    for<'r> F1: Fn(D, &'r D) -> D,
149    for<'r> F2: Fn(&'r D, &'r D) -> bool
150{
151    fn reduce(&self, d: D, other: &D) -> D {
152        (self.reduce_func)(d, other)
153    }
154
155    fn join_test(&self, d: &D, other: &D) -> bool {
156        (self.join_func)(d, other)
157    }
158}
159
160
161struct CompressFromGraph<'a, 'b, K: 'a + Kmer, D: 'a + PartialEq + SummaryData<DI>, DI, S: CompressionSpec<D>> {
162    stranded: bool,
163    d: PhantomData<D>,
164    di: PhantomData<DI>,
165    spec: &'b S,
166    available_nodes: BitSet,
167    graph: &'a DebruijnGraph<K, D>,
168}
169
170impl<K, D: SummaryData<DI>, DI, S> CompressFromGraph<'_, '_, K, D, DI, S>
171where
172    K: Kmer + Send + Sync,
173    D: Debug + Clone + PartialEq,
174    S: CompressionSpec<D>,
175{
176    #[inline(never)]
177    fn try_extend_node(&mut self, node: usize, dir: Dir) -> ExtModeNode {
178        let node = self.graph.get_node(node);
179        let bases = node.sequence();
180        let exts = node.exts();
181        let data = node.data();
182
183        if exts.num_ext_dir(dir) != 1
184            || (!self.stranded && node.len() == K::k() && bases.get_kmer::<K>(0).is_palindrome())
185        {
186            ExtModeNode::Terminal( TerminalExt::new(
187                exts.single_dir(dir), 
188                data.edge_mults().map(|em| em.single_dir(dir)), 
189                data.mapped_edge_ids().map(|em| em.single_dir(dir))
190            ))
191        } else {
192            // Get the next kmer
193            let ext_base = exts.get_unique_extension(dir).expect("should be unique");
194            let end_kmer: K = bases.term_kmer(dir);
195
196            let next_kmer = end_kmer.extend(ext_base, dir);
197            let (next_node_id, next_side_incoming, rc) = match self.graph.find_link(next_kmer, dir)
198            {
199                Some(e) => e,
200                None => {
201                    println!("dir: {:?}, Lmer: {:?}, exts: {:?}", dir, bases, exts);
202                    println!("end kmer: {:?}", end_kmer);
203                    println!("No kmer: {:?}", next_kmer);
204                    println!("rc: {:?}", next_kmer.min_rc());
205                    panic!("No kmer: {:?}", next_kmer)
206                }
207            };
208
209            let next_node = self.graph.get_node(next_node_id);
210            let next_exts = next_node.exts();
211
212            let consistent = (next_node.len() == K::k())
213                || match (dir, next_side_incoming, rc) {
214                    (Dir::Left, Dir::Right, false) => true,
215                    (Dir::Left, Dir::Left, true) => true,
216                    (Dir::Right, Dir::Left, false) => true,
217                    (Dir::Right, Dir::Right, true) => true,
218                    _ => {
219                        println!("dir: {:?}, Lmer: {:?}, exts: {:?}", dir, bases, exts);
220                        println!("end kmer: {:?}", end_kmer);
221                        println!("next kmer: {:?}", next_kmer);
222                        println!("rc: {:?}", next_kmer.min_rc());
223                        println!(
224                            "next bases: {:?}, next_side_incoming: {:?}, rc: {:?}",
225                            next_node.sequence(),
226                            next_side_incoming,
227                            rc
228                        );
229                        false
230                    }
231                };
232            assert!(consistent);
233
234            // We can include this kmer in the line if:
235            // a) it exists in the partition, and is still unused
236            // b) the kmer we go to has a unique extension back in our direction
237            // c) the new edge is not of length K and a palindrome
238            // d) the color of the current and next node is same
239
240            if !self.available_nodes.contains(next_node_id)
241                || (!self.stranded && next_kmer.is_palindrome())
242                || !self.spec.join_test(node.data(), next_node.data())
243            {
244                // Next kmer isn't in this partition,
245                // or we've already used it,
246                // or it's palindrom and we are not stranded
247                // or the colors were not same
248                return ExtModeNode::Terminal( TerminalExt::new(
249                    exts.single_dir(dir), 
250                    data.edge_mults().map(|em| em.single_dir(dir)), 
251                    data.mapped_edge_ids().map(|em| em.single_dir(dir))
252                ));
253            }
254
255            // orientation of next edge
256            let next_side_outgoing = next_side_incoming.flip();
257
258            let incoming_count = next_exts.num_ext_dir(next_side_incoming);
259            let outgoing_exts = next_exts.single_dir(next_side_outgoing);
260
261            if incoming_count == 0 {
262                println!("dir: {:?}, Lmer: {:?}, exts: {:?}", dir, bases, exts);
263                println!("end kmer: {:?}", end_kmer);
264                println!("next_node: {:?}", next_node);
265                println!("next_node data: {:?}", next_node.sequence());
266                panic!("unreachable");
267            } else if incoming_count == 1 {
268                // We have a unique path to next_kmer -- include it
269                ExtModeNode::Unique(next_node_id, next_side_outgoing, outgoing_exts)
270            } else {
271                // there's more than one path
272                // into the target kmer - don't include it
273                ExtModeNode::Terminal( TerminalExt::new(
274                exts.single_dir(dir), 
275                data.edge_mults().map(|em| em.single_dir(dir)), 
276                data.mapped_edge_ids().map(|em| em.single_dir(dir))
277            ))
278            }
279        }
280    }
281
282    /// Generate complete unbranched edges
283    fn extend_node(&mut self, start_node: usize, start_dir: Dir) -> (Vec<(usize, Dir)>, TerminalExt) {
284        let mut current_dir = start_dir;
285        let mut current_node = start_node;
286        let mut path = Vec::new();
287        // must get set below
288        let terminal: TerminalExt;
289
290        self.available_nodes.remove(start_node);
291
292        loop {
293            let ext_result = self.try_extend_node(current_node, current_dir);
294
295            match ext_result {
296                ExtModeNode::Unique(next_node, next_dir_outgoing, _) => {
297                    let next_dir_incoming = next_dir_outgoing.flip();
298                    path.push((next_node, next_dir_incoming));
299                    self.available_nodes.remove(next_node);
300                    current_node = next_node;
301                    current_dir = next_dir_outgoing;
302                }
303                ExtModeNode::Terminal(term) => {
304                    terminal = term;
305
306                    break;
307                }
308            }
309        }
310
311        (path, terminal)
312    }
313
314    // Determine the sequence and extensions of the maximal unbranched
315    // edge, centered around the given edge number
316    #[inline(never)]
317    fn build_node(&mut self, seed_node: usize) -> (DnaString, Exts, VecDeque<(usize, Dir)>, D) {
318        let (l_path, l_terminal) = self.extend_node(seed_node, Dir::Left);
319        let (r_path, r_terminal) = self.extend_node(seed_node, Dir::Right);
320
321        // Stick together edge chunks to get full edge sequence
322        let mut node_path = VecDeque::new();
323
324        let mut node_data: D = self.graph.get_node(seed_node).data().clone();
325        node_path.push_back((seed_node, Dir::Left));
326
327        // Add on the left path
328        for &(next_node, incoming_dir) in l_path.iter() {
329            node_path.push_front((next_node, incoming_dir.flip()));
330            node_data = self
331                .spec
332                .reduce(node_data, self.graph.get_node(next_node).data());
333        }
334
335        // Add on the right path
336        for &(next_node, incoming_dir) in r_path.iter() {
337            node_path.push_back((next_node, incoming_dir));
338            node_data = self
339                .spec
340                .reduce(node_data, self.graph.get_node(next_node).data());
341        }
342
343        let left_terminal = match l_path.last() {
344            None => l_terminal,
345            Some(&(_, Dir::Left)) => TerminalExt::new(
346                l_terminal.exts.complement(), 
347                l_terminal.edge_mults.map(|em| em.complement()), 
348                l_terminal.edge_maps.map(|em| em.complement())
349            ),
350            Some(&(_, Dir::Right)) => l_terminal,
351        };
352
353        let right_terminal = match r_path.last() {
354            None => r_terminal,
355            Some(&(_, Dir::Left)) => r_terminal,
356            Some(&(_, Dir::Right)) => TerminalExt::new(
357                r_terminal.exts.complement(), 
358                r_terminal.edge_mults.map(|em| em.complement()), 
359                r_terminal.edge_maps.map(|em| em.complement())
360            )
361        };
362
363        let path_seq = self.graph.sequence_of_path(node_path.iter());
364
365        let new_em = EdgeMult::from_single_dirs(&left_terminal.edge_mults, &right_terminal.edge_mults);
366        let new_emap = EdgeMap::from_single_dirs(&left_terminal.edge_maps, &right_terminal.edge_maps);
367
368        node_data.set_edge_mults(new_em);
369        node_data.set_mapped_edge_ids(new_emap);
370
371        // return sequence and extensions
372        (
373            path_seq,
374            Exts::from_single_dirs(left_terminal.exts, right_terminal.exts),
375            node_path,
376            node_data,
377        )
378    }
379
380    /// Simplify a compressed Debruijn graph by merging adjacent unbranched nodes, and optionally
381    /// censoring some nodes
382    fn compress_graph(
383        stranded: bool,
384        compression: &S,
385        mut old_graph: DebruijnGraph<K, D>,
386        censor_nodes: Option<Vec<usize>>,
387    ) -> DebruijnGraph<K, D> {
388        let n_nodes = old_graph.len();
389        let mut available_nodes = BitSet::with_capacity(n_nodes);
390        for i in 0..n_nodes {
391            available_nodes.insert(i);
392        }
393
394        if let Some(c) = censor_nodes {
395            for censor in c {
396                available_nodes.remove(censor);
397            }
398        }
399
400        old_graph.fix_exts(Some(&available_nodes));
401
402        let mut comp = CompressFromGraph {
403            spec: compression,
404            stranded,
405            graph: &old_graph,
406            available_nodes,
407            d: PhantomData,
408            di: PhantomData
409        };
410
411        // FIXME -- clarify requirements around state of extensions
412        let mut graph = BaseGraph::new(stranded);
413
414        for node_counter in 0..n_nodes {
415            if comp.available_nodes.contains(node_counter) {
416                let (seq, exts, _, data) = comp.build_node(node_counter);
417                graph.add(&seq, exts, data);
418            }
419        }
420
421        // We will have some hanging exts due to removed nodes
422        let mut dbg = graph.finish();
423        dbg.fix_exts(None); 
424        debug_assert!(dbg.is_compressed(compression).is_none());
425        dbg
426    }
427}
428
429/// Perform path-compression on a (possibly partially compressed) DeBruijn graph
430pub fn compress_graph<
431    K: Kmer + Send + Sync,
432    D: Clone + Debug + PartialEq + SummaryData<DI>,
433    DI,
434    S: CompressionSpec<D>,
435>(
436    stranded: bool,
437    spec: &S,
438    old_graph: DebruijnGraph<K, D>,
439    censor_nodes: Option<Vec<usize>>,
440) -> DebruijnGraph<K, D> {
441    CompressFromGraph::<K, D, DI, S>::compress_graph(stranded, spec, old_graph, censor_nodes)
442}
443
444//////////////////////////////
445// Compress from Hash a new Struct
446//////////////////////////////
447/// Generate a compressed DeBruijn graph from hash_index
448struct CompressFromHash<'a, 'b, K: 'a + Kmer, D: 'a + SummaryData<DI>, DI, S: CompressionSpec<D>> {
449    stranded: bool,
450    k: PhantomData<K>,
451    d: PhantomData<D>,
452    di: PhantomData<DI>,
453    spec: &'b S,
454    available_kmers: BitSet,
455    index: &'a BoomHashMap2<K, Exts, D>,
456}
457
458/// Compression of paths in Debruijn graph
459impl<K: Kmer, D: Clone + Debug + Send + Sync + SummaryData<DI>, DI, S: CompressionSpec<D> + Sync> CompressFromHash<'_, '_, K, D, DI, S> {
460    fn get_kmer_data(&self, kmer: &K) -> (&Exts, &D) {
461        match self.index.get(kmer) {
462            Some(data) => data,
463            None => panic!("couldn't find kmer {:?}", kmer),
464        }
465    }
466
467    fn get_kmer_id(&self, kmer: &K) -> Option<usize> {
468        self.index.get_key_id(kmer)
469    }
470
471    /// Attempt to extend kmer v in direction dir. Return:
472    ///  - Unique(nextKmer, nextDir) if a single unique extension
473    ///    is possible.  nextDir indicates the direction to extend nextMker
474    ///    to preserve the direction of the extension.
475    /// - Term(ext) no unique extension possible, indicating the extensions at this end of the line
476    fn try_extend_kmer(&self, kmer: K, dir: Dir) -> ExtMode<K> {
477        // metadata of start kmer
478        let (exts, kmer_data) = self.get_kmer_data(&kmer);
479
480        // kmer is marked terminal if it has not one extension in one direction (if clear path always 1) 
481        // or if the graph is not stranded and the kmer is a palindrome
482        if exts.num_ext_dir(dir) != 1 || (!self.stranded && kmer.is_palindrome()) {
483            ExtMode::Terminal( TerminalExt::new(
484                exts.single_dir(dir), 
485                kmer_data.edge_mults().map(|em| em.single_dir(dir)),
486                kmer_data.mapped_edge_ids().map(|em| em.single_dir(dir))
487            ))
488        } else {
489            // Get the next kmer
490            let ext_base = exts.get_unique_extension(dir).expect("should be unique");
491
492            let mut next_kmer = kmer.extend(ext_base, dir);
493
494            let mut do_flip = false;
495            
496            // decide if direction needs to be changed turn kmer into rc
497            if !self.stranded {
498                let flip_rc = next_kmer.min_rc_flip();
499                do_flip = flip_rc.1;
500                next_kmer = flip_rc.0;
501            }
502
503            let next_dir = dir.cond_flip(do_flip);
504            let is_palindrome = !self.stranded && next_kmer.is_palindrome();
505
506            // We can include this kmer in the line if:
507            // a) it exists in the partition and is still unused
508            // b) the kmer we go to has a unique extension back in our direction
509
510            // Check condition a)
511            match self.get_kmer_id(&next_kmer) {
512                Some(id) if self.available_kmers.contains(id) => (),
513
514                // This kmer isn't in this partition, or we've already used it
515                _ => return ExtMode::Terminal( TerminalExt::new(
516                    exts.single_dir(dir), 
517                    kmer_data.edge_mults().map(|em| em.single_dir(dir)),
518                    kmer_data.mapped_edge_ids().map(|em| em.single_dir(dir))
519                )),
520            }
521
522            // Check condition b)
523            // Direction we're approaching the new kmer from
524            let new_incoming_dir = dir.flip().cond_flip(do_flip);
525            let next_kmer_r = self.get_kmer_data(&next_kmer);
526            let (next_kmer_exts, ref next_kmer_data) = next_kmer_r;
527            let incoming_count = next_kmer_exts.num_ext_dir(new_incoming_dir);
528            let outgoing_exts = next_kmer_exts.single_dir(new_incoming_dir.flip());
529
530            // Test if the spec let's us combine these into the same path
531            let can_join = self.spec.join_test(kmer_data, next_kmer_data);
532
533            if incoming_count == 0 && !is_palindrome {
534                println!("{:?}, {:?}, {:?}", kmer, exts, kmer_data);
535                println!(
536                    "{:?}, {:?}, {:?}",
537                    next_kmer, next_kmer_exts, next_kmer_data
538                );
539                panic!("unreachable");
540            } else if can_join && incoming_count == 1 && !is_palindrome {
541                // We have a unique path to next_kmer -- include it
542                ExtMode::Unique(next_kmer, next_dir, outgoing_exts)
543            } else {
544                // there's more than one path
545                // into the target kmer - don't include it
546                ExtMode::Terminal( TerminalExt::new(
547                    exts.single_dir(dir), 
548                    kmer_data.edge_mults().map(|em| em.single_dir(dir)),
549                    kmer_data.mapped_edge_ids().map(|em| em.single_dir(dir))
550                ))
551            }
552        }
553    }
554
555    /// Build the maximal line starting at kmer in direction dir, at most max_dist long.
556    /// Also return the extensions at the end of this line.
557    /// Sub-lines break if their extensions are not available in this shard
558    #[inline(never)]
559    fn extend_kmer(&mut self, kmer: K, start_dir: Dir, path: &mut Vec<(K, Dir)>) -> TerminalExt {
560        let mut current_dir = start_dir;
561        let mut current_kmer = kmer;
562        path.clear();
563
564        // must get set below
565        let terminal: TerminalExt;
566
567        // get id of kmer and remove from available kmers
568        let id = self.get_kmer_id(&kmer).expect("should have this kmer");
569        let _ = self.available_kmers.remove(id);
570
571        loop {
572            let ext_result = self.try_extend_kmer(current_kmer, current_dir);
573
574            match ext_result {
575                ExtMode::Unique(next_kmer, next_dir, _) => {
576                    path.push((next_kmer, next_dir));
577                    let next_id = self.get_kmer_id(&next_kmer).expect("should have this kmer");
578                    self.available_kmers.remove(next_id);
579                    current_kmer = next_kmer;
580                    current_dir = next_dir;
581                }
582                ExtMode::Terminal(term) => {
583                    terminal = term;                   
584                    break;
585                }
586            }
587        }
588
589        terminal
590    }
591
592    /// Build the edge surrounding a kmer
593    #[inline(never)]
594    fn  build_node(
595        &mut self,
596        seed_id: usize,
597        path: &mut Vec<(K, Dir)>,
598        edge_seq: &mut VecDeque<u8>,
599    ) -> (Exts, D) {
600        let seed: K = *self.index.get_key(seed_id).expect("Index out of bound");
601        edge_seq.clear();
602        for i in 0..K::k() {
603            edge_seq.push_back(seed.get(i));
604        }
605
606        let mut node_data = self.get_kmer_data(&seed).1.clone();
607
608        // Unique path from seed kmer with Dir Left is built
609        let l_term = self.extend_kmer(seed, Dir::Left, path);
610
611
612        // Add on the left path
613        for &(next_kmer, dir) in path.iter() {
614            let kmer = match dir {
615                Dir::Left => next_kmer,
616                Dir::Right => next_kmer.rc(),
617            };
618
619            edge_seq.push_front(kmer.get(0));
620
621            // Reduce the data object
622            let (_, kmer_data) = self.get_kmer_data(&next_kmer);
623            node_data = self.spec.reduce(node_data, kmer_data)
624        }
625
626        let left_terminal  = match path.last() {
627            None => l_term,
628            Some(&(_, Dir::Left)) => l_term,
629            Some(&(_, Dir::Right)) => TerminalExt::new(
630                l_term.exts.complement(), 
631                l_term.edge_mults.map(|em| em.complement()), 
632                l_term.edge_maps.map(|em| em.complement())
633            )
634        };
635
636
637        // Unique path from seed kmer with Dir Right is built
638        let r_term = self.extend_kmer(seed, Dir::Right, path);
639
640        // Add on the right path
641        for &(next_kmer, dir) in path.iter() {
642            let kmer = match dir {
643                Dir::Left => next_kmer.rc(),
644                Dir::Right => next_kmer,
645            };
646
647            edge_seq.push_back(kmer.get(K::k() - 1));
648
649            let (_, kmer_data) = self.get_kmer_data(&next_kmer);
650            node_data = self.spec.reduce(node_data, kmer_data)
651        }
652
653        let right_terminal = match path.last() {
654            None => r_term,
655            Some(&(_, Dir::Left)) => TerminalExt::new(
656                r_term.exts.complement(), 
657                r_term.edge_mults.map(|em| em.complement()), 
658                r_term.edge_maps.map(|em| em.complement())
659            ),
660            Some(&(_, Dir::Right)) => r_term,
661        };
662
663        let new_em = EdgeMult::from_single_dirs(&left_terminal.edge_mults, &right_terminal.edge_mults);
664        let new_emap = EdgeMap::from_single_dirs(&left_terminal.edge_maps, &right_terminal.edge_maps);
665        node_data.set_edge_mults(new_em);
666        node_data.set_mapped_edge_ids(new_emap);
667        
668        (Exts::from_single_dirs(left_terminal.exts, right_terminal.exts), node_data)
669    }
670
671    /// Compress a set of kmers and their extensions and metadata into a base DeBruijn graph.
672    #[inline(never)]
673    pub fn compress_kmers(
674        stranded: bool,
675        spec: &S,
676        index: BoomHashMap2<K, Exts, D>,
677        progress: bool,
678    ) -> BaseGraph<K, D> {
679        
680        let n_kmers = index.len();
681        let mut available_kmers = BitSet::with_capacity(n_kmers);
682        let progress = if n_kmers < 128 { false } else { progress };
683
684        for i in 0..n_kmers {
685            available_kmers.insert(i);
686        }
687
688        let mut comp = CompressFromHash {
689            stranded,
690            spec,
691            k: PhantomData,
692            d: PhantomData,
693            di: PhantomData,
694            available_kmers,
695            index: &index,
696        };
697
698        // Path-compressed De Bruijn graph will be created here
699        let mut graph = BaseGraph::new(stranded);
700
701        // Paths will be get assembled here
702        let mut path_buf = Vec::new();
703
704        // Node sequences will get assembled here
705        let mut edge_seq_buf = VecDeque::new();
706
707        debug!("n of kmers: {}", n_kmers);
708
709        let steps = n_kmers as f32 / 128.;
710
711        if progress {
712            println!("Compressing kmers");
713            for _i in 0..127 {
714                print!("-");
715            }
716            println!("|");
717        }
718
719        let pb = ProgressBar::new(n_kmers as u64);
720        pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
721        pb.set_message(format!("{:<32}", "compressing graph"));
722
723
724        for kmer_counter in (0..n_kmers).progress_with(pb) {
725            if progress && (kmer_counter as f32 % steps >= 0.) & (kmer_counter as f32 % steps < 1.) { print!("|")}
726
727            if (kmer_counter as f32 % steps >= 0.) & (kmer_counter as f32 % steps < 1.) {
728                debug!("another 1/128 done: {}, data graph size: {}", (kmer_counter as f32 / steps) as i32, mem::size_of_val(&*graph.data));
729            }
730
731            if comp.available_kmers.contains(kmer_counter) {
732                let (node_exts, node_data) =
733                    comp.build_node(kmer_counter, &mut path_buf, &mut edge_seq_buf);
734                graph.add(&edge_seq_buf, node_exts, node_data);
735            }
736        }
737
738        graph.shrink_to_fit();
739
740        if progress { println!() };
741
742        graph
743    }
744}
745
746
747/// Take a BoomHash Object and build a compressed DeBruijn graph.
748#[inline(never)]
749pub fn compress_kmers_with_hash<K: Kmer, D: Clone + Debug + Send + Sync + SummaryData<DI>, DI, S: CompressionSpec<D> + Send + Sync>(
750    stranded: bool,
751    spec: &S,
752    index: BoomHashMap2<K, Exts, D>,
753    time: bool,
754    progress: bool,
755) -> BaseGraph<K, D> {
756    let before_compression = Instant::now();
757    let graph = CompressFromHash::<K, D, DI, S>::compress_kmers(stranded, spec, index, progress);
758    if time { println!("time compression (s): {}", before_compression.elapsed().as_secs_f32()) }
759    graph
760}
761
762/// Take (make) a BoomHash Object and build a compressed DeBruijn graph.
763#[inline(never)]
764pub fn compress_kmers<K: Kmer, D: Clone + Debug  + Send + Sync + SummaryData<DI>, DI, S: CompressionSpec<D> + Send + Sync>(
765    stranded: bool,
766    spec: &S,
767    kmer_exts: &[(K, (Exts, D))],
768) -> BaseGraph<K, D> {
769    let mut keys = Vec::with_capacity(kmer_exts.len());
770    let mut exts = Vec::with_capacity(kmer_exts.len());
771    let mut data = Vec::with_capacity(kmer_exts.len());
772
773    for (k, (e, d)) in kmer_exts {
774        keys.push(*k);
775        data.push(d.clone());
776        exts.push(*e);
777    }
778
779    let index = BoomHashMap2::new(keys, exts, data);
780    CompressFromHash::<K, D, DI, S>::compress_kmers(stranded, spec, index, false)
781}
782
783/// Build graph from a set of kmers with unknown extensions by finding the extensions on the fly.
784#[inline(never)]
785pub fn compress_kmers_no_exts<K: Kmer + Send + Sync, D: Clone + Debug + Send + Sync + SummaryData<DI>, DI, S: CompressionSpec<D> + Send + Sync>(
786    stranded: bool,
787    spec: &S,
788    kmer_exts: &[(K, D)],
789) -> BaseGraph<K, D> {
790    let kmer_set: std::collections::HashSet<_> = kmer_exts.iter().map(|(k, _)| k).collect();
791
792    let can = |k: K| k.min_rc();
793
794    let mut keys = Vec::with_capacity(kmer_exts.len());
795    let mut exts = Vec::with_capacity(kmer_exts.len());
796    let mut data = Vec::with_capacity(kmer_exts.len());
797    for (k, d) in kmer_exts {
798        let mut e = Exts::empty();
799
800        for l in 0..4 {
801            let new = can(k.extend_left(l));
802
803            if kmer_set.contains(&new) {
804                e = e.set(Dir::Left, l);
805            }
806        }
807
808        for r in 0..4 {
809            let new = can(k.extend_right(r));
810
811            if kmer_set.contains(&new) {
812                e = e.set(Dir::Right, r);
813            }
814        }
815
816        keys.push(*k);
817        data.push(d.clone());
818        exts.push(e);
819    }
820
821    assert_eq!(kmer_set.len(), keys.len());
822
823    let index = BoomHashMap2::new(keys, exts, data);
824    CompressFromHash::<K, D, DI, S>::compress_kmers(stranded, spec, index,false)
825}
826
827/// build an uncompressed graph from hashed k-mers
828pub fn uncompressed_graph<K: Kmer, D: Clone + Debug>(
829    index: BoomHashMap2<K, Exts, D>,
830    stranded: bool
831) -> BaseGraph<K, D> {
832
833    let mut graph: BaseGraph<K, D> = BaseGraph::new(stranded);
834    let mut kmer_seq: VecDeque<u8> = VecDeque::with_capacity(K::k());
835
836    for (kmer, exts, data) in index.into_iter() {
837        kmer_seq.clear();
838        for i in 0..K::k() {
839            kmer_seq.push_back(kmer.get(i));
840        }
841        graph.add(&kmer_seq, *exts, data.clone());
842    }
843
844    graph.shrink_to_fit();
845
846    graph
847}
848
849/// re-build an uncompressed graph while leaving out nodes
850pub fn rebuild_uncompressed_graph<K: Kmer + Sync + Send, D: Debug + Clone>(
851    stranded: bool,
852    old_graph: DebruijnGraph<K, D>,
853    censor_nodes: Vec<usize>,
854) -> DebruijnGraph<K, D> 
855{
856    // build bit set for efficency
857    let mut available_node = BitSet::with_capacity(old_graph.len());
858    for i in 0..old_graph.len() {
859        available_node.insert(i);
860    }
861
862    for node in censor_nodes {
863        available_node.remove(node);
864    }
865
866    let mut graph: BaseGraph<K, D> = BaseGraph::new(stranded);
867
868    for i in 0..old_graph.len() {
869        if available_node.contains(i) {
870            let node = old_graph.get_node(i);
871            let seq = node.sequence();
872            let exts = node.exts();
873            let data = node.data();
874
875            graph.add(&seq, exts, data.clone());
876        }
877    }
878
879    let mut graph = graph.finish();
880    graph.fix_exts(None);
881
882    graph
883}