File: supertree_variants.hpp

package info (click to toggle)
iqtree 2.0.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 14,620 kB
  • sloc: cpp: 142,571; ansic: 57,789; sh: 275; python: 242; makefile: 95
file content (179 lines) | stat: -rw-r--r-- 6,883 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#ifndef SUPERTREE_VARIANTS_HPP
#define SUPERTREE_VARIANTS_HPP

#include <terraces/constraints.hpp>

#include <terraces/bigint.hpp>
#include <terraces/clamped_uint.hpp>

#include "bipartitions.hpp"
#include "trees_impl.hpp"

namespace terraces {
namespace variants {

template <typename Result>
/**
 * An abstract callback used to control the execution of \ref tree_enumerator.
 * Every callback must declare a return type whose values are aggregated
 * (sum over different bipartitions) and combined (results from left and right subtree).
 * <p>Additionally, the enumeration can be terminated early using \ref fast_return
 * or \ref continue_iteration, if one does not want to iterate over the whole recursion tree.</p>
 */
class abstract_callback {
public:
	/** The result type returned by the @ref tree_enumerator. */
	using result_type = Result;

	/**
	 * Called when a (sub)call begins.
	 * \param leaves The leaf set inspected at the current recursive call.
	 */
	void enter(const ranked_bitvector& leaves) { (void)leaves; }
	/**
	 * Called when a (sub)call finishes.
	 * \param val The result that was either returned from \ref fast_return_value
	 *            or accumulated from the recursive subcalls.
	 * \returns The result that should be passed to the calling function.
	 */
	Result exit(Result val) { return val; }

	/** Returns the result for a single leaf. */
	Result base_one_leaf(index);
	/** Returns the result for two leaves. */
	Result base_two_leaves(index, index);
	/** Returns the result for multiple leaves without constraints. */
	Result base_unconstrained(const ranked_bitvector&);
	/** Returns an empty result. */
	Result null_result() const;

	/**
	 * Called before iterating over the bipartitions to check if we can return early.
	 * This may be used to avoid searching subtrees that are 'not interesting'.
	 * \see fast_return_value
	 * \returns true if and only if we want to skip iterating over the current bipartitions.
	 *          (default: false)
	 */
	bool fast_return(const bipartitions&) { return false; }
	/** Returns the result to be returned in case \ref fast_return is true. */
	Result fast_return_value(const bipartitions&);

	/**
	 * Called when we begin iterating over the bipartitions.
	 * \param
	 * \returns The initial value for the result used for accumulation
	 *          - something equivalent to 0.
	 *          (default: value-initialization of @p Result)
	 */
	Result begin_iteration(const bipartitions& bip_it, const bitvector& c_occ,
	                       const constraints& c) {
		(void)bip_it;
		(void)c_occ;
		(void)c;
		return Result{};
	}
	/**
	 * Returns true iff the iteration should continue.
	 * This may be used to stop iterating after a sufficient number of results have been
	 * accumulated.
	 * \returns true if and only if we want to continue iterating over
	 */
	bool continue_iteration(Result) { return true; }
	/** Called when an iteration step begins. */
	void step_iteration(const bipartitions&, index) {}
	/** Called when the last iteration step has finished. */
	void finish_iteration() {}
	/** Called before descending into the left subset. */
	void left_subcall() {}
	/** Called before descending into the right subset. */
	void right_subcall() {}
	/**
	 * Accumulates the results from multiple bipartitions.
	 * \param accumulator The current value for the accumulator.
	 * \param value The value to be added to the accumulator.
	 * \returns The new accumulator value.
	 */
	Result accumulate(Result accumulator, Result value);
	/**
	 * Combines the results from two subcalls.
	 * \param left The result from the left subcall.
	 * \param right The result from the right subcall.
	 * \returns The combined result.
	 */
	Result combine(Result left, Result right);
};

template <typename Number>
/**
 * A callback implementation that counts all trees using the \p Number type.
 * It gives correct results if the number type is integer and doesn't overflow.
 */
class count_callback : public abstract_callback<Number> {
public:
	using return_type = typename abstract_callback<Number>::result_type;
	// only one choice for a single leaf
	return_type base_one_leaf(index) { return 1; }
	// only one choice for two leaves
	return_type base_two_leaves(index, index) { return 1; }
	// (#unrooted trees) choices for leaves with no constraints
	return_type base_unconstrained(const ranked_bitvector& leaves) {
		return count_unrooted_trees<return_type>(leaves.count());
	}
	return_type null_result() const { return 0; }

	// The number of bipartitions gives a lower bound on the number of trees.
	index fast_return_value(const bipartitions& bip_it) { return bip_it.num_bip(); }

	// Multiple choices are counted independently
	return_type accumulate(return_type acc, return_type val) { return acc + val; }
	// Choices from two the subtrees can be combined in any possible way
	return_type combine(return_type left, return_type right) { return left * right; }
};

/**
 * A callback implementation that counts all trees using \ref clamped_uint.
 * Thus, the result will be clamped at the maximal value for uint64_t,
 * eliminating the need to fully walk through the recursion.
 */
class clamped_count_callback : public count_callback<clamped_uint> {
public:
	using return_type = typename count_callback<clamped_uint>::result_type;
	// No need to keep counting if we already overflowed.
	bool continue_iteration(return_type result) { return !result.is_clamped(); }
};

/**
 * A callback implementation that returns a simple lower bound to the number
 * of trees compatible with the given constraints.
 * This allows to quickly check whether a tree lies on a phylogenetic terrace.
 * It will always run faster than callbacks that enumerate all possible trees.
 */
class check_callback : public abstract_callback<index> {
public:
	using return_type = index;

	// No choices for a single leaf
	index base_one_leaf(index) { return 1; }
	// No choices for two leaves
	index base_two_leaves(index, index) { return 1; }
	// There are at least two possible trees if we have at least three unconstrained leaves
	index base_unconstrained(const ranked_bitvector&) { return 2; }
	return_type null_result() const { return 0; }

	/* Since we assume there are no incompatible constraints,
	 * every subcall will return at least 1, so the number of bipartitions
	 * gives a simple lower bound for the number of trees. */
	bool fast_return(const bipartitions& bip_it) { return bip_it.num_bip() > 1; }
	index fast_return_value(const bipartitions& bip_it) { return bip_it.num_bip(); }

	/* No need to keep on counting if we already know we are on a terrace. */
	bool continue_iteration(index acc) { return acc < 2; }

	index accumulate(index acc, index val) { return acc + val; }
	index combine(index left, index right) { return left * right; }
};

} // namespace variants
} // namespace terraces

#endif // SUPERTREE_VARIANTS_HPP