1use bimap::BiHashMap;
6use bio::io::fasta;
7use bit_set::BitSet;
8use indicatif::ProgressBar;
9use indicatif::ProgressIterator;
10use indicatif::ProgressStyle;
11use itertools::enumerate;
12use log::warn;
13use log::{debug, trace};
14use rayon::prelude::*;
15use rayon::current_num_threads;
16use serde_derive::{Deserialize, Serialize};
17use smallvec::SmallVec;
18use std::borrow::Borrow;
19
20use std::collections::HashSet;
21use std::collections::VecDeque;
22use std::f32;
23use std::fmt::{self, Debug, Display};
24use std::fs::{remove_file, File};
25use std::hash::Hash;
26use std::io::BufWriter;
27use std::io::{BufReader, Error, Read};
28use std::io::Write;
29use std::iter::FromIterator;
30use std::marker::PhantomData;
31use std::path::Path;
32
33use boomphf::hashmap::BoomHashMap;
34
35use serde_json;
36use serde_json::Value;
37
38type SmallVec4<T> = SmallVec<[T; 4]>;
39type SmallVec8<T> = SmallVec<[T; 8]>;
40
41use crate::BaseQuality;
42use crate::EdgeMap;
43use crate::bits_to_base;
44use crate::colors::ColorMode;
45use crate::colors::Colors;
46use crate::compression::CompressionSpec;
47use crate::dna_string::{DnaString, DnaStringSlice, PackedDnaStringSet};
48use crate::summarizer::SummaryConfig;
49use crate::summarizer::SummaryData;
50use crate::summarizer::Translator;
51use crate::summarizer::ID;
52use crate::BUF;
53use crate::PROGRESS_STYLE;
54use crate::{Dir, Exts, Kmer, Mer, Vmer};
55
56#[derive(Serialize, Deserialize, Clone, Debug)]
61pub struct BaseGraph<K, D> {
62 pub sequences: PackedDnaStringSet,
63 pub exts: Vec<Exts>,
64 pub data: Vec<D>,
65 pub stranded: bool,
66 phantom: PhantomData<K>,
67}
68
69impl<K, D> BaseGraph<K, D> {
70 pub fn new(stranded: bool) -> Self {
71 BaseGraph {
72 sequences: PackedDnaStringSet::new(),
73 exts: Vec::new(),
74 data: Vec::new(),
75 phantom: PhantomData,
76 stranded,
77 }
78 }
79
80 pub fn len(&self) -> usize {
81 self.sequences.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
85 self.sequences.is_empty()
86 }
87
88 pub fn combine<I: Iterator<Item = BaseGraph<K, D>>>(graphs: I) -> Self {
89 let mut sequences = PackedDnaStringSet::new();
90 let mut exts = Vec::new();
91 let mut data = Vec::new();
92 let mut stranded = Vec::new();
93
94 for g in graphs {
95 for s in 0..g.sequences.len() {
96 sequences.add(&g.sequences.get(s));
97 }
98
99 exts.extend(g.exts);
100 data.extend(g.data);
101 stranded.push(g.stranded);
102 }
103
104 let out_stranded = stranded.iter().all(|x| *x);
105
106 if !out_stranded && !stranded.iter().all(|x| !*x) {
107 panic!("attempted to combine stranded and unstranded graphs");
108 }
109
110 BaseGraph {
111 sequences,
112 stranded: out_stranded,
113 exts,
114 data,
115 phantom: PhantomData,
116 }
117 }
118
119 pub fn shrink_to_fit(&mut self) {
121 self.sequences.shrink_to_fit();
122 self.exts.shrink_to_fit();
123 self.data.shrink_to_fit();
124 }
125}
126
127impl<K: Kmer, D> BaseGraph<K, D> {
128 pub fn add<R: Borrow<u8>, S: IntoIterator<Item = R>>(
129 &mut self,
130 sequence: S,
131 exts: Exts,
132 data: D,
133 ) {
134 self.sequences.add(sequence);
135 self.exts.push(exts);
136 self.data.push(data);
137 }
138}
139
140impl<K: Kmer + Send + Sync, D> BaseGraph<K, D> {
141 pub fn finish(self) -> DebruijnGraph<K, D> {
142 let indices: Vec<u32> = (0..self.len() as u32).collect();
143
144 let left_order = {
145 let mut kmers: Vec<K> = Vec::with_capacity(self.len());
146 for idx in &indices {
147 kmers.push(self.sequences.get(*idx as usize).first_kmer());
148
149 }
150
151 BoomHashMap::new_parallel(kmers, indices.clone())
152 };
153
154 let right_order = {
155 let mut kmers: Vec<K> = Vec::with_capacity(self.len());
156 for idx in &indices {
157 kmers.push(self.sequences.get(*idx as usize).last_kmer());
158 }
159
160 BoomHashMap::new_parallel(kmers, indices)
161 };
162 debug!("finish graph loops: 2x {}", self.len());
163
164 DebruijnGraph {
165 base: self,
166 left_order,
167 right_order,
168 }
169 }
170}
171
172impl<K: Kmer, D> BaseGraph<K, D> {
173 pub fn finish_serial(self) -> DebruijnGraph<K, D> {
174 let indices: Vec<u32> = (0..self.len() as u32).collect();
175
176 let left_order = {
177 let mut kmers: Vec<K> = Vec::with_capacity(self.len());
178 let mut sequences: Vec<String> = Vec::new();
179 for idx in &indices {
180 kmers.push(self.sequences.get(*idx as usize).first_kmer());
181 sequences.push(self.sequences.get(*idx as usize).to_dna_string());
182 }
183
184 BoomHashMap::new(kmers, indices.clone())
185 };
186
187 let right_order = {
188 let mut kmers: Vec<K> = Vec::with_capacity(self.len());
189 let mut sequences: Vec<String> = Vec::new();
190 for idx in &indices {
191 kmers.push(self.sequences.get(*idx as usize).last_kmer());
192 sequences.push(self.sequences.get(*idx as usize).to_dna_string());
193 }
194
195 BoomHashMap::new(kmers, indices)
196 };
197
198 DebruijnGraph {
199 base: self,
200 left_order,
201 right_order,
202 }
203 }
204}
205
206#[derive(Serialize, Deserialize, Debug)]
210pub struct DebruijnGraph<K: Hash, D> {
211 pub base: BaseGraph<K, D>,
212 left_order: BoomHashMap<K, u32>,
213 right_order: BoomHashMap<K, u32>,
214}
215
216impl<K: Kmer, D: Debug> DebruijnGraph<K, D> {
217 pub fn len(&self) -> usize {
219 self.base.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
223 self.base.is_empty()
224 }
225
226 pub fn shrink_to_fit(&mut self) {
228 self.base.shrink_to_fit();
229 }
230
231 pub fn get_node(&'_ self, node_id: usize) -> Node<'_, K, D> {
233 Node {
234 node_id,
235 graph: self,
236 }
237 }
238
239 pub fn get_node_kmer(&'_ self, node_id: usize) -> NodeKmer<'_, K, D> {
241 let node = self.get_node(node_id);
242 let node_seq = node.sequence();
243
244 NodeKmer {
245 node_id,
246 node_seq_slice: node_seq,
247 phantom_d: PhantomData,
248 phantom_k: PhantomData,
249 }
250 }
251
252 pub fn iter_nodes(&'_ self) -> NodeIter<'_, K, D> {
254 NodeIter {
255 graph: self,
256 node_id: 0,
257 }
258 }
259
260 fn find_edges(&self, node_id: usize, dir: Dir) -> SmallVec4<(u8, usize, Dir, bool)> {
263 let exts = self.base.exts[node_id];
264 let sequence = self.base.sequences.get(node_id);
265 let kmer: K = sequence.term_kmer(dir);
266 let mut edges = SmallVec4::new();
267
268 for i in 0..4 {
269 if exts.has_ext(dir, i) {
270 let link = self.find_link(kmer.extend(i, dir), dir).expect("missing link");
271 edges.push((i, link.0, link.1, link.2));
272 }
273 }
274
275 edges
276 }
277
278 fn _find_edges_sharded(&self, node_id: usize, dir: Dir) -> SmallVec4<(u8, usize, Dir, bool)> {
283 let exts = self.base.exts[node_id];
284 let sequence = self.base.sequences.get(node_id);
285 let kmer: K = sequence.term_kmer(dir);
286 let mut edges = SmallVec4::new();
287
288 for i in 0..4 {
289 if exts.has_ext(dir, i) {
290 let link = self.find_link(kmer.extend(i, dir), dir); if let Some(l) = link {
292 edges.push((i, l.0, l.1, l.2));
293 }
294 }
297 }
298
299 edges
300 }
301
302 fn search_kmer(&self, kmer: K, side: Dir) -> Option<usize> {
304 match side {
305 Dir::Left => self.left_order.get(&kmer).map(|pos| *pos as usize),
306 Dir::Right => self.right_order.get(&kmer).map(|pos| *pos as usize),
307 }
308 }
309
310 pub fn find_link(&self, kmer: K, dir: Dir) -> Option<(usize, Dir, bool)> {
312 let rc = kmer.rc();
322
323 match dir {
324 Dir::Left => {
325 if let Some(idx) = self.search_kmer(kmer, Dir::Right) {
326 return Some((idx, Dir::Right, false));
327 }
328
329 if !self.base.stranded {
330 if let Some(idx) = self.search_kmer(rc, Dir::Left) {
331 return Some((idx, Dir::Left, true));
332 }
333 }
334 }
335
336 Dir::Right => {
337 if let Some(idx) = self.search_kmer(kmer, Dir::Left) {
338 return Some((idx, Dir::Left, false));
339 }
340
341 if !self.base.stranded {
342 if let Some(idx) = self.search_kmer(rc, Dir::Right) {
343 return Some((idx, Dir::Right, true));
344 }
345 }
346 }
347 }
348
349 None
350 }
351
352 pub fn is_compressed<S: CompressionSpec<D>>(&self, spec: &S) -> Option<(usize, usize)> {
356 for i in 0..self.len() {
357 let n = self.get_node(i);
358
359 for dir in &[Dir::Left, Dir::Right] {
360 let dir_edges = n.edges(*dir);
361 if dir_edges.len() == 1 {
362 let (_, next_id, return_dir, _) = dir_edges[0];
363 let next = self.get_node(next_id);
364
365 let ret_edges = next.edges(return_dir);
366 if ret_edges.len() == 1 {
367 if n.len() == K::k() && n.sequence().first_kmer::<K>().is_palindrome() {
369 continue;
370 }
371
372 if next.len() == K::k() && next.sequence().first_kmer::<K>().is_palindrome()
374 {
375 continue;
376 }
377
378 if n.node_id == next_id {
380 continue;
381 }
382
383 if spec.join_test(n.data(), next.data()) {
384 return Some((i, next_id));
386 }
387 }
388 }
389 }
390 }
391
392 None
393 }
394
395 pub fn fix_exts(&mut self, valid_nodes: Option<&BitSet>) {
399 for i in 0..self.len() {
400 let valid_exts = self.get_valid_exts(i, valid_nodes);
401 self.base.exts[i] = valid_exts;
402 }
403 }
404
405 pub fn get_valid_exts(&self, node_id: usize, valid_nodes: Option<&BitSet>) -> Exts {
406 let mut new_exts = Exts::empty();
407 let node = self.get_node(node_id);
408 let exts = node.exts();
409 let l_kmer: K = node.sequence().first_kmer();
410 let r_kmer: K = node.sequence().last_kmer();
411
412 let check_node = |id| match valid_nodes {
413 Some(bs) => bs.contains(id),
414 None => true,
415 };
416
417 for i in 0..4 {
418 if exts.has_ext(Dir::Left, i) {
419 match self.find_link(l_kmer.extend_left(i), Dir::Left) {
420 Some((target, _, _)) if check_node(target) => {
421 new_exts = new_exts.set(Dir::Left, i)
422 }
423 _ => (),
424 }
425 }
426
427 if exts.has_ext(Dir::Right, i) {
428 match self.find_link(r_kmer.extend_right(i), Dir::Right) {
429 Some((target, _, _)) if check_node(target) => {
430 new_exts = new_exts.set(Dir::Right, i)
431 }
432 _ => (),
433 }
434 }
435 }
436
437 new_exts
438 }
439
440 pub fn mut_data(&mut self, node_id: usize) -> &mut D {
442 &mut self.base.data[node_id]
443 }
444
445 pub fn max_path<F, F2>(&self, score: F, solid_path: F2) -> Vec<(usize, Dir)>
449 where
450 F: Fn(&D) -> f32,
451 F2: Fn(&D) -> bool,
452 {
453 if self.is_empty() {
454 return Vec::default();
455 }
456
457 let mut best_node = 0;
458 let mut best_score = f32::MIN;
459 for i in 0..self.len() {
460 let node = self.get_node(i);
461 let node_score = score(node.data());
462
463 if node_score > best_score {
464 best_node = i;
465 best_score = node_score;
466 }
467 }
468
469 let oscore = |state| match state {
470 None => 0.0,
471 Some((id, _)) => score(self.get_node(id).data()),
472 };
473
474 let osolid_path = |state| match state {
475 None => false,
476 Some((id, _)) => solid_path(self.get_node(id).data()),
477 };
478
479 let mut used_nodes = HashSet::new();
482 let mut path = VecDeque::new();
483
484 used_nodes.insert(best_node);
486 path.push_front((best_node, Dir::Left));
487
488 for init in [(best_node, Dir::Left, false), (best_node, Dir::Right, true)].iter() {
489 let &(start_node, dir, do_flip) = init;
490 let mut current = (start_node, dir);
491
492 loop {
493 let mut next = None;
494 let (cur_id, incoming_dir) = current;
495 let node = self.get_node(cur_id);
496 let edges = node.edges(incoming_dir.flip());
497
498 let mut solid_paths = 0;
499 for (_, id, dir, _) in edges {
500 let cand = Some((id, dir));
501 if osolid_path(cand) {
502 solid_paths += 1;
503 }
504
505 if oscore(cand) > oscore(next) {
506 next = cand;
507 }
508 }
509
510 if solid_paths > 1 {
511 break;
512 }
513
514 match next {
515 Some((next_id, next_incoming)) if !used_nodes.contains(&next_id) => {
516 if do_flip {
517 path.push_front((next_id, next_incoming.flip()));
518 } else {
519 path.push_back((next_id, next_incoming));
520 }
521
522 used_nodes.insert(next_id);
523 current = (next_id, next_incoming);
524 }
525 _ => break,
526 }
527 }
528 }
529
530 Vec::from_iter(path)
531 }
532
533
534 pub fn max_path_comp<F, F2>(&self, score: F, solid_path: F2) -> Vec<VecDeque<(usize, Dir)>>
539 where
540 F: Fn(&D) -> f32,
541 F2: Fn(&D) -> bool,
542 {
543 if self.is_empty() {
544 let vec: Vec<VecDeque<(usize, Dir)>> = Vec::new();
545 return vec;
546 }
547
548 let components = self.iter_components();
549 let mut paths: Vec<VecDeque<(usize, Dir)>> = Vec::new();
550
551 for component in components {
552
553 let current_comp = &component;
554
555
556 let mut best_node = current_comp[0];
557 let mut best_score = f32::MIN;
558 for c in current_comp.iter() {
559 let node = self.get_node(*c);
560 let node_score = score(node.data());
561
562 if node_score > best_score {
563 best_node = *c;
564 best_score = node_score;
565 }
566 }
567
568 let oscore = |state| match state {
569 None => 0.0,
570 Some((id, _)) => score(self.get_node(id).data()),
571 };
572
573 let osolid_path = |state| match state {
574 None => false,
575 Some((id, _)) => solid_path(self.get_node(id).data()),
576 };
577
578 let mut used_nodes = HashSet::new();
581 let mut path = VecDeque::new();
582
583 used_nodes.insert(best_node);
585 path.push_front((best_node, Dir::Left));
586
587 for init in [(best_node, Dir::Left, false), (best_node, Dir::Right, true)].iter() {
588 let &(start_node, dir, do_flip) = init;
589 let mut current = (start_node, dir);
590
591 loop {
592 let mut next = None;
593 let (cur_id, incoming_dir) = current;
594 let node = self.get_node(cur_id);
595 let edges = node.edges(incoming_dir.flip());
596
597 let mut solid_paths = 0;
598 for (_, id, dir, _) in edges {
599 let cand = Some((id, dir));
600 if osolid_path(cand) {
601 solid_paths += 1;
602 }
603
604 if oscore(cand) > oscore(next) {
605 next = cand;
606 }
607 }
608
609 if solid_paths > 1 {
610 break;
611 }
612
613 match next {
614 Some((next_id, next_incoming)) if !used_nodes.contains(&next_id) => {
615 if do_flip {
616 path.push_front((next_id, next_incoming.flip()));
617 } else {
618 path.push_back((next_id, next_incoming));
619 }
620
621 used_nodes.insert(next_id);
622 current = (next_id, next_incoming);
623 }
624 _ => break,
625 }
626 }
627 }
628
629 paths.push(path);
630 }
632
633 paths
634
635 }
636
637 pub fn iter_max_path_comp<F, F2>(&'_ self, score: F, solid_path: F2) -> PathCompIter<'_, K, D, F, F2>
638 where
639 F: Fn(&D) -> f32,
640 F2: Fn(&D) -> bool
641 {
642 let component_iterator = self.iter_components();
643 PathCompIter { graph: self, component_iterator, graph_pos: 0, score, solid_path }
644 }
645
646 pub fn path_to_fasta<F, F2>(&self, f: &mut dyn std::io::Write, path_iter: PathCompIter<K, D, F, F2>, return_lens: bool) -> (Vec<usize>, Vec<usize>)
648 where
649 F: Fn(&D) -> f32,
650 F2: Fn(&D) -> bool
651 {
652 let columns = 80;
654
655 let mut comp_sizes = Vec::new();
657 let mut path_lens = Vec::new();
658
659 for (seq_counter, (component, path)) in path_iter.enumerate() {
660 let seq = self.sequence_of_path(path.iter());
662
663 writeln!(f, ">path{} len={} start_node={}", seq_counter, seq.len(), path[0].0).unwrap();
665
666 let slices = (seq.len() / columns) + 1;
668 let mut ranges = Vec::with_capacity(slices);
669
670 let mut start = 0;
671 while start < seq.len() {
672 ranges.push(start..start + columns);
673 start += columns;
674 }
675
676 let last_start = ranges.pop().expect("no kmers in parallel ranges").start;
677 ranges.push(last_start..seq.len());
678
679 for range in ranges {
681 writeln!(f, "{:?}", seq.slice(range.start, range.end)).unwrap();
682 }
683
684 if return_lens {
685 comp_sizes.push(component.len());
686 path_lens.push(path.len());
687 }
688 }
689
690 (comp_sizes, path_lens)
691
692 }
693
694
695 pub fn sequence_of_path<'a, I: 'a + Iterator<Item = &'a (usize, Dir)>>(
697 &self,
698 path: I,
699 ) -> DnaString {
700 let mut seq = DnaString::new();
701
702 for (idx, &(node_id, dir)) in path.enumerate() {
703 let start = if idx == 0 { 0 } else { K::k() - 1 };
704
705 let node_seq = match dir {
706 Dir::Left => self.get_node(node_id).sequence(),
707 Dir::Right => self.get_node(node_id).sequence().rc(),
708 };
709
710 for p in start..node_seq.len() {
711 seq.push(node_seq.get(p))
712 }
713 }
714
715 seq
716 }
717
718 pub fn map_transcripts<P>(&self, path: P, translator: &mut Translator) -> Result<Vec<Box<[ID]>>, String>
722 where
723 P: AsRef<Path>
724 {
725 let reader = fasta::Reader::new(BufReader::new(File::open(path).unwrap()));
726 let mut node_transcript_ids = vec![Vec::new(); self.len()];
727
728 let id_tr = if let Some(id_tr) = translator.mut_id_translator() {
730 id_tr
731 } else {
732 let new_id_tr = BiHashMap::new();
733 translator.mut_id_translator().replace(new_id_tr);
734
735 let Some(id_tr) = translator.mut_id_translator() else { panic!("should not happen") };
736
737 id_tr
738 };
739
740 for result in reader.records() {
742 let record = result.expect("error parsing transcripts fasta");
743
744 let gene_id = match id_tr.get_by_left(&record.id().to_string()) {
746 Some(id) => *id,
747 None => {
748 let new_id = id_tr.len() as ID;
749 id_tr.insert(record.id().to_string(), new_id);
750 new_id
751 }
752 };
753
754 let sequence = DnaString::from_acgt_bytes(record.seq());
756 for kmer in sequence.iter_kmers::<K>() {
757 if self.base.stranded {
758 if let Some(node) = self.search_kmer(kmer, Dir::Right) {
759 node_transcript_ids[node].push(gene_id);
760 }
761 } else {
762 if let Some(node) = self.search_kmer(kmer, Dir::Right) {
764 node_transcript_ids[node].push(gene_id);
765 } else if let Some(node) = self.search_kmer(kmer.rc(), Dir::Right) {
766 node_transcript_ids[node].push(gene_id);
767 }
768 }
769
770 }
771 }
772
773 let boxed_transcripts = node_transcript_ids.into_iter().map(|vec| vec.into()).collect();
775
776 Ok(boxed_transcripts)
777 }
778
779 pub fn map_transcripts_to_edges<P>(&self, path: P, translator: &mut Translator) -> Result<Vec<EdgeMap>, String>
783 where
784 P: AsRef<Path>
785 {
786 let reader = fasta::Reader::new(BufReader::new(File::open(path).unwrap()));
787 let mut node_edge_transcript_ids = vec![EdgeMap::default(); self.len()];
788
789 let id_tr = if let Some(id_tr) = translator.mut_id_translator() {
791 id_tr
792 } else {
793 let new_id_tr = BiHashMap::new();
794 translator.mut_id_translator().replace(new_id_tr);
795
796 let Some(id_tr) = translator.mut_id_translator() else { panic!("should not happen") };
797
798 id_tr
799 };
800
801 for result in reader.records() {
803 let record = result.expect("error parsing transcripts fasta");
804
805 let gene_id = match id_tr.get_by_left(&record.id().to_string()) {
807 Some(id) => *id,
808 None => {
809 let new_id = id_tr.len() as ID;
810 id_tr.insert(record.id().to_string(), new_id);
811 new_id
812 }
813 };
814
815 let sequence = DnaString::from_acgt_bytes(record.seq());
817
818 for (kmer, exts) in sequence.iter_kmer_exts::<K>(Exts::empty()) {
819 if self.base.stranded {
820 if let Some(node) = self.search_kmer(kmer, Dir::Right) {
821 node_edge_transcript_ids[node].add_id(exts, gene_id);
822 }
823 } else {
824 if let Some(node) = self.search_kmer(kmer, Dir::Right) {
826 node_edge_transcript_ids[node].add_id(exts, gene_id);
827 } else if let Some(node) = self.search_kmer(kmer.rc(), Dir::Right) {
828 node_edge_transcript_ids[node].add_id(exts.rc(), gene_id);
829 }
830 }
831
832 }
833 }
834
835 node_edge_transcript_ids.shrink_to_fit();
837
838 Ok(node_edge_transcript_ids)
839 }
840
841 fn node_to_dot<FN: Fn(&Node<K, D>) -> String, FE: Fn(&Node<K, D>, u8, Dir, bool) -> String>(
851 &self,
852 node: &Node<'_, K, D>,
853 node_label: &FN,
854 edge_label: &FE,
855 f: &mut dyn Write,
856 ) {
857 writeln!(f, "n{} {}", node.node_id, node_label(node)).unwrap();
858 assert_eq!(node.exts().val.count_ones() as usize, node.l_edges().len() + node.r_edges().len());
859
860 for (base, id, incoming_dir, flipped) in node.l_edges() {
861 writeln!(f, "n{} -> n{} {}", id, node.node_id, edge_label(node, base, incoming_dir, flipped)).unwrap();
862 }
863
864 for (base, id, incoming_dir, flipped) in node.r_edges() {
865 writeln!(f, "n{} -> n{} {}", node.node_id, id, edge_label(node, base, incoming_dir, flipped)).unwrap();
866 }
867 }
868
869 pub fn to_dot<P, FN, FE>(&self, path: P, node_label: &FN, edge_label: &FE)
878 where
879 P: AsRef<Path>,
880 FN: Fn(&Node<K, D>) -> String,
881 FE: Fn(&Node<K, D>, u8, Dir, bool) -> String,
882 {
883 let mut f = BufWriter::with_capacity(BUF, File::create(path).expect("error creating dot file"));
884
885 let pb = ProgressBar::new(self.len() as u64);
886 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
887 pb.set_message(format!("{:<32}", "writing graph to DOT file"));
888
889 writeln!(&mut f, "digraph {{\nrankdir=\"LR\"\nmodel=subset").unwrap();
890 for i in (0..self.len()).progress_with(pb) {
891 self.node_to_dot(&self.get_node(i), node_label, edge_label, &mut f);
892 }
893 writeln!(&mut f, "}}").unwrap();
894
895 f.flush().unwrap();
896 debug!("large to dot loop: {}", self.len());
897 }
898
899 pub fn to_dot_with_path<P, FE, DI>(&self, path: P, edge_label: &FE, colors: &Colors<'_, D, DI>, translator: &Translator, config: &SummaryConfig, translate_id_groups: bool)
913 where
914 P: AsRef<Path>,
915 D: SummaryData<DI>,
916 FE: Fn(&Node<K, D>, u8, Dir, bool) -> String,
917 {
918 let mut f = BufWriter::with_capacity(BUF, File::create(path).expect("error creating dot file"));
919
920 writeln!(&mut f, "digraph {{\nrankdir=\"LR\"\nmodel=subset").unwrap();
921
922 for (component, path) in self.iter_max_path_comp(|d| d.sum().unwrap_or(1) as f32, |_| true) {
924 let hashed_path = path.into_iter().map(|(id, _)| id).collect::<HashSet<usize>>();
925 for node_id in component {
926 self.node_to_dot(
927 &self.get_node(node_id),
928 &|node| node.node_dot_default(colors, config, translator, hashed_path.contains(&node_id), translate_id_groups),
929 edge_label,
930 &mut f
931 );
932 }
933 }
934
935 writeln!(&mut f, "}}").unwrap();
936
937 f.flush().unwrap();
938 debug!("large to dot loop: {}", self.len());
939 }
940
941 pub fn to_dot_parallel<P, FN, FE>(&self, path: P, node_label: &FN, edge_label: &FE)
953 where
954 D: Sync,
955 K: Sync,
956 P: AsRef<Path> + Display + Sync,
957 FN: Fn(&Node<K, D>) -> String + Sync,
958 FE: Fn(&Node<K, D>, u8, Dir, bool) -> String + Sync,
959 {
960 let slices = current_num_threads();
961 let n_nodes = self.len();
962 let sz = n_nodes / slices + 1;
963
964 debug!("n_nodes: {}", n_nodes);
965 debug!("sz: {}", sz);
966
967 let mut parallel_ranges = Vec::with_capacity(slices);
968 let mut start = 0;
969 while start < n_nodes {
970 parallel_ranges.push(start..start + sz);
971 start += sz;
972 }
973
974 let last_start = parallel_ranges.pop().expect("no kmers in parallel ranges").start;
975 parallel_ranges.push(last_start..n_nodes);
976 debug!("parallel ranges: {:?}", parallel_ranges);
977
978 let mut files = Vec::with_capacity(current_num_threads());
979
980 for i in 0..parallel_ranges.len() {
981 files.push(format!("{}-{}.dot", path, i));
982 }
983
984 let pb = ProgressBar::new(self.len() as u64);
985 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
986 pb.set_message(format!("{:<32}", "writing graph to DOT files"));
987
988 parallel_ranges.into_par_iter().enumerate().for_each(|(i, range)| {
989 let mut f = BufWriter::with_capacity(BUF, File::create(&files[i]).expect("error creating parallel dot file"));
990
991 for i in range {
992 self.node_to_dot(&self.get_node(i), node_label, edge_label, &mut f);
993 pb.inc(1);
994 }
995
996 f.flush().unwrap();
997 });
998 pb.finish_and_clear();
999
1000 let mut out_file = BufWriter::with_capacity(BUF, File::create(path).expect("error creating combined dot file"));
1001
1002 writeln!(&mut out_file, "digraph {{\nrankdir=\"LR\"\nmodel=subset").unwrap();
1003
1004 let pb = ProgressBar::new(files.len() as u64);
1005 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1006 pb.set_message(format!("{:<32}", "combining files"));
1007
1008 for file in files.iter().progress_with(pb) {
1009 let open_file = File::open(file).expect("error opening parallel dot file");
1010 let mut reader = BufReader::new(open_file);
1011 let mut buffer = [0; BUF];
1012
1013 loop {
1014 let linecount = reader.read(&mut buffer).unwrap();
1015 if linecount == 0 { break }
1016 out_file.write_all(&buffer[..linecount]).unwrap();
1017 }
1018
1019 remove_file(file).unwrap();
1020 }
1021
1022 writeln!(&mut out_file, "}}").unwrap();
1023
1024 out_file.flush().unwrap();
1025
1026
1027 }
1028
1029
1030 pub fn to_dot_partial<P, FN, FE>(&self, path: P, node_label: &FN, edge_label: &FE, nodes: &[usize])
1040 where
1041 P: AsRef<Path>,
1042 FN: Fn(&Node<K, D>) -> String,
1043 FE: Fn(&Node<K, D>, u8, Dir, bool) -> String,
1044 {
1045 let mut f = BufWriter::with_capacity(BUF, File::create(path).expect("error creating dot file"));
1046
1047 let pb = ProgressBar::new(nodes.len() as u64);
1048 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1049 pb.set_message(format!("{:<32}", "writing graph to DOT file"));
1050
1051 writeln!(&mut f, "digraph {{\nrankdir=\"LR\"\nmodel=subset").unwrap();
1052 for i in nodes.iter().progress_with(pb) {
1053 self.node_to_dot(&self.get_node(*i), node_label, edge_label, &mut f);
1054 }
1055 writeln!(&mut f, "}}").unwrap();
1056
1057 f.flush().unwrap();
1058
1059 debug!("large to dot loop: {}", self.len());
1060 }
1061
1062 fn node_to_gfa<F: Fn(&Node<'_, K, D>) -> String>(
1063 &self,
1064 node: &Node<'_, K, D>,
1065 w: &mut dyn Write,
1066 tag_func: Option<&F>,
1067 ) -> Result<(), Error> {
1068 match tag_func {
1069 Some(f) => {
1070 let tags = (f)(node);
1071 writeln!(
1072 w,
1073 "S\t{}\t{}\t{}",
1074 node.node_id,
1075 node.sequence().to_dna_string(),
1076 tags
1077 )?;
1078 }
1079 _ => writeln!(
1080 w,
1081 "S\t{}\t{}",
1082 node.node_id,
1083 node.sequence().to_dna_string()
1084 )?,
1085 }
1086
1087 for (_, target, dir, _) in node.l_edges() {
1088 if target >= node.node_id {
1089 let to_dir = match dir {
1090 Dir::Left => "+",
1091 Dir::Right => "-",
1092 };
1093 writeln!(
1094 w,
1095 "L\t{}\t-\t{}\t{}\t{}M",
1096 node.node_id,
1097 target,
1098 to_dir,
1099 K::k() - 1
1100 )?;
1101 }
1102 }
1103
1104 for (_, target, dir, _) in node.r_edges() {
1105 if target > node.node_id {
1106 let to_dir = match dir {
1107 Dir::Left => "+",
1108 Dir::Right => "-",
1109 };
1110 writeln!(
1111 w,
1112 "L\t{}\t+\t{}\t{}\t{}M",
1113 node.node_id,
1114 target,
1115 to_dir,
1116 K::k() - 1
1117 )?;
1118 }
1119 }
1120
1121 Ok(())
1122 }
1123
1124 pub fn to_gfa<P: AsRef<Path>>(&self, gfa_out: P) -> Result<(), Error> {
1126 let wtr = BufWriter::with_capacity(BUF, File::create(gfa_out).expect("error creating gfa file"));
1127 self.write_gfa(&mut std::io::BufWriter::new(wtr))
1128 }
1129
1130 pub fn write_gfa(&self, wtr: &mut impl Write) -> Result<(), Error> {
1131 writeln!(wtr, "H\tVN:Z:debruijn-rs")?;
1132
1133 type DummyFn<K, D> = fn(&Node<'_, K, D>) -> String;
1134 let dummy_opt: Option<&DummyFn<K, D>> = None;
1135
1136 let pb = ProgressBar::new(self.len() as u64);
1137 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1138 pb.set_message(format!("{:<32}", "writing graph to GFA file"));
1139
1140 for i in (0..self.len()).progress_with(pb) {
1141 let n = self.get_node(i);
1142 self.node_to_gfa(&n, wtr, dummy_opt)?;
1143 }
1144
1145 wtr.flush().unwrap();
1146
1147 Ok(())
1148 }
1149
1150 pub fn to_gfa_with_tags<P: AsRef<Path>, F: Fn(&Node<'_, K, D>) -> String>(
1152 &self,
1153 gfa_out: P,
1154 tag_func: F,
1155 ) -> Result<(), Error> {
1156 let mut wtr = BufWriter::with_capacity(BUF, File::create(gfa_out).expect("error creatinf gfa file"));
1157 writeln!(wtr, "H\tVN:Z:debruijn-rs")?;
1158
1159 let pb = ProgressBar::new(self.len() as u64);
1160 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1161 pb.set_message(format!("{:<32}", "writing graph to GFA file"));
1162
1163 for i in (0..self.len()).progress_with(pb) {
1164 let n = self.get_node(i);
1165 self.node_to_gfa(&n, &mut wtr, Some(&tag_func))?;
1166 }
1167
1168 wtr.flush().unwrap();
1169
1170 Ok(())
1171 }
1172
1173 pub fn to_gfa_otags_parallel<P: AsRef<Path> + Display + Sync, F: Fn(&Node<'_, K, D>) -> String + Sync>(
1176 &self,
1177 gfa_out: P,
1178 tag_func: Option<&F>,
1179 ) -> Result<(), Error>
1180 where
1181 K: Sync,
1182 D: Sync,
1183 {
1184 let slices = current_num_threads();
1186 let n_nodes = self.len();
1187 let sz = n_nodes / slices + 1;
1188
1189 debug!("n_nodes: {}", n_nodes);
1190 debug!("sz: {}", sz);
1191
1192 let mut parallel_ranges = Vec::with_capacity(slices);
1193 let mut start = 0;
1194 while start < n_nodes {
1195 parallel_ranges.push(start..start + sz);
1196 start += sz;
1197 }
1198
1199 let last_start = parallel_ranges.pop().expect("no kmers in parallel ranges").start;
1200 parallel_ranges.push(last_start..n_nodes);
1201 debug!("parallel ranges: {:?}", parallel_ranges);
1202
1203 let mut files = Vec::with_capacity(current_num_threads());
1204
1205 for i in 0..parallel_ranges.len() {
1206 files.push(format!("{}-{}.gfa", gfa_out, i));
1207 }
1208
1209 let pb = ProgressBar::new(self.len() as u64);
1210 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1211 pb.set_message(format!("{:<32}", "writing graph to GFA file"));
1212
1213
1214 parallel_ranges.into_par_iter().enumerate().for_each(|(i, range)| {
1215 let mut wtr = BufWriter::with_capacity(BUF, File::create(&files[i]).expect("error creating parallel gfa file"));
1216
1217 for i in range {
1218 let n = self.get_node(i);
1219 self.node_to_gfa(&n, &mut wtr, tag_func).unwrap();
1220 pb.inc(1);
1221 }
1222
1223 wtr.flush().unwrap();
1224 });
1225
1226 pb.finish_and_clear();
1227
1228 let mut out_file = BufWriter::with_capacity(BUF, File::create(format!("{}.gfa", gfa_out)).expect("error creating combined gfa file"));
1230 writeln!(out_file, "H\tVN:Z:debruijn-rs")?;
1231
1232 let pb = ProgressBar::new(files.len() as u64);
1233 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1234 pb.set_message(format!("{:<32}", "combining files"));
1235
1236 for file in files.iter() {
1237 let open_file = File::open(file).expect("error opening parallel gfa file");
1238 let mut reader = BufReader::new(open_file);
1239 let mut buffer = [0; BUF];
1240
1241 loop {
1242 let linecount = reader.read(&mut buffer).unwrap();
1243 if linecount == 0 { break }
1244 out_file.write_all(&buffer[..linecount]).unwrap();
1245 }
1246
1247 remove_file(file).unwrap();
1248 }
1249
1250 out_file.flush().unwrap();
1251
1252 Ok(())
1253 }
1254
1255 pub fn to_gfa_partial<P: AsRef<Path>, F: Fn(&Node<'_, K, D>) -> String>(&self, gfa_out: P, tag_func: Option<&F>, nodes: Vec<usize>) -> Result<(), Error> {
1257 let mut wtr = BufWriter::with_capacity(BUF, File::create(gfa_out).expect("error creating gfa file"));
1258 writeln!(wtr, "H\tVN:Z:debruijn-rs")?;
1259
1260 let pb = ProgressBar::new(self.len() as u64);
1261 pb.set_style(ProgressStyle::with_template(PROGRESS_STYLE).unwrap().progress_chars("#/-"));
1262 pb.set_message(format!("{:<32}", "writing graph to GFA file"));
1263
1264 for i in nodes.into_iter().progress_with(pb) {
1265 let n = self.get_node(i);
1266 self.node_to_gfa(&n, &mut wtr, tag_func)?;
1267 }
1268
1269 wtr.flush().unwrap();
1270
1271 Ok(())
1272 }
1273
1274 fn node_to_tsv<W: Write, F>(&self, writer: &mut W, node_id: usize, data_format: F) -> Result<(), Box<dyn std::error::Error>>
1275 where
1276 F: Fn(&Node<'_, K, D>) -> String,
1277 {
1278
1279 let node = self.get_node(node_id);
1280 let l_e = node.l_edges();
1281 let r_e = node.r_edges();
1282
1283
1284 if self.base.stranded {
1285 let l_nb = l_e.iter().map(|(_b, nb, _d, _f)| *nb).collect::<Vec<_>>();
1287 let r_nb = r_e.iter().map(|(_b, nb, _d, _f)| *nb).collect::<Vec<_>>();
1288 writeln!(writer, "{node_id}\t{:?}\t{:?}\t{}\t{}", l_nb, r_nb, node.sequence(), data_format(&node))?
1289 } else {
1290 let l_nb = l_e.iter().map(|(_b, nb, _d, _f)| *nb).collect::<Vec<_>>();
1292 let r_nb = r_e.iter().map(|(_b, nb, _d, _f)| *nb).collect::<Vec<_>>();
1293 let l_nb_dirs = l_e.iter().map(|(_b, _nb, dir, _f)| *dir).collect::<Vec<_>>();
1294 let r_nb_dirs = r_e.iter().map(|(_b, _nb, dir, _f)| *dir).collect::<Vec<_>>();
1295 writeln!(writer, "{node_id}\t{:?}\t{:?}\t{:?}\t{:?}\t{}\t{}", l_nb, l_nb_dirs, r_nb, r_nb_dirs, node.sequence(), data_format(&node))?
1296 }
1297
1298 Ok(())
1299 }
1300
1301 pub fn to_tsv<P, F>(&self, path: P, data_format: F) -> Result<(), Box<dyn std::error::Error>>
1303 where
1304 F: Fn(&Node<'_, K, D>) -> String,
1305 P: AsRef<Path> + Display,
1306 {
1307 let mut writer = BufWriter::new(File::create(path)?);
1308
1309 if self.base.stranded {
1311 writeln!(writer, "node id\tleft neighbors\tright neighbors\tsequence\tdata")?;
1312 } else {
1313 writeln!(writer, "node id\tleft neighbors\tleft nb incoming dirs\tright neighbors\tright nb incoming dirs\tsequence\tdata")?;
1314 }
1315
1316 for i in 0..self.len() {
1317 self.node_to_tsv(&mut writer, i, &data_format)?
1318 }
1319
1320 Ok(())
1321 }
1322
1323 fn iter_optional_partial<'a>(&self, partial_nodes: Option<&'a Vec<usize>>) -> Box<dyn Iterator<Item = usize> + 'a> {
1325 if let Some(partial) = partial_nodes {
1326 Box::new(partial[..(partial.len()-1)].iter().copied())
1327 } else {
1328 Box::new(0..(self.len()-1))
1329 }
1330 }
1331
1332 pub fn to_json_3d<P, FN, FE>(&self,
1334 path: P,
1335 node_properties: &FN,
1336 edge_properties: &FE,
1337 partial_nodes: Option<&'_ Vec<usize>>
1338 ) -> Result<(), Box<dyn std::error::Error>>
1339 where
1340 P: AsRef<Path>,
1341 FN: Fn(&Node<K, D>) -> String,
1342 FE: Fn(&Node<K, D>, usize, u8, Dir, bool) -> String,
1343 {
1344 let mut writer = BufWriter::new(File::create(path)?);
1345
1346 writeln!(writer, "{{")?;
1347 writeln!(writer, "\t\"nodes\": [")?;
1348
1349 for node_id in self.iter_optional_partial(partial_nodes) {
1352 let node = self.get_node(node_id);
1353 let node_fmt = node_properties(&node);
1354
1355 writeln!(writer, "\t\t{{ {node_fmt} }},")?;
1356 }
1357
1358 let last_node_id = match partial_nodes {
1360 Some(partial) => *partial.last().expect("empty parial nodes vector"),
1361 None => self.len() - 1
1362 };
1363
1364 let last_node = self.get_node(last_node_id);
1365 let last_node_fmt = node_properties(&last_node);
1366
1367 writeln!(writer, "\t\t{{ {last_node_fmt} }}")?;
1368
1369 writeln!(writer, "\t],")?;
1370 writeln!(writer, "\t\"links\": [")?;
1371
1372 for node_id in self.iter_optional_partial(partial_nodes) {
1375 let node = self.get_node(node_id);
1376 for (base, target_id, dir, flipped) in node.r_edges() {
1378 let edge_fmt = edge_properties(&node, target_id, base, dir, flipped);
1379 writeln!(writer, "\t\t{{ {edge_fmt} }},")?;
1380 }
1381
1382 if self.base.stranded { continue; }
1384
1385 for (base, target_id, dir, flipped) in node.l_edges() {
1387 let edge_fmt = edge_properties(&node, target_id, base, dir, flipped);
1388 writeln!(writer, "\t\t{{ {edge_fmt} }},")?;
1389 }
1390 }
1391
1392 for (base, target_id, dir, flipped) in last_node.r_edges() {
1396 let edge_fmt = edge_properties(&last_node, target_id, base, dir, flipped);
1397 writeln!(writer, "\t\t{{ {edge_fmt} }}")?;
1398 }
1399
1400 if !self.base.stranded {
1402 for (base, target_id, dir, flipped) in last_node.l_edges() {
1404 let edge_fmt = edge_properties(&last_node, target_id, base, dir, flipped);
1405 writeln!(writer, "\t\t{{ {edge_fmt} }}")?;
1406 }
1407 }
1408
1409 writeln!(writer, "\t]")?;
1410 writeln!(writer, "}}")?;
1411
1412 Ok(())
1413 }
1414
1415 pub fn print(&self) {
1417 println!("DebruijnGraph {{ len: {}, K: {} }} :", self.len(), K::k());
1418 for node in self.iter_nodes() {
1419 println!("{:?}", node);
1420 }
1421 }
1422
1423 pub fn print_with_data(&self) {
1424 println!("DebruijnGraph {{ len: {}, K: {} }} :", self.len(), K::k());
1425 for node in self.iter_nodes() {
1426 println!("{:?} ({:?})", node, node.data());
1427 }
1428 }
1429
1430 pub fn max_path_beam<F, F2>(&self, beam: usize, score: F, _solid_path: F2) -> Vec<(usize, Dir)>
1431 where
1432 F: Fn(&D) -> f32,
1433 F2: Fn(&D) -> bool,
1434 {
1435 if self.is_empty() {
1436 return Vec::default();
1437 }
1438
1439 let mut states = Vec::new();
1440
1441 for i in 0..self.len() {
1442 let node = self.get_node(i);
1443
1444 if node.exts().num_exts_l() == 0 || node.exts().num_exts_r() == 0 {
1446 let dir = if node.exts().num_exts_l() > 0 {
1447 Dir::Right
1448 } else {
1449 Dir::Left
1450 };
1451
1452 let status = if node.exts().num_exts_l() == 0 && node.exts().num_exts_r() == 0 {
1453 Status::End
1454 } else {
1455 Status::Active
1456 };
1457
1458 let mut path = SmallVec8::new();
1459 path.push((i as u32, dir));
1460
1461 let s = State {
1462 path,
1463 status,
1464 score: score(node.data()),
1465 };
1466 states.push(s);
1467 }
1468 }
1469
1470 if states.is_empty() {
1472 let node = self.get_node(0);
1474 let mut path = SmallVec8::new();
1475 path.push((0, Dir::Left));
1476 states.push(State {
1477 path,
1478 status: Status::Active,
1479 score: score(node.data()),
1480 });
1481 }
1482
1483 let mut active = true;
1485 while active {
1486 let mut new_states = Vec::with_capacity(states.len());
1487 active = false;
1488
1489 for s in states {
1490 if s.status == Status::Active {
1491 active = true;
1492 let expanded = self.expand_state(&s, &score);
1493 new_states.extend(expanded);
1494 } else {
1495 new_states.push(s)
1496 }
1497 }
1498
1499 new_states.sort_by(|a, b| (-(a.score)).partial_cmp(&-(b.score)).unwrap());
1501 new_states.truncate(beam);
1502 states = new_states;
1503 }
1504
1505 for (i, state) in states.iter().take(5).enumerate() {
1506 trace!("i:{} -- {:?}", i, state);
1507 }
1508
1509 states[0]
1511 .path
1512 .iter()
1513 .map(|&(node, dir)| (node as usize, dir))
1514 .collect()
1515 }
1516
1517 fn expand_state<F>(&self, state: &State, score: &F) -> SmallVec4<State>
1518 where
1519 F: Fn(&D) -> f32,
1520 {
1521 if state.status != Status::Active {
1522 panic!("only attempt to expand active states")
1523 }
1524
1525 let (node_id, dir) = state.path[state.path.len() - 1];
1526 let node = self.get_node(node_id as usize);
1527 let mut new_states = SmallVec4::new();
1528
1529 for (_, next_node_id, incoming_dir, _) in node.edges(dir.flip()) {
1530 let next_node = self.get_node(next_node_id);
1531 let new_score = state.score + score(next_node.data());
1532
1533 let cycle = state
1534 .path
1535 .iter()
1536 .any(|&(prev_node, _)| prev_node == (next_node_id as u32));
1537
1538 let status = if cycle {
1539 Status::Cycle
1540 } else if next_node.edges(incoming_dir.flip()).is_empty() {
1541 Status::End
1542 } else {
1543 Status::Active
1544 };
1545
1546 let mut new_path = state.path.clone();
1547 new_path.push((next_node_id as u32, incoming_dir));
1548
1549 let next_state = State {
1550 path: new_path,
1551 score: new_score,
1552 status,
1553 };
1554
1555 new_states.push(next_state);
1556 }
1557
1558 new_states
1559 }
1560
1561
1562 pub fn iter_components(&'_ self) -> IterComponents<'_, K, D> {
1563 let mut visited: Vec<bool> = Vec::with_capacity(self.len());
1564 let pos = 0;
1565
1566 for _i in 0..self.len() {
1567 visited.push(false);
1568 }
1569
1570 IterComponents {
1571 graph: self,
1572 visited,
1573 pos }
1574 }
1575
1576
1577 pub fn components_i(&self) -> Vec<Vec<usize>> {
1579 let mut components: Vec<Vec<usize>> = Vec::with_capacity(self.len());
1580 let mut visited: Vec<bool> = Vec::with_capacity(self.len());
1581
1582 for _i in 0..self.len() {
1583 visited.push(false);
1584 }
1585
1586 for i in 0..self.len() {
1587 if !visited[i] {
1588 let comp = self.component_i(&mut visited, i);
1589 components.push(comp);
1590 }
1591 }
1592
1593 components
1594 }
1595
1596 pub fn components_r(&self) -> Vec<Vec<usize>> {
1600 let mut components: Vec<Vec<usize>> = Vec::with_capacity(self.len());
1601 let mut visited: Vec<bool> = Vec::with_capacity(self.len());
1602
1603 for _i in 0..self.len() {
1604 visited.push(false);
1605 }
1606
1607 for i in 0..self.len() {
1608 if !visited[i] {
1609 let mut comp: Vec<usize> = Vec::new();
1610 self.component_r(&mut visited, i, &mut comp);
1611 components.push(comp);
1612 }
1613 }
1614
1615 components
1616
1617 }
1618
1619 fn component_r<'a>(&'a self, visited: &'a mut Vec<bool>, i: usize, comp: &'a mut Vec<usize>) {
1620
1621 visited[i] = true;
1622 comp.push(i);
1623 let mut edges = self.find_edges(i, Dir::Left);
1624 let mut r_edges = self.find_edges(i, Dir::Right);
1625
1626 edges.append(&mut r_edges);
1627
1628 for (_, edge, _, _) in edges.iter() {
1629 if !visited[*edge] {
1630 self.component_r(visited, *edge, comp);
1631 }
1632 }
1633 }
1634
1635 fn component_i<'a>(&'a self, visited: &'a mut [bool], i: usize) -> Vec<usize> {
1636 let mut edges: Vec<usize> = Vec::new();
1637 let mut comp: Vec<usize> = Vec::new();
1638
1639 edges.push(i);
1640
1641 while let Some(current_edge) = edges.pop() {
1642 if !visited[current_edge] {
1643 comp.push(current_edge);
1644 visited[current_edge] = true;
1645
1646 let mut l_edges = self.find_edges(current_edge, Dir::Left);
1647 let mut r_edges = self.find_edges(current_edge, Dir::Right);
1648
1649 l_edges.append(&mut r_edges);
1650
1651 for (_, new_edge, _, _) in l_edges.into_iter() {
1652 if !visited[new_edge] {
1653 edges.push(new_edge);
1654 }
1655 }
1656 }
1657 }
1658 comp
1659 }
1660
1661 pub fn iter_edges(&self) -> EdgeIter<'_, K, D> {
1663 EdgeIter::new(self)
1664 }
1665
1666 pub fn find_bad_nodes<F: Fn(&Node<'_, K, D>) -> bool>(&self, valid: F) -> Vec<usize> {
1667 let mut bad_nodes = Vec::new();
1668
1669 for (i, node) in enumerate(self.iter_nodes()) {
1670 if !valid(&node) { bad_nodes.push(i); }
1671 }
1672
1673 bad_nodes
1674 }
1675}
1676
1677impl<K: Kmer, SD: Debug> DebruijnGraph<K, SD> {
1678 pub fn create_colors<'a, 'b: 'a, DI>(&'a self, config: &SummaryConfig, color_mode: ColorMode<'b>) -> Colors<'b, SD, DI>
1679 where
1680 SD: SummaryData<DI>,
1681 {
1682 Colors::new(self, config, color_mode)
1683 }
1684
1685 pub fn fix_edge_data<DI>(&mut self)
1687 where
1688 SD: SummaryData<DI>
1689 {
1690 if self.get_node(0).data().edge_mults().is_some() {
1691 for i in 0..self.len() {
1692 self.base.data[i].fix_edge_data(self.base.exts[i]);
1693 }
1694 }
1695 }
1696
1697 pub fn filter_edges<DI>(&mut self, min: u32) -> Result<(), String>
1699 where
1700 SD: SummaryData<DI>
1701 {
1702 if self.get_node(0).data().edge_mults().is_none() { return Err(String::from("no edge mults available")) };
1704
1705 for i in 0..self.len() {
1706 let em = self.get_node(i).data().edge_mults().expect("shold have em").clone();
1707 let edges = [(Dir::Left, 0), (Dir::Left, 1), (Dir::Left, 2), (Dir::Left, 3),
1708 (Dir::Right, 0), (Dir::Right, 1), (Dir::Right, 2), (Dir::Right, 3)];
1709
1710 for (dir, base) in edges {
1711 if min > em.edge_mult(base, dir) {
1712 let ext = self.base.exts[i].remove(dir, base);
1714 self.base.exts[i] = ext;
1715 }
1716 }
1717 }
1718
1719 Ok(())
1720 }
1721
1722 pub fn remove_lq_splits<DI>(&mut self, min_quality: BaseQuality) -> Result<(), String>
1725 where
1726 SD: SummaryData<DI>
1727 {
1728 if self.get_node(0).data().quality().is_none() { return Err(String::from("no quality scores available")); }
1730
1731 for (node_id, out_dir) in (0..self.len()).flat_map(|id| [(id, Dir::Right), (id, Dir::Left)]) {
1733 let out_edges = self.get_node(node_id).edges(out_dir);
1734
1735 if out_edges.len() < 2 {
1737 continue;
1739 }
1740
1741 let nb_qualities = out_edges
1743 .iter()
1744 .map(|(_, nb_id, nb_in_dir, _)| (*nb_id, *nb_in_dir, self
1745 .get_node(*nb_id)
1746 .data()
1747 .quality()
1748 .unwrap()
1749 )).collect::<Vec<_>>();
1750
1751 let has_good_nb = nb_qualities.iter()
1753 .any(|(_, _, quality)| *quality >= min_quality);
1754
1755 if !has_good_nb {
1756 continue;
1758 }
1759
1760 for (nb_id, nb_in_dir, _) in nb_qualities.iter().filter(|(_, _, quality)| *quality < min_quality ) {
1762 let path = vec![(node_id, out_dir.flip()), (*nb_id, *nb_in_dir)];
1763 if self.remove_path(path).is_err() {
1764 warn!("lq tip path could not be removed")
1765 }
1766 }
1767 }
1768
1769 Ok(())
1770 }
1771
1772 pub fn remove_lq_paths<DI>(&mut self, min_quality: BaseQuality, max_path_fac: usize) -> Result<(), String>
1775 where
1776 SD: SummaryData<DI>
1777 {
1778 if self.get_node(0).data().quality().is_none() { return Err(String::from("no quality scores available")); }
1780
1781 let min_path = 2 * K::k() - 1;
1782 let max_path = max_path_fac * K::k() - 1;
1783
1784 for (node_id, out_dir) in (0..self.len()).flat_map(|id| [(id, Dir::Right), (id, Dir::Left)]) {
1786 let node_out_edges = self.get_node(node_id).edges(out_dir);
1788
1789 let good_neighbors = node_out_edges.iter()
1790 .map(|(_, target_id, target_in_dir, _)| (*target_id, target_in_dir))
1791 .filter(|(target_id, _)| self.get_node(*target_id)
1792 .data()
1793 .quality()
1794 .unwrap() >= min_quality
1795 ).collect::<Vec<_>>();
1796
1797 let bad_neighbors = node_out_edges.iter()
1798 .map(|(_, target_id, target_in_dir, _)| (*target_id, target_in_dir))
1799 .filter(|(target_id, _)| self.get_node(*target_id)
1800 .data()
1801 .quality()
1802 .unwrap() < min_quality
1803 ).collect::<Vec<_>>();
1804
1805 if good_neighbors.is_empty() | bad_neighbors.is_empty() { continue; }
1806
1807 let mut possible_paths = Vec::new();
1809 let mut tips = Vec::new();
1810
1811 for (bn, bn_in_dir) in bad_neighbors {
1812 let mut current_in_dir = *bn_in_dir; let mut current_node_id = bn;
1814 let mut path_groups = vec![vec![(node_id, out_dir.flip())]];
1815 let mut path_length = K::k() - 1;
1816
1817 let mut state = LadderState::Singular;
1818 let mut q_state_high = false;
1819
1820 loop {
1821 let current_node = self.get_node(current_node_id);
1822 let out_edges = current_node.edges(current_in_dir.flip());
1823
1824 path_length += current_node.len() - K::k() + 1;
1826
1827 if path_length > max_path {
1829 break;
1830 }
1831
1832 let path_index = path_groups.len() - 1;
1834
1835 if matches!(state, LadderState::Singular) {
1837 path_groups[path_index].push((current_node_id, current_in_dir));
1838 }
1839
1840 let q_increase = (current_node.data().quality().unwrap() >= min_quality) & !q_state_high;
1842 let mult_increase = current_node.edges(current_in_dir).len() > 1;
1843
1844 if q_increase {
1845 q_state_high = true
1846 }
1847
1848 if q_increase | mult_increase {
1849 match state {
1850 LadderState::Singular => {
1851 state = LadderState::Double;
1852 }
1853 LadderState::Double => () };
1855 }
1856
1857 let q_decrease = (current_node.data().quality().unwrap() < min_quality) & q_state_high;
1859 let mult_decrease = out_edges.len() > 1;
1860
1861 if q_decrease {
1862 q_state_high = false;
1863 }
1864
1865 if q_decrease | mult_decrease {
1866 match state {
1867 LadderState::Singular => (), LadderState::Double => {
1869 state = LadderState::Singular;
1870 path_groups.push(vec![(current_node_id, current_in_dir)]); }
1872 }
1873 }
1874
1875 let quality_req = current_node.data().quality().unwrap() >= min_quality;
1877 let len_req = path_length >= min_path;
1878 let is_tip = out_edges.is_empty() & (path_groups.len() == 1); if quality_req & len_req {
1882 possible_paths.push(path_groups);
1883 break;
1884 } else if is_tip {
1885 tips.push(path_groups);
1886 break;
1887 }
1888
1889 let (next_node_id, next_in_dir) = if out_edges.len() == 1 {
1893 let (_, next_node_id, next_in_dir, _) = out_edges[0];
1894 (next_node_id, next_in_dir)
1895 } else if out_edges.is_empty() {
1896 break;
1898 } else {
1899 let worst_neighbor = out_edges.iter()
1902 .map(|(_, target_id, target_in_dir, _)| (*target_id, *target_in_dir, self.get_node(*target_id)
1903 .data()
1904 .quality()
1905 .unwrap())
1906 )
1907 .min_by(|(_, _, q_a), (_, _, q_b)| q_a.cmp(q_b));
1908
1909 if let Some((worst_nb_id, worst_nb_inc_dir, _)) = worst_neighbor {
1910 (worst_nb_id, worst_nb_inc_dir)
1911 } else {
1912 break;
1913 }
1914 };
1915
1916 current_node_id = next_node_id;
1917 current_in_dir = next_in_dir;
1918 }
1919 }
1920
1921 let possible_targets = possible_paths.iter().map(|p| p.last().unwrap().last().unwrap().0).collect::<Vec<_>>();
1922 let mut confirmed_targets = Vec::new();
1923 for (gn, gn_in_dir) in good_neighbors {
1926 let mut path_length = K::k() - 1;
1927 let mut current_node_id = gn;
1928 let mut current_in_dir = *gn_in_dir;
1929
1930 loop {
1931 if path_length > max_path {
1933 break;
1934 }
1935
1936 let current_node = self.get_node(current_node_id);
1938 path_length += current_node.len() - K::k() + 1;
1939
1940 if possible_targets.contains(¤t_node_id) {
1942 confirmed_targets.push(current_node_id);
1943 break;
1944 }
1945
1946 let out_edges = current_node.edges(current_in_dir.flip());
1948
1949 let (next_node_id, next_in_dir) = if out_edges.len() == 1 {
1950 let (_, next_node_id, next_in_dir, _) = out_edges[0];
1951 (next_node_id, next_in_dir)
1952 } else if out_edges.is_empty() {
1953 break;
1955 } else {
1956 let good_neighbor = out_edges.iter()
1959 .map(|(_, target_id, target_in_dir, _)| (*target_id, target_in_dir, self.get_node(*target_id)
1960 .data()
1961 .quality()
1962 .unwrap()))
1963 .max_by(|(_, _, q_a), (_, _, q_b)| q_a.cmp(q_b));
1964 if let Some((best_nb_id, best_nb_in_dir, _)) = good_neighbor {
1965 (best_nb_id, *best_nb_in_dir)
1966 } else {
1967 break;
1968 }
1969 };
1970
1971 current_node_id = next_node_id;
1972 current_in_dir = next_in_dir;
1973 }
1974 }
1975
1976 for path_group in possible_paths {
1979 let target = path_group.last().unwrap().last().unwrap().0;
1980
1981 if confirmed_targets.contains(&target) {
1982 for path in path_group {
1983 if let Err(_err) = self.remove_path(path.clone()) {
1984 warn!("lq ladder partial path could not be removed, likely cause: loop, edges were already removed. parital path: {:?}", path)
1985 }
1986 }
1987 }
1988 }
1989
1990 for path_group in tips {
1992 let path = path_group.into_iter().next().expect("empty tip path found");
1993 if let Err(_err) = self.remove_path(path.clone()) {
1994 warn!("lq tip partial path could not be removed, likely cause: loop, edges were already removed. parital path: {:?}", path)
1995 }
1996 }
1997
1998 }
1999
2000 Ok(())
2001 }
2002
2003 pub fn remove_lc_paths<DI>(&mut self, max_path_fac: usize, min_diff_factor: u32, max_avg_low_cov: f32) -> Result<(), String>
2008 where
2009 SD: SummaryData<DI>
2010 {
2011 const COV_STATE_FACTOR: f32 = 0.2;
2020 const COV_STATE_ADD: f32 = 2.;
2021
2022 if self.get_node(0).data().edge_mults().is_none() { return Err(String::from("no quality scores available")); }
2024
2025 let min_path = 2 * K::k() - 1;
2026 let max_path = max_path_fac * K::k() - 1;
2027
2028 for (node_id, start_out_dir) in (0..self.len()).flat_map(|id| [(id, Dir::Right), (id, Dir::Left)]) {
2030
2031 let node = self.get_node(node_id);
2032
2033 let node_out_coverages = node.data().edge_mults().expect("should have em").single_dir(start_out_dir).edge_mults;
2035
2036 let Some((out_max_base, &out_max_cov)) = node_out_coverages.iter().rev().enumerate().filter(|&(_, &c)| c > 0).max_by(|&(_b1, &c1), &(_b2, c2)| c1.cmp(c2)) else { continue };
2037 let smaller_outs = node_out_coverages.iter().copied().rev().enumerate().filter(|&(_b, c)| (c > 0) & (out_max_cov > c)).collect::<Vec<_>>();
2038
2039 if smaller_outs.is_empty() { continue; }
2040
2041 let mut possible_paths = Vec::new();
2043 let mut tips = Vec::new();
2044
2045 let node_term_kmer = node.sequence().term_kmer::<K>(start_out_dir);
2046
2047 for (start_out_base, start_out_cov) in smaller_outs {
2048 let next_kmer = node_term_kmer.extend(start_out_base as u8, start_out_dir);
2050 let (mut current_node_id, mut current_in_dir, _) = self.find_link(next_kmer, start_out_dir).expect("link should exist");
2051
2052 let mut current_cov = start_out_cov as f32;
2053
2054 let mut path_groups = vec![vec![(node_id, start_out_dir.flip())]];
2055 let mut path_length = K::k() - 1;
2056
2057 let mut state = LadderState::Singular;
2058 let mut c_state_high = false;
2059
2060 let mut cov_sum = start_out_cov;
2061 let mut cov_count = 1;
2062
2063 loop {
2064 let current_node = self.get_node(current_node_id);
2065 let out_edges = current_node.edges(current_in_dir.flip());
2066
2067 path_length += current_node.len() - K::k() + 1;
2069
2070 if path_length > max_path {
2072 break;
2073 }
2074
2075 let path_index = path_groups.len() - 1;
2077
2078 if matches!(state, LadderState::Singular) {
2080 path_groups[path_index].push((current_node_id, current_in_dir));
2081 }
2082
2083 let edge_coverages = current_node.data().edge_mults().expect("must have edge mults");
2088 let out_edge_coverages = edge_coverages.single_dir(current_in_dir.flip());
2089
2090 let mut closest_out_cov = None;
2091 let mut next_node_id = None;
2092 let mut next_in_dir = None;
2093 let mut cov_diff = i32::MAX;
2094 for (base, id, in_dir, _) in out_edges.iter() {
2095 let cov = out_edge_coverages.edge_mult(*base) as i32;
2096 let new_cov_diff = (current_cov as i32 - cov).abs();
2097 if new_cov_diff < cov_diff {
2098 (closest_out_cov, next_node_id, next_in_dir, cov_diff) = (Some(cov as f32), Some(*id), Some(*in_dir), new_cov_diff);
2099 }
2100 }
2101
2102 let highest_cov = out_edge_coverages.edge_mults.iter().max().unwrap_or(&0);
2106 let coverage_req = (*highest_cov as f32 > out_max_cov as f32 - out_max_cov as f32 * COV_STATE_FACTOR + COV_STATE_ADD) & !c_state_high; let len_req = path_length >= min_path; let is_tip = out_edges.is_empty() & (path_groups.len() == 1); if coverage_req & len_req {
2114 possible_paths.push((path_groups, cov_sum as f32 / cov_count as f32));
2115 break;
2116 } else if is_tip {
2117 tips.push((path_groups, cov_sum as f32 / cov_count as f32));
2118 break;
2119 }
2120
2121 let (Some(closest_out_cov), Some(next_node_id), Some(next_in_dir)) = (closest_out_cov, next_node_id, next_in_dir) else {
2123 break;
2124 };
2125
2126 let c_increase = (closest_out_cov > current_cov + current_cov * COV_STATE_FACTOR + COV_STATE_ADD) & !c_state_high;
2128 let mult_increase = current_node.edges(current_in_dir).len() > 1;
2129
2130 if c_increase {
2131 c_state_high = true
2132 }
2133
2134 if c_increase | mult_increase {
2135 match state {
2136 LadderState::Singular => {
2137 state = LadderState::Double;
2138 }
2139 LadderState::Double => () };
2141 }
2142
2143 let c_decrease = closest_out_cov < current_cov + current_cov * COV_STATE_FACTOR + COV_STATE_ADD; let mult_decrease = out_edges.len() > 1;
2147
2148 if c_decrease {
2149 c_state_high = false;
2150 }
2151
2152 if c_decrease | mult_decrease {
2153 match state {
2154 LadderState::Singular => (), LadderState::Double => {
2156 state = LadderState::Singular;
2157 path_groups.push(vec![(current_node_id, current_in_dir)]); }
2159 }
2160 }
2161
2162 current_node_id = next_node_id;
2164 current_in_dir = next_in_dir;
2165
2166 if matches!(state, LadderState::Singular) {
2168 current_cov = closest_out_cov;
2169 cov_sum += current_cov as u32;
2170 cov_count += 1;
2171 }
2172 }
2173 }
2174
2175 let possible_targets = possible_paths.iter().map(|(p, _c)| p.last().unwrap().last().unwrap().0).collect::<Vec<_>>();
2176 let mut confirmed_targets = Vec::new();
2177
2178 let next_kmer = node_term_kmer.extend(out_max_base as u8, start_out_dir);
2182 let (mut current_node_id, mut current_in_dir, _) = self.find_link(next_kmer, start_out_dir).expect("link should exist");
2183
2184 let mut path_length = K::k() - 1;
2185 let mut current_cov = out_max_cov;
2186
2187 let mut cov_sum = current_cov;
2189 let mut cov_count = 1;
2190
2191 loop {
2192 if path_length > max_path {
2194 break;
2195 }
2196
2197 let current_node = self.get_node(current_node_id);
2199 path_length += current_node.len() - K::k() + 1;
2200
2201 if possible_targets.contains(¤t_node_id) {
2203 confirmed_targets.push(current_node_id);
2204 }
2206
2207 let out_coverages = current_node.data().edge_mults().expect("must have edge mults").single_dir(current_in_dir.flip()).edge_mults;
2209
2210 let current_term_kmer = current_node.sequence().term_kmer::<K>(current_in_dir.flip());
2213 let Some((next_out_base, next_out_cov)) = out_coverages.iter().rev().enumerate().filter(|&(_, &c)| c > 0).max_by(|&(_b1, &c1), &(_b2, c2)| c1.cmp(c2)) else { break };
2214 let next_kmer = current_term_kmer.extend(next_out_base as u8, current_in_dir.flip());
2215 let (next_node_id, next_in_dir, _) = self.find_link(next_kmer, current_in_dir.flip()).expect("link should exist");
2216
2217
2218 current_node_id = next_node_id;
2219 current_in_dir = next_in_dir;
2220 current_cov = *next_out_cov;
2221 cov_sum += current_cov;
2222 cov_count += 1;
2223
2224 }
2225
2226 let avg_cov_high_path = (cov_sum as f32 / cov_count as f32) as u32;
2227
2228 for (path_group, avg_low_cov) in possible_paths {
2231 let target = path_group.last().unwrap().last().unwrap().0;
2232
2233 let cov_valid = (avg_low_cov.round() as u32 * min_diff_factor <= avg_cov_high_path) & (avg_low_cov <= max_avg_low_cov);
2234
2235 if confirmed_targets.contains(&target) & cov_valid {
2236 for path in path_group {
2237 if let Err(_err) = self.remove_path(path.clone()) {
2238 warn!("lq ladder partial path could not be removed, likely cause: loop, edges were already removed. parital path: {:?}", path)
2239 }
2240 }
2241 }
2242 }
2243
2244 for (path_group, avg_tip_cov) in tips {
2246 let path = path_group.into_iter().next().expect("empty tip path found");
2247 let cov_valid = (avg_tip_cov.round() as u32 * min_diff_factor <= avg_cov_high_path) & (avg_tip_cov <= max_avg_low_cov);
2248
2249 if cov_valid {
2250 if let Err(_err) = self.remove_path(path.clone()) {
2251 warn!("lq tip partial path could not be removed, likely cause: loop, edges were already removed. parital path: {:?}", path)
2252 }
2253 }
2254
2255 }
2256
2257 }
2258
2259 Ok(())
2260 }
2261
2262 pub fn remove_ladders<DI, P>(&mut self, min_diff_factor: u32, max_avg_low_cov: f32, out_path: Option<P>) -> Result<(), String>
2272 where
2273 SD: SummaryData<DI>,
2274 P: AsRef<Path>
2275 {
2276 if self.get_node(0).data().edge_mults().is_none() { return Err(String::from("no edge mults available")) };
2278
2279 let mut writer = out_path.map(|path| BufWriter::new(File::create(path).expect("error creating ladder stats file")));
2280 if let Some(wtr) = writer.as_mut() {
2281 writeln!(wtr, "avg high cov,avg low cov,high truth ratio,low truth ratio").unwrap();
2282 }
2283
2284 for (node_id, out_dir) in (0..self.len()).flat_map(|id| [(id, Dir::Right), (id, Dir::Left)]) {
2286 let outs = self.get_node(node_id).data().edge_mults().expect("should have em").single_dir(out_dir).edge_mults;
2288
2289 let Some((out_max_base, &out_max_cov)) = outs.iter().rev().enumerate().filter(|&(_, &c)| c > 0).max_by(|&(_b1, &c1), &(_b2, c2)| c1.cmp(c2)) else { continue };
2290 let smaller_outs = outs.iter().copied().rev().enumerate().filter(|&(_b, c)| (c > 0) & (out_max_cov > c)).collect::<Vec<_>>();
2291
2292 if smaller_outs.is_empty() { continue; }
2295
2296 let Some((target_node, avg_high_cov, high_cc)) = self.follow_ladder_path_high(node_id, out_max_base as u8, out_max_cov, out_dir) else { continue; };
2298
2299 for (s_base, s_cov) in smaller_outs {
2301 let Some((target_paths, avg_low_cov, low_cc)) = self.follow_ladder_path_low(node_id, s_base as u8, s_cov, out_dir) else { continue; };
2302 let other_target_node = target_paths.last().expect("should have at least one element").last().expect("should have at least two elements").0;
2303
2304 if target_node == other_target_node {
2305 if (s_cov * min_diff_factor <= out_max_cov) & (avg_low_cov <= max_avg_low_cov) {
2307 for path in target_paths.iter() {
2308 if self.remove_path(path.clone()).is_err() {
2309 warn!("removing ladders: partial path could not be removed, likely cause: loop, edges were already removed. parital path: {:?}", path)
2310 }
2311 }
2312 }
2313
2314 if let Some(wtr) = writer.as_mut() {
2315 writeln!(wtr, "{},{},{},{}", avg_high_cov, avg_low_cov, high_cc, low_cc).unwrap();
2316 }
2317 }
2318 }
2319 }
2320
2321 Ok(())
2322 }
2323
2324 fn follow_ladder_path_low<DI>(&self, start_node_id: usize, start_ext: u8, start_cov: u32, start_out_dir: Dir) -> Option<(Vec<Vec<(usize, Dir)>>, f32, f32)>
2326 where SD: SummaryData<DI>
2327 {
2328 const COV_STATE_FACTOR: f32 = 0.2;
2337 const COV_STATE_ADD: f32 = 2.;
2338
2339 let mut paths = Vec::new();
2341 paths.push(Vec::new());
2342 paths[0].push((start_node_id, start_out_dir.flip()));
2344
2345 let target_length = K::k();
2346 let mut path_length = 0;
2347 let mut n_correct_edges = 0;
2348
2349 let sequence = self.base.sequences.get(start_node_id);
2351 let term_kmer: K = sequence.term_kmer(start_out_dir);
2352 let next_kmer = term_kmer.extend(start_ext, start_out_dir);
2353 let (mut current_node_id, mut current_in_dir, _) = self.find_link(next_kmer, start_out_dir).expect("link should exist");
2354 paths[0].push((current_node_id, current_in_dir));
2355
2356 if self.check_edge_truth(start_node_id, current_node_id) {
2357 n_correct_edges += 1;
2358 }
2359
2360 let mut current_cov = start_cov as f32;
2361 let mut sum_path_cov = start_cov as f32;
2362 let mut coverage_counter = 1;
2363
2364 let mut state = LadderState::Singular;
2371
2372 loop {
2373 match path_length {
2375 len if len == target_length => return Some((paths, (sum_path_cov / coverage_counter as f32), (n_correct_edges as f32 / (path_length + 1) as f32))), len if len > target_length => return None, _ => () }
2379
2380 let len = self.get_node(current_node_id).len() - (K::k() - 1);
2382 path_length += len;
2383
2384 let current_node = self.get_node(current_node_id);
2386
2387 let in_edges = current_node.edges(current_in_dir);
2389 let out_edges = current_node.edges(current_in_dir.flip());
2390
2391 let out_edge_coverages = current_node.data().edge_mults().expect("must have edge mults").single_dir(current_in_dir.flip());
2394
2395 let mut out_ext = None;
2397 let mut next_node_id = None;
2398 let mut next_in_dir = None;
2399 let mut cov_diff = i32::MAX;
2400 for (e, id, in_dir, _) in out_edges.iter() {
2401 let cov = out_edge_coverages.edge_mult(*e) as i32;
2402 let new_cov_diff = (current_cov as i32 - cov).abs();
2403 if new_cov_diff < cov_diff {
2404 (out_ext, next_node_id, next_in_dir, cov_diff) = (Some(*e), Some(*id), Some(in_dir), new_cov_diff);
2405 }
2406 }
2407 let (Some(out_ext), Some(next_node_id), Some(next_in_dir)) = (out_ext, next_node_id, next_in_dir) else { return None; };
2408
2409 match in_edges.len() {
2412 1 => (),
2413 2 => match state {
2414 LadderState::Singular => state = LadderState::Double,
2415 LadderState::Double => return None }
2417 _ => return None
2418 }
2419
2420 match out_edges.len() {
2423 1 => (),
2424 2 => match state {
2425 LadderState::Singular => (), LadderState::Double => {
2427 state = LadderState::Singular;
2428 paths.push(vec![(current_node_id, current_in_dir)]); }
2430 }
2431 _ => return None
2432 }
2433
2434 let coverage = out_edge_coverages.edge_mult(out_ext) as f32;
2436 match state {
2437 LadderState::Singular => {
2438 if coverage > current_cov + current_cov * COV_STATE_FACTOR + COV_STATE_ADD {
2441 state = LadderState::Double;
2443 } else {
2444 current_cov = coverage;
2446 }
2447 }
2448 LadderState::Double => {
2449 if coverage < current_cov + current_cov * COV_STATE_FACTOR + COV_STATE_ADD {
2451 state = LadderState::Singular;
2453 paths.push(vec![(current_node_id, current_in_dir)]); current_cov = coverage;
2455 } }
2458 }
2459
2460 match state {
2462 LadderState::Singular => {
2463 if self.check_edge_truth(current_node_id, next_node_id) {
2464 n_correct_edges += len;
2465 }
2466 }
2467 LadderState::Double => ()
2468 }
2469
2470 current_node_id = next_node_id;
2472 current_in_dir = *next_in_dir;
2473
2474 match state {
2476 LadderState::Double => (),
2477 LadderState::Singular => {
2478 sum_path_cov += current_cov;
2479 coverage_counter += 1;
2480 let last_path = paths.len() - 1;
2481 paths[last_path].push((current_node_id, current_in_dir)); }
2483 }
2484 }
2485 }
2486
2487 fn follow_ladder_path_high<DI>(&self, start_node_id: usize, start_ext: u8, start_cov: u32, start_out_dir: Dir) -> Option<(usize, f32, f32)>
2490 where SD: SummaryData<DI>
2491 {
2492
2493 let target_length = K::k();
2494 let mut path_length = 0;
2495 let mut sum_path_cov = start_cov;
2496 let mut coverage_counter = 1;
2497 let mut n_correct_edges = 0;
2498
2499 let sequence = self.base.sequences.get(start_node_id);
2501 let term_kmer: K = sequence.term_kmer(start_out_dir);
2502 let next_kmer = term_kmer.extend(start_ext, start_out_dir);
2503 let (mut current_node_id, mut current_in_dir, _) = self.find_link(next_kmer, start_out_dir).expect("link should exist");
2504
2505 if self.check_edge_truth(start_node_id, current_node_id) {
2506 n_correct_edges += 1;
2507 }
2508
2509 loop {
2510 match path_length {
2512 pl if pl == target_length => return Some((current_node_id, (sum_path_cov as f32 / coverage_counter as f32), (n_correct_edges as f32 / (path_length + 1) as f32))), pl if pl > target_length => return None, _ => () }
2516
2517 let len = self.get_node(current_node_id).len() - (K::k() - 1);
2519 path_length += len;
2520
2521 let current_node = self.get_node(current_node_id);
2523
2524 let em = current_node.data().edge_mults().expect("must have edge mults").single_dir(current_in_dir.flip()).edge_mults;
2526 let (max_cov_base, &max_cov) = em.iter().rev().enumerate().filter(|&(_, &c)| c > 0).max_by(|&(_b1, &c1), &(_b2, c2)| c1.cmp(c2))?;
2527
2528 let sequence = self.base.sequences.get(current_node_id);
2530 let term_kmer: K = sequence.term_kmer(current_in_dir.flip());
2531 let next_kmer = term_kmer.extend(max_cov_base as u8, current_in_dir.flip());
2532 let (next_node_id, next_in_dir, _) = self.find_link(next_kmer, current_in_dir.flip()).expect("link should exist");
2533
2534 if self.check_edge_truth(current_node_id, next_node_id) {
2536 n_correct_edges += len;
2537 }
2538
2539 current_node_id = next_node_id;
2541 current_in_dir = next_in_dir;
2542 sum_path_cov += max_cov;
2543 coverage_counter += 1;
2544 }
2545 }
2546
2547 pub fn remove_tips<DI, P>(&mut self, min_diff_factor: u32, max_avg_tip_cov: f32, out_path: Option<P>) -> Result<(), String>
2551 where
2552 SD: SummaryData<DI>,
2553 P: AsRef<Path>
2554 {
2555 if self.get_node(0).data().edge_mults().is_none() { return Err(String::from("no edge mults available")) };
2557
2558 let mut writer = out_path.map(|path| BufWriter::new(File::create(path).expect("error creating ladder stats file")));
2559 if let Some(wtr) = writer.as_mut() {
2560 writeln!(wtr, "high cov,avg low cov,max true,tip truth ratio,tip len,dir").unwrap();
2561 }
2562
2563 let max_len = K::k();
2564
2565 for node_id in 0..self.len() {
2567 for dir in [Dir::Left, Dir::Right] {
2571 let current_node = self.get_node(node_id);
2572 let outs = current_node.data().edge_mults().expect("should have em").single_dir(dir).edge_mults;
2574
2575 let Some((out_max_base, &out_max_cov)) = outs.iter().rev().enumerate().filter(|&(_, &c)| c > 0).max_by(|&(_b1, &c1), &(_b2, c2)| c1.cmp(c2)) else { continue };
2576 let smaller_outs = outs.iter().copied().rev().enumerate().filter(|&(_b, c)| (c > 0) & (out_max_cov > c)).collect::<Vec<_>>();
2577
2578 if smaller_outs.is_empty() { continue; }
2579
2580 let max_connection_correct = {
2582 let sequence = self.base.sequences.get(node_id);
2583 let term_kmer: K = sequence.term_kmer(dir);
2584 let next_kmer = term_kmer.extend(out_max_base as u8, dir);
2585 let (next_node_id, _, _) = self.find_link(next_kmer, dir).expect("missing link");
2586
2587 self.check_edge_truth(node_id, next_node_id)
2588 }; for (s_base, s_cov) in smaller_outs {
2592 let Some((tip_path, avg_tip_coverage, truth_ratio, tip_len)) = self.follow_tip_path(node_id, s_base as u8, s_cov, dir) else { continue; };
2594
2595 if (s_cov * min_diff_factor <= out_max_cov) & (avg_tip_coverage <= max_avg_tip_cov) & (tip_len <= max_len) {
2597 self.remove_path(tip_path)?;
2598 }
2599
2600 if let Some(wtr) = writer.as_mut() {
2601 writeln!(wtr, "{},{},{},{},{},{:?}", out_max_cov, avg_tip_coverage, max_connection_correct, truth_ratio, tip_len, dir).unwrap();
2602 }
2603 }
2604 }
2605 }
2606
2607 Ok(())
2608 }
2609
2610 fn follow_tip_path<DI>(&self, start_node_id: usize, start_ext: u8, start_cov: u32, start_out_dir: Dir) -> Option<(Vec<(usize, Dir)>, f32, f32, usize)>
2612 where
2613 SD: SummaryData<DI>
2614 {
2615 const COV_MARGIN: f32 = 0.3;
2616 const COV_ADD_MARGIN: f32 = 4.;
2617
2618 let mut path = Vec::new();
2620 path.push((start_node_id, start_out_dir.flip()));
2621
2622 let mut path_length = 0;
2623 let mut n_correct_edges = 0;
2624
2625 let sequence = self.base.sequences.get(start_node_id);
2627 let term_kmer: K = sequence.term_kmer(start_out_dir);
2628 let next_kmer = term_kmer.extend(start_ext, start_out_dir);
2629 let (mut current_node_id, mut current_in_dir, _) = self.find_link(next_kmer, start_out_dir).expect("link should exist");
2630 path.push((current_node_id, current_in_dir));
2631
2632 if self.check_edge_truth(start_node_id, current_node_id) {
2633 n_correct_edges += 1;
2634 }
2635
2636 let mut current_cov = start_cov as f32;
2637 let mut sum_path_cov = start_cov as f32;
2638 let mut coverage_counter = 1;
2639
2640 loop {
2641 let current_node = self.get_node(current_node_id);
2643
2644 let len = current_node.len() - (K::k() - 1);
2646 path_length += len;
2647
2648 let in_edges = current_node.edges(current_in_dir);
2650 if in_edges.len() != 1 { return None; }
2651
2652 let out_edges = current_node.edges(current_in_dir.flip());
2653 match out_edges.len() {
2654 oe if oe > 1 => return None,
2655 0 => return Some((path, (sum_path_cov / coverage_counter as f32), (n_correct_edges as f32 / path_length as f32), path_length)),
2656 _ => ()
2657 }
2658
2659 let (out_ext, next_node_id, next_in_dir, _) = out_edges[0];
2661 if let Some(em) = current_node.data().edge_mults() {
2662 let coverage = em.edge_mult(out_ext, current_in_dir.flip()) as f32;
2663 if coverage < current_cov - current_cov * COV_MARGIN - COV_ADD_MARGIN || coverage > current_cov + current_cov * COV_MARGIN + COV_ADD_MARGIN {
2665 return None;
2666 } else {
2667 current_cov = coverage;
2668 }
2669 }
2670
2671
2672
2673 if self.check_edge_truth(current_node_id, next_node_id) {
2675 n_correct_edges += len;
2676 }
2677
2678 current_node_id = next_node_id;
2680 current_in_dir = next_in_dir;
2681 sum_path_cov += current_cov;
2682 coverage_counter += 1;
2683 path.push((current_node_id, current_in_dir));
2685 }
2686 }
2687
2688 fn remove_path<DI>(&mut self, path: Vec<(usize, Dir)>) -> Result<(), String>
2691 where SD: SummaryData<DI>
2692 {
2693 let mut path_iter = path.clone().into_iter();
2694 let Some((mut current_node_id, mut current_in_dir)) = path_iter.next() else { return Ok(()); };
2695
2696 loop {
2697 let Some((next_node_id, next_in_dir)) = path_iter.next() else { return Ok(()); }; let Some((out_base, _, _, _)) = self.get_node(current_node_id).edges(current_in_dir.flip()).iter().find(|&(_, id, _, _)| *id == next_node_id).copied()
2701 else {
2702 return Err( format!("no edge to remove (other dir), node 1: {:?}, node 2: {:?}",
2703 self.get_node(current_node_id),
2704 self.get_node(next_node_id)
2705 ))
2706 };
2707
2708 self.base.exts[current_node_id] = self.base.exts[current_node_id].remove(current_in_dir.flip(), out_base);
2710
2711
2712 let Some((in_base, _, _, _)) = self.get_node(next_node_id).edges(next_in_dir).iter().find(|&(_, id, _, _)| *id == current_node_id).copied()
2714 else {
2715 return Err( format!("no edge to remove (other dir), node 1: {:?}, node 2: {:?}",
2716 self.get_node(current_node_id),
2717 self.get_node(next_node_id)
2718 ))
2719 };
2720
2721 self.base.exts[next_node_id] = self.base.exts[next_node_id].remove(next_in_dir, in_base);
2723
2724 self.base.data[current_node_id].fix_edge_data(self.base.exts[current_node_id]);
2726 self.base.data[next_node_id].fix_edge_data(self.base.exts[next_node_id]);
2727
2728
2729 current_node_id = next_node_id;
2730 current_in_dir = next_in_dir;
2731 }
2732 }
2733
2734 pub fn check_edge_truth<DI>(&self, node_id_1: usize, node_id_2: usize) -> bool
2737 where
2738 SD: SummaryData<DI>
2739 {
2740 let mapped_ids_1 = self.get_node(node_id_1).data().mapped_ids();
2741 let mapped_ids_2 = self.get_node(node_id_2).data().mapped_ids();
2742
2743 if let (Some(mids1), Some(mids2)) = (mapped_ids_1, mapped_ids_2) {
2744 let hashed_mi2 = mids2.iter().copied().collect::<HashSet<_>>();
2745 for mid in mids1 {
2746 if hashed_mi2.contains(mid) {
2747 return true;
2748 }
2749 }
2750 }
2751
2752 false
2753 }
2754
2755 pub fn check_edge_truth_emap<DI>(&self, node_id_1: usize, node_id_2: usize) -> bool
2758 where
2759 SD: SummaryData<DI>
2760 {
2761 let mapped_ids_1 = self.get_node(node_id_1).data().mapped_edge_ids();
2762
2763 if let Some(emap) = mapped_ids_1 {
2764 let node_1 = self.get_node(node_id_1);
2765
2766 if let Some((out_base, _nb_id, _nb_inc_dir, _flip)) = node_1.l_edges().iter().find(|(_b, nb, _d, _f)| *nb == node_id_2) {
2768 let edge_map = emap.edge_map(*out_base, Dir::Left);
2769 if !edge_map.is_empty() {
2770 return true
2771 }
2772 } else if let Some((out_base, _nb_id, _nb_inc_dir, _flip)) = node_1.r_edges().iter().find(|(_b, nb, _d, _f)| *nb == node_id_2) {
2773 let edge_map = emap.edge_map(*out_base, Dir::Right);
2774 if !edge_map.is_empty() {
2775 return true
2776 }
2777 }
2778 }
2779
2780 false
2781 }
2782}
2783
2784
2785#[derive(Debug, Eq, PartialEq)]
2786enum Status {
2787 Active,
2788 End,
2789 Cycle,
2790}
2791
2792#[derive(Debug)]
2793struct State {
2794 path: SmallVec8<(u32, Dir)>,
2795 score: f32,
2796 status: Status,
2797}
2798
2799impl State {}
2800
2801#[derive(Debug, PartialEq, Eq)]
2802pub enum LadderState {
2803 Singular,
2804 Double
2805}
2806
2807pub struct NodeIter<'a, K: Kmer + 'a, D: Debug + 'a> {
2809 graph: &'a DebruijnGraph<K, D>,
2810 node_id: usize,
2811}
2812
2813impl<'a, K: Kmer + 'a, D: Debug + 'a> Iterator for NodeIter<'a, K, D> {
2814 type Item = Node<'a, K, D>;
2815
2816 fn next(&mut self) -> Option<Node<'a, K, D>> {
2817 if self.node_id < self.graph.len() {
2818 let node = self.graph.get_node(self.node_id);
2819 self.node_id += 1;
2820 Some(node)
2821 } else {
2822 None
2823 }
2824 }
2825}
2826
2827impl<'a, K: Kmer + 'a, D: Debug + 'a> IntoIterator for &'a DebruijnGraph<K, D> {
2828 type Item = NodeKmer<'a, K, D>;
2829 type IntoIter = NodeIntoIter<'a, K, D>;
2830
2831 fn into_iter(self) -> Self::IntoIter {
2832 NodeIntoIter {
2833 graph: self,
2834 node_id: 0,
2835 }
2836 }
2837}
2838
2839pub struct NodeIntoIter<'a, K: Kmer + 'a, D: Debug + 'a> {
2841 graph: &'a DebruijnGraph<K, D>,
2842 node_id: usize,
2843}
2844
2845impl<'a, K: Kmer + 'a, D: Debug + 'a> Iterator for NodeIntoIter<'a, K, D> {
2846 type Item = NodeKmer<'a, K, D>;
2847
2848 fn next(&mut self) -> Option<Self::Item> {
2849 if self.node_id < self.graph.len() {
2850 let node_id = self.node_id;
2851 let node = self.graph.get_node(node_id);
2852 let node_seq = node.sequence();
2853
2854 self.node_id += 1;
2855 Some(NodeKmer {
2856 node_id,
2857 node_seq_slice: node_seq,
2858 phantom_d: PhantomData,
2859 phantom_k: PhantomData,
2860 })
2861 } else {
2862 None
2863 }
2864 }
2865}
2866
2867#[derive(Clone)]
2869pub struct NodeKmer<'a, K: Kmer + 'a, D: Debug + 'a> {
2870 pub node_id: usize,
2871 node_seq_slice: DnaStringSlice<'a>,
2872 phantom_k: PhantomData<K>,
2873 phantom_d: PhantomData<D>,
2874}
2875
2876pub struct NodeKmerIter<'a, K: Kmer + 'a, D: Debug + 'a> {
2878 kmer_id: usize,
2879 kmer: K,
2880 num_kmers: usize,
2881 node_seq_slice: DnaStringSlice<'a>,
2882 phantom_k: PhantomData<K>,
2883 phantom_d: PhantomData<D>,
2884}
2885
2886impl<'a, K: Kmer + 'a, D: Debug + 'a> IntoIterator for NodeKmer<'a, K, D> {
2887 type Item = K;
2888 type IntoIter = NodeKmerIter<'a, K, D>;
2889
2890 fn into_iter(self) -> Self::IntoIter {
2891 let num_kmers = self.node_seq_slice.len() - K::k() + 1;
2892
2893 let kmer = if num_kmers > 0 {
2894 self.node_seq_slice.get_kmer::<K>(0)
2895 } else {
2896 K::empty()
2897 };
2898
2899 NodeKmerIter {
2900 kmer_id: 0,
2901 kmer,
2902 num_kmers,
2903 node_seq_slice: self.node_seq_slice,
2904 phantom_k: PhantomData,
2905 phantom_d: PhantomData,
2906 }
2907 }
2908}
2909
2910impl<'a, K: Kmer + 'a, D: Debug + 'a> Iterator for NodeKmerIter<'a, K, D> {
2911 type Item = K;
2912
2913 fn next(&mut self) -> Option<Self::Item> {
2914 if self.num_kmers == self.kmer_id {
2915 None
2916 } else {
2917 let current_kmer = self.kmer;
2918
2919 self.kmer_id += 1;
2920 if self.kmer_id < self.num_kmers {
2921 let next_base = self.node_seq_slice.get(self.kmer_id + K::k() - 1);
2922 let new_kmer = self.kmer.extend_right(next_base);
2923 self.kmer = new_kmer;
2924 }
2925
2926 Some(current_kmer)
2927 }
2928 }
2929
2930 fn size_hint(&self) -> (usize, Option<usize>) {
2931 (self.num_kmers, Some(self.num_kmers))
2932 }
2933
2934 fn nth(&mut self, n: usize) -> Option<Self::Item> {
2938 if n <= 4 {
2939 for _ in 0..n {
2941 self.next();
2942 }
2943 } else {
2944 self.kmer_id += n;
2945 self.kmer = self.node_seq_slice.get_kmer::<K>(self.kmer_id);
2946 }
2947
2948 self.next()
2949 }
2950}
2951
2952impl<'a, K: Kmer + 'a, D: Debug + 'a> ExactSizeIterator for NodeKmerIter<'a, K, D> {}
2954
2955pub struct Node<'a, K: Kmer + 'a, D: 'a> {
2957 pub node_id: usize,
2958 pub graph: &'a DebruijnGraph<K, D>,
2959}
2960
2961impl<'a, K: Kmer, D: Debug> Node<'a, K, D> {
2962 pub fn len(&self) -> usize {
2964 self.graph.base.sequences.get(self.node_id).len()
2965 }
2966
2967 pub fn is_empty(&self) -> bool {
2968 self.graph.base.sequences.get(self.node_id).is_empty()
2969 }
2970
2971 pub fn sequence(&self) -> DnaStringSlice<'a> {
2973 self.graph.base.sequences.get(self.node_id)
2974 }
2975
2976 pub fn data(&self) -> &'a D {
2978 &self.graph.base.data[self.node_id]
2979 }
2980
2981 pub fn exts(&self) -> Exts {
2983 self.graph.base.exts[self.node_id]
2984 }
2985
2986 pub fn l_edges(&self) -> SmallVec4<(u8, usize, Dir, bool)> {
2989 self.graph.find_edges(self.node_id, Dir::Left)
2990 }
2991
2992 pub fn r_edges(&self) -> SmallVec4<(u8, usize, Dir, bool)> {
2995 self.graph.find_edges(self.node_id, Dir::Right)
2996 }
2997
2998 pub fn edges(&self, dir: Dir) -> SmallVec4<(u8, usize, Dir, bool)> {
3001 self.graph.find_edges(self.node_id, dir)
3002 }
3003}
3004
3005impl<K: Kmer, SD: Debug> Node<'_, K, SD> {
3007 pub fn edge_dot_default<DI>(&self, colors: &Colors<SD, DI>, base: u8, incoming_dir: Dir, flipped: bool) -> String
3009 where SD: SummaryData<DI>
3010 {
3011 let color = match incoming_dir {
3013 Dir::Left => "blue",
3014 Dir::Right => "red"
3015 };
3016
3017 if let Some(em) = self.data().edge_mults() {
3018
3019 let dir = if flipped {
3020 incoming_dir
3021 } else {
3022 incoming_dir.flip()
3023 };
3024
3025 let count = em.edge_mult(base, dir);
3027 let penwidth = colors.edge_width(count);
3028
3029 if let Some(emap) = self.data().mapped_edge_ids() {
3030 let mapped_ids = emap.edge_map(base, dir);
3032
3033 let color = match incoming_dir {
3035 Dir::Left => if mapped_ids.is_empty() {"deepskyblue"} else {"blue"},
3036 Dir::Right => if mapped_ids.is_empty() {"salmon"} else {"red"},
3037 };
3038
3039 format!("[color={color}, penwidth={penwidth}, label=\"{}: {count}, {:?}\", weight={count}]", bits_to_base(base), mapped_ids)
3040 } else {
3041 format!("[color={color}, penwidth={penwidth}, label=\"{}: {count}\", weight={count}]", bits_to_base(base))
3042 }
3043
3044 } else {
3045 format!("[color={color}, penwidth={}]", colors.edge_width(1)) }
3047 }
3048
3049 pub fn node_dot_default<DI>(&self, colors: &Colors<SD, DI>, config: &SummaryConfig, translator: &Translator, outline: bool, translate_id_groups: bool) -> String
3051 where SD: SummaryData<DI>
3052 {
3053 let color = colors.node_color_dot(self.data(), config, outline);
3055 let translate_id_groups = if translate_id_groups { colors.id_group_ids() } else { None };
3056
3057 let data_info = self.data().print(translator, config, translate_id_groups);
3058 const MIN_TEXT_WIDTH: usize = 40;
3059 let wrap = if self.len() > MIN_TEXT_WIDTH { self.len() } else { MIN_TEXT_WIDTH };
3060
3061 let label = textwrap::fill(&format!("id: {}, len: {}, exts: {:?}, seq: {}\n{}",
3062 self.node_id,
3063 self.len(),
3064 self.exts(),
3065 self.sequence(),
3066 data_info
3067 ), wrap);
3068
3069 format!("[{color}, label=\"{label}\"]")
3070 }
3071
3072 pub fn edge_json_default<DI>(&self, target_node_id: usize, base: u8, incoming_dir: Dir, flipped: bool) -> String
3074 where SD: SummaryData<DI>
3075 {
3076 let dir = match incoming_dir {
3078 Dir::Right => 0,
3079 Dir::Left => 1
3080 };
3081
3082 let target_node = self.graph.get_node(target_node_id);
3084 let nb_base = if self.graph.base.stranded {
3085 let Some(nb_base) = target_node.l_edges().iter().filter_map(|(b, id, _in_dir, _flip)|
3087 if *id == self.node_id {
3088 Some(*b)
3089 } else { None }
3090 ).next() else { panic!("missing neighbor") };
3091 nb_base
3092 } else {
3093 let Some(nb_base) = target_node.r_edges().iter().chain(target_node.l_edges().iter()).filter_map(|(b, id, _in_dir, _flip)|
3095 if *id == self.node_id {
3096 Some(*b)
3097 } else { None }
3098 ).next() else { panic!("missing neighbor") };
3099 nb_base
3100 };
3101
3102 let value = if let Some(em) = self.data().edge_mults() {
3103
3104 let dir = if flipped {
3105 incoming_dir
3106 } else {
3107 incoming_dir.flip()
3108 };
3109
3110 let count = em.edge_mult(base, dir);
3111
3112 format!(", \"strength\": {count}")
3113 } else {
3114 String::from("")
3115 };
3116
3117 format!("\"source\": {}, \"target\": {target_node_id}, \"source_b\": \"{}\", \"target_b\": \"{}\", \"dir\": {dir}{value}",
3118 self.node_id,
3119 bits_to_base(base),
3120 bits_to_base(nb_base),
3121 )
3122 }
3123
3124 pub fn node_json_default<DI>(&self, colors: &Colors<SD, DI>, config: &SummaryConfig, translator: &Translator, translate_id_groups: bool) -> String
3126 where SD: SummaryData<DI>
3127 {
3128 let hue = colors.hue_json(self.data(), config);
3130 let translate_id_groups = if translate_id_groups { colors.id_group_ids() } else { None };
3131
3132 let data_info = self.data().print_json(translator, config, translate_id_groups);
3133
3134 format!("\"id\": {}, \"len\": {}, \"seq\": \"{}\", \"hue\": {hue}, {data_info}",
3135 self.node_id,
3136 self.len(),
3137 self.sequence(),
3138 )
3139 }
3140}
3141
3142impl<K: Kmer, D> fmt::Debug for Node<'_, K, D>
3143where
3144 D: Debug,
3145{
3146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3147 write!(
3148 f,
3149 "Node {{ id:{}, Exts: {:?}, L:{:?} R:{:?}, Seq: {:?}, Data: {:?} }}",
3150 self.node_id,
3151 self.exts(),
3152 self.l_edges(),
3153 self.r_edges(),
3154 self.sequence().len(),
3155 self.data()
3156 )
3157 }
3158}
3159
3160pub struct IterComponents<'a, K: Kmer, D> {
3161 graph: &'a DebruijnGraph<K, D>,
3162 visited: Vec<bool>,
3163 pos: usize,
3164}
3165
3166impl<K: Kmer, D: Debug> Iterator for IterComponents<'_, K, D> {
3167 type Item = Vec<usize>;
3168 fn next(&mut self) -> Option<Self::Item> {
3169 while self.pos < self.graph.len() {
3170 if !self.visited[self.pos] {
3171 let comp = self.graph.component_i(&mut self.visited, self.pos);
3172 self.pos += 1;
3173 return Some(comp)
3174 } else {
3175 self.pos += 1;
3176 }
3177 }
3178 assert!(self.visited.iter().map(|x| *x as usize).sum::<usize>() == self.graph.len());
3179 None
3180 }
3181
3182}
3183
3184pub struct PathCompIter<'a, K: Kmer, D: Debug, F, F2>
3185where
3186F: Fn(&D) -> f32,
3187F2: Fn(&D) -> bool
3188{
3189 graph: &'a DebruijnGraph<K, D>,
3190 component_iterator: IterComponents<'a, K, D>,
3191 graph_pos: usize,
3192 score: F,
3193 solid_path: F2,
3194}
3195
3196impl<K: Kmer, D: Debug, F, F2> Iterator for PathCompIter<'_, K, D, F, F2>
3198where
3199F: Fn(&D) -> f32,
3200F2: Fn(&D) -> bool
3201{
3202 type Item = (Vec<usize>, VecDeque<(usize, Dir)>,);
3203 fn next(&mut self) -> Option<Self::Item> {
3204 match self.component_iterator.next() {
3205 Some(component) => {
3206 let current_comp = component;
3207
3208
3209 let mut best_node = current_comp[0];
3210 let mut best_score = f32::MIN;
3211 for c in current_comp.iter() {
3212 let node = self.graph.get_node(*c);
3213 let node_score = (self.score)(node.data());
3214
3215 if node_score > best_score {
3216 best_node = *c;
3217 best_score = node_score;
3218 }
3219 }
3220
3221 let oscore = |state| match state {
3222 None => 0.0,
3223 Some((id, _)) => (self.score)(self.graph.get_node(id).data()),
3224 };
3225
3226 let osolid_path = |state| match state {
3227 None => false,
3228 Some((id, _)) => (self.solid_path)(self.graph.get_node(id).data()),
3229 };
3230
3231 let mut used_nodes = HashSet::new();
3234 let mut path = VecDeque::new();
3235
3236 used_nodes.insert(best_node);
3238 path.push_front((best_node, Dir::Left));
3239
3240 for init in [(best_node, Dir::Left, false), (best_node, Dir::Right, true)].iter() {
3241 let &(start_node, dir, do_flip) = init;
3242 let mut current = (start_node, dir);
3243
3244 loop {
3245 let mut next = None;
3246 let (cur_id, incoming_dir) = current;
3247 let node = self.graph.get_node(cur_id);
3248 let edges = node.edges(incoming_dir.flip());
3249
3250 let mut solid_paths = 0;
3251 for (_, id, dir, _) in edges {
3252 let cand = Some((id, dir));
3253 if osolid_path(cand) {
3254 solid_paths += 1;
3255
3256 if oscore(cand) > oscore(next) {
3259 next = cand;
3260 }
3261 }
3262 }
3263
3264 match next {
3270 Some((next_id, next_incoming)) if !used_nodes.contains(&next_id) => {
3271 if do_flip {
3272 path.push_front((next_id, next_incoming.flip()));
3273 } else {
3274 path.push_back((next_id, next_incoming));
3275 }
3276
3277 used_nodes.insert(next_id);
3278 current = (next_id, next_incoming);
3279 }
3280 _ => break,
3281 }
3282 }
3283 }
3284
3285
3286 Some((current_comp, path))
3287 },
3288 None => {
3289 self.graph_pos += 1;
3291 None
3292 }
3293 }
3294 }
3295}
3296
3297
3298pub struct EdgeIter<'a, K: Kmer, D: Debug> {
3300 graph: &'a DebruijnGraph<K, D>,
3301 visited_edges: HashSet<(usize, usize)>,
3302 current_node: usize,
3303 current_dir: Dir,
3304 node_edge_iter: smallvec::IntoIter<[(u8, usize, Dir, bool); 4]>
3305}
3306
3307impl<K: Kmer, D: Debug> EdgeIter<'_, K, D> {
3308 pub fn new(graph: &DebruijnGraph<K, D>) -> EdgeIter<'_, K, D>{
3309 let node_edge_iter = graph.get_node(0).l_edges().into_iter();
3310
3311 EdgeIter {
3312 graph,
3313 visited_edges: HashSet::new(),
3314 current_node: 0,
3315 current_dir: Dir::Left,
3316 node_edge_iter
3317 }
3318 }
3319}
3320
3321impl<K: Kmer, D: Debug> Iterator for EdgeIter<'_, K, D> {
3322 type Item = (usize, Dir, u8, usize); fn next(&mut self) -> Option<Self::Item> {
3325 loop {
3326 if let Some((base, nb_node_id, _, _)) = self.node_edge_iter.next() {
3327 let edge = if self.current_node > nb_node_id { (nb_node_id, self.current_node) } else { (self.current_node, nb_node_id) };
3328
3329 if self.visited_edges.insert(edge) { return Some((self.current_node, self.current_dir, base, nb_node_id)); } } else {
3332 match self.current_dir {
3333 Dir::Left => {
3334 self.current_dir = Dir::Right;
3336 self.node_edge_iter = self.graph.get_node(self.current_node).r_edges().into_iter();
3337
3338 }
3339 Dir::Right => {
3340 self.current_node += 1;
3342
3343 if self.current_node == self.graph.len() { return None }
3345
3346 self.current_dir = Dir::Left;
3347 self.node_edge_iter = self.graph.get_node(self.current_node).l_edges().into_iter();
3348 }
3349 }
3350 }
3351
3352 }
3353 }
3354}
3355
3356#[cfg(test)]
3357mod test {
3358 use std::fs::remove_file;
3359
3360 use crate::{BaseQuality, EdgeMap, Exts, build_test_graph, colors::Colors, compression::{CheckCompress, ScmapCompress, compress_kmers_with_hash, uncompressed_graph}, dna_string::DnaString, filter::filter_kmers, kmer::{Kmer6, Kmer16, Kmer22}, reads::{Reads, ReadsPaired, Strandedness}, summarizer::{IDMapEMData, IDMapEMQualityData, IDTag, MapEMEmapQualityData, SampleInfo, SummaryConfig, TagsCountsData, TagsCountsSumData, Translator}, test::random_dna};
3361
3362 use crate::{summarizer::SummaryData, Dir};
3363
3364
3365 #[test]
3366 #[cfg(not(feature = "sample128"))]
3367 fn test_components() {
3368 use crate::{kmer::Kmer16, test::build_test_graph};
3369
3370 let (_, _, ser_graph) = build_test_graph::<Kmer16, TagsCountsSumData, _>();
3371 let graph = ser_graph.graph();
3372
3373 let components = graph.iter_components();
3374
3375 let check_components = [
3376 vec![0, 7, 43, 24, 47, 22, 37, 89, 25, 79, 63, 95, 64, 9, 96, 13, 11, 86, 74, 71, 92, 51, 94, 45, 12, 76, 21],
3377 vec![1, 54, 44, 5, 57, 65, 84, 10, 58, 35, 42, 73, 30, 83, 77, 15, 80, 72, 81, 78, 67, 49, 69, 91, 2, 90, 33, 87, 55, 8, 17, 88, 31, 56, 52, 27, 4, 6, 99, 40, 93, 28, 26, 62, 59, 97, 82, 46],
3378 vec![3, 41, 36, 34, 38, 85, 75, 19, 48, 16, 61, 66, 23, 20, 14, 18, 39, 29, 70, 32, 50, 53, 68, 60, 98],
3379 ];
3380
3381 let mut counter = 0;
3382
3383 for component in components {
3384 if component.len() > 1 {
3385 println!("component: {:?}", component);
3386 assert_eq!(component, check_components[counter]);
3387 counter += 1;
3388 }
3389 }
3390
3391 assert_eq!(
3392 vec![(88, Dir::Left), (17, Dir::Left), (8, Dir::Left), (55, Dir::Left), (87, Dir::Left), (33, Dir::Left), (90, Dir::Left), (2, Dir::Left), (91, Dir::Left), (69, Dir::Left), (49, Dir::Left), (78, Dir::Left), (81, Dir::Left), (72, Dir::Left), (1, Dir::Left), (54, Dir::Left), (44, Dir::Left), (5, Dir::Left), (57, Dir::Left)],
3393 graph.max_path(|data| data.sum().unwrap_or(1) as f32, |_| true)
3394 );
3395 }
3396
3397 #[test]
3398 fn test_iter_edges() {
3399 use crate::{compression::uncompressed_graph, filter::filter_kmers, reads::{Reads, ReadsPaired}, summarizer::{SampleInfo, SummaryConfig, TagsData}};
3400
3401 let read1 = "CAGCATCGATGCGACGAGCGCTCGCATCGA".as_bytes();
3402 let read2 = "ACGATCGTACGTAGCTAGCTGACTGAGC".as_bytes();
3403
3404 let mut reads = Reads::new(crate::reads::Strandedness::Forward);
3405 reads.add_from_bytes(read1, None, 0u8);
3406 reads.add_from_bytes(read2, None, 1);
3407
3408 let reads_paired = ReadsPaired::Unpaired { reads };
3409
3410 let sample_info = SampleInfo::new(0b1, 0b10, vec![12, 12]);
3411 let summary_config = SummaryConfig::new(sample_info);
3412 let (kmers, _) = filter_kmers::<TagsData, Kmer16, _>(&reads_paired, &summary_config, false, 1., false);
3413
3414 let graph = uncompressed_graph(kmers, true).finish();
3415
3416 let check_edges: Vec<(usize, Dir, u8, usize)> = vec![(0, Dir::Left, 2, 16), (0, Dir::Right, 1, 2), (1, Dir::Left, 0, 11),
3417 (1, Dir::Right, 0, 16), (3, Dir::Left, 0, 13), (3, Dir::Right, 3, 10), (4, Dir::Left, 2, 21), (4, Dir::Right, 1, 17), (5, Dir::Left, 1, 27),
3418 (5, Dir::Right, 2, 19), (6, Dir::Left, 1, 24), (6, Dir::Right, 2, 12), (7, Dir::Left, 2, 23), (7, Dir::Right, 3, 11), (8, Dir::Left, 0, 12),
3419 (9, Dir::Left, 3, 17), (9, Dir::Right, 3, 24), (10, Dir::Right, 1, 21), (13, Dir::Left, 1, 18), (14, Dir::Right, 0, 26),
3420 (15, Dir::Left, 2, 20), (15, Dir::Right, 3, 22), (18, Dir::Left, 2, 19), (20, Dir::Left, 1, 26), (22, Dir::Right, 2, 25), (23, Dir::Left, 1, 25)];
3421
3422 let edges = graph.iter_edges().collect::<Vec<_>>();
3423
3424 assert_eq!(check_edges, edges);
3425 }
3426
3427 #[test]
3428 fn test_map_transcripts() {
3429 let t_ref_path = "test_data/test_transcriptome_reference.fasta";
3430 let (_, ser_kmers, _) = build_test_graph::<Kmer22, u32, _>();
3431 let (kmers, mut translator, _) = ser_kmers.dissolve();
3432
3433 let unc_graph = uncompressed_graph(kmers, true).finish();
3434
3435 let mut id_strings = translator.id_translator().clone().unwrap().into_iter().map(|(name, _id)| name).collect::<Vec<_>>();
3436 id_strings.sort();
3437
3438 let t_map = unc_graph.map_transcripts(t_ref_path, &mut translator).unwrap();
3439 assert_eq!(t_map.len(), unc_graph.len());
3440 assert_eq!(t_map.iter().filter(|&ids| !ids.is_empty()).collect::<Vec<_>>().len(), 439);
3441
3442 let mut new_translator = Translator::empty();
3444 let t_map = unc_graph.map_transcripts(t_ref_path, &mut new_translator).unwrap();
3445 assert_eq!(t_map.len(), unc_graph.len());
3446 assert_eq!(t_map.iter().filter(|&ids| !ids.is_empty()).collect::<Vec<_>>().len(), 439);
3447 let mut new_id_strings = new_translator.id_translator().clone().unwrap().into_iter().map(|(name, _id)| name).collect::<Vec<_>>();
3448 new_id_strings.sort();
3449
3450 assert_eq!(id_strings, new_id_strings);
3451 }
3452
3453
3454 #[test]
3455 fn test_map_transcripts_to_edges() {
3456 let t_ref_path = "test_data/test_transcriptome_reference.fasta";
3457 let (_, ser_kmers, _) = build_test_graph::<Kmer22, u32, _>();
3458 let (kmers, mut translator, _) = ser_kmers.dissolve();
3459
3460 let unc_graph = uncompressed_graph(kmers, true).finish();
3461
3462 let mut id_strings = translator.id_translator().clone().unwrap().into_iter().map(|(name, _id)| name).collect::<Vec<_>>();
3463 id_strings.sort();
3464
3465 let t_map = unc_graph.map_transcripts_to_edges(t_ref_path, &mut translator).unwrap();
3466 assert_eq!(t_map.len(), unc_graph.len());
3467 assert_eq!(t_map.iter().filter(|&emap| !emap.is_empty()).collect::<Vec<_>>().len(), 439);
3468
3469 let mut new_translator = Translator::empty();
3471 let t_map = unc_graph.map_transcripts(t_ref_path, &mut new_translator).unwrap();
3472 assert_eq!(t_map.len(), unc_graph.len());
3473 assert_eq!(t_map.iter().filter(|&ids| !ids.is_empty()).collect::<Vec<_>>().len(), 439);
3474 let mut new_id_strings = new_translator.id_translator().clone().unwrap().into_iter().map(|(name, _id)| name).collect::<Vec<_>>();
3475 new_id_strings.sort();
3476
3477 assert_eq!(id_strings, new_id_strings);
3478 }
3479
3480 fn build_reads_quality_test(strand: Strandedness) -> ReadsPaired<IDTag> {
3481 let correct1 = "CGATGCTGCTGATGCTGAGTCTGACGTATGCGATCGATCGACGATCGTACTAGCTGACTGTGCAGCTAGCTGACTGATCGTAGCTAGCTACGTGCTAGCTACTAGCACTGATGC";
3482 let qu_corr1 = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
3483 let incorrect1 = "CGACGATCGTACTAGCTGACTGTGCAGCTAGCTGACTGATCGTGGCTAGCTACGTGCTAGCTA";
3484 let qu_incorr1 = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC-CCCCCCCCCCCCCCCCCCC";
3485 let correct2 = "GCATCGATCGACGACGTTACGTACGATCTACGTAGCTAGCTAGCTGACATGCTAGCTAGCTCTGACTGATCGTGGCTAGCTGACTGACTGTAGCT";
3486 let qu_corr2 = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
3487 let incorrect2 = "ATGCTGCTGATGCTGAGTCTGACGTAGGCGATCGATCGACGATCGTACTAGCTGACT";
3488 let qu_incorr2 = "CCCCCCCCCCCCCCCCCCCCCCCCCC-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
3489 let incorrect3 = "ATGCTGCTGATGCTGACTCTGACGTAGGCGATCGATCGATGATCGTACTAGCTGACT";
3490 let qu_incorr3 = "CCCCCCCCCCCCCCCC-CCCCCCCCC-CCCCCCCCCCCC-CCCCCCCCCCCCCCCCC";
3491
3492 let incorrect4_ins = "GACGTATGCGATCGATCGACGATCGTACTAGCTGACTTGTGCAGCTAGCTGACTGAT";
3493 let qu_incorr4_ins = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC-CCCCCCCCCCCCCCCCCCC";
3494
3495 let incorrect5_tip = "AGCTGACTGATCGTAGCTAGCTACGTGCTAGCTACTATCACTGATGC";
3496 let qu_incorr5_tip = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC-CCCCCCCCC";
3497
3498
3499 let mut reads = Reads::new_with_quality(strand);
3500
3501 for _i in 0..20 {
3502 reads.add_read(DnaString::from_acgt_bytes(correct1.as_bytes()), None, IDTag::new(1, 0), Some(qu_corr1.as_bytes()));
3503 }
3504
3505 for _i in 0..40 {
3506 reads.add_read(DnaString::from_acgt_bytes(correct2.as_bytes()), None, IDTag::new(2, 0), Some(qu_corr2.as_bytes()));
3507 }
3508
3509 reads.add_read(DnaString::from_acgt_bytes(incorrect1.as_bytes()), None, IDTag::new(3, 1), Some(qu_incorr1.as_bytes()));
3510 reads.add_read(DnaString::from_acgt_bytes(incorrect2.as_bytes()), None, IDTag::new(4, 1), Some(qu_incorr2.as_bytes()));
3511 reads.add_read(DnaString::from_acgt_bytes(incorrect3.as_bytes()), None, IDTag::new(5, 1), Some(qu_incorr3.as_bytes()));
3512 reads.add_read(DnaString::from_acgt_bytes(incorrect4_ins.as_bytes()), None, IDTag::new(6, 1), Some(qu_incorr4_ins.as_bytes()));
3513 reads.add_read(DnaString::from_acgt_bytes(incorrect5_tip.as_bytes()), None, IDTag::new(7, 1), Some(qu_incorr5_tip.as_bytes()));
3514
3515
3516 ReadsPaired::Unpaired { reads }
3517 }
3518
3519 #[test]
3520 fn test_remove_lq_splits_s() {
3521 test_remove_lq_splits(true);
3522 }
3523
3524 #[test]
3525 fn test_remove_lq_splits_us() {
3526 test_remove_lq_splits(false);
3527 }
3528
3529 fn test_remove_lq_splits(stranded: bool) {
3530 let print = false;
3531 let strandedness = if stranded { Strandedness::Forward } else { Strandedness::Unstranded };
3532
3533
3534 type K = Kmer16;
3535
3536 let seqs = build_reads_quality_test(strandedness);
3537 let sample_info = SampleInfo::new(1, 0b111110, vec![1000, 10, 20, 20, 20, 20]);
3538 let summary_config = SummaryConfig::new(sample_info);
3539 let (kmers, _) = filter_kmers::<IDMapEMQualityData, K, IDTag>(&seqs, &summary_config, false, 1., false);
3540
3541
3542 let mut unc_graph = uncompressed_graph(kmers.clone(), stranded).finish();
3544
3545 for i in 0..unc_graph.len() {
3547 let data = unc_graph.mut_data(i);
3548 if let Some(ids) = data.ids() {
3549 if ids.contains(&1) {
3550 data.set_mapped_ids(vec![1].into());
3551 }
3552 else if ids.contains(&2) {
3553 data.set_mapped_ids(vec![2].into());
3554 }
3555 }
3556 }
3557
3558 let colors = Colors::new(&unc_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 7 });
3559
3560 if print { unc_graph.to_dot("uncompressed_bf-lq-splits.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3561 let n_edges = unc_graph.iter_edges().count();
3562 unc_graph.remove_lq_splits(BaseQuality::Medium).unwrap();
3563 if print { unc_graph.to_dot("uncompressed_af-lq-splits.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3564
3565 let n_edges_af = unc_graph.iter_edges().count();
3566 assert_eq!(n_edges, n_edges_af + 11);
3567
3568 let spec = CheckCompress::new(|d: IDMapEMQualityData, _| d, |d, d1| d.join_test(d1));
3570 let mut c_graph = compress_kmers_with_hash(stranded, &spec, kmers, false, false).finish();
3571
3572 for i in 0..c_graph.len() {
3574 let data = c_graph.mut_data(i);
3575 if let Some(ids) = data.ids() {
3576 if ids.contains(&1) {
3577 data.set_mapped_ids(vec![1].into());
3578 }
3579 else if ids.contains(&2) {
3580 data.set_mapped_ids(vec![2].into());
3581 }
3582 }
3583 }
3584
3585 let colors = Colors::new(&c_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 7 });
3586
3587 if print { c_graph.to_dot("compressed_bf-lq-splits.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3588 let n_edges = c_graph.iter_edges().count();
3589 c_graph.remove_lq_splits(BaseQuality::Medium).unwrap();
3590 if print { c_graph.to_dot("compressed_af-lq-splits.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3591
3592 let n_edges_af = c_graph.iter_edges().count();
3593 assert_eq!(n_edges, n_edges_af + 11);
3594 }
3595
3596 #[test]
3597 fn test_remove_lq_paths_s() {
3598 test_remove_lq_paths(true);
3599 }
3600
3601 #[test]
3602 fn test_remove_lq_paths_us() {
3603 test_remove_lq_paths(false);
3604 }
3605
3606 fn test_remove_lq_paths(stranded: bool) {
3607 let print = false;
3608 let strandedness = if stranded { Strandedness::Forward } else { Strandedness::Unstranded };
3609
3610 type K = Kmer16;
3611
3612 let seqs = build_reads_quality_test(strandedness);
3613 let sample_info = SampleInfo::new(1, 0b111110, vec![1000, 10, 20, 20, 20, 20]);
3614 let summary_config = SummaryConfig::new(sample_info);
3615 let (kmers, _) = filter_kmers::<IDMapEMQualityData, K, IDTag>(&seqs, &summary_config, false, 1., false);
3616
3617
3618 let mut unc_graph = uncompressed_graph(kmers.clone(), stranded).finish();
3620
3621 for i in 0..unc_graph.len() {
3623 let data = unc_graph.mut_data(i);
3624 if let Some(ids) = data.ids() {
3625 if ids.contains(&1) {
3626 data.set_mapped_ids(vec![1].into());
3627 }
3628 else if ids.contains(&2) {
3629 data.set_mapped_ids(vec![2].into());
3630 }
3631 }
3632 }
3633
3634 let colors = Colors::new(&unc_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 7 });
3635
3636 if print { unc_graph.to_dot("uncompressed_bf-lq.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3637 let n_edges = unc_graph.iter_edges().count();
3638 unc_graph.remove_lq_paths(BaseQuality::Medium, 4).unwrap();
3639 if print { unc_graph.to_dot("uncompressed_af-lq.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3640
3641 let n_edges_af = unc_graph.iter_edges().count();
3642 assert_eq!(n_edges, n_edges_af + 90);
3643
3644 let spec = CheckCompress::new(|d: IDMapEMQualityData, _| d, |d, d1| d.join_test(d1));
3646 let mut c_graph = compress_kmers_with_hash(stranded, &spec, kmers, false, false).finish();
3647
3648 for i in 0..c_graph.len() {
3650 let data = c_graph.mut_data(i);
3651 if let Some(ids) = data.ids() {
3652 if ids.contains(&1) {
3653 data.set_mapped_ids(vec![1].into());
3654 }
3655 else if ids.contains(&2) {
3656 data.set_mapped_ids(vec![2].into());
3657 }
3658 }
3659 }
3660
3661 let colors = Colors::new(&c_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 7 });
3662
3663 if print { c_graph.to_dot("compressed_bf-lq.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3664 let n_edges = c_graph.iter_edges().count();
3665 c_graph.remove_lq_paths(BaseQuality::Medium, 4).unwrap();
3666 if print { c_graph.to_dot("compressed_af-lq.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3667
3668 let n_edges_af = c_graph.iter_edges().count();
3669 assert_eq!(n_edges, n_edges_af + 15);
3670 }
3671
3672 #[test]
3673 fn test_remove_lc_paths_s() {
3674 test_remove_lc_paths(true);
3675 }
3676
3677 #[test]
3678 fn test_remove_lc_paths_us() {
3679 test_remove_lc_paths(false);
3680 }
3681
3682 fn test_remove_lc_paths(stranded: bool) {
3683 let print = true;
3684 let strandedness = if stranded { Strandedness::Forward } else { Strandedness::Unstranded };
3685
3686 let (n_diff_uc, n_diff_c) = if stranded { (27, 8) } else { (27, 10) };
3687
3688 let correct = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3689 let incorrect = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGGCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3690 let incorrec2 = "TGACAGCTGACGGCTGACTACTACGTCACTGACGATGCTGACAC".as_bytes();
3691 let incorrec3 = "AAAAAAAAAGCTGACTGCTGACGGCTG".as_bytes();
3692 let incorrec4 = "ACGGCTGACTACTGACTGAAAAAAAAAAA".as_bytes();
3693
3694 let insertion = "ACGATCGATCGCGATCGATAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3695
3696 let mut reads = Reads::new(strandedness);
3697 for _i in 0..1000 {
3698 reads.add_from_bytes(correct, None, IDTag::new(0, 0));
3699 }
3700
3701 for _i in 0..2 {
3702 reads.add_from_bytes(incorrect, None, IDTag::new(1, 1)); }
3704
3705 for _i in 0..15 {
3706 reads.add_from_bytes(incorrec2, None, IDTag::new(2, 2)); }
3708
3709 for _i in 0..25 {
3710 reads.add_from_bytes(incorrec3, None, IDTag::new(3, 3)); }
3712
3713 for _i in 0..30 {
3714 reads.add_from_bytes(incorrec4, None, IDTag::new(4, 4)); }
3716
3717
3718 for _i in 0..1 {
3719 reads.add_from_bytes(insertion, None, IDTag::new(1, 3)); }
3721
3722 let seqs = ReadsPaired::Unpaired { reads };
3723 let sample_info = SampleInfo::new(1, 0b111110, vec![1000, 10, 20, 20, 20, 20]);
3724 let summary_config = SummaryConfig::new(sample_info);
3725 let (kmers, _) = filter_kmers::<IDMapEMData, Kmer16, IDTag>(&seqs, &summary_config, false, 1., false);
3726
3727
3728 let mut unc_graph = uncompressed_graph(kmers.clone(), stranded).finish();
3730 for i in 0..unc_graph.len() {
3732 let data = unc_graph.mut_data(i);
3733 if let Some(ids) = data.ids() {
3734 if ids.contains(&0) {
3735 data.set_mapped_ids(vec![0].into());
3736 }
3737 }
3738 }
3739 let colors = Colors::new(&unc_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 5 });
3740 if print { unc_graph.to_dot("uncompressed_bf-lcp.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3741 let n_edges = unc_graph.iter_edges().count();
3742 unc_graph.remove_lc_paths(10, 10, 10.).unwrap();
3743 if print { unc_graph.to_dot("uncompressed_af-lcp.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3744 assert_eq!(n_edges - n_diff_uc, unc_graph.iter_edges().count());
3745
3746 let spec = CheckCompress::new(|d: IDMapEMData, _| d, |d, d1| d.join_test(d1));
3748 let mut c_graph = compress_kmers_with_hash(stranded, &spec, kmers, false, false).finish();
3749 for i in 0..c_graph.len() {
3751 let data = c_graph.mut_data(i);
3752 if let Some(ids) = data.ids() {
3753 if ids.contains(&0) {
3754 data.set_mapped_ids(vec![0].into());
3755 }
3756 }
3757 }
3758 let colors = Colors::new(&c_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 5 });
3759 if print { c_graph.to_dot("compressed_bf-lcp.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3760 let n_edges = c_graph.iter_edges().count();
3761 c_graph.remove_lc_paths(10, 10, 10.).unwrap();
3762 if print { c_graph.to_dot("compressed_af-lcp.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3763 assert_eq!(n_edges - n_diff_c, c_graph.iter_edges().count());
3764 }
3765
3766 #[test]
3767 fn test_remove_ladders_s() {
3768 test_remove_ladders(true);
3769 }
3770
3771 #[test]
3772 fn test_remove_ladders_us() {
3773 test_remove_ladders(false);
3774 }
3775
3776 fn test_remove_ladders(stranded: bool) {
3777 let print = true;
3778 let strandedness = if stranded { Strandedness::Forward } else { Strandedness::Unstranded };
3779
3780 let c_csv = if print { Some("c_ladders.csv") } else { None };
3781 let uc_csv = if print { Some("uc_ladders.csv") } else { None };
3782
3783 let correct = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3784 let incorrect = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGGCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3785 let incorrec2 = "TGACAGCTGACGGCTGACTACTACGTCACTGACGATGCTGACAC".as_bytes();
3786 let incorrec3 = "AAAAAAAAAGCTGACTGCTGACGGCTG".as_bytes();
3787 let incorrec4 = "ACGGCTGACTACTGACTGAAAAAAAAAAA".as_bytes();
3788
3789 let insertion = "ACGATCGATCGCGATCGATAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3790
3791 let mut reads = Reads::new(strandedness);
3792 for _i in 0..1000 {
3793 reads.add_from_bytes(correct, None, IDTag::new(0, 0));
3794 }
3795
3796 for _i in 0..2 {
3797 reads.add_from_bytes(incorrect, None, IDTag::new(1, 1)); }
3799
3800 for _i in 0..15 {
3801 reads.add_from_bytes(incorrec2, None, IDTag::new(2, 2)); }
3803
3804 for _i in 0..25 {
3805 reads.add_from_bytes(incorrec3, None, IDTag::new(3, 3)); }
3807
3808 for _i in 0..30 {
3809 reads.add_from_bytes(incorrec4, None, IDTag::new(4, 4)); }
3811
3812
3813 for _i in 0..1 {
3814 reads.add_from_bytes(insertion, None, IDTag::new(1, 3)); }
3816
3817 let seqs = ReadsPaired::Unpaired { reads };
3818 let sample_info = SampleInfo::new(1, 0b111110, vec![1000, 10, 20, 20, 20, 20]);
3819 let summary_config = SummaryConfig::new(sample_info);
3820 let (kmers, _) = filter_kmers::<IDMapEMData, Kmer16, IDTag>(&seqs, &summary_config, false, 1., false);
3821
3822
3823 let mut unc_graph = uncompressed_graph(kmers.clone(), stranded).finish();
3825 for i in 0..unc_graph.len() {
3827 let data = unc_graph.mut_data(i);
3828 if let Some(ids) = data.ids() {
3829 if ids.contains(&0) {
3830 data.set_mapped_ids(vec![0].into());
3831 }
3832 }
3833 }
3834 let colors = Colors::new(&unc_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 5 });
3835 if print { unc_graph.to_dot("uncompressed_bf-lad.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3836 let n_edges = unc_graph.iter_edges().count();
3837 unc_graph.remove_ladders(10, 10., uc_csv).unwrap();
3838 if print { unc_graph.to_dot("uncompressed_af-lad.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3839 assert_eq!(n_edges - 10, unc_graph.iter_edges().count());
3840
3841 let spec = CheckCompress::new(|d: IDMapEMData, _| d, |d, d1| d.join_test(d1));
3843 let mut c_graph = compress_kmers_with_hash(stranded, &spec, kmers, false, false).finish();
3844 for i in 0..c_graph.len() {
3846 let data = c_graph.mut_data(i);
3847 if let Some(ids) = data.ids() {
3848 if ids.contains(&0) {
3849 data.set_mapped_ids(vec![0].into());
3850 }
3851 }
3852 }
3853 let colors = Colors::new(&c_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 5 });
3854 if print { c_graph.to_dot("compressed_bf-lad.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3855 let n_edges = c_graph.iter_edges().count();
3856 c_graph.remove_ladders(10, 10., c_csv).unwrap();
3857 if print { c_graph.to_dot("compressed_af-lad.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3858 assert_eq!(n_edges - 6, c_graph.iter_edges().count());
3859 }
3860
3861
3862 #[test]
3863 fn test_remove_tips_s() {
3864 test_remove_tips(true);
3865 }
3866
3867 #[test]
3868 fn test_remove_tips_us() {
3869 test_remove_tips(false);
3870 }
3871
3872 fn test_remove_tips(stranded: bool) {
3873 let print = false;
3874 let strandedness = if stranded { Strandedness::Forward } else { Strandedness::Unstranded };
3875
3876 let c_csv = if print { Some("c_tips.csv") } else { None };
3877 let uc_csv = if print { Some("uc_tips.csv") } else { None };
3878
3879 let correct = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3880 let incorrect_r = "ACGATCGATCGCGATCGTAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTAACGTGAC".as_bytes();
3881 let incorrect_l = "ACGTTCGATCGCGATCGTAGCTGACTGCTGACGTCTGACTACTGACTGATGCTAGCTATCGTGAC".as_bytes();
3882
3883
3884
3885 let mut reads = Reads::new(strandedness);
3886 for _i in 0..1000 {
3887 reads.add_from_bytes(correct, None, IDTag::new(0, 0));
3888 }
3889
3890 for _i in 0..10 {
3891 reads.add_from_bytes(incorrect_r, None, IDTag::new(1, 1)); }
3893
3894 for _i in 0..10 {
3895 reads.add_from_bytes(incorrect_l, None, IDTag::new(2, 2)); }
3897
3898 let seqs = ReadsPaired::Unpaired { reads };
3899 let sample_info = SampleInfo::new(1, 6, vec![1000, 10, 20]);
3900 let summary_config = SummaryConfig::new(sample_info);
3901 let (kmers, _) = filter_kmers::<IDMapEMData, Kmer16, IDTag>(&seqs, &summary_config, false, 1., false);
3902
3903
3904 let mut unc_graph = uncompressed_graph(kmers.clone(), stranded).finish();
3906 for i in 0..unc_graph.len() {
3908 let data = unc_graph.mut_data(i);
3909 if let Some(ids) = data.ids() {
3910 if ids.contains(&0) {
3911 data.set_mapped_ids(vec![0].into());
3912 }
3913 }
3914 }
3915
3916 let colors = Colors::new(&unc_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 3 });
3917 if print { unc_graph.to_dot("uncompressed_bf-tips.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3918 let n_edges = unc_graph.iter_edges().count();
3919
3920 unc_graph.remove_tips(10, 10., uc_csv).unwrap();
3921 if print { unc_graph.to_dot("uncompressed_af-tips.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3922 assert_eq!(n_edges - 11, unc_graph.iter_edges().count());
3923
3924 let spec = CheckCompress::new(|d: IDMapEMData, _| d, |d, d1| d.join_test(d1));
3926 let mut c_graph = compress_kmers_with_hash(stranded, &spec, kmers, false, false).finish();
3927 for i in 0..c_graph.len() {
3929 let data = c_graph.mut_data(i);
3930 if let Some(ids) = data.ids() {
3931 if ids.contains(&0) {
3932 data.set_mapped_ids(vec![0].into());
3933 }
3934 }
3935 }
3936
3937 let colors = Colors::new(&c_graph, &summary_config, crate::colors::ColorMode::IDS { n_ids: 3 });
3938 if print { c_graph.to_dot("compressed_bf-tips.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3939 let n_edges = c_graph.iter_edges().count();
3940
3941 c_graph.remove_tips(10, 10., c_csv).unwrap();
3942 if print { c_graph.to_dot("compressed_af-tips.dot", &|node| node.node_dot_default(&colors, &summary_config, &Translator::empty(), false, false), &|node, base, dir, flip| node.edge_dot_default(&colors, base, dir, flip)); }
3943 assert_eq!(n_edges - 2, c_graph.iter_edges().count());
3944
3945 }
3946
3947 #[test]
3948 fn test_check_edge_truth() {
3949 let (_, _, ser_graph) = build_test_graph::<Kmer16, MapEMEmapQualityData, _>();
3950 let (mut graph, _translator, _config) = ser_graph.dissolve();
3951
3952 graph.mut_data(0).set_mapped_ids(vec![0, 1].into());
3954 graph.mut_data(11).set_mapped_ids(vec![0].into());
3955 graph.mut_data(16).set_mapped_ids(vec![1, 2].into());
3956
3957 assert!(graph.check_edge_truth(0, 11)); assert!(graph.check_edge_truth(0, 16)); assert!(!graph.check_edge_truth(0, 2)); assert!(!graph.check_edge_truth(1, 2)); let mut emap = EdgeMap::default();
3964 emap.add_id_to_edge_map_at_index(0, 5);
3965 graph.mut_data(0).set_mapped_edge_ids(Some(emap));
3966
3967 let mut emap = EdgeMap::default();
3969 emap.add_id_to_edge_map_at_index(0, 0);
3970 graph.mut_data(11).set_mapped_edge_ids(Some(emap));
3971
3972 assert!(graph.check_edge_truth_emap(0, 11)); assert!(!graph.check_edge_truth_emap(0, 16)); assert!(!graph.check_edge_truth_emap(0, 2)); }
3976
3977 #[test]
3978 fn test_to_tsv() {
3979 let reads_us = Reads::from_vmer_vec(
3980 (0..10).map(|i| (DnaString::from_bytes(&random_dna(100)), Exts::empty(), i as u8)).collect::<Vec<_>>(),
3981 crate::reads::Strandedness::Unstranded
3982 );
3983
3984 let reads_paired = ReadsPaired::Unpaired { reads: reads_us };
3985
3986 let sample_info = SampleInfo::new(0b1111100000, 0b0000011111, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
3987 let summary_config = SummaryConfig::new(sample_info);
3988 let (kmers, _) = filter_kmers::<TagsCountsData, Kmer6, _>(&reads_paired, &summary_config, false, 1., false);
3989
3990 let graph = compress_kmers_with_hash(false, &ScmapCompress::new(), kmers, false, false).finish();
3991
3992 graph.to_tsv("test_graph_unstranded.tsv", |node| node.data().print_ol(&Translator::empty(), &summary_config, None)).unwrap();
3993
3994
3995 let reads_us = Reads::from_vmer_vec(
3996 (0..10).map(|i| (DnaString::from_bytes(&random_dna(100)), Exts::empty(), i as u8)).collect::<Vec<_>>(),
3997 crate::reads::Strandedness::Forward
3998 );
3999
4000 let reads_paired = ReadsPaired::Unpaired { reads: reads_us };
4001
4002 let sample_info = SampleInfo::new(0b1111100000, 0b0000011111, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
4003 let summary_config = SummaryConfig::new(sample_info);
4004 let (kmers, _) = filter_kmers::<TagsCountsData, Kmer6, _>(&reads_paired, &summary_config, false, 1., false);
4005
4006 let graph = compress_kmers_with_hash(true, &ScmapCompress::new(), kmers, false, false).finish();
4007
4008 graph.to_tsv("test_graph_stranded.tsv", |node| node.data().print_ol(&Translator::empty(), &summary_config, None)).unwrap();
4009
4010 remove_file("test_graph_unstranded.tsv").unwrap();
4011 remove_file("test_graph_stranded.tsv").unwrap();
4012 }
4013}
4014