bpp-phyl  2.2.0
NNITopologySearch.cpp
Go to the documentation of this file.
1 //
2 // File: NNITopologySearch.cpp
3 // Created by: Julien Dutheil
4 // Created on: Wed Oct 12 10:52 2005
5 //
6 
7 /*
8  Copyright or © or Copr. Bio++ Development Team, (November 16, 2004)
9 
10  This software is a computer program whose purpose is to provide classes
11  for phylogenetic data analysis.
12 
13  This software is governed by the CeCILL license under French law and
14  abiding by the rules of distribution of free software. You can use,
15  modify and/ or redistribute the software under the terms of the CeCILL
16  license as circulated by CEA, CNRS and INRIA at the following URL
17  "http://www.cecill.info".
18 
19  As a counterpart to the access to the source code and rights to copy,
20  modify and redistribute granted by the license, users are provided only
21  with a limited warranty and the software's author, the holder of the
22  economic rights, and the successive licensors have only limited
23  liability.
24 
25  In this respect, the user's attention is drawn to the risks associated
26  with loading, using, modifying and/or developing or reproducing the
27  software by the user in light of its specific status of free software,
28  that may mean that it is complicated to manipulate, and that also
29  therefore means that it is reserved for developers and experienced
30  professionals having in-depth computer knowledge. Users are therefore
31  encouraged to load and test the software's suitability as regards their
32  requirements in conditions enabling the security of their systems and/or
33  data to be ensured and, more generally, to use and operate it in the
34  same conditions as regards security.
35 
36  The fact that you are presently reading this means that you have had
37  knowledge of the CeCILL license and that you accept its terms.
38  */
39 
40 #include "NNITopologySearch.h"
42 
43 #include <Bpp/Text/TextTools.h>
44 #include <Bpp/App/ApplicationTools.h>
45 #include <Bpp/Numeric/VectorTools.h>
46 
47 using namespace bpp;
48 
49 // From the STL:
50 #include <cmath>
51 
52 using namespace std;
53 
54 const string NNITopologySearch::FAST = "Fast";
55 const string NNITopologySearch::BETTER = "Better";
56 const string NNITopologySearch::PHYML = "PhyML";
57 
59 {
60  searchableTree_->topologyChangePerformed(event);
61  for (size_t i = 0; i < topoListeners_.size(); i++)
62  {
63  topoListeners_[i]->topologyChangePerformed(event);
64  }
65 }
66 
68 {
69  searchableTree_->topologyChangeTested(event);
70  for (size_t i = 0; i < topoListeners_.size(); i++)
71  {
72  topoListeners_[i]->topologyChangeTested(event);
73  }
74 }
75 
77 {
78  searchableTree_->topologyChangeSuccessful(event);
79  for (size_t i = 0; i < topoListeners_.size(); i++)
80  {
81  topoListeners_[i]->topologyChangeSuccessful(event);
82  }
83 }
84 
85 void NNITopologySearch::search() throw (Exception)
86 {
87  if (algorithm_ == FAST)
88  searchFast();
89  else if (algorithm_ == BETTER)
90  searchBetter();
91  else if (algorithm_ == PHYML)
92  searchPhyML();
93  else
94  throw Exception("Unknown NNI algorithm: " + algorithm_ + ".\n");
95 }
96 
97 void NNITopologySearch::searchFast() throw (Exception)
98 {
99  bool test = true;
100  do
101  {
102  TreeTemplate<Node> tree(searchableTree_->getTopology());
103  vector<Node*> nodes = tree.getNodes();
104 
105  vector<Node*> nodesSub = nodes;
106  for (size_t i = nodesSub.size(); i > 0; i--)
107  {
108  // !!! must not reach i==0 because of size_t
109  if (!(nodesSub[i - 1]->hasFather()))
110  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
111  else if (!(nodesSub[i - 1]->getFather()->hasFather()))
112  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
113  }
114 
115  // Test all NNIs:
116  test = false;
117  for (size_t i = 0; !test && i < nodesSub.size(); i++)
118  {
119  Node* node = nodesSub[i];
120  double diff = searchableTree_->testNNI(node->getId());
121  if (verbose_ >= 3)
122  {
123  ApplicationTools::displayResult(" Testing node " + TextTools::toString(node->getId())
124  + " at " + TextTools::toString(node->getFather()->getId()),
125  TextTools::toString(diff));
126  }
127 
128  if (diff < 0.)
129  { // Good NNI found...
130  if (verbose_ >= 2)
131  {
132  ApplicationTools::displayResult(" Swapping node " + TextTools::toString(node->getId())
133  + " at " + TextTools::toString(node->getFather()->getId()),
134  TextTools::toString(diff));
135  }
136  searchableTree_->doNNI(node->getId());
137  // Notify:
138  notifyAllPerformed(TopologyChangeEvent());
139  test = true;
140 
141  if (verbose_ >= 1)
142  ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
143  }
144  }
145  }
146  while (test);
147 }
148 
149 void NNITopologySearch::searchBetter() throw (Exception)
150 {
151  bool test = true;
152  do
153  {
154  TreeTemplate<Node> tree(searchableTree_->getTopology());
155  vector<Node*> nodes = tree.getNodes();
156 
157  if (verbose_ >= 3)
158  ApplicationTools::displayTask("Test all possible NNIs...");
159 
160  vector<Node*> nodesSub = nodes;
161  for (size_t i = nodesSub.size(); i > 0; i--)
162  { // !!! must not reach i==0 because of size_t
163  if (!(nodesSub[i - 1]->hasFather()))
164  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
165  else if (!(nodesSub[i - 1]->getFather()->hasFather()))
166  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
167  }
168 
169  // Test all NNIs:
170  vector<Node*> improving;
171  vector<double> improvement;
172  if (verbose_ >= 2 && ApplicationTools::message)
173  ApplicationTools::message->endLine();
174  for (size_t i = 0; i < nodesSub.size(); i++)
175  {
176  Node* node = nodesSub[i];
177  double diff = searchableTree_->testNNI(node->getId());
178  if (verbose_ >= 3)
179  {
180  ApplicationTools::displayResult(" Testing node " + TextTools::toString(node->getId())
181  + " at " + TextTools::toString(node->getFather()->getId()),
182  TextTools::toString(diff));
183  }
184 
185  if (diff < 0.)
186  {
187  improving.push_back(node);
188  improvement.push_back(diff);
189  }
190  }
191  if (verbose_ >= 3)
192  ApplicationTools::displayTaskDone();
193  test = improving.size() > 0;
194  if (test)
195  {
196  size_t nodeMin = VectorTools::whichMin(improvement);
197  Node* node = improving[nodeMin];
198  if (verbose_ >= 2)
199  ApplicationTools::displayResult(" Swapping node " + TextTools::toString(node->getId())
200  + " at " + TextTools::toString(node->getFather()->getId()),
201  TextTools::toString(improvement[nodeMin]));
202  searchableTree_->doNNI(node->getId());
203 
204  // Notify:
205  notifyAllPerformed(TopologyChangeEvent());
206 
207  if (verbose_ >= 1)
208  ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
209  }
210  }
211  while (test);
212 }
213 
214 void NNITopologySearch::searchPhyML() throw (Exception)
215 {
216  bool test = true;
217  do
218  {
219  if (verbose_ >= 3)
220  ApplicationTools::displayTask("Test all possible NNIs...");
221  TreeTemplate<Node> tree(searchableTree_->getTopology());
222  vector<Node*> nodes = tree.getNodes();
223  vector<Node*> nodesSub = nodes;
224  for (size_t i = nodesSub.size(); i > 0; i--)
225  {
226  // !!! must not reach i==0 because of size_t
227  if (!(nodesSub[i - 1]->hasFather()))
228  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove root node.
229  else if (!(nodesSub[i - 1]->getFather()->hasFather()))
230  nodesSub.erase(nodesSub.begin() + static_cast<ptrdiff_t>(i - 1)); // Remove son of root node.
231  }
232 
233  // Test all NNIs:
234  vector<int> improving;
235  vector<Node*> improvingNodes;
236  vector<double> improvement;
237  if (verbose_ >= 2 && ApplicationTools::message)
238  ApplicationTools::message->endLine();
239  for (size_t i = 0; i < nodesSub.size(); i++)
240  {
241  Node* node = nodesSub[i];
242  double diff = searchableTree_->testNNI(node->getId());
243  if (verbose_ >= 3)
244  {
245  ApplicationTools::displayResult(" Testing node " + TextTools::toString(node->getId())
246  + " at " + TextTools::toString(node->getFather()->getId()),
247  TextTools::toString(diff));
248  }
249 
250  if (diff < 0.)
251  {
252  bool ok = true;
253  // Must test for incompatible NNIs...
254  for (size_t j = improving.size(); j > 0; j--)
255  {
256  if (improvingNodes[j - 1]->getFather()->getId() == node->getFather()->getId()
257  || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getFather()->getFather()->getId()
258  || improvingNodes[j - 1]->getId() == node->getFather()->getId() || improvingNodes[j - 1]->getFather()->getId() == node->getId()
259  || improvingNodes[j - 1]->getId() == node->getFather()->getFather()->getId() || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getId()
260  || improvingNodes[j - 1]->getFather()->getId() == node->getFather()->getFather()->getId() || improvingNodes[j - 1]->getFather()->getFather()->getId() == node->getFather()->getId())
261  {
262  // These are incompatible NNIs. We only keep the best:
263  if (diff < improvement[j - 1])
264  { // Erase previous node
265  improvingNodes.erase(improvingNodes.begin() + static_cast<ptrdiff_t>(j - 1));
266  improving.erase(improving.begin() + static_cast<ptrdiff_t>(j - 1));
267  improvement.erase(improvement.begin() + static_cast<ptrdiff_t>(j - 1));
268  } // Otherwise forget about this NNI.
269  else
270  {
271  ok = false;
272  }
273  }
274  }
275  if (ok)
276  { // We add this NNI to the list,
277  // by decreasing improvement:
278  size_t pos = improvement.size();
279  for (size_t j = 0; j < improvement.size(); j++)
280  {
281  if (diff < improvement[j])
282  {
283  pos = j; break;
284  }
285  }
286  if (pos < improvement.size())
287  {
288  improvingNodes.insert(improvingNodes.begin() + static_cast<ptrdiff_t>(pos), node);
289  improving.insert(improving.begin() + static_cast<ptrdiff_t>(pos), node->getId());
290  improvement.insert(improvement.begin() + static_cast<ptrdiff_t>(pos), diff);
291  }
292  else
293  {
294  improvingNodes.insert(improvingNodes.end(), node);
295  improving.insert(improving.end(), node->getId());
296  improvement.insert(improvement.end(), diff);
297  }
298  }
299  }
300  }
301  // This array is no more useful.
302  // Moreover, if a backward movement is performed,
303  // the underlying node will not exist anymore...
304  improvingNodes.clear();
305  if (verbose_ >= 3)
306  ApplicationTools::displayTaskDone();
307  test = improving.size() > 0;
308  if (test)
309  {
310  double currentValue = searchableTree_->getTopologyValue();
311  bool test2 = true;
312  // Make a backup copy:
313  NNISearchable* backup = dynamic_cast<NNISearchable*>(searchableTree_->clone());
314  do
315  {
316  if (verbose_ >= 1)
317  ApplicationTools::displayMessage("Trying to perform " + TextTools::toString(improving.size()) + " NNI(s).");
318  for (size_t i = 0; i < improving.size(); i++)
319  {
320  int nodeId = improving[i];
321  if (verbose_ >= 2)
322  {
323  ApplicationTools::displayResult(string(" Swapping node ") + TextTools::toString(nodeId)
324  + string(" at ") + TextTools::toString(searchableTree_->getTopology().getFatherId(nodeId)),
325  TextTools::toString(improvement[i]));
326  }
327  searchableTree_->doNNI(improving[i]);
328  }
329 
330  // Notify:
331  notifyAllTested(TopologyChangeEvent());
332  if (verbose_ >= 1)
333  ApplicationTools::displayResult(" Current value", TextTools::toString(searchableTree_->getTopologyValue(), 10));
334  if (searchableTree_->getTopologyValue() >= currentValue)
335  {
336  // No improvement!
337  // Restore backup:
338  delete searchableTree_;
339  searchableTree_ = dynamic_cast<NNISearchable*>(backup->clone());
340  if (verbose_ >= 1)
341  {
342  ApplicationTools::displayResult("Score >= current score! Moving backward", TextTools::toString(searchableTree_->getTopologyValue()));
343  }
344  // And try doing half of the movements:
345  if (improving.size() == 1)
346  {
347  // Problem! This should have worked!!!
348  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!");
349  }
350  size_t n = (size_t)ceil((double)improving.size() / 2.);
351  improving.erase(improving.begin() + static_cast<ptrdiff_t>(n), improving.end());
352  improvement.erase(improvement.begin() + static_cast<ptrdiff_t>(n), improvement.end());
353  }
354  else
355  {
356  test2 = false;
357  }
358  }
359  while (test2);
360  delete backup;
361  // Notify:
362  notifyAllSuccessful(TopologyChangeEvent());
363  }
364  }
365  while (test);
366 }
367 
void notifyAllTested(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
static const std::string FAST
void search()
Performs the search.
void notifyAllPerformed(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
STL namespace.
Interface for Nearest Neighbor Interchanges algorithms.
Definition: NNISearchable.h:96
The phylogenetic tree class.
virtual const Node * getFather() const
Get the father of this node is there is one.
Definition: Node.h:339
virtual int getId() const
Get the node&#39;s id.
Definition: Node.h:203
virtual NNISearchable * clone() const =0
Class for notifying new toplogy change events.
The phylogenetic node class.
Definition: Node.h:90
static const std::string BETTER
static const std::string PHYML
void notifyAllSuccessful(const TopologyChangeEvent &event)
Process a TopologyChangeEvent to all listeners.
virtual std::vector< const N * > getNodes() const
Definition: TreeTemplate.h:411