make Sequence a subclass of std::string
[mussa.git] / alg / mussa.cpp
1 //  This file is part of the Mussa source distribution.
2 //  http://mussa.caltech.edu/
3 //  Contact author: Tristan  De Buysscher, tristan@caltech.edu
4
5 // This program and all associated source code files are Copyright (C) 2005
6 // the California Institute of Technology, Pasadena, CA, 91125 USA.  It is
7 // under the GNU Public License; please see the included LICENSE.txt
8 // file for more information, or contact Tristan directly.
9
10
11 //                        ----------------------------------------
12 //                          ---------- mussa_class.cc -----------
13 //                        ----------------------------------------
14
15 #include <boost/filesystem/operations.hpp>
16 #include <boost/filesystem/fstream.hpp>
17 namespace fs = boost::filesystem;
18
19 #include <iostream>
20 #include <sstream>
21
22 #include "mussa_exceptions.hpp"
23 #include "alg/mussa.hpp"
24 #include "alg/flp.hpp"
25
26 using namespace std;
27
28
29 Mussa::Mussa()
30 {
31   clear();
32   connect(&the_paths, SIGNAL(progress(const std::string&, int, int)), 
33           this, SIGNAL(progress(const std::string&, int, int)));
34 }
35
36 Mussa::Mussa(const Mussa& m)
37   : analysis_name(m.analysis_name),
38     window(m.window),
39     threshold(m.threshold),
40     soft_thres(m.soft_thres),
41     ana_mode(m.ana_mode),
42     win_append(m.win_append),
43     thres_append(m.thres_append),
44     motif_sequences(m.motif_sequences),
45     color_mapper(m.color_mapper)
46 {
47   connect(&the_paths, SIGNAL(progress(const std::string&, int, int)), 
48           this, SIGNAL(progress(const std::string&, int, int)));
49 }
50
51 // set all parameters to null state
52 void
53 Mussa::clear()
54 {
55   analysis_name = "";
56   ana_mode = TransitiveNway;
57   window = 0;
58   threshold = 0;
59   soft_thres = 0;
60   win_append = false;
61   thres_append = false;
62   motif_sequences.clear();
63   color_mapper.clear();
64   the_paths.clear();
65 }
66
67 // these 5 simple methods manually set the parameters for doing an analysis
68 // used so that the gui can take input from user and setup the analysis
69 // note - still need a set_append(bool, bool) method...
70 void
71 Mussa::set_name(string a_name)
72 {
73   analysis_name = a_name;
74 }
75
76 string Mussa::get_name()
77 {
78   return analysis_name;
79 }
80
81 int 
82 Mussa::size() const
83 {
84   if (the_seqs.size() > 0)
85     return the_seqs.size();
86   else
87     return 0;
88 }
89
90 void
91 Mussa::set_window(int a_window)
92 {
93   window = a_window;
94 }
95
96 int Mussa::get_window() const
97 {
98   return window;
99 }
100
101 void
102 Mussa::set_threshold(int a_threshold)
103 {
104   threshold = a_threshold;
105   if (a_threshold > soft_thres) 
106     soft_thres = a_threshold;
107 }
108
109 int Mussa::get_threshold() const
110 {
111   return threshold;
112 }
113
114 void
115 Mussa::set_soft_threshold(int new_threshold)
116 {
117   if (new_threshold < threshold) {
118     soft_thres = threshold;
119   } else if (new_threshold > window) {
120     soft_thres = window; 
121   } else {
122     soft_thres = new_threshold;
123   }
124 }
125
126 int Mussa::get_soft_threshold() const
127 {
128   return soft_thres;
129 }
130
131 void
132 Mussa::set_analysis_mode(enum analysis_modes new_ana_mode)
133 {
134   ana_mode = new_ana_mode;
135 }
136
137 enum Mussa::analysis_modes Mussa::get_analysis_mode() const
138 {
139   return ana_mode;
140 }
141
142 string Mussa::get_analysis_mode_name() const
143 {
144   switch (ana_mode)
145   {
146   case TransitiveNway:
147     return string("Transitive");
148     break;
149   case RadialNway:
150     return string("Radial");
151     break;
152   case EntropyNway:
153     return string("Entropy");
154     break;
155   case RecursiveNway:
156     return string("[deprecated] Recursive");
157     break;
158   default:
159     throw runtime_error("invalid analysis mode type");
160     break;
161   }
162 }
163
164 const NwayPaths& Mussa::paths() const
165 {
166   return the_paths;
167 }
168
169 //template <class IteratorT>
170 //void Mussa::createLocalAlignment(IteratorT begin, IteratorT end)
171 void Mussa::createLocalAlignment(std::list<ConservedPath>::iterator begin, 
172                                  std::list<ConservedPath>::iterator end, 
173                                  std::list<ConservedPath::path_type>& result,
174                                  std::list<std::vector<bool> >& reversed)
175 {
176   const vector<Sequence>& raw_seq = the_seqs;
177   ConservedPath::path_type aligned_path;
178   size_t i2, i3;
179   int x_start, x_end;
180   int window_length, win_i;
181   int rc_1 = 0; 
182   int rc_2 = 0;
183   vector<bool> rc_list;
184   bool full_match;
185   vector<bool> matched;
186   int align_counter;
187
188   align_counter = 0;
189   for(std::list<ConservedPath>::iterator pathz_i=begin; pathz_i != end; ++pathz_i)
190   {
191     ConservedPath& a_path = *pathz_i;
192     window_length = a_path.window_size;
193     // determine which parts of the path are RC relative to first species
194     rc_list = a_path.reverseComplimented();
195     
196     // loop over each bp in the conserved region for all sequences
197     for(win_i = 0; win_i < window_length; win_i++)
198     {
199       aligned_path.clear();
200       // determine which exact base pairs match between the sequences
201       full_match = true;
202       for(i2 = 0; i2 < a_path.size()-1; i2++)
203       {
204         // assume not rc as most likely, adjust below
205         rc_1 = 0;
206         rc_2 = 0;
207         // no matter the case, any RC node needs adjustments
208         if (a_path[i2] < 0)
209           rc_1 = window_length-1;
210         if (a_path[i2+1] < 0)
211           rc_2 = window_length-1;        
212          
213         x_start = (abs(a_path[i2]-rc_1+win_i));
214         x_end =   (abs(a_path[i2+1]-rc_2+win_i));
215         
216         // RC case handling
217         // ugh, and xor...only want rc coloring if just one of the nodes is rc
218         // if both nodes are rc, then they are 'normal' relative to each other
219         if((rc_list[i2] || rc_list[i2+1] )&&!(rc_list[i2] && rc_list[i2+1]))
220         { //the hideous rc matching logic - not complex, but annoying
221           if(!(( (raw_seq[i2][x_start]=='A')&&(raw_seq[i2+1][x_end]=='T')) ||
222                 ((raw_seq[i2][x_start]=='T')&&(raw_seq[i2+1][x_end]=='A')) ||
223                 ((raw_seq[i2][x_start]=='G')&&(raw_seq[i2+1][x_end]=='C')) ||
224                 ((raw_seq[i2][x_start]=='C')&&(raw_seq[i2+1][x_end]=='G'))) ) 
225           {
226             full_match = false;
227           } else {
228             aligned_path.push_back(x_start);
229           }
230         }
231         else
232         { // forward match
233           if (!( (raw_seq[i2][x_start] == raw_seq[i2+1][x_end]) &&
234                 (raw_seq[i2][x_start] != 'N') &&
235                 (raw_seq[i2+1][x_end] != 'N') ) ) {
236             full_match = false;
237           } else {
238             aligned_path.push_back(x_start);
239           }
240         }
241       }
242       // grab the last part of our path, assuming we matched
243       if (full_match)
244         aligned_path.push_back(x_end);
245
246       if (aligned_path.size() == a_path.size()) {
247         result.push_back(aligned_path);
248         reversed.push_back(rc_list);
249       }
250     }
251     align_counter++;
252   }
253 }
254
255
256 // takes a string and sets it as the next seq 
257 void
258 Mussa::add_a_seq(string a_seq)
259 {
260   Sequence aSeq(a_seq);
261   the_seqs.push_back(aSeq);
262 }
263
264 const vector<Sequence>& 
265 Mussa::sequences() const
266 {
267   return the_seqs;
268 }
269
270 void Mussa::load_sequence(fs::path seq_file, fs::path annot_file, 
271                           int fasta_index, int sub_seq_start, int sub_seq_end)
272 {
273   Sequence aseq;
274   aseq.load_fasta(seq_file, fasta_index, sub_seq_start, sub_seq_end);
275   if ( not annot_file.empty() ) {
276     aseq.load_annot(annot_file, sub_seq_start, sub_seq_end);
277   }
278   the_seqs.push_back(aseq);
279 }
280
281 void
282 Mussa::load_mupa_file(fs::path para_file_path)
283 {
284   fs::ifstream para_file;
285   string file_data_line;
286   string param, value; 
287   fs::path annot_file;
288   int split_index, fasta_index;
289   int sub_seq_start, sub_seq_end;
290   bool seq_params, did_seq;
291   string err_msg;
292   bool parsing_path;
293   string::size_type new_index, dir_index;
294
295   // initialize values
296   clear();
297
298   // if file was opened, read the parameter values
299   if (fs::exists(para_file_path))
300   {
301     para_file.open(para_file_path, ios::in);
302
303     // what directory is the mupa file in?
304     fs::path file_path_base = para_file_path.branch_path();
305
306     // setup loop by getting file's first line
307     getline(para_file,file_data_line);
308     split_index = file_data_line.find(" ");
309     param = file_data_line.substr(0,split_index);
310     value = file_data_line.substr(split_index+1);
311     
312     while (!para_file.eof())
313     {
314       did_seq = false;
315       if (param == "ANA_NAME")
316         analysis_name = value;
317       else if (param == "APPEND_WIN")
318         win_append = true;
319       else if (param == "APPEND_THRES")
320         thres_append = true;
321       else if (param == "SEQUENCE_NUM")
322         ; // ignore sequence_num now
323       else if (param == "WINDOW")
324         window = atoi(value.c_str());
325       else if (param == "THRESHOLD")
326         threshold = atoi(value.c_str());
327       else if (param == "SEQUENCE")
328       {
329         fs::path seq_file = file_path_base / value;
330         //cout << "seq_file_name " << seq_files.back() << endl;
331         fasta_index = 1;
332         annot_file = "";
333         sub_seq_start = 0;
334         sub_seq_end = 0;
335         seq_params = true;
336
337         while ((!para_file.eof()) && seq_params)
338         {
339           getline(para_file,file_data_line);
340           split_index = file_data_line.find(" ");
341           param = file_data_line.substr(0,split_index);
342           value = file_data_line.substr(split_index+1);
343
344           if (param == "FASTA_INDEX")
345             fasta_index = atoi(value.c_str());
346           else if (param == "ANNOTATION")
347             annot_file = file_path_base / value;
348           else if (param == "SEQ_START")
349             sub_seq_start = atoi(value.c_str());
350           else if (param == "SEQ_END")
351           {
352             sub_seq_end = atoi(value.c_str());
353           }
354           //ignore empty lines or that start with '#'
355           else if ((param == "") || (param == "#")) {}
356           else seq_params = false;
357         }
358         load_sequence(seq_file, annot_file, fasta_index, sub_seq_start, 
359                       sub_seq_end);
360         did_seq = true;
361       }
362       //ignore empty lines or that start with '#'
363       else if ((param == "") || (param == "#")) {}
364       else
365       {
366         clog << "Illegal/misplaced mussa parameter in file\n";
367         clog << param << "\n";
368       }
369
370       if (!did_seq)
371       {
372         getline(para_file,file_data_line);
373         split_index = file_data_line.find(" ");
374         param = file_data_line.substr(0,split_index);
375         value = file_data_line.substr(split_index+1);
376         did_seq = false;
377       }
378     }
379
380     para_file.close();
381
382     soft_thres = threshold;
383     //cout << "nway mupa: analysis_name = " << analysis_name 
384     //     << " window = " << window 
385     //     << " threshold = " << threshold << endl;
386   }
387   // no file was loaded, signal error
388   else
389   {
390     throw mussa_load_error("Config File: " + para_file_path.string() + " not found");
391   }
392 }
393
394
395 void
396 Mussa::analyze()
397 {
398   time_t t1, t2, begin, end;
399   double seqloadtime, seqcomptime, nwaytime, savetime, totaltime;
400
401   begin = time(NULL);
402
403   t1 = time(NULL);
404         
405   if (the_seqs.size() < 2) {
406     throw mussa_analysis_error("you need to have at least 2 sequences to "
407                                "do an analysis.");
408   }
409   //cout << "nway ana: seq_num = " << the_seqs.size() << endl;
410
411   t2 = time(NULL);
412   seqloadtime = difftime(t2, t1);
413
414   t1 = time(NULL);
415   seqcomp();
416   t2 = time(NULL);
417   seqcomptime = difftime(t2, t1);
418
419   t1 = time(NULL);
420   the_paths.setup(window, threshold);
421   nway();
422   t2 = time(NULL);
423   nwaytime = difftime(t2, t1);
424
425   t1 = time(NULL);
426   save();
427   t2 = time(NULL);
428   savetime = difftime(t2, t1);
429
430   end = time(NULL);
431   totaltime = difftime(end, begin);
432
433   //cout << "seqload\tseqcomp\tnway\tsave\ttotal\n";
434   //cout << seqloadtime << "\t";
435   //cout << seqcomptime << "\t";
436   //cout << nwaytime << "\t";
437   //cout << savetime << "\t";
438   //cout << totaltime << "\n";
439 }
440
441 void
442 Mussa::seqcomp()
443 {
444   vector<int> seq_lens;
445   vector<FLPs> empty_FLP_vector;
446   FLPs dummy_comp;
447   string save_file_string;
448
449   empty_FLP_vector.clear();
450   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
451   {
452     all_comps.push_back(empty_FLP_vector); 
453     for(vector<Sequence>::size_type i2 = 0; i2 < the_seqs.size(); i2++)
454       all_comps[i].push_back(dummy_comp);
455   }
456   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++) {
457     seq_lens.push_back(the_seqs[i].size());
458   }
459   int seqcomps_done = 0;
460   int seqcomps_todo = (the_seqs.size() * (the_seqs.size()-1)) / 2;
461   emit progress("seqcomp", seqcomps_done, seqcomps_todo);
462
463   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
464     for(vector<Sequence>::size_type i2 = i+1; i2 < the_seqs.size(); i2++)
465     {
466       //cout << "seqcomping: " << i << " v. " << i2 << endl;
467       all_comps[i][i2].setup(window, threshold);
468       all_comps[i][i2].seqcomp(the_seqs[i], the_seqs[i2], false);
469       all_comps[i][i2].seqcomp(the_seqs[i], the_seqs[i2].rev_comp(),true);
470       ++seqcomps_done;
471       emit progress("seqcomp", seqcomps_done, seqcomps_todo);
472     }
473 }
474
475 void
476 Mussa::nway()
477 {
478   vector<string> some_Seqs;
479
480   the_paths.set_soft_threshold(soft_thres);
481
482   if (ana_mode == TransitiveNway) {
483     the_paths.trans_path_search(all_comps);
484   }
485   else if (ana_mode == RadialNway) {
486     the_paths.radiate_path_search(all_comps);
487   }
488   else if (ana_mode == EntropyNway)
489   {
490     //unlike other methods, entropy needs to look at the sequence at this stage
491     some_Seqs.clear();
492     for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
493     {
494       some_Seqs.push_back(the_seqs[i]);
495     }
496
497     the_paths.setup_ent(ent_thres, some_Seqs); // ent analysis extra setup
498     the_paths.entropy_path_search(all_comps);
499   }
500
501   // old recursive transitive analysis function
502   else if (ana_mode == RecursiveNway)
503     the_paths.find_paths_r(all_comps);
504
505   the_paths.simple_refine();
506 }
507
508 void
509 Mussa::save()
510 {
511   string save_name;
512   fs::path flp_filepath;
513   fs::fstream save_file;
514   ostringstream append_info;
515   int dir_create_status;
516
517   if (not analysis_name.empty()) {
518     // not sure why, but gotta close file each time since can't pass 
519     // file streams
520     save_name = analysis_name;
521
522     // gotta do bit with adding win & thres if to be appended
523     if (win_append)
524     {
525       append_info.str("");
526       append_info <<  "_w" << window;
527       save_name += append_info.str();
528     }
529
530     if (thres_append)
531     {
532       append_info.str("");
533       append_info <<  "_t" << threshold;
534       save_name += append_info.str();
535     }
536     fs::path save_path( save_name);
537
538     if (not fs::exists(save_path)) {
539       fs::create_directory(save_path);
540     }
541     // save sequence and annots to a special mussa file
542     save_file.open(save_path / (save_name+".museq"), ios::out);
543     save_file << "<Mussa_Sequence>" << endl;
544
545     for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
546     {
547       the_seqs[i].save(save_file);
548     }
549
550     save_file << "</Mussa_Sequence>" << endl;
551     save_file.close();
552
553     // save nway paths to its mussa save file
554     the_paths.save(save_path / (save_name + ".muway"));
555
556     for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
557       for(vector<Sequence>::size_type i2 = i+1; i2 < the_seqs.size(); i2++)
558       {
559         append_info.str("");
560         append_info <<  "_sp_" << i << "v" << i2;
561         all_comps[i][i2].save(save_path/(save_name+append_info.str()+".flp"));
562       }
563   }
564 }
565
566 void
567 Mussa::save_muway(fs::path save_path)
568 {
569   the_paths.save(save_path);
570 }
571
572 void
573 Mussa::load(fs::path ana_file)
574 {
575   int i, i2;
576   fs::path file_path_base;
577   fs::path a_file_path; 
578   fs::path ana_path(ana_file);
579   bool parsing_path;
580   Sequence tmp_seq;
581   string err_msg;
582   ostringstream append_info;
583   vector<FLPs> empty_FLP_vector;
584   FLPs dummy_comp;
585
586   //cout << "ana_file name " << ana_file.string() << endl;
587   analysis_name = ana_path.leaf();
588   //cout << " ana_name " << analysis_name << endl;
589   file_path_base =  ana_path.branch_path() / analysis_name;
590   a_file_path = file_path_base / (analysis_name + ".muway");
591   //cout << " loading museq: " << a_file_path.string() << endl;
592   the_paths.load(a_file_path);
593   // perhaps this could be more elegent, but at least this'll let
594   // us know what our threshold and window sizes were when we load a muway
595   window = the_paths.get_window();
596   threshold = the_paths.get_threshold();
597   soft_thres = threshold;
598
599   int seq_num = the_paths.sequence_count();
600
601   a_file_path = file_path_base / (analysis_name + ".museq");
602
603   // this is a bit of a hack due to C++ not acting like it should with files
604   for (i = 1; i <= seq_num; i++)
605   {
606     tmp_seq.clear();
607     //cout << "mussa_class: loading museq frag... " << a_file_path.string() << endl;
608     tmp_seq.load_museq(a_file_path, i);
609     the_seqs.push_back(tmp_seq);
610   }
611   
612   empty_FLP_vector.clear();
613   for(i = 0; i < seq_num; i++)
614   {
615     all_comps.push_back(empty_FLP_vector); 
616     for(i2 = 0; i2 < seq_num; i2++)
617       all_comps[i].push_back(dummy_comp);
618   }
619   
620   for(i = 0; i < seq_num; i++)
621   {
622     for(i2 = i+1; i2 < seq_num; i2++)
623     {
624       append_info.str("");
625       append_info << analysis_name <<  "_sp_" << i << "v" << i2 << ".flp";
626       //cout << append_info.str() << endl;
627       a_file_path = file_path_base / append_info.str();
628       //cout << "path " << a_file_path.string() << endl;
629       all_comps[i][i2].load(a_file_path);
630       //cout << "real size = " << all_comps[i][i2].size() << endl;
631     }
632   }
633 }
634
635
636 void
637 Mussa::save_old()
638 {
639   fs::fstream save_file;
640
641   save_file.open(analysis_name, ios::out);
642
643   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
644     save_file << the_seqs[i] << endl;
645
646   save_file << window << endl;
647   save_file.close();
648   //note more complex eventually since analysis_name may need to have
649   //window size, threshold and other stuff to modify it...
650   the_paths.save_old(analysis_name);
651 }
652
653
654 void
655 Mussa::load_old(char * load_file_path, int s_num)
656 {
657   fstream save_file;
658   string file_data_line; 
659   int i, space_split_i, comma_split_i;
660   vector<int> loaded_path;
661   string node_pair, node;
662   Sequence a_seq;
663
664   int seq_num = s_num;
665   the_paths.setup(0, 0);
666   save_file.open(load_file_path, ios::in);
667
668   // currently loads old mussa format
669
670   // get sequences
671   for(i = 0; i < seq_num; i++)
672   {
673     getline(save_file, file_data_line);
674     a_seq = file_data_line;
675     the_seqs.push_back(a_seq);
676   }
677
678   // get window size
679   getline(save_file, file_data_line);
680   window = atoi(file_data_line.c_str());
681   // get paths
682
683   while (!save_file.eof())
684   {
685     loaded_path.clear();
686     getline(save_file, file_data_line);
687     if (file_data_line != "")
688     for(i = 0; i < seq_num; i++)
689     {
690       space_split_i = file_data_line.find(" ");
691       node_pair = file_data_line.substr(0,space_split_i);
692       //cout << "np= " << node_pair;
693       comma_split_i = node_pair.find(",");
694       node = node_pair.substr(comma_split_i+1);
695       //cout << "n= " << node << " ";
696       loaded_path.push_back(atoi (node.c_str()));
697       file_data_line = file_data_line.substr(space_split_i+1);
698     }
699     //cout << endl;
700     // FIXME: do we have any information about what the threshold should be?
701     the_paths.add_path(0, loaded_path);
702   }
703   save_file.close();
704
705   //the_paths.save("tmp.save");
706 }
707
708 void Mussa::add_motifs(const vector<string>& motifs, 
709                        const vector<Color>& colors)
710 {
711   if (motifs.size() != colors.size()) {
712     throw mussa_error("motif and color vectors must be the same size");
713   }
714
715   for(size_t i = 0; i != motifs.size(); ++i)
716   {
717     motif_sequences.insert(motifs[i]);
718     color_mapper.appendInstanceColor("motif", motifs[i], colors[i]);
719   }
720   update_sequences_motifs();
721 }
722
723 // I mostly split the ifstream out so I can use a stringstream to test it.
724 void Mussa::load_motifs(std::istream &in)
725 {
726   string seq;
727   float red;
728   float green;
729   float blue;
730
731   while(in.good())
732   {
733     in >> seq >> red >> green >> blue;
734     // if we couldn't read this line 'cause we're like at the end of the file
735     // try to exit the loop
736     if (!in.good())
737       break;
738     try {
739       seq = Sequence::motif_normalize(seq);
740     } catch(motif_normalize_error e) {
741       clog << "unable to parse " << seq << " skipping" << endl;
742       clog << e.what() << endl;
743       continue;
744     }
745     if (red < 0.0 or red > 1.0) {
746       clog << "invalid red value " << red << ". must be in range [0..1]" 
747            << endl;
748       continue;
749     }
750     if (green < 0.0 or green > 1.0) {
751       clog << "invalid green value " << green << ". must be in range [0..1]" 
752            << endl;
753       continue;
754     }
755     if (blue < 0.0 or blue > 1.0) {
756       clog << "invalid blue value " << blue << ". must be in range [0..1]" 
757            << endl;
758       continue;
759     }
760     if (motif_sequences.find(seq) == motif_sequences.end()) {
761       // sequence wasn't found
762       motif_sequences.insert(seq);
763       Color c(red, green, blue);
764       color_mapper.appendInstanceColor("motif", seq, c);
765     } else {
766       clog << "sequence " << seq << " was already defined skipping" 
767            << endl;
768       continue;
769     }
770   }
771   update_sequences_motifs();
772 }
773
774 void Mussa::load_motifs(fs::path filename)
775 {
776   fs::ifstream f;
777   f.open(filename, ifstream::in);
778   load_motifs(f);
779 }
780
781 void Mussa::update_sequences_motifs()
782 {
783   // once we've loaded all the motifs from the file, 
784   // lets attach them to the sequences
785   for(vector<Sequence>::iterator seq_i = the_seqs.begin();
786       seq_i != the_seqs.end();
787       ++seq_i)
788   {
789     // clear out old motifs
790     seq_i->clear_motifs();
791     // for all the motifs in our set, attach them to the current sequence
792     for(set<string>::iterator motif_i = motif_sequences.begin();
793         motif_i != motif_sequences.end();
794         ++motif_i)
795     {
796       seq_i->add_motif(*motif_i);
797     }
798   }
799 }
800
801 const set<string>& Mussa::motifs() const
802 {
803   return motif_sequences;
804 }
805
806 AnnotationColors& Mussa::colorMapper()
807 {
808   return color_mapper;
809 }