Thicker bars, better letters
[mussa.git] / alg / mussa_class.cxx
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 "mussa_exceptions.hh"
16 #include "mussa_class.hh"
17 #include "flp.hh"
18 #include <fstream>
19 #include <iostream>
20 #include <sstream>
21
22 using namespace std;
23
24 Mussa::Mussa()
25 {
26   clear();
27 }
28
29 // set all parameters to null state
30 void
31 Mussa::clear()
32 {
33   analysis_name = "";
34   ana_mode = TransitiveNway;
35   window = 0;
36   threshold = 0;
37   soft_thres = 0;
38   win_append = false;
39   thres_append = false;
40   seq_files.clear();
41   fasta_indices.clear();
42   annot_files.clear();
43   sub_seq_starts.clear();
44   sub_seq_ends.clear();
45 }
46
47 // these 5 simple methods manually set the parameters for doing an analysis
48 // used so that the gui can take input from user and setup the analysis
49 // note - still need a set_append(bool, bool) method...
50 void
51 Mussa::set_name(string a_name)
52 {
53   analysis_name = a_name;
54 }
55
56 string Mussa::get_name()
57 {
58   return analysis_name;
59 }
60
61 int 
62 Mussa::size() const
63 {
64   if (the_seqs.size() > 0)
65     return the_seqs.size();
66   else if (seq_files.size() > 0)
67     return seq_files.size();
68   else
69     return 0;
70 }
71
72 void
73 Mussa::set_window(int a_window)
74 {
75   window = a_window;
76 }
77
78 int Mussa::get_window() const
79 {
80   return window;
81 }
82
83 void
84 Mussa::set_threshold(int a_threshold)
85 {
86   threshold = a_threshold;
87   //soft_thres = a_threshold;
88 }
89
90 int Mussa::get_threshold() const
91 {
92   return threshold;
93 }
94
95 void
96 Mussa::set_soft_thres(int sft_thres)
97 {
98   soft_thres = sft_thres;
99 }
100
101 void
102 Mussa::set_analysis_mode(enum analysis_modes new_ana_mode)
103 {
104   ana_mode = new_ana_mode;
105 }
106
107 enum Mussa::analysis_modes Mussa::get_analysis_mode() const
108 {
109   return ana_mode;
110 }
111
112 string Mussa::get_analysis_mode_name() const
113 {
114   switch (ana_mode)
115   {
116   case TransitiveNway:
117     return string("Transitive");
118     break;
119   case RadialNway:
120     return string("Radial");
121     break;
122   case EntropyNway:
123     return string("Entropy");
124     break;
125   case RecursiveNway:
126     return string("[deprecated] Recursive");
127     break;
128   default:
129     throw runtime_error("invalid analysis mode type");
130     break;
131   }
132 }
133
134 const NwayPaths& Mussa::paths() const
135 {
136   return the_paths;
137 }
138
139 // takes a string and sets it as the next seq 
140 void
141 Mussa::add_a_seq(string a_seq)
142 {
143   Sequence aSeq;
144
145   aSeq.set_seq(a_seq);
146   the_seqs.push_back(aSeq);
147 }
148
149
150 // sets info for just 1 seq at a time
151 void
152 Mussa::set_seq_info(string seq_file, string annot_file, int fa_i, int a_start,                      int the_end)
153 {
154   seq_files.push_back(seq_file);
155   fasta_indices.push_back(fa_i);
156   annot_files.push_back(annot_file);
157   sub_seq_starts.push_back(a_start);
158   sub_seq_ends.push_back(the_end);
159 }
160
161 const vector<Sequence>& 
162 Mussa::sequences() const
163 {
164   return the_seqs;
165 }
166
167 void
168 Mussa::load_mupa_file(string para_file_path)
169 {
170   ifstream para_file;
171   string file_data_line;
172   string param, value, annot_file;
173   int split_index, fasta_index;
174   int sub_seq_start, sub_seq_end;
175   bool seq_params, did_seq;
176   string err_msg;
177   bool parsing_path;
178   string::size_type new_index, dir_index;
179
180   // initialize values
181   clear();
182
183   para_file.open(para_file_path.c_str(), ios::in);
184
185   // if file was opened, read the parameter values
186   if (para_file)
187   {
188     // need to find the path to the .mupa file
189     parsing_path = true;
190     dir_index = 0;
191     while (parsing_path)
192     {
193       new_index = (para_file_path.substr(dir_index)).find("/");
194       if (new_index != string::npos)
195         dir_index += new_index + 1;
196       else
197         parsing_path = false;
198     }
199
200     file_path_base = para_file_path.substr(0,dir_index);
201
202     // setup loop by getting file's first line
203     getline(para_file,file_data_line);
204     split_index = file_data_line.find(" ");
205     param = file_data_line.substr(0,split_index);
206     value = file_data_line.substr(split_index+1);
207     
208     while (!para_file.eof())
209     {
210       did_seq = false;
211       if (param == "ANA_NAME")
212         analysis_name = value;
213       else if (param == "APPEND_WIN")
214         win_append = true;
215       else if (param == "APPEND_THRES")
216         thres_append = true;
217       else if (param == "SEQUENCE_NUM")
218         ; // ignore sequence_num now
219       else if (param == "WINDOW")
220         window = atoi(value.c_str());
221       else if (param == "THRESHOLD")
222         threshold = atoi(value.c_str());
223       else if (param == "SEQUENCE")
224       {
225         seq_files.push_back(file_path_base + value);
226         //cout << "seq_file_name " << seq_files.back() << endl;
227         fasta_index = 1;
228         annot_file = "";
229         sub_seq_start = 0;
230         sub_seq_end = 0;
231         seq_params = true;
232
233         while ((!para_file.eof()) && seq_params)
234         {
235           getline(para_file,file_data_line);
236           split_index = file_data_line.find(" ");
237           param = file_data_line.substr(0,split_index);
238           value = file_data_line.substr(split_index+1);
239
240           if (param == "FASTA_INDEX")
241             fasta_index = atoi(value.c_str());
242           else if (param == "ANNOTATION")
243             annot_file = file_path_base + value;
244           else if (param == "SEQ_START")
245             sub_seq_start = atoi(value.c_str());
246           else if (param == "SEQ_END")
247           {
248             sub_seq_end = atoi(value.c_str());
249           }
250           //ignore empty lines or that start with '#'
251           else if ((param == "") || (param == "#")) {}
252           else seq_params = false;
253         }
254
255         fasta_indices.push_back(fasta_index);
256         annot_files.push_back(annot_file);
257         sub_seq_starts.push_back(sub_seq_start);
258         sub_seq_ends.push_back(sub_seq_end);
259         did_seq = true;
260       }
261       //ignore empty lines or that start with '#'
262       else if ((param == "") || (param == "#")) {}
263       else
264       {
265         clog << "Illegal/misplaced mussa parameter in file\n";
266         clog << param << "\n";
267       }
268
269       if (!did_seq)
270       {
271         getline(para_file,file_data_line);
272         split_index = file_data_line.find(" ");
273         param = file_data_line.substr(0,split_index);
274         value = file_data_line.substr(split_index+1);
275         did_seq = false;
276       }
277     }
278
279     para_file.close();
280
281     soft_thres = threshold;
282     //cout << "nway mupa: analysis_name = " << analysis_name 
283     //     << " window = " << window 
284     //     << " threshold = " << threshold << endl;
285   }
286   // no file was loaded, signal error
287   else
288   {
289     throw mussa_load_error("Config File: " + para_file_path + " not found");
290   }
291 }
292
293
294 void
295 Mussa::analyze(int w, int t, enum Mussa::analysis_modes the_ana_mode, double new_ent_thres)
296 {
297   time_t t1, t2, begin, end;
298   double seqloadtime, seqcomptime, nwaytime, savetime, totaltime;
299
300   begin = time(NULL);
301
302   ana_mode = the_ana_mode;
303   ent_thres = new_ent_thres;
304   if (w > 0)
305     window  = w;
306   if (t > 0)
307   {
308     threshold = t;
309     soft_thres = t;
310   }
311
312   t1 = time(NULL);
313   load_sequence_data();
314         
315   if (the_seqs.size() < 2) {
316     throw mussa_analysis_error("you need to have at least 2 sequences to "
317                                "do an analysis.");
318   }
319   //cout << "nway ana: seq_num = " << the_seqs.size() << endl;
320
321   t2 = time(NULL);
322   seqloadtime = difftime(t2, t1);
323
324   t1 = time(NULL);
325   seqcomp();
326   t2 = time(NULL);
327   seqcomptime = difftime(t2, t1);
328
329
330   t1 = time(NULL);
331   the_paths.setup(window, threshold);
332   nway();
333   t2 = time(NULL);
334   nwaytime = difftime(t2, t1);
335
336   t1 = time(NULL);
337   save();
338   t2 = time(NULL);
339   savetime = difftime(t2, t1);
340
341   end = time(NULL);
342   totaltime = difftime(end, begin);
343
344
345   //cout << "seqload\tseqcomp\tnway\tsave\ttotal\n";
346   //cout << seqloadtime << "\t";
347   //cout << seqcomptime << "\t";
348   //cout << nwaytime << "\t";
349   //cout << savetime << "\t";
350   //cout << totaltime << "\n";
351 }
352
353
354 void
355 Mussa::load_sequence_data()
356 {
357   list<string>::iterator seq_files_i, annot_files_i;
358   list<int>::iterator fasta_indices_i, seq_starts_i, seq_ends_i;
359   Sequence aSeq;
360   string err_msg;
361
362
363   seq_files_i = seq_files.begin();
364   fasta_indices_i = fasta_indices.begin();
365   annot_files_i = annot_files.begin();
366   seq_starts_i = sub_seq_starts.begin();
367   seq_ends_i = sub_seq_ends.begin();
368
369   while ( (seq_files_i != seq_files.end()) && (err_msg == "") )
370           /* it should be guarenteed that each of the following exist
371              should I bother checking, and how to deal with if not true...
372  &&
373           (fasta_indices_i != fasta_indices.end()) &&
374           (annot_files_i != annot_files.end())  && 
375           (seq_starts_i != sub_seq_starts.end())  &&
376           (seq_ends_i != sub_seq_ends.end())         )
377           */
378   {
379     aSeq.load_fasta(*seq_files_i, *fasta_indices_i,*seq_starts_i, *seq_ends_i);
380     if (annot_files_i->size() > 0) 
381       aSeq.load_annot(*annot_files_i, *seq_starts_i, *seq_ends_i); 
382     
383     the_seqs.push_back(aSeq);
384     //cout << aSeq.get_header() << endl;
385     //cout << aSeq.get_seq() << endl;
386     aSeq.clear();
387     ++seq_files_i;      // advance all the iterators
388     ++fasta_indices_i;
389     ++annot_files_i;
390     ++seq_starts_i;
391     ++seq_ends_i;
392   }
393 }
394
395
396 void
397 Mussa::seqcomp()
398 {
399   vector<int> seq_lens;
400   vector<FLPs> empty_FLP_vector;
401   FLPs dummy_comp;
402   string save_file_string;
403
404   empty_FLP_vector.clear();
405   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
406   {
407     all_comps.push_back(empty_FLP_vector); 
408     for(vector<Sequence>::size_type i2 = 0; i2 < the_seqs.size(); i2++)
409       all_comps[i].push_back(dummy_comp);
410   }
411   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
412     seq_lens.push_back(the_seqs[i].size());
413
414   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
415     for(vector<Sequence>::size_type i2 = i+1; i2 < the_seqs.size(); i2++)
416     {
417       //cout << "seqcomping: " << i << " v. " << i2 << endl;
418       all_comps[i][i2].setup(window, threshold);
419       all_comps[i][i2].seqcomp(the_seqs[i].get_seq(), the_seqs[i2].get_seq(), false);
420       all_comps[i][i2].seqcomp(the_seqs[i].get_seq(),the_seqs[i2].rev_comp(),true);
421     }
422 }
423
424 void
425 Mussa::nway()
426 {
427   vector<string> some_Seqs;
428
429   the_paths.set_soft_thres(soft_thres);
430
431   if (ana_mode == TransitiveNway) {
432     the_paths.trans_path_search(all_comps);
433   }
434   else if (ana_mode == RadialNway) {
435     the_paths.radiate_path_search(all_comps);
436   }
437   else if (ana_mode == EntropyNway)
438   {
439     //unlike other methods, entropy needs to look at the sequence at this stage
440     some_Seqs.clear();
441     for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
442     {
443       some_Seqs.push_back(the_seqs[i].get_seq());
444     }
445
446     the_paths.setup_ent(ent_thres, some_Seqs); // ent analysis extra setup
447     the_paths.entropy_path_search(all_comps);
448   }
449
450   // old recursive transitive analysis function
451   else if (ana_mode == RecursiveNway)
452     the_paths.find_paths_r(all_comps);
453
454   the_paths.simple_refine();
455 }
456
457 void
458 Mussa::save()
459 {
460   string save_name, save_path, create_dir_cmd, flp_filepath;
461   fstream save_file;
462   ostringstream append_info;
463   int dir_create_status;
464
465
466   // not sure why, but gotta close file each time since can't pass file streams
467
468   save_name = analysis_name;
469
470   // gotta do bit with adding win & thres if to be appended
471   if (win_append)
472   {
473     append_info.str("");
474     append_info <<  "_w" << window;
475     save_name += append_info.str();
476   }
477
478   if (thres_append)
479   {
480     append_info.str("");
481     append_info <<  "_t" << threshold;
482     save_name += append_info.str();
483   }
484
485 //#include <stdlib.h>
486   // ******* use appropriate for os ------- 1 of 4
487   // the additions for osX make it more sane where it saves the analysis
488   // will come up with a cleaner sol'n later...
489   create_dir_cmd = "mkdir " + save_name;       //linux
490   //create_dir_cmd = "mkdir " + file_path_base + save_name;  //osX
491
492   dir_create_status = system( (const char*) create_dir_cmd.c_str());
493   //cout << "action: " << dir_create_status << endl;
494
495   // save sequence and annots to a special mussa file
496
497   // ******** use appropriate for OS ---------- 2 of 4
498   save_path = save_name + "/" + save_name + ".museq";   //linux
499   //save_path = file_path_base + save_name + "/" + save_name + ".museq"; //osX
500
501   save_file.open(save_path.c_str(), ios::out);
502   save_file << "<Mussa_Sequence>" << endl;
503   //save_file.close();
504
505   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
506   {
507     the_seqs[i].save(save_file);
508   }
509
510   //save_file.open(save_path.c_str(), ios::app);
511   save_file << "</Mussa_Sequence>" << endl;
512   save_file.close();
513
514   // save nway paths to its mussa save file
515
516   // ******** use appropriate for OS -------- 3 of 4
517   save_path = save_name + "/" + save_name + ".muway";  //linux
518   //save_path = file_path_base + save_name + "/" + save_name + ".muway"; //os X
519   the_paths.save(save_path);
520
521   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
522     for(vector<Sequence>::size_type i2 = i+1; i2 < the_seqs.size(); i2++)
523     {
524       append_info.str("");
525       append_info <<  "_sp_" << i << "v" << i2;
526       // ******** use appropriate for OS --------- 4 of 4
527       //linux
528       save_path = save_name + "/" + save_name + append_info.str() + ".flp";
529       //osX
530       //save_path = file_path_base + save_name + "/" + save_name + append_info.str() + ".flp";
531       all_comps[i][i2].save(save_path);
532     }
533 }
534
535 void
536 Mussa::save_muway(string save_path)
537 {
538   the_paths.save(save_path);
539 }
540
541 void
542 Mussa::load(string ana_file)
543 {
544   int i, i2;
545   string::size_type start_index, end_index;
546   string file_path_base, a_file_path, ana_path;
547   bool parsing_path;
548   Sequence tmp_seq;
549   string err_msg;
550   ostringstream append_info;
551   vector<FLPs> empty_FLP_vector;
552   FLPs dummy_comp;
553
554   //cout << "ana_file name " << ana_file << endl;
555   ana_path = ana_file;
556   parsing_path = true;
557   end_index = ana_path.size()-1;
558   if (ana_path[end_index] == '/') { 
559     --end_index;
560   }
561   start_index = ana_path.rfind('/', end_index);
562   if (start_index == string::npos) {
563     // no / to be found
564     start_index = 0;
565   } else {
566     // skip the / we found
567     ++start_index;
568   }
569   analysis_name = ana_path.substr(start_index, end_index-start_index+1);
570   //cout << " ana_name " << analysis_name << endl;
571   file_path_base =  ana_path.substr(0, start_index) + analysis_name 
572                     + "/" + analysis_name;
573   a_file_path = file_path_base + ".muway";
574   //cout << " loading museq: " << a_file_path << endl;
575   the_paths.load(a_file_path);
576
577   int seq_num = the_paths.sequence_count();
578
579   a_file_path = file_path_base + ".museq";
580
581   // this is a bit of a hack due to C++ not acting like it should with files
582   for (i = 1; i <= seq_num; i++)
583   {
584     tmp_seq.clear();
585     //cout << "mussa_class: loading museq frag... " << a_file_path << endl;
586     tmp_seq.load_museq(a_file_path, i);
587     the_seqs.push_back(tmp_seq);
588   }
589   
590   empty_FLP_vector.clear();
591   for(i = 0; i < seq_num; i++)
592   {
593     all_comps.push_back(empty_FLP_vector); 
594     for(i2 = 0; i2 < seq_num; i2++)
595       all_comps[i].push_back(dummy_comp);
596   }
597   
598   for(i = 0; i < seq_num; i++)
599   {
600     for(i2 = i+1; i2 < seq_num; i2++)
601     {
602       append_info.str("");
603       append_info <<  "_sp_" << i << "v" << i2;
604       //cout << append_info.str() << endl;
605       a_file_path = file_path_base + append_info.str() + ".flp";
606       all_comps[i][i2].load(a_file_path);
607       //cout << "real size = " << all_comps[i][i2].size() << endl;
608     }
609   }
610 }
611
612
613 void
614 Mussa::save_old()
615 {
616   fstream save_file;
617
618   save_file.open(analysis_name.c_str(), ios::out);
619
620   for(vector<Sequence>::size_type i = 0; i < the_seqs.size(); i++)
621     save_file << the_seqs[i].get_seq() << endl;
622
623   save_file << window << endl;
624   save_file.close();
625   //note more complex eventually since analysis_name may need to have
626   //window size, threshold and other stuff to modify it...
627   the_paths.save_old(analysis_name);
628 }
629
630
631 void
632 Mussa::load_old(char * load_file_path, int s_num)
633 {
634   fstream save_file;
635   string file_data_line; 
636   int i, space_split_i, comma_split_i;
637   vector<int> loaded_path;
638   string node_pair, node;
639   Sequence a_seq;
640
641   int seq_num = s_num;
642   the_paths.setup(0, 0);
643   save_file.open(load_file_path, ios::in);
644
645   // currently loads old mussa format
646
647   // get sequences
648   for(i = 0; i < seq_num; i++)
649   {
650     getline(save_file, file_data_line);
651     a_seq.set_seq(file_data_line);
652     the_seqs.push_back(a_seq);
653   }
654
655   // get window size
656   getline(save_file, file_data_line);
657   window = atoi(file_data_line.c_str());
658   // get paths
659
660   while (!save_file.eof())
661   {
662     loaded_path.clear();
663     getline(save_file, file_data_line);
664     if (file_data_line != "")
665     for(i = 0; i < seq_num; i++)
666     {
667       space_split_i = file_data_line.find(" ");
668       node_pair = file_data_line.substr(0,space_split_i);
669       //cout << "np= " << node_pair;
670       comma_split_i = node_pair.find(",");
671       node = node_pair.substr(comma_split_i+1);
672       //cout << "n= " << node << " ";
673       loaded_path.push_back(atoi (node.c_str()));
674       file_data_line = file_data_line.substr(space_split_i+1);
675     }
676     //cout << endl;
677     // FIXME: do we have any information about what the threshold should be?
678     the_paths.add_path(0, loaded_path);
679   }
680   save_file.close();
681
682   //the_paths.save("tmp.save");
683 }