bpp-core3  3.0.0
LogsumHmmLikelihood.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: The Bio++ Development Group
2 //
3 // SPDX-License-Identifier: CECILL-2.1
4 
5 #include "LogsumHmmLikelihood.h"
6 
7 // from the STL:
8 #include <iostream>
9 #include <algorithm>
10 using namespace bpp;
11 using namespace std;
12 
14  std::shared_ptr<HmmStateAlphabet> hiddenAlphabet,
15  std::shared_ptr<HmmTransitionMatrix> transitionMatrix,
16  std::shared_ptr<HmmEmissionProbabilities> emissionProbabilities,
17  const std::string& prefix) :
19  AbstractParametrizable(prefix),
20  hiddenAlphabet_(hiddenAlphabet),
21  transitionMatrix_(transitionMatrix),
22  emissionProbabilities_(emissionProbabilities),
23  logLikelihood_(),
24  partialLogLikelihoods_(),
25  logLik_(),
26  dLogLikelihood_(),
27  partialDLogLikelihoods_(),
28  d2LogLikelihood_(),
29  partialD2LogLikelihoods_(),
30  backLogLikelihood_(),
31  backLogLikelihoodUpToDate_(false),
32  breakPoints_(),
33  nbStates_(),
34  nbSites_()
35 {
36  if (!hiddenAlphabet)
37  throw Exception("LogsumHmmLikelihood: null pointer passed for HmmStateAlphabet.");
38  if (!transitionMatrix)
39  throw Exception("LogsumHmmLikelihood: null pointer passed for HmmTransitionMatrix.");
40  if (!emissionProbabilities)
41  throw Exception("LogsumHmmLikelihood: null pointer passed for HmmEmissionProbabilities.");
42 
43  if (!hiddenAlphabet_->worksWith(transitionMatrix_->hmmStateAlphabet()))
44  throw Exception("LogsumHmmLikelihood: HmmTransitionMatrix and HmmEmissionProbabilities should point toward the same HmmStateAlphabet object.");
45  if (!hiddenAlphabet_->worksWith(emissionProbabilities_->hmmStateAlphabet()))
46  throw Exception("LogsumHmmLikelihood: HmmTransitionMatrix and HmmEmissionProbabilities should point toward the same HmmStateAlphabet object.");
47  nbStates_ = hiddenAlphabet_->getNumberOfStates();
48  nbSites_ = emissionProbabilities_->getNumberOfPositions();
49 
50  // Manage parameters:
51  addParameters_(hiddenAlphabet_->getParameters());
52  addParameters_(transitionMatrix_->getParameters());
53  addParameters_(emissionProbabilities_->getParameters());
54 
55  // Init arrays:
57 
58  // Compute:
60 }
61 
62 void LogsumHmmLikelihood::setNamespace(const std::string& nameSpace)
63 {
65 
66  hiddenAlphabet_->setNamespace(nameSpace);
67  transitionMatrix_->setNamespace(nameSpace);
68  emissionProbabilities_->setNamespace(nameSpace);
69 }
70 
72 {
73  dVariable_ = "";
74  d2Variable_ = "";
75 
76  bool alphabetChanged = hiddenAlphabet_->matchParametersValues(pl);
77  bool transitionsChanged = transitionMatrix_->matchParametersValues(pl);
78  bool emissionChanged = emissionProbabilities_->matchParametersValues(pl);
79  // these lines are necessary because the transitions and emissions can depend on the alphabet.
80  // we could use a StateChangeEvent, but this would result in computing some calculations twice in some cases
81  // (when both the alphabet and other parameter changed).
82  if (alphabetChanged && !transitionsChanged)
83  transitionMatrix_->setParametersValues(transitionMatrix_->getParameters());
84  if (alphabetChanged && !emissionChanged)
85  emissionProbabilities_->setParametersValues(emissionProbabilities_->getParameters());
86 
89 }
90 
92 {
94 }
95 
96 /***************************************************************************************************************************/
97 
99 {
100  double x, a;
101  vector<double> logTrans(nbStates_ * nbStates_);
102 
103  // Transition probabilities:
104  for (size_t i = 0; i < nbStates_; i++)
105  {
106  size_t ii = i * nbStates_;
107  for (size_t j = 0; j < nbStates_; j++)
108  {
109  logTrans[ii + j] = log(transitionMatrix_->Pij(j, i));
110  }
111  }
112 
113  // Initialisation:
114  const vector<double>* emissions = &(*emissionProbabilities_)(0);
115 
116  for (size_t j = 0; j < nbStates_; j++)
117  {
118  size_t jj = j * nbStates_;
119  x = logTrans[jj] + log(transitionMatrix_->getEquilibriumFrequencies()[0]);
120 
121  for (size_t k = 1; k < nbStates_; k++)
122  {
123  a = logTrans[k + jj] + log(transitionMatrix_->getEquilibriumFrequencies()[k]);
124  x = NumTools::logsum(x, a);
125  }
126 
127  logLikelihood_[j] = log((*emissions)[j]) + x;
128  }
129 
130  // Recursion:
131  size_t nextBrkPt = nbSites_; // next break point
132  vector<size_t>::const_iterator bpIt = breakPoints_.begin();
133  if (bpIt != breakPoints_.end())
134  nextBrkPt = *bpIt;
135  partialLogLikelihoods_.clear();
136 
137  for (size_t i = 1; i < nbSites_; i++)
138  {
139  size_t ii = i * nbStates_;
140  size_t iip = (i - 1) * nbStates_;
141  emissions = &(*emissionProbabilities_)(i);
142  if (i < nextBrkPt)
143  {
144  for (size_t j = 0; j < nbStates_; j++)
145  {
146  size_t jj = j * nbStates_;
147  x = logTrans[jj] + logLikelihood_[iip];
148  for (size_t k = 1; k < nbStates_; k++)
149  {
150  a = logTrans[jj + k] + logLikelihood_[iip + k];
151  x = NumTools::logsum(x, a);
152  }
153  logLikelihood_[ii + j] = log((*emissions)[j]) + x;
154  }
155  }
156  else // Reset markov chain:
157  {
158  // Termination of previous segment:
159  double tmpLog = logLikelihood_[(i - 1) * nbStates_];
160  for (size_t k = 1; k < nbStates_; k++)
161  {
162  tmpLog = NumTools::logsum(tmpLog, logLikelihood_[(i - 1) * nbStates_ + k]);
163  }
164  partialLogLikelihoods_.push_back(tmpLog);
165 
166  for (size_t j = 0; j < nbStates_; j++)
167  {
168  size_t jj = j * nbStates_;
169  x = logTrans[jj] + log(transitionMatrix_->getEquilibriumFrequencies()[0]);
170  for (size_t k = 1; k < nbStates_; k++)
171  {
172  a = logTrans[jj + k] + log(transitionMatrix_->getEquilibriumFrequencies()[k]);
173  x = NumTools::logsum(x, a);
174  }
175  logLikelihood_[ii + j] = log((*emissions)[j]) + x;
176  }
177  bpIt++;
178  if (bpIt != breakPoints_.end())
179  nextBrkPt = *bpIt;
180  else
181  nextBrkPt = nbSites_;
182  }
183  }
184 
185  // Termination:
186  double tmpLog = logLikelihood_[(nbSites_ - 1) * nbStates_];
187  for (size_t k = 1; k < nbStates_; k++)
188  {
189  tmpLog = NumTools::logsum(tmpLog, logLikelihood_[(nbSites_ - 1) * nbStates_ + k]);
190  }
191  partialLogLikelihoods_.push_back(tmpLog);
192 
193  // Compute likelihood:
194  logLik_ = 0;
195  vector<double> copy = partialLogLikelihoods_; // We need to keep the original order for posterior decoding.
196  sort(copy.begin(), copy.end());
197  for (size_t i = copy.size(); i > 0; --i)
198  {
199  logLik_ += copy[i - 1];
200  }
201 }
202 
203 /***************************************************************************************************************************/
204 
206 {
207  if (backLogLikelihood_.size() == 0)
208  {
210  for (size_t i = 0; i < nbSites_; i++)
211  {
212  backLogLikelihood_[i].resize(nbStates_);
213  }
214  }
215 
216  double x;
217 
218  // Transition probabilities:
219  vector<double> logTrans(nbStates_ * nbStates_);
220  for (size_t i = 0; i < nbStates_; i++)
221  {
222  size_t ii = i * nbStates_;
223  for (size_t j = 0; j < nbStates_; j++)
224  {
225  logTrans[ii + j] = log(transitionMatrix_->Pij(i, j));
226  }
227  }
228 
229 
230  // Initialisation:
231  const vector<double>* emissions = 0;
232  size_t nextBrkPt = 0; // next break point
233  vector<size_t>::const_reverse_iterator bpIt = breakPoints_.rbegin();
234  if (bpIt != breakPoints_.rend())
235  nextBrkPt = *bpIt;
236 
237  for (size_t k = 0; k < nbStates_; k++)
238  {
239  backLogLikelihood_[nbSites_ - 1][k] = 0.;
240  }
241 
242  // Recursion:
243  for (size_t i = nbSites_ - 1; i > 0; i--)
244  {
245  emissions = &(*emissionProbabilities_)(i);
246  if (i > nextBrkPt)
247  {
248  for (size_t j = 0; j < nbStates_; j++)
249  {
250  size_t jj = j * nbStates_;
251  x = log((*emissions)[0]) + logTrans[jj] + backLogLikelihood_[i][0];
252  for (size_t k = 1; k < nbStates_; k++)
253  {
254  x = NumTools::logsum(x, log((*emissions)[k]) + logTrans[jj + k] + backLogLikelihood_[i][k]);
255  }
256  backLogLikelihood_[i - 1][j] = x;
257  }
258  }
259  else // Reset markov chain
260  {
261  for (unsigned int j = 0; j < nbStates_; j++)
262  {
263  backLogLikelihood_[i - 1][j] = 0.;
264  }
265  bpIt++;
266  if (bpIt != breakPoints_.rend())
267  nextBrkPt = *bpIt;
268  else
269  nextBrkPt = 0;
270  }
271  }
272 
274 }
275 
276 
277 /***************************************************************************************************************************/
278 
280 {
282  double x = 0;
283  for (size_t i = 0; i < nbStates_; i++)
284  {
285  x += probs[i] * (*emissionProbabilities_)(site, i);
286  }
287 
288  return x;
289 }
290 
292 {
293  std::vector< std::vector<double>> vv;
295 
296  Vdouble ret(nbSites_);
297  for (size_t i = 0; i < nbSites_; i++)
298  {
299  ret[i] = 0;
300  for (size_t j = 0; j < nbStates_; j++)
301  {
302  ret[i] += vv[i][j] * (*emissionProbabilities_)(i, j);
303  }
304  }
305 
306  return ret;
307 }
308 
309 
310 /***************************************************************************************************************************/
311 
313 {
316 
317  Vdouble probs(nbStates_);
318 
319  vector<size_t>::const_iterator bpIt = breakPoints_.begin();
320  vector<double>::const_iterator logLikIt = partialLogLikelihoods_.begin();
321  while (bpIt != breakPoints_.end())
322  {
323  if (site >= (*bpIt))
324  logLikIt++;
325  else
326  break;
327  bpIt++;
328  }
329 
330  for (size_t j = 0; j < nbStates_; j++)
331  {
332  probs[j] = exp(logLikelihood_[site * nbStates_ + j] + backLogLikelihood_[site][j] - *logLikIt);
333  }
334 
335  return probs;
336 }
337 
338 void LogsumHmmLikelihood::getHiddenStatesPosteriorProbabilities(std::vector< std::vector<double>>& probs, bool append) const
339 {
340  size_t offset = append ? probs.size() : 0;
341  probs.resize(offset + nbSites_);
342  for (size_t i = 0; i < nbSites_; i++)
343  {
344  probs[offset + i].resize(nbStates_);
345  }
346 
349 
350  size_t nextBrkPt = nbSites_; // next break point
351  vector<size_t>::const_iterator bpIt = breakPoints_.begin();
352  if (bpIt != breakPoints_.end())
353  nextBrkPt = *bpIt;
354 
355  vector<double>::const_iterator logLikIt = partialLogLikelihoods_.begin();
356  for (size_t i = 0; i < nbSites_; i++)
357  {
358  if (i == nextBrkPt)
359  {
360  logLikIt++;
361  bpIt++;
362  if (bpIt != breakPoints_.end())
363  nextBrkPt = *bpIt;
364  else
365  nextBrkPt = nbSites_;
366  }
367 
368  size_t ii = i * nbStates_;
369  for (size_t j = 0; j < nbStates_; j++)
370  {
371  probs[offset + i][j] = exp(logLikelihood_[ii + j] + backLogLikelihood_[i][j] - *logLikIt);
372  }
373  }
374 }
375 
376 /***************************************************************************************************************************/
377 
379 {
380  // Init arrays:
381  if (dLogLikelihood_.size() == 0)
382  {
383  dLogLikelihood_.resize(nbSites_);
384  for (size_t i = 0; i < nbSites_; i++)
385  {
386  dLogLikelihood_[i].resize(nbStates_);
387  }
388  }
389 
390  partialDLogLikelihoods_.clear();
391 
392  vector<double> num(nbStates_), num2(nbStates_);
393 
394  // Transition probabilities:
395  const ColMatrix<double> trans(transitionMatrix_->getPij());
396 
397  // Initialisation:
398  const vector<double>* emissions = &(*emissionProbabilities_)(0);
399  const vector<double>* dEmissions = &emissionProbabilities_->getDEmissionProbabilities(0);
400 
401  for (size_t j = 0; j < nbStates_; j++)
402  {
403  dLogLikelihood_[0][j] = (*dEmissions)[j] / (*emissions)[j];
404  }
405 
406  // Recursion:
407  size_t nextBrkPt = nbSites_; // next break point
408  vector<size_t>::const_iterator bpIt = breakPoints_.begin();
409  if (bpIt != breakPoints_.end())
410  nextBrkPt = *bpIt;
411  partialDLogLikelihoods_.clear();
412 
413  for (size_t i = 1; i < nbSites_; i++)
414  {
415  size_t iip = (i - 1) * nbStates_;
416 
417  emissions = &(*emissionProbabilities_)(i);
418  dEmissions = &emissionProbabilities_->getDEmissionProbabilities(i);
419 
420  for (size_t kp = 0; kp < nbStates_; kp++)
421  {
422  num[kp] = logLikelihood_[iip + kp];
423  }
424 
425  num -= num[VectorTools::whichMax(num)];
426 
427  if (i < nextBrkPt)
428  {
429  for (size_t j = 0; j < nbStates_; j++)
430  {
431  num2 = dLogLikelihood_[i - 1] * trans.getCol(j);
432 
433  dLogLikelihood_[i][j] = (*dEmissions)[j] / (*emissions)[j] + VectorTools::sumExp(num, num2) / VectorTools::sumExp(num, trans.getCol(j));
434  }
435  }
436  else // Reset markov chain:
437  {
438  // Termination of previous segment
439 
441 
442  for (size_t j = 0; j < nbStates_; j++)
443  {
444  dLogLikelihood_[i][j] = (*dEmissions)[j] / (*emissions)[j];
445  }
446 
447  bpIt++;
448  if (bpIt != breakPoints_.end())
449  nextBrkPt = *bpIt;
450  else
451  nextBrkPt = nbSites_;
452  }
453  }
454 
455  // Termination:
456  for (size_t kp = 0; kp < nbStates_; kp++)
457  {
458  num[kp] = logLikelihood_[nbStates_ * (nbSites_ - 1) + kp];
459  }
460 
461  num -= num[VectorTools::whichMax(num)];
462 
464 
465  // Compute dLogLikelihood
466 
467  dLogLik_ = 0;
468  vector<double> copy = partialDLogLikelihoods_; // We need to keep the original order for posterior decoding.
469  sort(copy.begin(), copy.end());
470  for (size_t i = copy.size(); i > 0; --i)
471  {
472  dLogLik_ += copy[i - 1];
473  }
474 }
475 
477 {
478  return partialDLogLikelihoods_[site];
479 }
480 
481 /***************************************************************************************************************************/
482 
484 {
485  // Make sure that Dlikelihoods are correctly computed
487 
488  // Init arrays:
489  if (d2LogLikelihood_.size() == 0)
490  {
491  d2LogLikelihood_.resize(nbSites_);
492  for (size_t i = 0; i < nbSites_; i++)
493  {
494  d2LogLikelihood_[i].resize(nbStates_);
495  }
496  }
497 
498  partialD2LogLikelihoods_.clear();
499 
500  vector<double> num(nbStates_), num2(nbStates_), num3(nbStates_);
501 
502  // Transition probabilities:
503  const ColMatrix<double> trans(transitionMatrix_->getPij());
504 
505  // Initialisation:
506  const vector<double>* emissions = &(*emissionProbabilities_)(0);
507  const vector<double>* dEmissions = &emissionProbabilities_->getDEmissionProbabilities(0);
508  const vector<double>* d2Emissions = &emissionProbabilities_->getD2EmissionProbabilities(0);
509 
510  for (size_t j = 0; j < nbStates_; j++)
511  {
512  d2LogLikelihood_[0][j] = (*d2Emissions)[j] / (*emissions)[j] - pow((*dEmissions)[j] / (*emissions)[j], 2);
513  }
514 
515  // Recursion:
516  size_t nextBrkPt = nbSites_; // next break point
517  vector<size_t>::const_iterator bpIt = breakPoints_.begin();
518  if (bpIt != breakPoints_.end())
519  nextBrkPt = *bpIt;
520  partialDLogLikelihoods_.clear();
521 
522  for (size_t i = 1; i < nbSites_; i++)
523  {
524  size_t iip = (i - 1) * nbStates_;
525 
526  emissions = &(*emissionProbabilities_)(i);
527  dEmissions = &emissionProbabilities_->getDEmissionProbabilities(i);
528  d2Emissions = &emissionProbabilities_->getD2EmissionProbabilities(i);
529 
530  for (size_t kp = 0; kp < nbStates_; kp++)
531  {
532  num[kp] = logLikelihood_[iip + kp];
533  }
534 
535  num -= num[VectorTools::whichMax(num)];
536 
537  if (i < nextBrkPt)
538  {
539  for (size_t j = 0; j < nbStates_; j++)
540  {
541  double den = VectorTools::sumExp(num, trans.getCol(j));
542 
543  num2 = dLogLikelihood_[i - 1] * trans.getCol(j);
544 
545  num3 = (dLogLikelihood_[i - 1] * dLogLikelihood_[i - 1] + d2LogLikelihood_[i - 1]) * trans.getCol(j);
546 
547  d2LogLikelihood_[i][j] = VectorTools::sumExp(num, num3) / den - pow(VectorTools::sumExp(num, num2) / den, 2);
548  }
549  }
550  else // Reset markov chain:
551  {
552  // Termination of previous segment:
553 
554  double den = VectorTools::sumExp(num);
555 
556  num2 = dLogLikelihood_[i - 1] * dLogLikelihood_[i - 1] + d2LogLikelihood_[i - 1];
557 
558  partialD2LogLikelihoods_.push_back(VectorTools::sumExp(num, num2) / den - pow(VectorTools::sumExp(num, dLogLikelihood_[i - 1]) / den, 2));
559 
560  for (size_t j = 0; j < nbStates_; j++)
561  {
562  d2LogLikelihood_[i][j] = (*d2Emissions)[j] / (*emissions)[j] - pow((*dEmissions)[j] / (*emissions)[j], 2);
563  }
564 
565 
566  bpIt++;
567  if (bpIt != breakPoints_.end())
568  nextBrkPt = *bpIt;
569  else
570  nextBrkPt = nbSites_;
571  }
572  }
573 
574  // Termination:
575  for (size_t kp = 0; kp < nbStates_; kp++)
576  {
577  num[kp] = logLikelihood_[nbStates_ * (nbSites_ - 1) + kp];
578  }
579 
580  num -= num[VectorTools::whichMax(num)];
581 
582  double den = VectorTools::sumExp(num);
583 
584  num2 = dLogLikelihood_[nbSites_ - 1] * dLogLikelihood_[nbSites_ - 1] + d2LogLikelihood_[nbSites_ - 1];
585 
586  partialD2LogLikelihoods_.push_back(VectorTools::sumExp(num, num2) / den - pow(VectorTools::sumExp(num, dLogLikelihood_[nbSites_ - 1]) / den, 2));
587 
588 
589  // Compute d2LogLikelihood
590 
591  d2LogLik_ = 0;
592  vector<double> copy = partialD2LogLikelihoods_; // We need to keep the original order for posterior decoding.
593  sort(copy.begin(), copy.end());
594  for (size_t i = copy.size(); i > 0; --i)
595  {
596  d2LogLik_ += copy[i - 1];
597  }
598 }
599 
601 {
602  return partialD2LogLikelihoods_[site];
603 }
static T sumExp(const std::vector< T > &v1)
Definition: VectorTools.h:710
std::shared_ptr< HmmEmissionProbabilities > emissionProbabilities_
void setNamespace(const std::string &nameSpace) override
Set the namespace for the parameter names.
std::vector< double > partialDLogLikelihoods_
std::vector< double > logLikelihood_
The likelihood array.
static size_t whichMax(const std::vector< T > &v)
Template function to get the index of the maximum value of a std::vector.
Definition: VectorTools.h:1133
A partial implementation of the Parametrizable interface.
void setNamespace(const std::string &prefix) override
Set the namespace for the parameter names.
STL namespace.
double getD2LogLikelihoodForASite(size_t site) const override
static T logsum(T lnx, T lny)
Compute the logarithm of a sum from the sum of logarithms.
Definition: NumTools.h:96
std::vector< std::vector< double > > d2LogLikelihood_
void fireParameterChanged(const ParameterList &pl) override
Notify the class when one or several parameters have changed.
std::shared_ptr< HmmTransitionMatrix > transitionMatrix_
The parameter list object.
Definition: ParameterList.h:27
Vdouble getLikelihoodForEachSite() const override
Get the likelihood for each site.
Matrix storage by column.
Definition: Matrix.h:200
Vdouble getHiddenStatesPosteriorProbabilitiesForASite(size_t site) const override
std::vector< std::vector< double > > backLogLikelihood_
backward logLikelihood
std::vector< double > partialLogLikelihoods_
std::vector< double > Vdouble
Definition: VectorTools.h:34
std::vector< double > partialD2LogLikelihoods_
void getHiddenStatesPosteriorProbabilities(std::vector< std::vector< double >> &probs, bool append=false) const override
partial impmementation of Hmm Likelihoods.
std::shared_ptr< HmmStateAlphabet > hiddenAlphabet_
The alphabet describing the hidden states.
Exception base class. Overload exception constructor (to control the exceptions mechanism). Destructor is already virtual (from std::exception)
Definition: Exceptions.h:20
double getFirstOrderDerivative(const std::string &variable) const
Get the derivative of the function at the current point.
double getLikelihoodForASite(size_t site) const override
Get the likelihood for a site, and its derivatives.
std::vector< std::vector< double > > dLogLikelihood_
The DLogLikelihood arrays.
std::vector< size_t > breakPoints_
LogsumHmmLikelihood(std::shared_ptr< HmmStateAlphabet > hiddenAlphabet, std::shared_ptr< HmmTransitionMatrix > transitionMatrix, std::shared_ptr< HmmEmissionProbabilities > emissionProbabilities, const std::string &prefix="")
Build a new LogsumHmmLikelihood object.
double getDLogLikelihoodForASite(size_t site) const override
virtual void addParameters_(const ParameterList &parameters)