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