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 
7 #include <Bpp/Text/TextTools.h>
8 
9 #include "../Likelihood/NNIHomogeneousTreeLikelihood.h"
10 #include "NNITopologySearch.h"
11 
12 using namespace bpp;
13 
14 // From the STL:
15 #include <cmath>
16 
17 using namespace std;
18 
19 const string NNITopologySearch::FAST = "Fast";
20 const string NNITopologySearch::BETTER = "Better";
21 const string NNITopologySearch::PHYML = "PhyML";
22 
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)
55  searchBetter();
56  else if (algorithm_ == PHYML)
57  searchPhyML();
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()),
90  TextTools::toString(diff));
91  }
92 
93  if (diff < 0.)
94  { // Good NNI found...
95  if (verbose_ >= 2)
96  {
97  ApplicationTools::displayResult(" Swapping node " + TextTools::toString(node->getId())
98  + " at " + TextTools::toString(node->getFather()->getId()),
99  TextTools::toString(diff));
100  }
101  searchableTree_->doNNI(node->getId());
102  // Notify:
103  notifyAllPerformed(TopologyChangeEvent());
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;
137  if (verbose_ >= 2 && ApplicationTools::message)
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  {
145  ApplicationTools::displayResult(" Testing node " + TextTools::toString(node->getId())
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)
164  ApplicationTools::displayResult(" Swapping node " + TextTools::toString(node->getId())
165  + " at " + TextTools::toString(node->getFather()->getId()),
166  TextTools::toString(improvement[nodeMin]));
167  searchableTree_->doNNI(node->getId());
168 
169  // Notify:
170  notifyAllPerformed(TopologyChangeEvent());
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;
202  if (verbose_ >= 2 && ApplicationTools::message)
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  {
210  ApplicationTools::displayResult(" Testing node " + TextTools::toString(node->getId())
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:
296  notifyAllTested(TopologyChangeEvent());
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:
326  notifyAllSuccessful(TopologyChangeEvent());
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
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:397
static size_t whichMin(const std::vector< T > &v)
std::string toString(T t)
Defines the basic types of data flow nodes.