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