bpp-phyl3 3.0.0
NNITopologySearch.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: The Bio++ Development Group
2//
3// SPDX-License-Identifier: CECILL-2.1
4
8
9#include "../Likelihood/NNIHomogeneousTreeLikelihood.h"
10#include "NNITopologySearch.h"
11
12using namespace bpp;
13
14// From the STL:
15#include <cmath>
16
17using namespace std;
18
19const string NNITopologySearch::FAST = "Fast";
20const string NNITopologySearch::BETTER = "Better";
21const string NNITopologySearch::PHYML = "PhyML";
22
23void NNITopologySearch::notifyAllPerformed(const TopologyChangeEvent& event)
24{
25 searchableTree_->topologyChangePerformed(event);
26 for (size_t i = 0; i < topoListeners_.size(); i++)
27 {
28 topoListeners_[i]->topologyChangePerformed(event);
29 }
30}
31
33{
34 searchableTree_->topologyChangeTested(event);
35 for (size_t i = 0; i < topoListeners_.size(); i++)
36 {
37 topoListeners_[i]->topologyChangeTested(event);
38 }
39}
40
42{
43 searchableTree_->topologyChangeSuccessful(event);
44 for (size_t i = 0; i < topoListeners_.size(); i++)
45 {
46 topoListeners_[i]->topologyChangeSuccessful(event);
47 }
48}
49
51{
52 if (algorithm_ == FAST)
53 searchFast();
54 else if (algorithm_ == BETTER)
56 else if (algorithm_ == PHYML)
58 else
59 throw Exception("Unknown NNI algorithm: " + algorithm_ + ".\n");
60}
61
63{
64 bool test = true;
65 do
66 {
67 TreeTemplate<Node> tree(searchableTree_->topology());
68 vector<Node*> nodes = tree.getNodes();
69
70 vector<Node*> nodesSub = nodes;
71 for (size_t i = nodesSub.size(); i > 0; i--)
72 {
73 // !!! must not reach i==0 because of size_t
74 if (!(nodesSub[i - 1]->hasFather()))
75 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
76 else if (!(nodesSub[i - 1]->getFather()->hasFather()))
77 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
78 }
79
80 // Test all NNIs:
81 test = false;
82 for (size_t i = 0; !test && i < nodesSub.size(); i++)
83 {
84 Node* node = nodesSub[i];
85 double diff = searchableTree_->testNNI(node->getId());
86 if (verbose_ >= 3)
87 {
89 + " at " + TextTools::toString(node->getFather()->getId()),
91 }
92
93 if (diff < 0.)
94 { // Good NNI found...
95 if (verbose_ >= 2)
96 {
98 + " at " + TextTools::toString(node->getFather()->getId()),
100 }
101 searchableTree_->doNNI(node->getId());
102 // Notify:
104 test = true;
105
106 if (verbose_ >= 1)
107 ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
108 }
109 }
110 }
111 while (test);
112}
113
115{
116 bool test = true;
117 do
118 {
119 TreeTemplate<Node> tree(searchableTree_->topology());
120 vector<Node*> nodes = tree.getNodes();
121
122 if (verbose_ >= 3)
123 ApplicationTools::displayTask("Test all possible NNIs...");
124
125 vector<Node*> nodesSub = nodes;
126 for (size_t i = nodesSub.size(); i > 0; i--)
127 { // !!! must not reach i==0 because of size_t
128 if (!(nodesSub[i - 1]->hasFather()))
129 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
130 else if (!(nodesSub[i - 1]->getFather()->hasFather()))
131 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
132 }
133
134 // Test all NNIs:
135 vector<Node*> improving;
136 vector<double> improvement;
138 ApplicationTools::message->endLine();
139 for (size_t i = 0; i < nodesSub.size(); i++)
140 {
141 Node* node = nodesSub[i];
142 double diff = searchableTree_->testNNI(node->getId());
143 if (verbose_ >= 3)
144 {
146 + " at " + TextTools::toString(node->getFather()->getId()),
147 TextTools::toString(diff));
148 }
149
150 if (diff < 0.)
151 {
152 improving.push_back(node);
153 improvement.push_back(diff);
154 }
155 }
156 if (verbose_ >= 3)
158 test = improving.size() > 0;
159 if (test)
160 {
161 size_t nodeMin = VectorTools::whichMin(improvement);
162 Node* node = improving[nodeMin];
163 if (verbose_ >= 2)
165 + " at " + TextTools::toString(node->getFather()->getId()),
166 TextTools::toString(improvement[nodeMin]));
167 searchableTree_->doNNI(node->getId());
168
169 // Notify:
171
172 if (verbose_ >= 1)
173 ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
174 }
175 }
176 while (test);
177}
178
180{
181 bool test = true;
182 do
183 {
184 if (verbose_ >= 3)
185 ApplicationTools::displayTask("Test all possible NNIs...");
186 TreeTemplate<Node> tree(searchableTree_->topology());
187 vector<Node*> nodes = tree.getNodes();
188 vector<Node*> nodesSub = nodes;
189 for (size_t i = nodesSub.size(); i > 0; i--)
190 {
191 // !!! must not reach i==0 because of size_t
192 if (!(nodesSub[i - 1]->hasFather()))
193 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
194 else if (!(nodesSub[i - 1]->getFather()->hasFather()))
195 nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
196 }
197
198 // Test all NNIs:
199 vector<int> improving;
200 vector<Node*> improvingNodes;
201 vector<double> improvement;
203 ApplicationTools::message->endLine();
204 for (size_t i = 0; i < nodesSub.size(); i++)
205 {
206 Node* node = nodesSub[i];
207 double diff = searchableTree_->testNNI(node->getId());
208 if (verbose_ >= 3)
209 {
211 + " at " + TextTools::toString(node->getFather()->getId()),
212 TextTools::toString(diff));
213 }
214
215 if (diff < 0.)
216 {
217 bool ok = true;
218 // Must test for incompatible NNIs...
219 for (size_t j = improving.size(); j > 0; j--)
220 {
221 if (improvingNodes[j - 1]->getFather()->getId() == node->getFather()->getId()
222 || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getFather()->getFather()->getId()
223 || improvingNodes[j - 1]->getId() == node->getFather()->getId() || improvingNodes[j - 1]->getFather()->getId() == node->getId()
224 || improvingNodes[j - 1]->getId() == node->getFather()->getFather()->getId() || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getId()
225 || improvingNodes[j - 1]->getFather()->getId() == node->getFather()->getFather()->getId() || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getFather()->getId())
226 {
227 // These are incompatible NNIs. We only keep the best:
228 if (diff < improvement[j - 1])
229 { // Erase previous node
230 improvingNodes.erase(improvingNodes.begin() + static_cast<ptrdiff_t>(j - 1));
231 improving.erase(improving.begin() + static_cast<ptrdiff_t>(j - 1));
232 improvement.erase(improvement.begin() + static_cast<ptrdiff_t>(j - 1));
233 } // Otherwise forget about this NNI.
234 else
235 {
236 ok = false;
237 }
238 }
239 }
240 if (ok)
241 { // We add this NNI to the list,
242 // by decreasing improvement:
243 size_t pos = improvement.size();
244 for (size_t j = 0; j < improvement.size(); j++)
245 {
246 if (diff < improvement[j])
247 {
248 pos = j; break;
249 }
250 }
251 if (pos < improvement.size())
252 {
253 improvingNodes.insert(improvingNodes.begin() + static_cast<ptrdiff_t>(pos), node);
254 improving.insert(improving.begin() + static_cast<ptrdiff_t>(pos), node->getId());
255 improvement.insert(improvement.begin() + static_cast<ptrdiff_t>(pos), diff);
256 }
257 else
258 {
259 improvingNodes.insert(improvingNodes.end(), node);
260 improving.insert(improving.end(), node->getId());
261 improvement.insert(improvement.end(), diff);
262 }
263 }
264 }
265 }
266 // This array is no more useful.
267 // Moreover, if a backward movement is performed,
268 // the underlying node will not exist anymore...
269 improvingNodes.clear();
270 if (verbose_ >= 3)
272 test = improving.size() > 0;
273 if (test)
274 {
275 double currentValue = searchableTree_->getTopologyValue();
276 bool test2 = true;
277 // Make a backup copy:
278 NNISearchable* backup = dynamic_cast<NNISearchable*>(searchableTree_->clone());
279 do
280 {
281 if (verbose_ >= 1)
282 ApplicationTools::displayMessage("Trying to perform " + TextTools::toString(improving.size()) + " NNI(s).");
283 for (size_t i = 0; i < improving.size(); i++)
284 {
285 int nodeId = improving[i];
286 if (verbose_ >= 2)
287 {
288 ApplicationTools::displayResult(string(" Swapping node ") + TextTools::toString(nodeId)
289 + string(" at ") + TextTools::toString(searchableTree_->topology().getFatherId(nodeId)),
290 TextTools::toString(improvement[i]));
291 }
292 searchableTree_->doNNI(improving[i]);
293 }
294
295 // Notify:
297 if (verbose_ >= 1)
298 ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
299 if (searchableTree_->getTopologyValue() >= currentValue)
300 {
301 // No improvement!
302 // Restore backup:
303 searchableTree_.reset(backup->clone());
304 if (verbose_ >= 1)
305 {
306 ApplicationTools::displayResult("Score >= current score! Moving backward", TextTools::toString(searchableTree_->getTopologyValue()));
307 }
308 // And try doing half of the movements:
309 if (improving.size() == 1)
310 {
311 // Problem! This should have worked!!!
312 throw Exception("NNITopologySearch::searchPhyML. Error, no improving NNI!\n This may be due to a change in parameters between testNNI and doNNI. Check your code!");
313 }
314 size_t n = (size_t)ceil((double)improving.size() / 2.);
315 improving.erase(improving.begin() + static_cast<ptrdiff_t>(n), improving.end());
316 improvement.erase(improvement.begin() + static_cast<ptrdiff_t>(n), improvement.end());
317 }
318 else
319 {
320 test2 = false;
321 }
322 }
323 while (test2);
324 delete backup;
325 // Notify:
327 }
328 }
329 while (test);
330}
static void displayMessage(const std::string &text)
static void displayTask(const std::string &text, bool eof=false)
static std::shared_ptr< OutputStream > message
static void displayTaskDone()
static void displayResult(const std::string &text, const T &result)
Interface for Nearest Neighbor Interchanges algorithms.
Definition: NNISearchable.h:63
virtual NNISearchable * clone() const =0
static const std::string PHYML
std::vector< std::shared_ptr< TopologyListener > > topoListeners_
std::shared_ptr< NNISearchable > searchableTree_
void notifyAllTested(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
void notifyAllPerformed(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
static const std::string FAST
static const std::string BETTER
void search()
Performs the search.
void notifyAllSuccessful(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
The phylogenetic node class.
Definition: Node.h:59
virtual int getId() const
Get the node's id.
Definition: Node.h:170
virtual const Node * getFather() const
Get the father of this node is there is one.
Definition: Node.h:306
Class for notifying new toplogy change events.
The phylogenetic tree class.
Definition: TreeTemplate.h:59
virtual std::vector< const N * > getNodes() const
Definition: TreeTemplate.h:421
static size_t whichMin(const std::vector< T > &v)
std::string toString(T t)
Defines the basic types of data flow nodes.