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