@@ -884,33 +884,31 @@ void restrictSamples (std::string samples_filename, MAT::Tree& T) {
884884 }
885885}
886886
887- std::unordered_set<std::string > mutation_set_from_node (MAT ::Tree* T, MAT ::Node* node, bool include_node, bool include_ancestors) {
888- std::unordered_set<std::string > mutations;
887+ std::vector< MAT ::Mutation > mutation_set_from_node (MAT ::Tree* T, MAT ::Node* node, bool include_node, bool include_ancestors) {
888+ std::vector< MAT ::Mutation > mutations;
889889 if (include_ancestors) {
890- for (auto an: T->rsearch (node->identifier , include_node)) {
890+ std::vector<MAT ::Node*> ancestors = T->rsearch (node->identifier , include_node);
891+ // Reverse so we proceed from root to node.
892+ std::reverse (ancestors.begin (), ancestors.end ());
893+ // Make a node so we can use the logic in Node::add_mutation to deal with multiple mutations at the same position.
894+ MAT ::Node tmp_node = MAT::Node ();
895+ for (auto an: ancestors) {
891896 for (auto mut: an->mutations ) {
892897 if (mut.is_masked ()) {
893898 continue ;
894899 }
895- MAT ::Mutation mut_opposite = mut.copy ();
896- // we check for whether this mutation is going to negate with something in the set
897- // by identifying its opposite and checking whether the opposite is already present on the traversal.
898- mut_opposite.par_nuc = mut.mut_nuc ;
899- mut_opposite.mut_nuc = mut.par_nuc ;
900- auto cml = mutations.find (mut_opposite.get_string ());
901- if (cml != mutations.end ()) {
902- mutations.erase (cml);
903- } else {
904- mutations.insert (mut.get_string ());
905- }
900+ tmp_node.add_mutation (mut);
906901 }
907902 }
903+ for (auto mut: tmp_node.mutations ) {
904+ mutations.push_back (mut.copy ());
905+ }
908906 } else if (include_node) {
909907 for (auto mut: node->mutations ) {
910908 if (mut.is_masked ()) {
911909 continue ;
912910 }
913- mutations.insert (mut.get_string ());
911+ mutations.push_back (mut.copy ());
914912 }
915913 } else {
916914 fprintf (stderr, " ERROR: mutation_set_from_node: at least one of include_node and include_ancestors should be true.\n " );
@@ -919,7 +917,32 @@ std::unordered_set<std::string> mutation_set_from_node(MAT::Tree* T, MAT::Node*
919917 return mutations;
920918}
921919
920+ void remove_mutation_from_node (MAT ::Node *node, MAT ::Mutation &mut) {
921+ // Find and remove the element of node->mutations that has the same position and mut_nuc as mut.
922+ std::vector<MAT ::Mutation>::iterator to_remove = node->mutations .end ();
923+ for (auto iter = node->mutations .begin (); iter != node->mutations .end (); iter++) {
924+ MAT ::Mutation nodemut = *iter;
925+ if (nodemut.position == mut.position && nodemut.mut_nuc == mut.mut_nuc ) {
926+ to_remove = iter;
927+ break ;
928+ }
929+ }
930+ if (to_remove == node->mutations .end ()) {
931+ fprintf (stderr, " ERROR: remove_mutation_from_node: node %s does not have mutation %s\n " ,
932+ node->identifier .c_str (), mut.get_string ().c_str ());
933+ exit (1 );
934+ }
935+ node->mutations .erase (to_remove);
936+ }
922937
938+ bool mutation_vec_has_position (std::vector<MAT ::Mutation>& mutations, int position) {
939+ for (auto mut: mutations) {
940+ if (mut.position == position) {
941+ return true ;
942+ }
943+ }
944+ return false ;
945+ }
923946
924947void moveNodes (std::string node_filename, MAT ::Tree* T) {
925948 // Function to move nodes between two identical placement paths. That is, move the target node so that is a child of the indicated new parent node,
@@ -959,9 +982,9 @@ void moveNodes (std::string node_filename, MAT::Tree* T) {
959982 }
960983 // accumulate the set of mutations belonging to the current and the new placement
961984 // not counting mutations belonging to the target, but counting ones to the putative new parent.
962- std::unordered_set<std::string> curr_mutations = mutation_set_from_node (T, mn, false , true );
963- std::unordered_set<std::string > new_mutations = mutation_set_from_node (T, np, true , true );
964- if (curr_mutations == new_mutations) {
985+ std::vector< MAT ::Mutation> curr_parent_mutations = mutation_set_from_node (T, mn, false , true );
986+ std::vector< MAT ::Mutation > new_mutations = mutation_set_from_node (T, np, true , true );
987+ if (curr_parent_mutations == new_mutations) {
965988 // we can now proceed with the move.
966989 T->move_node (mn->identifier , np->identifier );
967990 fprintf (stderr, " Move of node %s to node %s successful.\n " , words[0 ].c_str (), words[1 ].c_str ());
@@ -970,45 +993,84 @@ void moveNodes (std::string node_filename, MAT::Tree* T) {
970993 fprintf (stderr, " The current (%s) and new (%s) node paths do not involve the same set of mutations.\n " ,
971994 mn->identifier .c_str (), np->identifier .c_str ());
972995
973- std::unordered_set<std::string> extra_mutations;
974- size_t curr_in_new_count = 0 ;
975- for (auto mut: curr_mutations) {
976- if (new_mutations.find (mut) != new_mutations.end ()) {
977- curr_in_new_count++;
978- } else {
979- extra_mutations.insert (mut);
996+ // Find mutations in the new parent but not in the current parent
997+ std::vector<MAT ::Mutation> new_parent_extra_muts;
998+ for (auto mut: new_mutations) {
999+ if (std::find (curr_parent_mutations.begin (), curr_parent_mutations.end (), mut) == curr_parent_mutations.end ()) {
1000+ new_parent_extra_muts.push_back (mut);
1001+ }
1002+ }
1003+ // Find mutations in mn relative to its parent. Can't just call mutation_set_from_node
1004+ // with true, false because mn might have a mutation at the same position as one of its
1005+ // parent's mutations -- we need those combined to get the reference allele as the
1006+ // ref_nuc/par_nuc for comparison with new_parent_extra_muts. So get all muts, and then
1007+ // erase all parental muts to get only the (collapsed) muts on mn.
1008+ std::vector<MAT ::Mutation> curr_node_extra_muts = mutation_set_from_node (T, mn, true , true );
1009+ for (MAT ::Mutation parent_mut: curr_parent_mutations) {
1010+ auto iter = std::find (curr_node_extra_muts.begin (), curr_node_extra_muts.end (), parent_mut);
1011+ if (iter != curr_node_extra_muts.end ()) {
1012+ curr_node_extra_muts.erase (iter);
1013+ }
1014+ }
1015+ // Find mutations in the current parent (mn's parent) but not in the new parent
1016+ std::vector<MAT ::Mutation> curr_parent_extra_muts;
1017+ for (auto mut: curr_parent_mutations) {
1018+ if (std::find (new_mutations.begin (), new_mutations.end (), mut) == new_mutations.end ()) {
1019+ // but discard parental mutations at the same position as in curr_node_extra_muts
1020+ // because those were overridden in the node to be moved.
1021+ if (! mutation_vec_has_position (curr_node_extra_muts, mut.position )) {
1022+ curr_parent_extra_muts.push_back (mut);
1023+ }
9801024 }
9811025 }
982- if (extra_mutations.size () == 0 || curr_in_new_count != new_mutations.size ()) {
983- fprintf (stderr, " ERROR: the new parent (%s) has mutations not found in the current node (%s); %ld in common, %ld in new\n " ,
984- np->identifier .c_str (), mn->identifier .c_str (), curr_in_new_count, new_mutations.size ());
985- exit (1 );
1026+ // If new parent has mutation(s) not found in current parent: if they are already in
1027+ // curr_node_extra_muts, then remove from curr_node_extra_muts and mn, otherwise error.
1028+ for (auto mut: new_parent_extra_muts) {
1029+ auto iter = std::find (curr_node_extra_muts.begin (), curr_node_extra_muts.end (), mut);
1030+ if (iter != curr_node_extra_muts.end ()) {
1031+ curr_node_extra_muts.erase (iter);
1032+ remove_mutation_from_node (mn, mut);
1033+ fprintf (stderr, " mut %s is in new parent %s but not current parent, but is in current node %s; removed it.\n " ,
1034+ mut.get_string ().c_str (), np->identifier .c_str (), mn->identifier .c_str ());
1035+ } else {
1036+ fprintf (stderr, " ERROR: new parent %s has mutation %s not found in the current node %s's path.\n " ,
1037+ np->identifier .c_str (), mut.get_string ().c_str (), mn->identifier .c_str ());
1038+ for (auto m: curr_node_extra_muts) {
1039+ fprintf (stderr, " %s," , m.get_string ().c_str ());
1040+ }
1041+ fprintf (stderr, " \n " );
1042+ exit (1 );
1043+ }
9861044 }
987- // Look for a child of np that already has extra_mutations . If there is such a child
1045+ // Look for a child of np that already has curr_parent_extra_muts . If there is such a child
9881046 // then move mn to that child. Otherwise add those mutations to mn and move it to np.
9891047 MAT ::Node *child_with_muts = NULL ;
9901048 for (auto child: np->children ) {
991- std::unordered_set<std::string > mut_set = mutation_set_from_node (T, child, true , false );
992- if (mut_set == extra_mutations ) {
1049+ std::vector< MAT ::Mutation > mut_set = mutation_set_from_node (T, child, true , false );
1050+ if (mut_set == curr_parent_extra_muts ) {
9931051 child_with_muts = child;
994- fprintf (stderr, " Found child with extra_mutations : %s\n " , child->identifier .c_str ());
1052+ fprintf (stderr, " Found child with curr_parent_extra_muts : %s\n " , child->identifier .c_str ());
9951053 break ;
9961054 }
9971055 }
9981056 if (child_with_muts != NULL ) {
9991057 T->move_node (mn->identifier , child_with_muts->identifier );
10001058 } else {
10011059 // Preserve chronological order expected by add_mutation by adding mn's mutations
1002- // after extra_mutations instead of vice versa.
1060+ // after curr_parent_extra_muts instead of vice versa.
10031061 std::vector<MAT ::Mutation>mn_mutations;
10041062 for (auto mut: mn->mutations ) {
10051063 mn_mutations.push_back (mut);
10061064 }
10071065 mn->mutations .clear ();
1008- for (auto mut: extra_mutations) {
1009- mn->add_mutation (*(MAT::mutation_from_string (mut)));
1066+ for (auto mut: curr_parent_extra_muts) {
1067+ fprintf (stderr, " Added current parent's extra mutation %s to node to be moved (%s)\n " ,
1068+ mut.get_string ().c_str (), mn->identifier .c_str ());
1069+ mn->add_mutation (mut);
10101070 }
10111071 for (auto mut: mn_mutations) {
1072+ fprintf (stderr, " Layering on node to be moved (%s)'s mutation %s\n " ,
1073+ mn->identifier .c_str (), mut.get_string ().c_str ());
10121074 mn->add_mutation (mut);
10131075 }
10141076 T->move_node (mn->identifier , np->identifier );
0 commit comments