Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
amkozlov committed Dec 22, 2023
2 parents 5f5f03f + 623e364 commit e339652
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 21 deletions.
40 changes: 37 additions & 3 deletions src/algorithm/algo_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
#include "pllmod_algorithm.h"
#include "../pllmod_common.h"

/* constraint tree debugging */
//#define CONS_DEBUG

/* if not defined, branch length optimization will use
* the same starting set of branch lengths for every topology */
#define PLLMOD_SEARCH_GREEDY_BLO
Expand Down Expand Up @@ -733,6 +736,7 @@ static int best_reinsert_edge(pllmod_treeinfo_t * treeinfo,
/* do not re-insert if resulting tree would contradict the constraint */
if (check_cons && !pllmod_treeinfo_constraint_check_spr(treeinfo, p_edge, r_edge))
{
DBG("SKIP incompatible: %u %u\n", j, r_edge->clv_index);
++j;
continue;
}
Expand All @@ -749,6 +753,18 @@ static int best_reinsert_edge(pllmod_treeinfo_t * treeinfo,
retval = algo_utree_regraft(treeinfo, params, p_edge, r_edge);
assert(retval == PLL_SUCCESS);

#ifdef CONS_DEBUG
if (!pllmod_treeinfo_constraint_check_current(treeinfo))
{
pll_utree_show_ascii(treeinfo->root, PLL_UTREE_SHOW_LABEL | PLL_UTREE_SHOW_BRANCH_LENGTH |
PLL_UTREE_SHOW_CLV_INDEX );
printf("Constraint check failed after REGRAFT: %u %u\n", p_edge->clv_index, r_edge->clv_index);
pllmod_set_error(PLLMOD_TREE_ERROR_INVALID_TREE,
"Constraint check failed after applying SPR!");
return PLL_FAILURE;
}
#endif

/* place root at the pruning branch and invalidate CLV at the new root */
pllmod_treeinfo_set_root(treeinfo, p_edge);
pllmod_treeinfo_invalidate_clv(treeinfo, p_edge);
Expand Down Expand Up @@ -947,12 +963,14 @@ static double reinsert_nodes(pllmod_treeinfo_t * treeinfo, pll_unode_t ** nodes,
if (!retval)
return PLL_FAILURE;

#ifdef DEBUG
#ifdef CONS_DEBUG
if (!pllmod_treeinfo_constraint_check_current(treeinfo))
{
// pll_utree_show_ascii(treeinfo->root, PLL_UTREE_SHOW_LABEL | PLL_UTREE_SHOW_BRANCH_LENGTH |
// PLL_UTREE_SHOW_CLV_INDEX );
pll_utree_show_ascii(treeinfo->root, PLL_UTREE_SHOW_LABEL | PLL_UTREE_SHOW_BRANCH_LENGTH |
PLL_UTREE_SHOW_CLV_INDEX );
printf("Constraint check failed after applying SPR: %u %u\n", p_edge->clv_index, best_r_edge->clv_index);
pllmod_set_error(PLLMOD_TREE_ERROR_INVALID_TREE,
"Constraint check failed after applying SPR!");
return PLL_FAILURE;
}
#endif
Expand Down Expand Up @@ -1092,6 +1110,14 @@ PLL_EXPORT double pllmod_algo_spr_round(pllmod_treeinfo_t * treeinfo,
/* reset error */
pll_errno = 0;

/* make sure initial topology is compatible with constraint */
if (!pllmod_treeinfo_constraint_check_current(treeinfo))
{
pllmod_set_error(PLLMOD_TREE_ERROR_INVALID_TREE,
"Constraint check failed before SPR round!");
return PLL_FAILURE;
}

/* allocate brlen buffers */
for (i = 0; i < BRLEN_BUF_COUNT; ++i)
{
Expand Down Expand Up @@ -1155,6 +1181,14 @@ PLL_EXPORT double pllmod_algo_spr_round(pllmod_treeinfo_t * treeinfo,
goto error_exit;
}

/* make sure intermediate topology is compatible with constraint */
if (!pllmod_treeinfo_constraint_check_current(treeinfo))
{
pllmod_set_error(PLLMOD_TREE_ERROR_INVALID_TREE,
"Constraint check failed after reinsert_nodes() in SPR round!");
return PLL_FAILURE;
}

/* in FAST mode, we re-insert a subset of best-scoring subtrees with BLO
* (i.e., in SLOW mode) */
if (!params.thorough && bestnode_list->current > 0)
Expand Down
37 changes: 35 additions & 2 deletions src/tree/utree_constraint.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@

#include "../pllmod_common.h"

static inline unsigned int split_popcount(const pll_split_t bitv,
unsigned int bit_count,
unsigned int split_len)
{
unsigned int split_size = sizeof(pll_split_base_t) * 8;
unsigned int setb = 0;
unsigned int i;

if (!split_len)
split_len = bitv_length(bit_count);

for (i = 0; i < split_len; ++i)
{
setb += (unsigned int) PLL_POPCNT32(bitv[i]);
}

/* IMPORTANT: correct for padding bits in the last element! */
unsigned int split_offset = bit_count % split_size;
if (split_offset)
{
unsigned int mask = (1<<split_offset) - 1;
unsigned int last = bitv[split_len - 1];
/* count set bits in the padding part of the bit vector */
last &= ~mask;
setb -= (unsigned int) PLL_POPCNT32(last);
}

return setb;
}

static inline void invert_split(pll_split_t bitv, unsigned int bit_count)
{
unsigned int split_size = sizeof(pll_split_base_t) * 8;
Expand Down Expand Up @@ -385,7 +415,10 @@ PLL_EXPORT int pllmod_utree_constraint_check_spr(pll_split_set_t * cons_splits,
const pll_split_t prune_split = get_node_split(splits, p_edge->back);
pll_split_t new_split = (pll_split_t) calloc(1, cons_split_len * sizeof(pll_split_base_t));

if (bitv_popcount(prune_split, cons_tip_count, cons_split_len) < cons_tip_count-1)
unsigned int pruned_count = split_popcount(prune_split, cons_tip_count, cons_split_len);
assert(pruned_count <= cons_tip_count);

if (pruned_count < cons_tip_count-1)
{
/* remaining subtree contains at least 2 constrained taxa -> traverse into regraft subtree */
regraft_split = find_nonempty_regraft_split(splits, cons_split_len, prune_split, r_edge);
Expand All @@ -399,7 +432,7 @@ PLL_EXPORT int pllmod_utree_constraint_check_spr(pll_split_set_t * cons_splits,
{
/* remaining subtree contains just 1 constrained taxon -> traverse into pruned subtree */
copy_split(new_split, prune_split, cons_split_len);
invert_split(new_split, cons_split_len);
invert_split(new_split, cons_tip_count);

regraft_split = find_nonempty_regraft_split(splits, cons_split_len, new_split, p_edge);

Expand Down
33 changes: 17 additions & 16 deletions test/runtest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
#
# Copyright (C) 2015 Diego Darriba
#
Expand Down Expand Up @@ -40,9 +40,9 @@
#####################
# Configuration #
#####################
do_memtest = 0 # Evaluate memory leaks
do_memtest = 1 # Evaluate memory leaks
num_replicates = 20 # Number of samples for the speed test
all_args = [6, 4, 0, 1, 2, 3, 4, 6]
all_args = [0, 1, 2, 3, 4, 6]
# 0: No vector / No tip pattern
# 1: No vector / Tip pattern
# 2: AVX / No tip pattern
Expand Down Expand Up @@ -138,7 +138,7 @@ def testFAIL():
def diffOutput(filename):
p2 = Popen(["diff", "tmp", filename], stdout=PIPE)
output = p2.communicate()[0]
return output
return output.decode()

def runSpeedTest(files):
speedtest_header()
Expand Down Expand Up @@ -216,15 +216,15 @@ def runSpeedTest(files):
# Check the output
skipOutput = diffOutput("out/skip.out")

if skipOutput == "":
if not skipOutput:
fancyprint("orange", "\b\b\b\b\b")
testSKIP()
test_ok = -1
break
else:
output = diffOutput("out/"+filename+".out")

if output != "":
if output:
fancyprint("red", " Test failed\n")
call(["mv", "tmp", "result/"+filename+"_"+typestr+"_"+nowstr])
call(["mv", "tmperr", "result/"+filename+"_"+typestr+"_"+nowstr+".err"])
Expand Down Expand Up @@ -259,7 +259,7 @@ def runSpeedTest(files):

success_count = success_count + result_ok*memory_ok

print
print()

fancyprint("yellow", "{:<80}"
.format(" "),True)
Expand Down Expand Up @@ -296,7 +296,7 @@ def runValidation(files):
typestr += "C"

fancyprint("bluebg", "{:<80}"
.format(attribstr.rjust(40 + len(attribstr)/2)), True)
.format(attribstr.rjust(40 + int(len(attribstr)/2))), True)

# Process each test case
fancyprint("yellow", "{:<7} {:<8} {:<18} {:>11} Result {:<17}"
Expand Down Expand Up @@ -338,21 +338,22 @@ def runValidation(files):
# Check the output
skipOutput = diffOutput("out/skip.out")

if skipOutput == "":
if not skipOutput:
testSKIP()
print
print()
result_ok = 1
memory_ok = 1
else:
output = diffOutput("out/"+filename+".out")
if output == "":
if not output:
result_ok = 1
testOK()
os.remove("tmp")
os.remove("tmperr")
else:
testFAIL()
print
print(output)
print()
call(["mv", "tmp", "result/"+filename+"_"+typestr+"_"+nowstr])
call(["mv", "tmperr", "result/"+filename+"_"+typestr+"_"+nowstr+".err"])
continue
Expand All @@ -362,7 +363,7 @@ def runValidation(files):
if (do_memtest == 1 and not filename.endswith("exe")):
# Check memory leaks
p3 = Popen(["./eval_valgrind.sh", "obj/"+filename, nowstr, attrib], stdout=PIPE)
output = p3.communicate()[0]
output = p3.communicate()[0].decode()
deflost = int(output.split(' ')[0])
indlost = int(output.split(' ')[1])
reachable = int(output.split(' ')[2])
Expand Down Expand Up @@ -406,7 +407,7 @@ def runValidation(files):
num_modules = len(test_modules)

print(" %d modules" % num_modules)
print
print()

files = []
for cur_mod in test_modules:
Expand Down Expand Up @@ -444,7 +445,7 @@ def runValidation(files):
success_count = runValidation(files)

# Final summary
print
print()
global_run_time = int(time.time()*1000) - global_start_time
fancyprint("-", "Tests done... it took %d ms" % global_run_time, True)
fancyprint("green", " %d/%d (%3.2f%%) OK"
Expand All @@ -453,4 +454,4 @@ def runValidation(files):
fancyprint("red", " %d/%d (%3.2f%%) FAIL"
% (num_tests-success_count, num_tests, 100 - 100*success_count/num_tests), True)

print
print()

0 comments on commit e339652

Please sign in to comment.