22#include < sstream>
33#include < algorithm>
44#include < queue>
5+ #include < iomanip>
6+ #include < set>
57
68// #define debug
79
@@ -126,7 +128,8 @@ void RGFACover::compute(const PathHandleGraph* graph,
126128 nid_t node_id = graph->get_id (graph->get_handle_of_step (step_handle));
127129 if (node_to_interval.count (node_id)) {
128130 cerr << " [rgfa error]: node " << node_id << " covered by two reference paths,"
129- << " including " << graph->get_path_name (ref_path_handle)
131+ << " including " << graph->get_path_name (ref_path_handle) << " and "
132+ << graph->get_path_name (graph->get_path_handle_of_step (rgfa_intervals.at (node_to_interval.at (node_id)).first ))
130133 << " . rGFA support current requires disjoint acyclic reference paths" << endl;
131134 exit (1 );
132135 }
@@ -930,6 +933,24 @@ vector<pair<int64_t, nid_t>> RGFACover::get_reference_nodes(nid_t node_id, bool
930933 return output_reference_nodes;
931934}
932935
936+ pair<handle_t , handle_t > RGFACover::parse_variant_id (const string& variant_id) const {
937+
938+ if (variant_id[0 ] == ' >' || variant_id[0 ] == ' <' ) {
939+ size_t p = variant_id.find_first_of (" <>" , 2 );
940+ if (p != string::npos) {
941+ int64_t id1 = parse<int64_t >(variant_id.substr (1 , p-1 ));
942+ int64_t id2 = parse<int64_t >(variant_id.substr (p+1 ));
943+ if (!graph->has_node (id1) || !graph->has_node (id2)) {
944+ throw runtime_error (" Unable to parse " + variant_id + " : node(s) not found in graph" );
945+ }
946+ return make_pair (graph->get_handle (id1, variant_id[0 ] == ' <' ),
947+ graph->get_handle (id2, variant_id[p] == ' <' ));
948+ }
949+ }
950+ throw runtime_error (" Failed to parse " + variant_id);
951+ return make_pair (handle_t (), handle_t ());
952+ }
953+
933954void RGFACover::annotate_vcf (vcflib::VariantCallFile& vcf, ostream& os) {
934955 vcflib::Variant var (vcf);
935956
@@ -951,12 +972,6 @@ void RGFACover::annotate_vcf(vcflib::VariantCallFile& vcf, ostream& os) {
951972 }
952973 }
953974
954- // also want an rGFA path lookup
955- unordered_map<string, path_handle_t > name_to_rgfa_path;
956- graph->for_each_path_of_sample (RGFACover::rgfa_sample_name, [&](path_handle_t path_handle) {
957- name_to_rgfa_path[RGFACover::revert_rgfa_path_name (graph->get_path_name (path_handle))] = path_handle;
958- });
959-
960975 // remove header lines we're going to add
961976 vcf.removeInfoHeaderLine (" R_CHROM" );
962977 vcf.removeInfoHeaderLine (" R_START" );
@@ -971,80 +986,156 @@ void RGFACover::annotate_vcf(vcflib::VariantCallFile& vcf, ostream& os) {
971986
972987 os << vcf.header << endl;
973988
974- string prev_sequence_name;
975- string prev_r_chrom;
976- string prev_r_start;
977- string prev_r_end;
978- string prev_rank;
979989 while (vcf.getNextVariant (var)) {
980- if (name_to_rgfa_path.count (var.sequenceName )) {
981- string r_chrom;
982- string r_start;
983- string r_end;
984- string rank;
985- if (var.sequenceName == prev_sequence_name) {
986- // just use the previous values, which will be the same
987- r_chrom = prev_r_chrom;
988- r_start = prev_r_start;
989- r_end = prev_r_end;
990- rank = prev_rank;
991- } else {
992- // compute from the cover
993- path_handle_t path_handle = name_to_rgfa_path.at (var.sequenceName );
994- nid_t first_node = graph->get_id (graph->get_handle_of_step (graph->path_begin (path_handle)));
995- vector<pair<int64_t , nid_t >> ref_nodes = this ->get_reference_nodes (first_node, false );
996-
997- int64_t min_ref_pos = numeric_limits<int64_t >::max ();
998- int64_t max_ref_pos = -1 ;
999- int64_t min_rank = numeric_limits<int64_t >::max ();
1000- for (const pair<int64_t , nid_t >& rank_node : ref_nodes) {
1001- step_handle_t ref_step = this ->rgfa_intervals .at (node_to_interval.at (rank_node.second )).first ;
1002- path_handle_t ref_path = graph->get_path_handle_of_step (ref_step);
1003- string name = graph->get_path_name (ref_path);
1004- // we assume one reference contig (which is built into the whole structure)
1005- assert (r_chrom.empty () || r_chrom == name);
1006- r_chrom = name;
1007- int64_t ref_pos = node_to_ref_pos.at (rank_node.second );
1008- // assume snarl is forward on both reference nodes
1009- // todo: this won't be exact for some inversion cases, I don't think --
1010- // need to test these and either add check / or move to oriented search
1011- min_ref_pos = min (min_ref_pos, ref_pos + (int64_t )graph->get_length (graph->get_handle (rank_node.second )));
1012- max_ref_pos = max (max_ref_pos, ref_pos);
1013- min_rank = min (min_rank, rank_node.first );
1014- }
990+ pair<handle_t , handle_t > id_handles = parse_variant_id (var.id );
991+ string r_chrom;
992+ string r_start;
993+ string r_end;
994+ string rank;
995+
996+ // compute from the cover
997+ nid_t first_node = graph->get_id (id_handles.first );
998+ vector<pair<int64_t , nid_t >> ref_nodes = this ->get_reference_nodes (first_node, false );
999+
1000+ int64_t min_ref_pos = numeric_limits<int64_t >::max ();
1001+ int64_t max_ref_pos = -1 ;
1002+ int64_t min_rank = numeric_limits<int64_t >::max ();
1003+ for (const pair<int64_t , nid_t >& rank_node : ref_nodes) {
1004+ step_handle_t ref_step = this ->rgfa_intervals .at (node_to_interval.at (rank_node.second )).first ;
1005+ path_handle_t ref_path = graph->get_path_handle_of_step (ref_step);
1006+ string name = graph->get_path_name (ref_path);
1007+ // we assume one reference contig (which is built into the whole structure)
1008+ assert (r_chrom.empty () || r_chrom == name);
1009+ r_chrom = name;
1010+ int64_t ref_pos = node_to_ref_pos.at (rank_node.second );
1011+ // assume snarl is forward on both reference nodes
1012+ // todo: this won't be exact for some inversion cases, I don't think --
1013+ // need to test these and either add check / or move to oriented search
1014+ min_ref_pos = min (min_ref_pos, ref_pos + (int64_t )graph->get_length (graph->get_handle (rank_node.second )));
1015+ max_ref_pos = max (max_ref_pos, ref_pos);
1016+ min_rank = min (min_rank, rank_node.first );
1017+ }
10151018
1016- r_start = std::to_string (min_ref_pos);
1017- r_end = std::to_string (max_ref_pos);
1018- rank = std::to_string (min_rank);
1019- }
1019+ r_start = std::to_string (min_ref_pos);
1020+ r_end = std::to_string (max_ref_pos);
1021+ rank = std::to_string (min_rank);
1022+
1023+ var.info [" R_CHROM" ] = {r_chrom};
1024+ var.info [" R_START" ] = {r_start};
1025+ var.info [" R_END" ] = {r_end};
1026+ var.info [" RANK" ] = {rank};
1027+
1028+ os << var << endl;
1029+ }
1030+ }
10201031
1021- if (!var.info .count (" R_CHROM" )) {
1022- var.format .push_back (" R_CHROM" );
1023- }
1024- var.info [" R_CHROM" ] = {r_chrom};
1025- if (!var.info .count (" R_START" )) {
1026- var.format .push_back (" R_START" );
1027- }
1028- var.info [" R_START" ] = {r_start};
1029- if (!var.info .count (" R_END" )) {
1030- var.format .push_back (" R_END" );
1031- }
1032- var.info [" R_END" ] = {r_end};
1033- if (!var.info .count (" RANK" )) {
1034- var.format .push_back (" RANK" );
1032+ void RGFACover::print_stats (ostream& os) {
1033+
1034+ // todo: mostly copied from annotate function above. should probably refactor common
1035+ // logic into its own, shared thing.y
1036+ unordered_map<nid_t , int64_t > node_to_ref_pos;
1037+ for (int64_t i = 0 ; i < this ->num_ref_intervals ; ++i) {
1038+ const pair<step_handle_t , step_handle_t >& ref_interval = this ->rgfa_intervals .at (i);
1039+ // assumption: ref intervals span entire path
1040+ assert (graph->path_begin (graph->get_path_handle_of_step (ref_interval.first )) == ref_interval.first );
1041+ int64_t pos = 0 ;
1042+ for (step_handle_t step_handle = ref_interval.first ; step_handle != ref_interval.second ;
1043+ step_handle = graph->get_next_step (step_handle)) {
1044+ handle_t handle = graph->get_handle_of_step (step_handle);
1045+ nid_t node_id = graph->get_id (handle);
1046+ assert (!graph->get_is_reverse (handle));
1047+ assert (!node_to_ref_pos.count (node_id));
1048+ node_to_ref_pos[node_id] = pos;
1049+ pos += graph->get_length (handle);
1050+ }
1051+ }
1052+
1053+ // the header
1054+ os << " #Sample" << " \t "
1055+ << " Haplotype" << " \t "
1056+ << " Locus" << " \t "
1057+ << " Start" << " \t "
1058+ << " End" << " \t "
1059+ << " NodeStart" << " \t "
1060+ << " NodeEnd" << " \t "
1061+ << " Rank" << " \t "
1062+ << " AvgDepth" << " \t "
1063+ << " AvgSampleDepth" << " \t "
1064+ << " RefSequence" << " \t "
1065+ << " RefStart" << " \t "
1066+ << " RefEnd" << endl;
1067+
1068+ for (int64_t i = this ->num_ref_intervals ; i < this ->rgfa_intervals .size (); ++i) {
1069+ const pair<step_handle_t , step_handle_t >& interval = this ->rgfa_intervals [i];
1070+ path_handle_t path_handle = graph->get_path_handle_of_step (interval.first );
1071+ subrange_t rgfa_subrange = graph->get_subrange (path_handle);
1072+ assert (rgfa_subrange != PathMetadata::NO_SUBRANGE );
1073+
1074+ int64_t path_length = 0 ;
1075+ int64_t tot_depth = 0 ;
1076+ int64_t tot_sample_depth = 0 ;
1077+ int64_t tot_steps = 0 ;
1078+ for (step_handle_t step = interval.first ; step != interval.second ; step = graph->get_next_step (step)) {
1079+ path_length += graph->get_length (graph->get_handle_of_step (step));
1080+ set<string> sample_set;
1081+ vector<step_handle_t > steps = graph->steps_of_handle (graph->get_handle_of_step (step));
1082+ for (step_handle_t & other_step : steps) {
1083+ path_handle_t other_path = graph->get_path_handle_of_step (other_step);
1084+ sample_set.insert (graph->get_sample_name (other_path));
10351085 }
1036- var.info [" RANK" ] = {rank};
1037-
1038- prev_sequence_name = var.sequenceName ;
1039- prev_r_chrom = r_chrom;
1040- prev_r_start = r_start;
1041- prev_r_end = r_end;
1042- prev_rank = rank;
1043-
1044- } else {
1045- // cerr << "rGFA [warning]: VCF reference " << var.sequenceName << " not found in graph" << endl;
1086+ tot_depth += steps.size ();
1087+ tot_sample_depth += sample_set.size ();
1088+ // we don't want to count the rgfa cover
1089+ // todo (can remove this when move away from path-based scheme)
1090+ assert (sample_set.count (RGFACover::rgfa_sample_name));
1091+ --tot_depth;
1092+ --tot_sample_depth;
1093+ ++tot_steps;
10461094 }
1047- os << var << endl;
1095+ pair<string, string> sample_locus = RGFACover::parse_rgfa_locus_name (graph->get_locus_name (path_handle));
1096+ size_t haplotype = graph->get_haplotype (path_handle);
1097+
1098+ nid_t first_node = graph->get_id (graph->get_handle_of_step (interval.first ));
1099+ vector<pair<int64_t , nid_t >> ref_nodes = this ->get_reference_nodes (first_node, false );
1100+ // interval is open ended, so we go back to last node
1101+ handle_t last_handle = graph->get_handle_of_step (graph->get_previous_step (interval.second ));
1102+
1103+ int64_t min_ref_pos = numeric_limits<int64_t >::max ();
1104+ int64_t max_ref_pos = -1 ;
1105+ int64_t min_rank = numeric_limits<int64_t >::max ();
1106+ string r_chrom;
1107+ for (const pair<int64_t , nid_t >& rank_node : ref_nodes) {
1108+ step_handle_t ref_step = this ->rgfa_intervals .at (node_to_interval.at (rank_node.second )).first ;
1109+ path_handle_t ref_path = graph->get_path_handle_of_step (ref_step);
1110+ string name = graph->get_path_name (ref_path);
1111+ // we assume one reference contig (which is built into the whole structure)
1112+ assert (r_chrom.empty () || r_chrom == name);
1113+ r_chrom = name;
1114+ int64_t ref_pos = node_to_ref_pos.at (rank_node.second );
1115+ // assume snarl is forward on both reference nodes
1116+ // todo: this won't be exact for some inversion cases, I don't think --
1117+ // need to test these and either add check / or move to oriented search
1118+ min_ref_pos = min (min_ref_pos, ref_pos + (int64_t )graph->get_length (graph->get_handle (rank_node.second )));
1119+ max_ref_pos = max (max_ref_pos, ref_pos);
1120+ min_rank = min (min_rank, rank_node.first );
1121+ }
1122+
1123+
1124+ os << sample_locus.first << " \t "
1125+ << sample_locus.second << " \t "
1126+ << (haplotype != PathMetadata::NO_HAPLOTYPE ? (int64_t )haplotype : (int64_t )-1 ) << " \t "
1127+ << rgfa_subrange.first << " \t "
1128+ << (rgfa_subrange.first + path_length) << " \t "
1129+ << (graph->get_is_reverse (graph->get_handle_of_step (interval.first )) ? " <" : " >" )
1130+ << graph->get_id (graph->get_handle_of_step (interval.first )) << " \t "
1131+ << (graph->get_is_reverse (last_handle) ? " <" : " >" )
1132+ << graph->get_id (last_handle) << " \t "
1133+ << min_rank << " \t "
1134+ << std::fixed << std::setprecision (2 ) << (tot_depth / tot_steps) << " \t "
1135+ << std::fixed << std::setprecision (2 ) << (tot_sample_depth / tot_steps) << " \t "
1136+ << Paths::strip_subrange (r_chrom) << " \t "
1137+ << min_ref_pos << " \t "
1138+ << max_ref_pos << " \n " ;
10481139 }
10491140}
10501141
0 commit comments