1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : PARS_dtree.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include "pars_dtree.hxx" |
---|
12 | #include "pars_main.hxx" |
---|
13 | #include "pars_debug.hxx" |
---|
14 | #include "ap_tree_nlen.hxx" |
---|
15 | |
---|
16 | #include <AP_seq_dna.hxx> |
---|
17 | #include <AP_seq_protein.hxx> |
---|
18 | #include <AP_filter.hxx> |
---|
19 | |
---|
20 | #include <ColumnStat.hxx> |
---|
21 | #include <awt_sel_boxes.hxx> |
---|
22 | #include <awt_filter.hxx> |
---|
23 | |
---|
24 | #include <gui_aliview.hxx> |
---|
25 | |
---|
26 | #include <aw_preset.hxx> |
---|
27 | #include <aw_awar.hxx> |
---|
28 | #include <aw_msg.hxx> |
---|
29 | #include <arb_progress.h> |
---|
30 | #include <aw_root.hxx> |
---|
31 | #include <aw_question.hxx> |
---|
32 | |
---|
33 | static void AWT_graphic_parsimony_root_changed(void *cd, AP_tree *old, AP_tree *newroot) { |
---|
34 | AWT_graphic_tree *agt = (AWT_graphic_tree*)cd; |
---|
35 | |
---|
36 | if (old == agt->displayed_root) agt->displayed_root = newroot; |
---|
37 | } |
---|
38 | |
---|
39 | static AliView *pars_generate_aliview(WeightedFilter *pars_weighted_filter) { |
---|
40 | GBDATA *gb_main = pars_weighted_filter->get_gb_main(); |
---|
41 | char *ali_name; |
---|
42 | { |
---|
43 | GB_transaction ta(gb_main); |
---|
44 | ali_name = GBT_read_string(gb_main, AWAR_ALIGNMENT); |
---|
45 | } |
---|
46 | GB_ERROR error = NULL; |
---|
47 | AliView *aliview = pars_weighted_filter->create_aliview(ali_name, error); |
---|
48 | if (!aliview) aw_popup_exit(error); |
---|
49 | free(ali_name); |
---|
50 | return aliview; |
---|
51 | } |
---|
52 | |
---|
53 | void PARS_tree_init(AWT_graphic_tree *agt) { |
---|
54 | ap_assert(agt->get_root_node()); |
---|
55 | ap_assert(agt == ap_main->get_tree_root()); |
---|
56 | |
---|
57 | GB_transaction ta(GLOBAL_gb_main); |
---|
58 | |
---|
59 | const char *use = ap_main->get_aliname(); |
---|
60 | long ali_len = GBT_get_alignment_len(GLOBAL_gb_main, use); |
---|
61 | if (ali_len <= 1) { |
---|
62 | aw_popup_exit("No valid alignment selected! Try again"); |
---|
63 | } |
---|
64 | |
---|
65 | agt->tree_static->set_root_changed_callback(AWT_graphic_parsimony_root_changed, agt); |
---|
66 | } |
---|
67 | |
---|
68 | static double funktion_quadratisch(double wert, double *param_list, int param_anz) { |
---|
69 | if (param_anz != 3) { |
---|
70 | ap_assert(0); // wrong number of parameters |
---|
71 | return 0; |
---|
72 | } |
---|
73 | return wert * wert * param_list[0] + wert * param_list[1] + param_list[2]; |
---|
74 | } |
---|
75 | |
---|
76 | |
---|
77 | void ArbParsimony::kernighan_optimize_tree(AP_tree *at) { |
---|
78 | GB_push_transaction(GLOBAL_gb_main); |
---|
79 | |
---|
80 | long prevCombineCount = AP_sequence::combine_count(); |
---|
81 | |
---|
82 | AP_FLOAT pars_start = get_root_node()->costs(); |
---|
83 | const AP_FLOAT pars_org = pars_start; |
---|
84 | |
---|
85 | int rek_deep_max = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/maxdepth"); |
---|
86 | |
---|
87 | AP_KL_FLAG funktype = (AP_KL_FLAG)*GBT_read_int(GLOBAL_gb_main, "genetic/kh/function_type"); |
---|
88 | |
---|
89 | int param_anz; |
---|
90 | double param_list[3]; |
---|
91 | double f_startx, f_maxy, f_maxx, f_max_deep; |
---|
92 | f_max_deep = (double)rek_deep_max; // x2 |
---|
93 | f_startx = (double)*GBT_read_int(GLOBAL_gb_main, "genetic/kh/dynamic/start"); |
---|
94 | f_maxy = (double)*GBT_read_int(GLOBAL_gb_main, "genetic/kh/dynamic/maxy"); |
---|
95 | f_maxx = (double)*GBT_read_int(GLOBAL_gb_main, "genetic/kh/dynamic/maxx"); |
---|
96 | |
---|
97 | double (*funktion)(double wert, double *param_list, int param_anz); |
---|
98 | switch (funktype) { |
---|
99 | default: |
---|
100 | case AP_QUADRAT_START: |
---|
101 | funktion = funktion_quadratisch; |
---|
102 | param_anz = 3; |
---|
103 | param_list[2] = f_startx; |
---|
104 | param_list[0] = (f_startx - f_maxy) / (f_maxx * f_maxx); |
---|
105 | param_list[1] = -2.0 * param_list[0] * f_maxx; |
---|
106 | break; |
---|
107 | case AP_QUADRAT_MAX: // parameter liste fuer quadratische gleichung (y =ax^2 +bx +c) |
---|
108 | funktion = funktion_quadratisch; |
---|
109 | param_anz = 3; |
---|
110 | param_list[0] = - f_maxy / ((f_max_deep - f_maxx) * (f_max_deep - f_maxx)); |
---|
111 | param_list[1] = -2.0 * param_list[0] * f_maxx; |
---|
112 | param_list[2] = f_maxy + param_list[0] * f_maxx * f_maxx; |
---|
113 | break; |
---|
114 | } |
---|
115 | |
---|
116 | |
---|
117 | AP_KL_FLAG searchflag=(AP_KL_FLAG)0; |
---|
118 | if (*GBT_read_int(GLOBAL_gb_main, "genetic/kh/dynamic/enable")) { |
---|
119 | searchflag = AP_DYNAMIK; |
---|
120 | } |
---|
121 | if (*GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/enable")) { |
---|
122 | searchflag = (AP_KL_FLAG)(searchflag|AP_STATIC); |
---|
123 | } |
---|
124 | |
---|
125 | int rek_breite[8]; |
---|
126 | rek_breite[0] = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/depth0"); |
---|
127 | rek_breite[1] = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/depth1"); |
---|
128 | rek_breite[2] = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/depth2"); |
---|
129 | rek_breite[3] = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/depth3"); |
---|
130 | rek_breite[4] = *GBT_read_int(GLOBAL_gb_main, "genetic/kh/static/depth4"); |
---|
131 | int rek_breite_anz = 5; |
---|
132 | |
---|
133 | int anzahl = (int)(*GBT_read_float(GLOBAL_gb_main, "genetic/kh/nodes")*at->count_leafs()); |
---|
134 | AP_tree **list = at->getRandomNodes(anzahl); |
---|
135 | |
---|
136 | arb_progress progress(anzahl); |
---|
137 | |
---|
138 | progress.subtitle(GBS_global_string("Old Parsimony: %.1f", pars_start)); |
---|
139 | |
---|
140 | GB_pop_transaction(GLOBAL_gb_main); |
---|
141 | |
---|
142 | for (int i=0; i<anzahl && !progress.aborted(); i++) { |
---|
143 | AP_tree_nlen *tree_elem = DOWNCAST(AP_tree_nlen*, list[i]); // @@@ pass 'at' as AP_tree_nlen |
---|
144 | |
---|
145 | bool in_folded_group = tree_elem->gr.hidden || |
---|
146 | (tree_elem->father && tree_elem->get_father()->gr.hidden); |
---|
147 | |
---|
148 | if (!in_folded_group) { |
---|
149 | bool better_tree_found = false; |
---|
150 | ap_main->push(); |
---|
151 | display_clear(funktion, param_list, param_anz, (int)pars_start, (int)rek_deep_max); |
---|
152 | |
---|
153 | tree_elem->kernighan_rek(0, |
---|
154 | rek_breite, rek_breite_anz, rek_deep_max, |
---|
155 | funktion, param_list, param_anz, |
---|
156 | pars_start, pars_start, pars_org, |
---|
157 | searchflag, &better_tree_found); |
---|
158 | |
---|
159 | if (better_tree_found) { |
---|
160 | ap_main->clear(); |
---|
161 | pars_start = get_root_node()->costs(); |
---|
162 | progress.subtitle(GBS_global_string("New parsimony: %.1f (gain: %.1f)", pars_start, pars_org-pars_start)); |
---|
163 | } |
---|
164 | else { |
---|
165 | ap_main->pop(); |
---|
166 | } |
---|
167 | } |
---|
168 | progress.inc(); |
---|
169 | } |
---|
170 | delete list; |
---|
171 | printf("Combines: %li\n", AP_sequence::combine_count()-prevCombineCount); |
---|
172 | } |
---|
173 | |
---|
174 | |
---|
175 | |
---|
176 | void ArbParsimony::optimize_tree(AP_tree *at, arb_progress& progress) { |
---|
177 | AP_tree *oldrootleft = get_root_node()->get_leftson(); |
---|
178 | AP_tree *oldrootright = get_root_node()->get_rightson(); |
---|
179 | const AP_FLOAT org_pars = get_root_node()->costs(); |
---|
180 | AP_FLOAT prev_pars = org_pars; |
---|
181 | |
---|
182 | progress.subtitle(GBS_global_string("Old parsimony: %.1f", org_pars)); |
---|
183 | |
---|
184 | while (!progress.aborted()) { |
---|
185 | AP_FLOAT nni_pars = DOWNCAST(AP_tree_nlen*, at)->nn_interchange_rek(-1, AP_BL_NNI_ONLY, false); |
---|
186 | |
---|
187 | if (nni_pars == prev_pars) { // NNI did not reduce costs -> kern-lin |
---|
188 | kernighan_optimize_tree(at); |
---|
189 | AP_FLOAT ker_pars = get_root_node()->costs(); |
---|
190 | if (ker_pars == prev_pars) break; // kern-lin did not improve tree -> done |
---|
191 | prev_pars = ker_pars; |
---|
192 | } |
---|
193 | else { |
---|
194 | prev_pars = nni_pars; |
---|
195 | } |
---|
196 | progress.subtitle(GBS_global_string("New parsimony: %.1f (gain: %.1f)", prev_pars, org_pars-prev_pars)); |
---|
197 | } |
---|
198 | |
---|
199 | if (oldrootleft->father == oldrootright) oldrootleft->set_root(); |
---|
200 | else oldrootright->set_root(); |
---|
201 | |
---|
202 | get_root_node()->costs(); |
---|
203 | } |
---|
204 | |
---|
205 | AWT_graphic_parsimony::AWT_graphic_parsimony(ArbParsimony& parsimony_, GBDATA *gb_main_, AD_map_viewer_cb map_viewer_cb_) |
---|
206 | : AWT_graphic_tree(parsimony_.get_awroot(), gb_main_, map_viewer_cb_), |
---|
207 | parsimony(parsimony_) |
---|
208 | {} |
---|
209 | |
---|
210 | void ArbParsimony::generate_tree(WeightedFilter *pars_weighted_filter) { |
---|
211 | AliView *aliview = pars_generate_aliview(pars_weighted_filter); |
---|
212 | AP_sequence *seq_templ = 0; |
---|
213 | |
---|
214 | GBDATA *gb_main = aliview->get_gb_main(); |
---|
215 | { |
---|
216 | GB_transaction ta(gb_main); |
---|
217 | bool is_aa = GBT_is_alignment_protein(gb_main, aliview->get_aliname()); |
---|
218 | |
---|
219 | if (is_aa) seq_templ = new AP_sequence_protein(aliview); |
---|
220 | else seq_templ = new AP_sequence_parsimony(aliview); |
---|
221 | } |
---|
222 | |
---|
223 | tree = new AWT_graphic_parsimony(*this, aliview->get_gb_main(), PARS_map_viewer); |
---|
224 | tree->init(new AP_TreeNlenNodeFactory, aliview, seq_templ, true, false); |
---|
225 | ap_main->set_tree_root(tree); |
---|
226 | } |
---|
227 | |
---|
228 | AW_gc_manager AWT_graphic_parsimony::init_devices(AW_window *aww, AW_device *device, AWT_canvas* ntw) { |
---|
229 | AW_init_color_group_defaults("arb_pars"); |
---|
230 | |
---|
231 | AW_gc_manager gc_manager = |
---|
232 | AW_manage_GC(aww, |
---|
233 | ntw->get_gc_base_name(), |
---|
234 | device, AWT_GC_CURSOR, AWT_GC_MAX, /* AWT_GC_CURSOR+7, */ AW_GCM_DATA_AREA, |
---|
235 | makeWindowCallback(AWT_resize_cb, ntw), |
---|
236 | true, // uses color groups |
---|
237 | "#AAAA55", |
---|
238 | |
---|
239 | // Important note : |
---|
240 | // Many gc indices are shared between ABR_NTREE and ARB_PARSIMONY |
---|
241 | // e.g. the tree drawing routines use same gc's for drawing both trees |
---|
242 | // (check AWT_dtree.cxx AWT_graphic_tree::init_devices) |
---|
243 | |
---|
244 | "Cursor$#FFFFFF", |
---|
245 | "Branch remarks$#DBE994", |
---|
246 | "+-Bootstrap$#DBE994", "-B.(limited)$white", |
---|
247 | "--unused$#ff0000", |
---|
248 | "Marked$#FFFF00", |
---|
249 | "Some marked$#eeee88", |
---|
250 | "Not marked$black", |
---|
251 | "Zombies etc.$#cc5924", |
---|
252 | |
---|
253 | "--unused", "--unused", // these reserve the numbers which are used for probe colors in ARB_NTREE |
---|
254 | "--unused", "--unused", // (this is necessary because ARB_PARS and ARB_NTREE use the same tree painting routines) |
---|
255 | "--unused", "--unused", |
---|
256 | "--unused", "--unused", |
---|
257 | |
---|
258 | NULL); |
---|
259 | return gc_manager; |
---|
260 | } |
---|
261 | |
---|
262 | void AWT_graphic_parsimony::show(AW_device *device) { |
---|
263 | AP_tree_nlen *root_node = parsimony.get_root_node(); |
---|
264 | AW_awar *awar_pars = aw_root->awar(AWAR_PARSIMONY); |
---|
265 | AW_awar *awar_best = aw_root->awar(AWAR_BEST_PARSIMONY); |
---|
266 | |
---|
267 | long parsval = root_node ? root_node->costs() : 0; |
---|
268 | awar_pars->write_int(parsval); |
---|
269 | |
---|
270 | long best_pars = awar_best->read_int(); |
---|
271 | if (parsval < best_pars || 0==best_pars) awar_best->write_int(parsval); |
---|
272 | |
---|
273 | AWT_graphic_tree::show(device); |
---|
274 | } |
---|
275 | |
---|
276 | void AWT_graphic_parsimony::handle_command(AW_device *device, AWT_graphic_event& event) { |
---|
277 | ClickedTarget clicked(this, event.best_click()); |
---|
278 | |
---|
279 | switch (event.cmd()) { |
---|
280 | // @@@ something is designed completely wrong here! |
---|
281 | // why do all commands close TA and reopen when done? |
---|
282 | |
---|
283 | case AWT_MODE_NNI: |
---|
284 | if (event.type()==AW_Mouse_Press) { |
---|
285 | GB_pop_transaction(gb_main); |
---|
286 | switch (event.button()) { |
---|
287 | case AW_BUTTON_LEFT: { |
---|
288 | if (clicked.node()) { |
---|
289 | arb_progress progress("NNI optimize subtree"); |
---|
290 | AP_tree_nlen *atn = DOWNCAST(AP_tree_nlen*, clicked.node()); |
---|
291 | atn->nn_interchange_rek(-1, AP_BL_NNI_ONLY, false); |
---|
292 | exports.save = 1; |
---|
293 | ASSERT_VALID_TREE(get_root_node()); |
---|
294 | } |
---|
295 | break; |
---|
296 | } |
---|
297 | case AW_BUTTON_RIGHT: { |
---|
298 | arb_progress progress("NNI optimize tree"); |
---|
299 | long prevCombineCount = AP_sequence::combine_count(); |
---|
300 | |
---|
301 | AP_tree_nlen *atn = DOWNCAST(AP_tree_nlen*, get_root_node()); |
---|
302 | atn->nn_interchange_rek(-1, AP_BL_NNI_ONLY, false); |
---|
303 | printf("Combines: %li\n", AP_sequence::combine_count()-prevCombineCount); |
---|
304 | |
---|
305 | exports.save = 1; |
---|
306 | ASSERT_VALID_TREE(get_root_node()); |
---|
307 | break; |
---|
308 | } |
---|
309 | |
---|
310 | default: break; |
---|
311 | } |
---|
312 | GB_begin_transaction(gb_main); |
---|
313 | } |
---|
314 | break; |
---|
315 | case AWT_MODE_KERNINGHAN: |
---|
316 | if (event.type()==AW_Mouse_Press) { |
---|
317 | GB_pop_transaction(gb_main); |
---|
318 | switch (event.button()) { |
---|
319 | case AW_BUTTON_LEFT: |
---|
320 | if (clicked.node()) { |
---|
321 | arb_progress progress("Kernighan-Lin optimize subtree"); |
---|
322 | parsimony.kernighan_optimize_tree(clicked.node()); |
---|
323 | this->exports.save = 1; |
---|
324 | ASSERT_VALID_TREE(get_root_node()); |
---|
325 | } |
---|
326 | break; |
---|
327 | case AW_BUTTON_RIGHT: { |
---|
328 | arb_progress progress("Kernighan-Lin optimize tree"); |
---|
329 | parsimony.kernighan_optimize_tree(get_root_node()); |
---|
330 | this->exports.save = 1; |
---|
331 | ASSERT_VALID_TREE(get_root_node()); |
---|
332 | break; |
---|
333 | } |
---|
334 | default: break; |
---|
335 | } |
---|
336 | GB_begin_transaction(gb_main); |
---|
337 | } |
---|
338 | break; |
---|
339 | case AWT_MODE_OPTIMIZE: |
---|
340 | if (event.type()==AW_Mouse_Press) { |
---|
341 | GB_pop_transaction(gb_main); |
---|
342 | switch (event.button()) { |
---|
343 | case AW_BUTTON_LEFT: |
---|
344 | if (clicked.node()) { |
---|
345 | arb_progress progress("Optimizing subtree"); |
---|
346 | parsimony.optimize_tree(clicked.node(), progress); |
---|
347 | this->exports.save = 1; |
---|
348 | ASSERT_VALID_TREE(get_root_node()); |
---|
349 | } |
---|
350 | break; |
---|
351 | case AW_BUTTON_RIGHT: { |
---|
352 | arb_progress progress("Optimizing tree"); |
---|
353 | |
---|
354 | parsimony.optimize_tree(get_root_node(), progress); |
---|
355 | this->exports.save = 1; |
---|
356 | ASSERT_VALID_TREE(get_root_node()); |
---|
357 | break; |
---|
358 | } |
---|
359 | default: break; |
---|
360 | } |
---|
361 | GB_begin_transaction(gb_main); |
---|
362 | } |
---|
363 | break; |
---|
364 | |
---|
365 | default: |
---|
366 | AWT_graphic_tree::handle_command(device, event); |
---|
367 | break; |
---|
368 | } |
---|
369 | |
---|
370 | if (exports.save == 1) { |
---|
371 | arb_progress progress("Recalculating branch lengths"); |
---|
372 | rootEdge()->calc_branchlengths(); |
---|
373 | reorder_tree(BIG_BRANCHES_TO_TOP); // beautify after recalc_branch_lengths |
---|
374 | } |
---|
375 | } |
---|
376 | |
---|
377 | |
---|
378 | // -------------------------------------------------------------------------------- |
---|
379 | |
---|
380 | #ifdef UNIT_TESTS |
---|
381 | #include <arb_diff.h> |
---|
382 | #ifndef TEST_UNIT_H |
---|
383 | #include <test_unit.h> |
---|
384 | #endif |
---|
385 | |
---|
386 | void fake_AW_init_color_groups(); |
---|
387 | |
---|
388 | struct fake_agt : public AWT_graphic_tree, virtual Noncopyable { |
---|
389 | AP_sequence_parsimony *templ; |
---|
390 | |
---|
391 | fake_agt() |
---|
392 | : AWT_graphic_tree(NULL, GLOBAL_gb_main, NULL), |
---|
393 | templ(NULL) |
---|
394 | { |
---|
395 | } |
---|
396 | ~fake_agt() { |
---|
397 | delete templ; |
---|
398 | } |
---|
399 | void init(AliView *aliview) { |
---|
400 | fake_AW_init_color_groups(); // acts like no species has a color |
---|
401 | delete templ; |
---|
402 | templ = aliview->has_data() ? new AP_sequence_parsimony(aliview) : NULL; |
---|
403 | AWT_graphic_tree::init(new AP_TreeNlenNodeFactory, aliview, templ, true, false); |
---|
404 | } |
---|
405 | }; |
---|
406 | |
---|
407 | class PARSIMONY_testenv : virtual Noncopyable { |
---|
408 | GB_shell shell; |
---|
409 | AP_main apMain; |
---|
410 | fake_agt *agt; |
---|
411 | |
---|
412 | void common_init(const char *dbname) { |
---|
413 | GLOBAL_gb_main = NULL; |
---|
414 | apMain.open(dbname); |
---|
415 | |
---|
416 | TEST_EXPECT_NULL(ap_main); |
---|
417 | ap_main = &apMain; |
---|
418 | |
---|
419 | agt = new fake_agt; |
---|
420 | apMain.set_tree_root(agt); |
---|
421 | } |
---|
422 | |
---|
423 | public: |
---|
424 | PARSIMONY_testenv(const char *dbname) { |
---|
425 | common_init(dbname); |
---|
426 | agt->init(new AliView(GLOBAL_gb_main)); |
---|
427 | } |
---|
428 | PARSIMONY_testenv(const char *dbname, const char *aliName) { |
---|
429 | common_init(dbname); |
---|
430 | GB_transaction ta(GLOBAL_gb_main); |
---|
431 | size_t aliLength = GBT_get_alignment_len(GLOBAL_gb_main, aliName); |
---|
432 | |
---|
433 | AP_filter filter(aliLength); |
---|
434 | if (!filter.is_invalid()) { |
---|
435 | AP_weights weights(&filter); |
---|
436 | agt->init(new AliView(GLOBAL_gb_main, filter, weights, aliName)); |
---|
437 | } |
---|
438 | } |
---|
439 | ~PARSIMONY_testenv() { |
---|
440 | TEST_EXPECT_EQUAL(ap_main, &apMain); |
---|
441 | ap_main = NULL; |
---|
442 | |
---|
443 | delete agt; |
---|
444 | GB_close(GLOBAL_gb_main); |
---|
445 | GLOBAL_gb_main = NULL; |
---|
446 | } |
---|
447 | |
---|
448 | GB_ERROR load_tree(const char *tree_name) { |
---|
449 | GB_transaction ta(GLOBAL_gb_main); // @@@ do inside AWT_graphic_tree::load? |
---|
450 | return agt->load(GLOBAL_gb_main, tree_name, 0, 0); |
---|
451 | } |
---|
452 | AP_tree_nlen *tree_root() { return apMain.get_root_node(); } |
---|
453 | |
---|
454 | void push() { apMain.push(); } |
---|
455 | void pop() { apMain.pop(); } |
---|
456 | }; |
---|
457 | |
---|
458 | void TEST_tree_modifications() { |
---|
459 | PARSIMONY_testenv env("TEST_trees.arb"); |
---|
460 | TEST_EXPECT_NO_ERROR(env.load_tree("tree_test")); |
---|
461 | |
---|
462 | { |
---|
463 | AP_tree_nlen *root = env.tree_root(); |
---|
464 | TEST_REJECT_NULL(root); |
---|
465 | |
---|
466 | AP_tree_edge::initialize(root); // builds edges |
---|
467 | TEST_ASSERT_VALID_TREE(root); |
---|
468 | |
---|
469 | root->compute_tree(); |
---|
470 | |
---|
471 | // first check initial state: |
---|
472 | { |
---|
473 | AP_tree_members& root_info = root->gr; |
---|
474 | |
---|
475 | TEST_EXPECT_EQUAL(root_info.grouped, 0); |
---|
476 | TEST_EXPECT_EQUAL(root_info.hidden, 0); |
---|
477 | TEST_EXPECT_EQUAL(root_info.has_marked_children, 1); |
---|
478 | TEST_EXPECT_EQUAL(root_info.leaf_sum, 15); |
---|
479 | |
---|
480 | TEST_EXPECT_SIMILAR(root_info.max_tree_depth, 1.624975, 0.000001); |
---|
481 | TEST_EXPECT_SIMILAR(root_info.min_tree_depth, 0.341681, 0.000001); |
---|
482 | |
---|
483 | GB_transaction ta(GLOBAL_gb_main); |
---|
484 | GBT_mark_all(GLOBAL_gb_main, 0); // unmark all species |
---|
485 | root->compute_tree(); |
---|
486 | TEST_EXPECT_EQUAL(root_info.has_marked_children, 0); |
---|
487 | } |
---|
488 | |
---|
489 | |
---|
490 | #define B1_TOP "(((((CloTyro3:1.046,CloTyro4:0.061):0.026,CloTyro2:0.017):0.017,CloTyrob:0.009):0.274,CloInnoc:0.371):0.057,CloBifer:0.388):0.124" |
---|
491 | #define B1_BOT "(CloBifer:0.388,(CloInnoc:0.371,(CloTyrob:0.009,(CloTyro2:0.017,(CloTyro3:1.046,CloTyro4:0.061):0.026):0.017):0.274):0.057):0.124" |
---|
492 | #define B2_TOP "(((CloButy2:0.009,CloButyr:0.000):0.564,CloCarni:0.120):0.010,CloPaste:0.179):0.131" |
---|
493 | #define B2_BOT "(CloPaste:0.179,(CloCarni:0.120,(CloButy2:0.009,CloButyr:0.000):0.564):0.010):0.131" |
---|
494 | |
---|
495 | |
---|
496 | #define B3_LEFT_TOP_SONS "(((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522):0.053,CelBiazo:0.059)" |
---|
497 | #define B3_TOP_SONS B3_LEFT_TOP_SONS ":0.207,CytAquat:0.711" |
---|
498 | #define B3_TOP_SONS_CCR "((CorAquat:0.187,CorGluta:0.522):0.053,CelBiazo:0.059):0.207,CytAquat:0.711" // CCR = CurCitre removed |
---|
499 | #define B3_TOP "(" B3_TOP_SONS "):0.081" |
---|
500 | #define B3_BOT "(CytAquat:0.711,(CelBiazo:0.059,(CorGluta:0.522,(CorAquat:0.084,CurCitre:0.058):0.103):0.053):0.207):0.081" |
---|
501 | |
---|
502 | |
---|
503 | const char *top_topo = "((" B1_TOP "," B2_TOP "):0.081," B3_TOP ");"; |
---|
504 | const char *edge_topo = "((" B1_TOP "," B2_BOT "):0.081," B3_BOT ");"; |
---|
505 | const char *bottom_topo = "(" B3_BOT ",(" B2_BOT "," B1_BOT "):0.081);"; |
---|
506 | |
---|
507 | const char *radial_topo = |
---|
508 | "(((CloPaste:0.179,((CloButy2:0.009,CloButyr:0.000):0.564,CloCarni:0.120):0.010):0.131," |
---|
509 | "((CloInnoc:0.371,((CloTyro2:0.017,(CloTyro3:1.046,CloTyro4:0.061):0.026):0.017,CloTyrob:0.009):0.274):0.057,CloBifer:0.388):0.124):0.081," |
---|
510 | "((CelBiazo:0.059,((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522):0.053):0.207,CytAquat:0.711):0.081);"; |
---|
511 | const char *radial_topo2 = |
---|
512 | "(((CloBifer:0.388,(CloInnoc:0.371,(((CloTyro3:1.046,CloTyro4:0.061):0.026,CloTyro2:0.017):0.017,CloTyrob:0.009):0.274):0.057):0.124," B2_TOP "):0.081," |
---|
513 | "(CytAquat:0.711," B3_LEFT_TOP_SONS ":0.207):0.081);"; |
---|
514 | |
---|
515 | // expect that no mode reproduces another mode: |
---|
516 | TEST_EXPECT_DIFFERENT(top_topo, edge_topo); |
---|
517 | TEST_EXPECT_DIFFERENT(top_topo, bottom_topo); |
---|
518 | TEST_EXPECT_DIFFERENT(top_topo, radial_topo); |
---|
519 | TEST_EXPECT_DIFFERENT(top_topo, radial_topo2); |
---|
520 | TEST_EXPECT_DIFFERENT(edge_topo, bottom_topo); |
---|
521 | TEST_EXPECT_DIFFERENT(edge_topo, radial_topo); |
---|
522 | TEST_EXPECT_DIFFERENT(edge_topo, radial_topo2); |
---|
523 | TEST_EXPECT_DIFFERENT(bottom_topo, radial_topo); |
---|
524 | TEST_EXPECT_DIFFERENT(bottom_topo, radial_topo2); |
---|
525 | TEST_EXPECT_DIFFERENT(radial_topo, radial_topo2); |
---|
526 | |
---|
527 | env.push(); // 1st stack level (=top_topo) |
---|
528 | |
---|
529 | TEST_ASSERT_VALID_TREE(root); |
---|
530 | |
---|
531 | TEST_EXPECT_NEWICK(nLENGTH, root, top_topo); |
---|
532 | // test reorder_tree: |
---|
533 | root->reorder_tree(BIG_BRANCHES_TO_EDGE); TEST_EXPECT_NEWICK(nLENGTH, root, edge_topo); env.push(); // 2nd stack level (=edge_topo) |
---|
534 | root->reorder_tree(BIG_BRANCHES_TO_BOTTOM); TEST_EXPECT_NEWICK(nLENGTH, root, bottom_topo); env.push(); // 3rd stack level (=bottom_topo) |
---|
535 | root->reorder_tree(BIG_BRANCHES_TO_CENTER); TEST_EXPECT_NEWICK(nLENGTH, root, radial_topo); |
---|
536 | root->reorder_tree(BIG_BRANCHES_ALTERNATING); TEST_EXPECT_NEWICK(nLENGTH, root, radial_topo2); |
---|
537 | root->reorder_tree(BIG_BRANCHES_TO_TOP); TEST_EXPECT_NEWICK(nLENGTH, root, top_topo); |
---|
538 | |
---|
539 | TEST_ASSERT_VALID_TREE(root); |
---|
540 | |
---|
541 | // test set root: |
---|
542 | AP_tree_nlen *CloTyrob = root->findLeafNamed("CloTyrob"); |
---|
543 | TEST_REJECT_NULL(CloTyrob); |
---|
544 | |
---|
545 | ARB_edge rootEdge(root->get_leftson(), root->get_rightson()); |
---|
546 | CloTyrob->set_root(); |
---|
547 | |
---|
548 | TEST_ASSERT_VALID_TREE(root); |
---|
549 | |
---|
550 | const char *rootAtCloTyrob_topo = |
---|
551 | "(CloTyrob:0.004," |
---|
552 | "(((CloTyro3:1.046,CloTyro4:0.061):0.026,CloTyro2:0.017):0.017," |
---|
553 | "((((" B3_TOP_SONS "):0.162," B2_TOP "):0.124,CloBifer:0.388):0.057,CloInnoc:0.371):0.274):0.004);"; |
---|
554 | |
---|
555 | TEST_EXPECT_NEWICK(nLENGTH, root, rootAtCloTyrob_topo); |
---|
556 | env.push(); // 4th stack level (=rootAtCloTyrob_topo) |
---|
557 | |
---|
558 | TEST_ASSERT_VALID_TREE(root); |
---|
559 | |
---|
560 | AP_tree_nlen *CelBiazoFather = root->findLeafNamed("CelBiazo")->get_father(); |
---|
561 | TEST_REJECT_NULL(CelBiazoFather); |
---|
562 | CelBiazoFather->set_root(); |
---|
563 | |
---|
564 | const char *rootAtCelBiazoFather_topo = "(" B3_LEFT_TOP_SONS ":0.104,((" B1_TOP "," B2_TOP "):0.162,CytAquat:0.711):0.104);"; |
---|
565 | TEST_EXPECT_NEWICK(nLENGTH, root, rootAtCelBiazoFather_topo); |
---|
566 | |
---|
567 | TEST_ASSERT_VALID_TREE(root); |
---|
568 | |
---|
569 | ARB_edge oldRootEdge(rootEdge.source(), rootEdge.dest()); |
---|
570 | DOWNCAST(AP_tree_nlen*,oldRootEdge.son())->set_root(); |
---|
571 | |
---|
572 | const char *rootSetBack_topo = top_topo; |
---|
573 | TEST_EXPECT_NEWICK(nLENGTH, root, rootSetBack_topo); |
---|
574 | env.push(); // 5th stack level (=rootSetBack_topo) |
---|
575 | |
---|
576 | TEST_ASSERT_VALID_TREE(root); |
---|
577 | |
---|
578 | // test remove: |
---|
579 | AP_tree_nlen *CurCitre = root->findLeafNamed("CurCitre"); |
---|
580 | TEST_REJECT_NULL(CurCitre); |
---|
581 | TEST_REJECT_NULL(CurCitre->get_father()); |
---|
582 | |
---|
583 | CurCitre->remove(); |
---|
584 | const char *CurCitre_removed_topo = "((" B1_TOP "," B2_TOP "):0.081,(" B3_TOP_SONS_CCR "):0.081);"; |
---|
585 | // ------------------------------------------------------------------- ^^^ = B3_TOP_SONS minus CurCitre |
---|
586 | TEST_EXPECT_NEWICK(nLENGTH, root, CurCitre_removed_topo); |
---|
587 | |
---|
588 | TEST_ASSERT_VALID_TREE(root); |
---|
589 | TEST_ASSERT_VALID_TREE(CurCitre); |
---|
590 | |
---|
591 | TEST_EXPECT_EQUAL(root->gr.leaf_sum, 15); // out of date |
---|
592 | root->compute_tree(); |
---|
593 | TEST_EXPECT_EQUAL(root->gr.leaf_sum, 14); |
---|
594 | |
---|
595 | env.push(); // 6th stack level (=CurCitre_removed_topo) |
---|
596 | |
---|
597 | TEST_ASSERT_VALID_TREE(root); |
---|
598 | |
---|
599 | // test insert: |
---|
600 | AP_tree_nlen *CloCarni = root->findLeafNamed("CloCarni"); |
---|
601 | TEST_REJECT_NULL(CloCarni); |
---|
602 | CurCitre->insert(CloCarni); // this creates two extra edges (not destroyed by destroy() below) and one extra node |
---|
603 | |
---|
604 | const char *CurCitre_inserted_topo = "((" B1_TOP ",(((CloButy2:0.009,CloButyr:0.000):0.564,(CurCitre:0.060,CloCarni:0.060):0.060):0.010,CloPaste:0.179):0.131):0.081,(" B3_TOP_SONS_CCR "):0.081);"; |
---|
605 | TEST_EXPECT_NEWICK(nLENGTH, root, CurCitre_inserted_topo); |
---|
606 | |
---|
607 | AP_tree_nlen *node_del_manually = CurCitre->get_father(); |
---|
608 | AP_tree_edge *edge1_del_manually = CurCitre->edgeTo(node_del_manually); |
---|
609 | AP_tree_edge *edge2_del_manually = CurCitre->get_brother()->edgeTo(node_del_manually); |
---|
610 | |
---|
611 | TEST_ASSERT_VALID_TREE(root); |
---|
612 | |
---|
613 | // now check pops: |
---|
614 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, CurCitre_removed_topo); |
---|
615 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, rootSetBack_topo); |
---|
616 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, rootAtCloTyrob_topo); |
---|
617 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, bottom_topo); |
---|
618 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, edge_topo); |
---|
619 | env.pop(); TEST_EXPECT_NEWICK(nLENGTH, root, top_topo); |
---|
620 | |
---|
621 | TEST_ASSERT_VALID_TREE(root); |
---|
622 | |
---|
623 | AP_tree_edge::destroy(root); |
---|
624 | |
---|
625 | // delete memory allocated by insert() above and lost due to pop()s |
---|
626 | delete edge1_del_manually; |
---|
627 | delete edge2_del_manually; |
---|
628 | |
---|
629 | node_del_manually->forget_origin(); |
---|
630 | node_del_manually->father = NULL; |
---|
631 | node_del_manually->leftson = NULL; |
---|
632 | node_del_manually->rightson = NULL; |
---|
633 | delete node_del_manually; |
---|
634 | } |
---|
635 | } |
---|
636 | |
---|
637 | // @@@ Tests wanted: |
---|
638 | // - NNI |
---|
639 | // - tree optimize |
---|
640 | // - ... |
---|
641 | |
---|
642 | void TEST_calc_bootstraps() { |
---|
643 | PARSIMONY_testenv env("TEST_trees.arb", "ali_5s"); |
---|
644 | TEST_EXPECT_NO_ERROR(env.load_tree("tree_test")); |
---|
645 | |
---|
646 | const char *bs_origi_topo = "(((((((CloTyro3,CloTyro4)'40%',CloTyro2)'0%',CloTyrob)'97%',CloInnoc)'0%',CloBifer)'53%',(((CloButy2,CloButyr)'100%',CloCarni)'33%',CloPaste)'97%')'100%',((((CorAquat,CurCitre)'100%',CorGluta)'17%',CelBiazo)'40%',CytAquat)'100%');"; |
---|
647 | const char *bs_limit_topo = "(((((((CloTyro3,CloTyro4)'87%',CloTyro2)'0%',CloTyrob)'100%',CloInnoc)'87%',CloBifer)'83%',(((CloButy2,CloButyr)'99%',CloCarni)'17%',CloPaste)'56%')'61%',((((CorAquat,CurCitre)'78%',CorGluta)'0%',CelBiazo)'59%',CytAquat)'61%');"; |
---|
648 | const char *bs_estim_topo = "(((((((CloTyro3,CloTyro4)'75%',CloTyro2)'0%',CloTyrob)'100%',CloInnoc)'75%',CloBifer)'78%',(((CloButy2,CloButyr)'99%',CloCarni)'13%',CloPaste)'32%')'53%',((((CorAquat,CurCitre)'74%',CorGluta)'0%',CelBiazo)'56%',CytAquat)'53%');"; |
---|
649 | |
---|
650 | { |
---|
651 | AP_tree_nlen *root = env.tree_root(); |
---|
652 | TEST_REJECT_NULL(root); |
---|
653 | |
---|
654 | AP_tree_edge::initialize(root); // builds edges |
---|
655 | TEST_EXPECT_EQUAL(root, rootNode()); // need tree-access via global 'ap_main' (too much code is based on that) |
---|
656 | |
---|
657 | AP_tree_edge *root_edge = rootEdge(); |
---|
658 | TEST_REJECT_NULL(root_edge); |
---|
659 | |
---|
660 | root->reorder_tree(BIG_BRANCHES_TO_TOP); TEST_EXPECT_NEWICK(nREMARK, root, bs_origi_topo); |
---|
661 | |
---|
662 | root_edge->nni_rek(-1, false, AP_BL_MODE(AP_BL_BL_ONLY|AP_BL_BOOTSTRAP_LIMIT), NULL); root->reorder_tree(BIG_BRANCHES_TO_TOP); TEST_EXPECT_NEWICK(nREMARK, root, bs_limit_topo); |
---|
663 | root_edge->nni_rek(-1, false, AP_BL_MODE(AP_BL_BL_ONLY|AP_BL_BOOTSTRAP_ESTIMATE), NULL); root->reorder_tree(BIG_BRANCHES_TO_TOP); TEST_EXPECT_NEWICK(nREMARK, root, bs_estim_topo); |
---|
664 | |
---|
665 | TEST_EXPECT_EQUAL(env.tree_root(), root); |
---|
666 | AP_tree_edge::destroy(root); |
---|
667 | } |
---|
668 | } |
---|
669 | |
---|
670 | void TEST_tree_remove_add_all() { |
---|
671 | // reproduces crash as described in #527 |
---|
672 | PARSIMONY_testenv env("TEST_trees.arb", "ali_5s"); |
---|
673 | TEST_EXPECT_NO_ERROR(env.load_tree("tree_nj")); |
---|
674 | |
---|
675 | const int LEAFS = 6; |
---|
676 | AP_tree_nlen *leaf[LEAFS]; |
---|
677 | const char *name[LEAFS] = { |
---|
678 | "CloButy2", |
---|
679 | "CloButyr", |
---|
680 | "CytAquat", |
---|
681 | "CorAquat", |
---|
682 | "CurCitre", |
---|
683 | "CorGluta", |
---|
684 | }; |
---|
685 | |
---|
686 | AP_tree_nlen *root = env.tree_root(); |
---|
687 | TEST_REJECT_NULL(root); |
---|
688 | |
---|
689 | for (int i = 0; i<LEAFS; ++i) { |
---|
690 | leaf[i] = root->findLeafNamed(name[i]); |
---|
691 | TEST_REJECT_NULL(leaf[i]); |
---|
692 | } |
---|
693 | |
---|
694 | AP_tree_edge::initialize(root); // builds edges |
---|
695 | TEST_EXPECT_EQUAL(root, rootNode()); // need tree-access via global 'ap_main' (too much code is based on that) |
---|
696 | |
---|
697 | AP_tree_root *troot = leaf[0]->get_tree_root(); |
---|
698 | TEST_REJECT_NULL(troot); |
---|
699 | |
---|
700 | // Note: following loop leaks father nodes and edges |
---|
701 | for (int i = 0; i<LEAFS-1; ++i) { // removing the second to last leaf, "removes" both remaining leafs |
---|
702 | TEST_ASSERT_VALID_TREE(root); |
---|
703 | leaf[i]->remove(); |
---|
704 | TEST_ASSERT_VALID_TREE(leaf[i]); |
---|
705 | } |
---|
706 | leaf[LEAFS-1]->father = NULL; // correct final leaf (not removed regularily) |
---|
707 | |
---|
708 | leaf[0]->initial_insert(leaf[1], troot); |
---|
709 | for (int i = 2; i<LEAFS; ++i) { |
---|
710 | TEST_ASSERT_VALID_TREE(leaf[i-1]); |
---|
711 | TEST_ASSERT_VALID_TREE(leaf[i]); |
---|
712 | leaf[i]->insert(leaf[i-1]); |
---|
713 | } |
---|
714 | } |
---|
715 | |
---|
716 | #endif // UNIT_TESTS |
---|
717 | |
---|
718 | // -------------------------------------------------------------------------------- |
---|