IsoSpec
Loading...
Searching...
No Matches
isoSpec++.h
1
16
17#pragma once
18
19#include <unordered_map>
20#include <queue>
21#include <limits>
22#include <string>
23#include <vector>
24#include <algorithm>
25#include "platform.h"
26#include "dirtyAllocator.h"
27#include "summator.h"
28#include "operators.h"
29#include "marginalTrek++.h"
30
31
32
33namespace IsoSpec
34{
35
36// This function is NOT guaranteed to be secure against malicious input. It should be used only for debugging.
37unsigned int parse_formula(const char* formula,
38 std::vector<double>& isotope_masses,
39 std::vector<double>& isotope_probabilities,
40 int** isotopeNumbers,
41 int** atomCounts,
42 unsigned int* confSize,
43 bool use_nominal_masses = false);
44
45
47
50class ISOSPEC_EXPORT_SYMBOL Iso {
51 private:
53
59 void setupMarginals(const double* _isotopeMasses,
60 const double* _isotopeProbabilities);
61 bool disowned;
62
63 protected:
67 unsigned int confSize;
68 int allDim;
70
71 bool doMarginalsNeedSorting() const;
72
73 public:
74 Iso();
75
77
84 Iso(
85 int _dimNumber,
86 const int* _isotopeNumbers,
87 const int* _atomCounts,
88 const double* _isotopeMasses,
89 const double* _isotopeProbabilities
90 );
91 Iso(
92 int _dimNumber,
93 const int* _isotopeNumbers,
94 const int* _atomCounts,
95 const double* const * _isotopeMasses,
96 const double* const * _isotopeProbabilities
97 );
98
100 Iso(const char* formula, bool use_nominal_masses = false); // NOLINT(runtime/explicit) - constructor deliberately left to be used as a conversion
101
103 inline Iso(const std::string& formula, bool use_nominal_masses = false) : Iso(formula.c_str(), use_nominal_masses) {} // NOLINT(runtime/explicit) - constructor deliberately left to be used as a conversion
104
106
114 static Iso FromFASTA(const char* fasta, bool use_nominal_masses = false, bool add_water = true);
115
117 static inline Iso FromFASTA(const std::string& fasta, bool use_nominal_masses = false, bool add_water = true) { return FromFASTA(fasta.c_str(), use_nominal_masses, add_water); }
118
120 Iso(Iso&& other);
121
122 /* We're not exactly following standard copy and assign semantics with Iso objects, so delete the default assign constructor just in case, so noone tries to use it. Copy ctor declared below. */
123 Iso& operator=(const Iso& other) = delete;
124
126
130 Iso(const Iso& other, bool fullcopy);
131
133 virtual ~Iso();
134
136 double getLightestPeakMass() const;
137
139 double getHeaviestPeakMass() const;
140
146 double getMonoisotopicPeakMass() const;
147
149 double getModeLProb() const;
150
152 double getUnlikeliestPeakLProb() const;
153
155 double getModeMass() const;
156
158 double getTheoreticalAverageMass() const;
159
161 double variance() const;
162
164 double stddev() const { return sqrt(variance()); }
165
167 inline int getDimNumber() const { return dimNumber; }
168
170 inline int getAllDim() const { return allDim; }
171
173 void addElement(int atomCount, int noIsotopes, const double* isotopeMasses, const double* isotopeProbabilities);
174
176 void saveMarginalLogSizeEstimates(double* priorities, double target_total_prob) const;
177};
178
179
181
184class ISOSPEC_EXPORT_SYMBOL IsoGenerator : public Iso
185{
186 public:
187 const double mode_lprob;
188
189 protected:
192 double* partialProbs;
193
194 public:
196
199 virtual bool advanceToNextConfiguration() = 0;
200
202
205 virtual double lprob() const { return partialLProbs[0]; }
206
208
211 virtual double mass() const { return partialMasses[0]; }
212
214
217 virtual double prob() const { return partialProbs[0]; }
218
220 virtual void get_conf_signature(int* space) const = 0;
221
223 IsoGenerator(Iso&& iso, bool alloc_partials = true); // NOLINT(runtime/explicit) - constructor deliberately left to be used as a conversion
224
226 virtual ~IsoGenerator();
227};
228
229
230
232
237template<typename MarginalType>
238class ISOSPEC_EXPORT_SYMBOL IsoOrderedGeneratorTemplate: public IsoGenerator
239{
240 private:
241 MarginalType** marginalResults;
242 std::priority_queue<void*, pod_vector<void*>, ConfOrder> pq;
243 void* topConf;
244 DirtyAllocator allocator;
245 const pod_vector<double>** logProbs;
246 const pod_vector<double>** masses;
247 double currentLProb;
248 double currentMass;
249 double currentProb;
250 int ccount;
251
252 public:
253 IsoOrderedGeneratorTemplate(const IsoOrderedGeneratorTemplate& other) = delete;
254 IsoOrderedGeneratorTemplate& operator=(const IsoOrderedGeneratorTemplate& other) = delete;
255
256 bool advanceToNextConfiguration() override final;
257
259
263 inline void get_conf_signature(int* space) const override final
264 {
265 if constexpr (std::is_same<MarginalType, MarginalTrek>::value)
266 {
267 int* c = getConf(topConf);
268
269 if (ccount >= 0)
270 c[ccount]--;
271
272 for(int ii = 0; ii < dimNumber; ii++)
273 {
274 memcpy(space, marginalResults[ii]->confs()[c[ii]], isotopeNumbers[ii]*sizeof(int));
275 space += isotopeNumbers[ii];
276 }
277
278 if (ccount >= 0)
279 c[ccount]++;
280 }
281 else
282 throw std::runtime_error("IsoOrderedGeneratorTemplate::get_conf_signature() called on a non-MarginalTrek generator. This is not supported yet.");
283 };
284
286 IsoOrderedGeneratorTemplate(Iso&& iso, int _tabSize = 1000, int _hashSize = 1000); // NOLINT(runtime/explicit) - constructor deliberately left to be used as a conversion
287
290
291 inline void get_conf_by_indexes(int* space)
292 {
293 if constexpr (std::is_same<MarginalType, SingleAtomMarginal<false>>::value)
294 {
295 if(dimNumber == 0)
296 return;
297
298 int* c = getConf(topConf);
299 space[0] = std::max(c[0]-1, 0);
300
301 for(int ii = 1; ii < dimNumber; ii++)
302 space[ii] = c[ii];
303 }
304 }
305};
306
307using IsoOrderedGenerator = IsoOrderedGeneratorTemplate<MarginalTrek>;
308
309
311
316class ISOSPEC_EXPORT_SYMBOL IsoThresholdGenerator: public IsoGenerator
317{
318 private:
319 int* counter;
320 double* maxConfsLPSum;
321 const double Lcutoff;
322 PrecalculatedMarginal** marginalResults;
323 PrecalculatedMarginal** marginalResultsUnsorted;
324 int* marginalOrder;
325
326 const double* lProbs_ptr;
327 const double* lProbs_ptr_start;
328 double* partialLProbs_second;
329 double partialLProbs_second_val, lcfmsv;
330 bool empty;
331
332 public:
333 IsoThresholdGenerator(const IsoThresholdGenerator& other) = delete;
334 IsoThresholdGenerator& operator=(const IsoThresholdGenerator& other) = delete;
335
336 inline void get_conf_signature(int* space) const override final
337 {
338 counter[0] = lProbs_ptr - lProbs_ptr_start;
339 if(marginalOrder != nullptr)
340 {
341 for(int ii = 0; ii < dimNumber; ii++)
342 {
343 int jj = marginalOrder[ii];
344 memcpy(space, marginalResultsUnsorted[ii]->get_conf(counter[jj]), isotopeNumbers[ii]*sizeof(int));
345 space += isotopeNumbers[ii];
346 }
347 }
348 else
349 {
350 for(int ii = 0; ii < dimNumber; ii++)
351 {
352 memcpy(space, marginalResultsUnsorted[ii]->get_conf(counter[ii]), isotopeNumbers[ii]*sizeof(int));
353 space += isotopeNumbers[ii];
354 }
355 }
356 };
357
359
367 IsoThresholdGenerator(Iso&& iso, double _threshold, bool _absolute = true, int _tabSize = 1000, int _hashSize = 1000, bool reorder_marginals = true);
368
370
371 // Perform highly aggressive inling as this function is often called as while(advanceToNextConfiguration()) {}
372 // which leads to an extremely tight loop and some compilers miss this (potentially due to the length of the function).
373 ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
374 {
375 lProbs_ptr++;
376
377 if(ISOSPEC_LIKELY(*lProbs_ptr >= lcfmsv))
378 {
379 return true;
380 }
381
382 // If we reached this point, a carry is needed
383
384 int idx = 0;
385 lProbs_ptr = lProbs_ptr_start;
386
387 int * cntr_ptr = counter;
388
389 while(idx < dimNumber-1)
390 {
391 // counter[idx] = 0;
392 *cntr_ptr = 0;
393 idx++;
394 cntr_ptr++;
395 // counter[idx]++;
396 (*cntr_ptr)++;
397 partialLProbs[idx] = partialLProbs[idx+1] + marginalResults[idx]->get_lProb(counter[idx]);
398 if(partialLProbs[idx] + maxConfsLPSum[idx-1] >= Lcutoff)
399 {
400 partialMasses[idx] = partialMasses[idx+1] + marginalResults[idx]->get_mass(counter[idx]);
401 partialProbs[idx] = partialProbs[idx+1] * marginalResults[idx]->get_prob(counter[idx]);
402 recalc(idx-1);
403 return true;
404 }
405 }
406
408 return false;
409 }
410
411
412 ISOSPEC_FORCE_INLINE double lprob() const override final { return partialLProbs_second_val + (*(lProbs_ptr)); }
413 ISOSPEC_FORCE_INLINE double mass() const override final { return partialMasses[1] + marginalResults[0]->get_mass(lProbs_ptr - lProbs_ptr_start); }
414 ISOSPEC_FORCE_INLINE double prob() const override final { return partialProbs[1] * marginalResults[0]->get_prob(lProbs_ptr - lProbs_ptr_start); }
415
417 void terminate_search();
418
423 void reset();
424
429 size_t count_confs();
430
431 private:
433 ISOSPEC_FORCE_INLINE void recalc(int idx)
434 {
435 for(; idx > 0; idx--)
436 {
437 partialLProbs[idx] = partialLProbs[idx+1] + marginalResults[idx]->get_lProb(counter[idx]);
438 partialMasses[idx] = partialMasses[idx+1] + marginalResults[idx]->get_mass(counter[idx]);
439 partialProbs[idx] = partialProbs[idx+1] * marginalResults[idx]->get_prob(counter[idx]);
440 }
441 partialLProbs_second_val = *partialLProbs_second;
442 partialLProbs[0] = *partialLProbs_second + marginalResults[0]->get_lProb(counter[0]);
443 lcfmsv = Lcutoff - partialLProbs_second_val;
444 }
445
446 ISOSPEC_FORCE_INLINE void short_recalc(int idx)
447 {
448 for(; idx > 0; idx--)
449 partialLProbs[idx] = partialLProbs[idx+1] + marginalResults[idx]->get_lProb(counter[idx]);
450 partialLProbs_second_val = *partialLProbs_second;
451 partialLProbs[0] = *partialLProbs_second + marginalResults[0]->get_lProb(counter[0]);
452 lcfmsv = Lcutoff - partialLProbs_second_val;
453 }
454};
455
456
457
458
459template<typename MarginalType>
460class ISOSPEC_EXPORT_SYMBOL IsoLayeredGeneratorTemplate : public IsoGenerator
461{
462 private:
463 int* counter;
464 double* maxConfsLPSum;
465 double currentLThreshold, lastLThreshold;
466 MarginalType** marginalResults;
467 MarginalType** marginalResultsUnsorted;
468 int* marginalOrder;
469
470 const double* lProbs_ptr;
471 const double* lProbs_ptr_start;
472 const double** resetPositions;
473 double* partialLProbs_second;
474 double partialLProbs_second_val, lcfmsv, last_lcfmsv;
475 bool marginalsNeedSorting;
476
477
478 public:
479 IsoLayeredGeneratorTemplate(const IsoLayeredGeneratorTemplate& other) = delete;
480 IsoLayeredGeneratorTemplate& operator=(const IsoLayeredGeneratorTemplate& other) = delete;
481
482 inline void get_conf_signature(int* space) const override final
483 {
484 counter[0] = lProbs_ptr - lProbs_ptr_start;
485 if(marginalOrder != nullptr)
486 {
487 for(int ii = 0; ii < dimNumber; ii++)
488 {
489 int jj = marginalOrder[ii];
490 memcpy(space, marginalResultsUnsorted[ii]->get_conf(counter[jj]), isotopeNumbers[ii]*sizeof(int));
491 space += isotopeNumbers[ii];
492 }
493 }
494 else
495 {
496 for(int ii = 0; ii < dimNumber; ii++)
497 {
498 memcpy(space, marginalResultsUnsorted[ii]->get_conf(counter[ii]), isotopeNumbers[ii]*sizeof(int));
499 space += isotopeNumbers[ii];
500 }
501 }
502 };
503
504 inline double get_currentLThreshold() const { return currentLThreshold; }
505
506 IsoLayeredGeneratorTemplate(Iso&& iso, int _tabSize = 1000, int _hashSize = 1000, bool reorder_marginals = true, double t_prob_hint = 0.99); // NOLINT(runtime/explicit) - constructor deliberately left to be used as a conversion
507
508 ~IsoLayeredGeneratorTemplate();
509
510 ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
511 {
512 do
513 {
514 if(advanceToNextConfigurationWithinLayer())
515 return true;
516 } while(IsoLayeredGeneratorTemplate<MarginalType>::nextLayer(-2.0));
517 return false;
518 }
519
520 ISOSPEC_FORCE_INLINE bool advanceToNextConfigurationWithinLayer()
521 {
522 do{
523 lProbs_ptr++;
524
525 if(ISOSPEC_LIKELY(*lProbs_ptr >= lcfmsv))
526 return true;
527 }
528 while(carry()); // NOLINT(whitespace/empty_loop_body) - cpplint bug, that's not an empty loop body, that's a do{...}while(...) construct
529 return false;
530 }
531
532 ISOSPEC_FORCE_INLINE double lprob() const override final { return partialLProbs_second_val + (*(lProbs_ptr)); };
533 ISOSPEC_FORCE_INLINE double mass() const override final { return partialMasses[1] + marginalResults[0]->get_mass(lProbs_ptr - lProbs_ptr_start); };
534 ISOSPEC_FORCE_INLINE double prob() const override final { return partialProbs[1] * marginalResults[0]->get_prob(lProbs_ptr - lProbs_ptr_start); };
535
538
539
541 ISOSPEC_FORCE_INLINE void recalc(int idx)
542 {
543 for(; idx > 0; idx--)
544 {
545 partialLProbs[idx] = partialLProbs[idx+1] + marginalResults[idx]->get_lProb(counter[idx]);
546 partialMasses[idx] = partialMasses[idx+1] + marginalResults[idx]->get_mass(counter[idx]);
547 partialProbs[idx] = partialProbs[idx+1] * marginalResults[idx]->get_prob(counter[idx]);
548 }
549 partialLProbs_second_val = *partialLProbs_second;
550 partialLProbs[0] = partialLProbs_second_val + marginalResults[0]->get_lProb(counter[0]);
551 lcfmsv = currentLThreshold - partialLProbs_second_val;
552 last_lcfmsv = lastLThreshold - partialLProbs_second_val;
553 }
554
555 bool nextLayer(double offset);
556
557 void get_conf_by_indexes(int* space) const
558 {
559 if constexpr (std::is_same<MarginalType, SingleAtomMarginal<true>>::value)
560 {
561 counter[0] = lProbs_ptr - lProbs_ptr_start;
562 if(marginalOrder != nullptr)
563 {
564 for(int ii = 0; ii < dimNumber; ii++)
565 {
566 int jj = marginalOrder[ii];
567 space[ii] = marginalResultsUnsorted[ii]->get_original_position(counter[jj]);
568 }
569 }
570 else
571 {
572 for(int ii = 0; ii < dimNumber; ii++)
573 space[ii] = marginalResultsUnsorted[ii]->get_original_position(counter[ii]);
574 }
575 }
576 else
577 throw std::runtime_error("IsoLayeredGeneratorTemplate::get_conf_by_indexes() called on a non-SingleAtomMarginal generator. This is not supported yet.");
578 }
579
580 private:
581 bool carry();
582};
583using IsoLayeredGenerator = IsoLayeredGeneratorTemplate<LayeredMarginal>;
584
585template<typename IsoType>
586class IsoStochasticGeneratorTemplate : public IsoGenerator
587{
588 IsoType ILG;
589 size_t to_sample_left;
590 const double precision;
591 const double beta_bias;
592 double confs_prob;
593 double chasing_prob;
594 size_t current_count;
595 std::mt19937& rdvariate_gen;
596
597 public:
598 IsoStochasticGeneratorTemplate(Iso&& iso, size_t no_molecules, double precision = 0.9999, double beta_bias = 5.0, std::mt19937& rdvariate_gen = random_gen);
599
600 ISOSPEC_FORCE_INLINE size_t count() const { return current_count; }
601
602 ISOSPEC_FORCE_INLINE double mass() const override final { return ILG.mass(); }
603
604 ISOSPEC_FORCE_INLINE double prob() const override final { return static_cast<double>(count()); }
605
606 ISOSPEC_FORCE_INLINE double lprob() const override final { return log(prob()); }
607
608 ISOSPEC_FORCE_INLINE void get_conf_signature(int* space) const override final { ILG.get_conf_signature(space); }
609
610 ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
611 {
612 /* This function will be used mainly in very small, tight loops, therefore it makes sense to
613 * aggressively inline it, despite its seemingly large body.
614 */
615 while(true)
616 {
617 double curr_conf_prob_left, current_prob;
618
619 if(to_sample_left <= 0)
620 return false;
621
622 if(confs_prob < chasing_prob)
623 {
624 // Beta was last
625 current_count = 1;
626 to_sample_left--;
627 if(!ILG.advanceToNextConfiguration())
628 return false;
629 current_prob = ILG.prob();
630 confs_prob += current_prob;
631 while(confs_prob <= chasing_prob)
632 {
633 if(!ILG.advanceToNextConfiguration())
634 return false;
635 current_prob = ILG.prob();
636 confs_prob += current_prob;
637 }
638 if(to_sample_left <= 0)
639 return true;
640 curr_conf_prob_left = confs_prob - chasing_prob;
641 }
642 else
643 {
644 // Binomial was last
645 current_count = 0;
646 if(!ILG.advanceToNextConfiguration())
647 return false;
648 current_prob = ILG.prob();
649 confs_prob += current_prob;
650 curr_conf_prob_left = current_prob;
651 }
652
653 double prob_left_to_1 = precision - chasing_prob;
654 double expected_confs = curr_conf_prob_left * to_sample_left / prob_left_to_1;
655
656 if(expected_confs <= beta_bias)
657 {
658 // Beta mode: we keep making beta jumps until we leave the current configuration
659 chasing_prob += rdvariate_beta_1_b(to_sample_left, rdvariate_gen) * prob_left_to_1;
660 while(chasing_prob <= confs_prob)
661 {
662 current_count++;
663 to_sample_left--;
664 if(to_sample_left == 0)
665 return true;
666 prob_left_to_1 = precision - chasing_prob;
667 chasing_prob += rdvariate_beta_1_b(to_sample_left, rdvariate_gen) * prob_left_to_1;
668 }
669 if(current_count > 0)
670 return true;
671 }
672 else
673 {
674 // Binomial mode: a single binomial step
675 size_t rbin = rdvariate_binom(to_sample_left, curr_conf_prob_left/prob_left_to_1, rdvariate_gen);
676 current_count += rbin;
677 to_sample_left -= rbin;
678 chasing_prob = confs_prob;
679 if(current_count > 0)
680 return true;
681 }
682 };
683 }
684
685 ISOSPEC_FORCE_INLINE void get_indexes(int* space)
686 {
687 ILG.get_conf_by_indexes(space);
688 }
689};
690
691using IsoStochasticGenerator = IsoStochasticGeneratorTemplate<IsoLayeredGenerator>;
692
693} // namespace IsoSpec
virtual void get_conf_signature(int *space) const =0
Write the signature of configuration into target memory location. It must be large enough to accomoda...
virtual bool advanceToNextConfiguration()=0
Advance to the next, not yet visited, most probable isotopologue.
virtual double mass() const
Get the mass of the current isotopologue.
Definition isoSpec++.h:211
IsoGenerator(Iso &&iso, bool alloc_partials=true)
Move constructor.
virtual double lprob() const
Get the log-probability of the current isotopologue.
Definition isoSpec++.h:205
virtual double prob() const
Get the probability of the current isotopologue.
Definition isoSpec++.h:217
The Iso class for the calculation of the isotopic distribution.
Definition isoSpec++.h:50
double stddev() const
Get the standard deviation of the theoretical distribution.
Definition isoSpec++.h:164
static Iso FromFASTA(const std::string &fasta, bool use_nominal_masses=false, bool add_water=true)
Constructor (named) from aminoacid FASTA sequence as C++ std::string. See above for details.
Definition isoSpec++.h:117
int getDimNumber() const
Get the number of elements in the chemical formula of the molecule.
Definition isoSpec++.h:167
int getAllDim() const
Get the total number of isotopes of elements present in a chemical formula.
Definition isoSpec++.h:170
int * isotopeNumbers
Definition isoSpec++.h:65
double variance() const
Get the theoretical variance of the distribution.
unsigned int confSize
Definition isoSpec++.h:67
int * atomCounts
Definition isoSpec++.h:66
Iso(const std::string &formula, bool use_nominal_masses=false)
Constructor from C++ std::string chemical formula.
Definition isoSpec++.h:103
Marginal ** marginals
Definition isoSpec++.h:69
ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
Advance to the next, not yet visited, most probable isotopologue.
Definition isoSpec++.h:510
ISOSPEC_FORCE_INLINE double mass() const override final
Get the mass of the current isotopologue.
Definition isoSpec++.h:533
ISOSPEC_FORCE_INLINE void recalc(int idx)
Recalculate the current partial log-probabilities, masses, and probabilities.
Definition isoSpec++.h:541
void get_conf_signature(int *space) const override final
Write the signature of configuration into target memory location. It must be large enough to accomoda...
Definition isoSpec++.h:482
ISOSPEC_FORCE_INLINE double prob() const override final
Get the probability of the current isotopologue.
Definition isoSpec++.h:534
void terminate_search()
Block the subsequent search of isotopologues.
ISOSPEC_FORCE_INLINE double lprob() const override final
Get the log-probability of the current isotopologue.
Definition isoSpec++.h:532
The generator of isotopologues sorted by their probability of occurrence.
Definition isoSpec++.h:239
bool advanceToNextConfiguration() override final
Advance to the next, not yet visited, most probable isotopologue.
void get_conf_signature(int *space) const override final
Definition isoSpec++.h:263
IsoOrderedGeneratorTemplate(Iso &&iso, int _tabSize=1000, int _hashSize=1000)
The move-contstructor.
virtual ~IsoOrderedGeneratorTemplate()
Destructor.
ISOSPEC_FORCE_INLINE double prob() const override final
Get the probability of the current isotopologue.
Definition isoSpec++.h:604
ISOSPEC_FORCE_INLINE double mass() const override final
Get the mass of the current isotopologue.
Definition isoSpec++.h:602
ISOSPEC_FORCE_INLINE double lprob() const override final
Get the log-probability of the current isotopologue.
Definition isoSpec++.h:606
ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
Advance to the next, not yet visited, most probable isotopologue.
Definition isoSpec++.h:610
ISOSPEC_FORCE_INLINE void get_conf_signature(int *space) const override final
Write the signature of configuration into target memory location. It must be large enough to accomoda...
Definition isoSpec++.h:608
The generator of isotopologues above a given threshold value.
Definition isoSpec++.h:317
ISOSPEC_FORCE_INLINE double lprob() const override final
Get the log-probability of the current isotopologue.
Definition isoSpec++.h:412
void get_conf_signature(int *space) const override final
Write the signature of configuration into target memory location. It must be large enough to accomoda...
Definition isoSpec++.h:336
ISOSPEC_FORCE_INLINE bool advanceToNextConfiguration() override final
Advance to the next, not yet visited, most probable isotopologue.
Definition isoSpec++.h:373
ISOSPEC_FORCE_INLINE double prob() const override final
Get the probability of the current isotopologue.
Definition isoSpec++.h:414
void terminate_search()
Block the subsequent search of isotopologues.
ISOSPEC_FORCE_INLINE double mass() const override final
Get the mass of the current isotopologue.
Definition isoSpec++.h:413
The marginal distribution class (a subisotopologue).
Precalculated Marginal class.
const double & get_prob(int idx) const
Get the probability of the idx-th subisotopologue.
const double & get_lProb(int idx) const
Get the log-probability of the idx-th subisotopologue.
const double & get_mass(int idx) const
Get the mass of the idx-th subisotopologue.