Home  · Classes  · Annotated Classes  · Modules  · Members  · Namespaces  · Related Pages

IntegerMassDecomposer.h

Go to the documentation of this file.
00001 // --------------------------------------------------------------------------
00002 //                   OpenMS -- Open-Source Mass Spectrometry
00003 // --------------------------------------------------------------------------
00004 // Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
00005 // ETH Zurich, and Freie Universitaet Berlin 2002-2012.
00006 //
00007 // This software is released under a three-clause BSD license:
00008 //  * Redistributions of source code must retain the above copyright
00009 //    notice, this list of conditions and the following disclaimer.
00010 //  * Redistributions in binary form must reproduce the above copyright
00011 //    notice, this list of conditions and the following disclaimer in the
00012 //    documentation and/or other materials provided with the distribution.
00013 //  * Neither the name of any author or any participating institution
00014 //    may be used to endorse or promote products derived from this software
00015 //    without specific prior written permission.
00016 // For a full list of authors, refer to the file AUTHORS.
00017 // --------------------------------------------------------------------------
00018 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00019 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 // ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
00022 // INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00023 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00024 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
00025 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00026 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00027 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00028 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 //
00030 // --------------------------------------------------------------------------
00031 // $Maintainer: Stephan Aiche $
00032 // $Authors: Anton Pervukhin <Anton.Pervukhin@CeBiTec.Uni-Bielefeld.DE> $
00033 // --------------------------------------------------------------------------
00034 //
00035 
00036 #ifndef OPENMS_CHEMISTRY_MASSDECOMPOSITION_IMS_INTEGERMASSDECOMPOSER_H
00037 #define OPENMS_CHEMISTRY_MASSDECOMPOSITION_IMS_INTEGERMASSDECOMPOSER_H
00038 
00039 #include <vector>
00040 #include <utility>
00041 
00042 #include <OpenMS/CHEMISTRY/MASSDECOMPOSITION/IMS/Weights.h>
00043 #include <OpenMS/CHEMISTRY/MASSDECOMPOSITION/IMS/MassDecomposer.h>
00044 
00045 #include <OpenMS/MATH/MISC/MathFunctions.h>
00046 
00047 namespace OpenMS
00048 {
00049 
00050   namespace ims
00051   {
00052 
00069     template <typename ValueType = long unsigned int,
00070               typename DecompositionValueType = unsigned int>
00071     class IntegerMassDecomposer :
00072       public MassDecomposer<ValueType, DecompositionValueType>
00073     {
00074 public:
00076       typedef typename MassDecomposer<ValueType, DecompositionValueType>::value_type value_type;
00077 
00079       typedef typename MassDecomposer<ValueType, DecompositionValueType>::decomposition_value_type decomposition_value_type;
00080 
00082       typedef typename MassDecomposer<ValueType, DecompositionValueType>::decomposition_type decomposition_type;
00083 
00085       typedef typename MassDecomposer<ValueType, DecompositionValueType>::decompositions_type decompositions_type;
00086 
00088       typedef typename decomposition_type::size_type size_type;
00089 
00095       explicit IntegerMassDecomposer(const Weights & alphabet);
00096 
00103       virtual bool exist(value_type mass);
00104 
00111       virtual decomposition_type getDecomposition(value_type mass);
00112 
00119       virtual decompositions_type getAllDecompositions(value_type mass);
00120 
00129       virtual decomposition_value_type getNumberOfDecompositions(value_type mass);
00130 
00131 private:
00132 
00136       typedef std::vector<std::pair<size_type, decomposition_value_type> > witness_vector_type;
00137 
00141       typedef std::vector<value_type> residues_table_row_type;
00142 
00146       typedef std::vector<residues_table_row_type> residues_table_type;
00147 
00151       Weights alphabet_;
00152 
00158       residues_table_type ertable_;
00159 
00164       residues_table_row_type lcms_;
00165 
00171       residues_table_row_type mass_in_lcms_;
00172 
00176       value_type infty_;
00177 
00182       witness_vector_type witness_vector_;
00183 
00187       void fillExtendedResidueTable_(const Weights & _alphabet, residues_table_row_type & _lcms,
00188                                      residues_table_row_type & _mass_in_lcms, const value_type _infty,
00189                                      witness_vector_type & _witness_vector, residues_table_type & _ertable);
00190 
00199       void collectDecompositionsRecursively_(value_type mass, size_type alphabetMassIndex,
00200                                              decomposition_type decomposition, decompositions_type & decompositionsStore);
00201     };
00202 
00203 
00204     template <typename ValueType, typename DecompositionValueType>
00205     IntegerMassDecomposer<ValueType, DecompositionValueType>::IntegerMassDecomposer(
00206       const Weights & alphabet) :
00207       alphabet_(alphabet)
00208     {
00209 
00210       lcms_.resize(alphabet.size());
00211       mass_in_lcms_.resize(alphabet.size());
00212 
00213       infty_ = alphabet.getWeight(0) * alphabet.getWeight(alphabet.size() - 1);
00214 
00215       fillExtendedResidueTable_(alphabet, lcms_, mass_in_lcms_, infty_, witness_vector_, ertable_);
00216 
00217     }
00218 
00219     template <typename ValueType, typename DecompositionValueType>
00220     void IntegerMassDecomposer<ValueType, DecompositionValueType>::fillExtendedResidueTable_(
00221       const Weights & _alphabet, residues_table_row_type & _lcms, residues_table_row_type & _mass_in_lcms,
00222       const value_type _infty, witness_vector_type & _witnessVector, residues_table_type & _ertable)
00223     {
00224 
00225       if (_alphabet.size() < 2)
00226       {
00227         return;
00228       }
00229       // caches the most often used mass - smallest mass
00230       value_type smallestMass = _alphabet.getWeight(0), secondMass = _alphabet.getWeight(1);
00231 
00232       // initializes table: infinity everywhere except in the first field of every column
00233       _ertable.reserve(_alphabet.size());
00234       _ertable.assign(_alphabet.size(), std::vector<value_type>(smallestMass, _infty));
00235 
00236       for (size_type i = 0; i < _alphabet.size(); ++i)
00237       {
00238         _ertable[i][0] = 0;
00239       }
00240 
00241       // initializes witness vector
00242       _witnessVector.resize(smallestMass);
00243 
00244       // fills second column (the first one is already correct)
00245       size_type it_inc = secondMass % smallestMass, witness = 1;
00246       //typename residues_table_row_type::iterator it = _ertable[1].begin() + it_inc;
00247       value_type mass = secondMass;
00248       // initializes counter to create a witness vector
00249       decomposition_value_type counter = 0;
00250       size_type it_i = it_inc;
00251       while (it_i != 0)
00252       {
00253         _ertable[1][it_i] = mass;
00254         mass += secondMass;
00255         ++counter;
00256         _witnessVector[it_i] = std::make_pair(witness, counter);
00257         //std::cerr << "BLA: " << counter << " " << &_ertable[1][0] << " " << it - _ertable[1].begin() << " " << _ertable[1].size() << std::endl;
00258         it_i += it_inc;
00259         if (it_i >= _ertable[1].size())
00260         {
00261           it_i -= _ertable[1].size();
00262         }
00263       }
00264       // fills cache variables for i==1
00265       value_type d = Math::gcd(smallestMass, secondMass);
00266       _lcms[1] = secondMass * smallestMass / d;
00267       _mass_in_lcms[1] = smallestMass / d;
00268 
00269       // fills remaining table. i is the column index.
00270       for (size_type i = 2; i < _alphabet.size(); ++i)
00271       {
00272         // caches often used i-th alphabet mass
00273         value_type currentMass = _alphabet.getWeight(i);
00274 
00275         value_type d = Math::gcd(smallestMass, currentMass);
00276 
00277         // fills cache for various variables.
00278         // note that values for i==0 are never assigned since they're unused anyway.
00279         _lcms[i] = currentMass * smallestMass / d;
00280         _mass_in_lcms[i] = smallestMass / d;
00281 
00282         // Nijenhuis' improvement: Is currentMass composable with smaller alphabet?
00283         if (currentMass >= _ertable[i - 1][currentMass % smallestMass])
00284         {
00285           _ertable[i] = _ertable[i - 1];
00286           continue;
00287         }
00288 
00289         const residues_table_row_type & prev_column = _ertable[i - 1];
00290         residues_table_row_type & cur_column = _ertable[i];
00291 
00292         if (d == 1)
00293         {
00294           // This loop is for the case that the gcd is 1. The optimization used below
00295           // is not applicable here.
00296 
00297           // p_inc is used to change residue (p) efficiently
00298           size_type p_inc = currentMass % smallestMass;
00299 
00300           // n is the value that will be written into the table
00301           value_type n = 0;
00302           // current residue (in paper variable 'r' is used)
00303           size_type p = 0;
00304           // counter for creation of witness vector
00305           decomposition_value_type counter = 0;
00306 
00307           for (size_type m = smallestMass; m > 0; --m)
00308           {
00309             n += currentMass;
00310             p += p_inc;
00311             ++counter;
00312             if (p >= smallestMass)
00313             {
00314               p -= smallestMass;
00315             }
00316             if (n > prev_column[p])
00317             {
00318               n = prev_column[p];
00319               counter = 0;
00320             }
00321             else
00322             {
00323               _witnessVector[p] = std::make_pair(i, counter);
00324             }
00325             cur_column[p] = n;
00326           }
00327         }
00328         else
00329         {
00330           // If we're here, the gcd is not 1. We can use the following cache-optimized
00331           // version of the algorithm. The trick is to put the iteration over all
00332           // residue classes into the _inner_ loop.
00333           //
00334           // One could see it as going through one column in blocks which are gcd entries long.
00335           size_type cur = currentMass % smallestMass;
00336           size_type prev = 0;
00337           size_type p_inc = cur - d;
00338           // counters for creation of one witness vector
00339           std::vector<decomposition_value_type> counters(smallestMass);
00340 
00341           // copies first block from prev_column to cur_column
00342           for (size_type j = 1; j < d; ++j)
00343           {
00344             cur_column[j] = prev_column[j];
00345           }
00346 
00347           // first loop: goes through all blocks, updating cur_column for the first time.
00348           for (size_type m = smallestMass / d; m > 1; m--)
00349           {
00350             // r: current residue class
00351             for (size_type r = 0; r < d; r++)
00352             {
00353 
00354               ++counters[cur];
00355               if (cur_column[prev] + currentMass > prev_column[cur])
00356               {
00357                 cur_column[cur] = prev_column[cur];
00358                 counters[cur] = 0;
00359               }
00360               else
00361               {
00362                 cur_column[cur] = cur_column[prev] + currentMass;
00363                 _witnessVector[cur] = std::make_pair(i, counters[cur]);
00364               }
00365 
00366               prev++;
00367               cur++;
00368             }
00369 
00370             prev = cur - d;
00371 
00372             // this does: cur = (cur + currentMass) % smallestMass - d;
00373             cur += p_inc;
00374             if (cur >= smallestMass)
00375             {
00376               cur -= smallestMass;
00377             }
00378           }
00379 
00380           // second loop:
00381           bool cont = true;
00382           while (cont)
00383           {
00384             cont = false;
00385             prev++;
00386             cur++;
00387             ++counters[cur];
00388             for (size_type r = 1; r < d; ++r)
00389             {
00390               if (cur_column[prev] + currentMass < cur_column[cur])
00391               {
00392                 cur_column[cur] = cur_column[prev] + currentMass;
00393                 cont = true;
00394                 _witnessVector[cur] = std::make_pair(i, counters[cur]);
00395               }
00396               else
00397               {
00398                 counters[cur] = 0;
00399               }
00400               prev++;
00401               cur++;
00402             }
00403 
00404             prev = cur - d;
00405 
00406             cur += p_inc;
00407             if (cur >= smallestMass)
00408             {
00409               cur -= smallestMass;
00410             }
00411           }
00412         }
00413 
00414       }
00415     }
00416 
00417     template <typename ValueType, typename DecompositionValueType>
00418     bool IntegerMassDecomposer<ValueType, DecompositionValueType>::
00419     exist(value_type mass)
00420     {
00421 
00422       value_type residue = ertable_.back().at(mass % alphabet_.getWeight(0));
00423       return residue != infty_ && mass >= residue;
00424     }
00425 
00426     template <typename ValueType, typename DecompositionValueType>
00427     typename IntegerMassDecomposer<ValueType, DecompositionValueType>::decomposition_type
00428     IntegerMassDecomposer<ValueType, DecompositionValueType>::getDecomposition(value_type mass)
00429     {
00430 
00431       decomposition_type decomposition;
00432       if (!this->exist(mass))
00433       {
00434         return decomposition;
00435       }
00436 
00437       decomposition.reserve(alphabet_.size());
00438       decomposition.resize(alphabet_.size());
00439 
00440       // initial mass residue: in FIND-ONE algorithm in paper corresponds variable "r"
00441       value_type r = mass % alphabet_.getWeight(0);
00442       value_type m = ertable_.back().at(r);
00443 
00444       decomposition.at(0) = static_cast<decomposition_value_type>
00445                             ((mass - m) / alphabet_.getWeight(0));
00446 
00447       while (m != 0)
00448       {
00449         size_type i = witness_vector_.at(r).first;
00450         decomposition_value_type j = witness_vector_.at(r).second;
00451         decomposition.at(i) += j;
00452         if (m < j * alphabet_.getWeight(i))
00453         {
00454           break;
00455         }
00456         m -= j * alphabet_.getWeight(i);
00457         r = m % alphabet_.getWeight(0);
00458       }
00459       return decomposition;
00460     }
00461 
00462     template <typename ValueType, typename DecompositionValueType>
00463     typename IntegerMassDecomposer<ValueType, DecompositionValueType>::decompositions_type
00464     IntegerMassDecomposer<ValueType, DecompositionValueType>::getAllDecompositions(value_type mass)
00465     {
00466       decompositions_type decompositionsStore;
00467       decomposition_type decomposition(alphabet_.size());
00468       collectDecompositionsRecursively_(mass, alphabet_.size() - 1, decomposition, decompositionsStore);
00469       return decompositionsStore;
00470     }
00471 
00472     template <typename ValueType, typename DecompositionValueType>
00473     void IntegerMassDecomposer<ValueType, DecompositionValueType>::
00474     collectDecompositionsRecursively_(value_type mass, size_type alphabetMassIndex,
00475                                       decomposition_type decomposition, decompositions_type & decompositionsStore)
00476     {
00477       if (alphabetMassIndex == 0)
00478       {
00479         value_type numberOfMasses0 = mass / alphabet_.getWeight(0);
00480         if (numberOfMasses0 * alphabet_.getWeight(0) == mass)
00481         {
00482           decomposition[0] = static_cast<decomposition_value_type>(numberOfMasses0);
00483           decompositionsStore.push_back(decomposition);
00484         }
00485         return;
00486       }
00487 
00488       // tested: caching these values gives us 15% better performance, at least
00489       // with aminoacid-mono.masses
00490       const value_type lcm = lcms_[alphabetMassIndex];
00491       const value_type mass_in_lcm = mass_in_lcms_[alphabetMassIndex]; // this is alphabet mass divided by gcd
00492 
00493       value_type mass_mod_alphabet0 = mass % alphabet_.getWeight(0); // trying to avoid modulo
00494       const value_type mass_mod_decrement = alphabet_.getWeight(alphabetMassIndex) % alphabet_.getWeight(0);
00495 
00496       for (value_type i = 0; i < mass_in_lcm; ++i)
00497       {
00498         // here is the conversion from value_type to decomposition_value_type
00499         decomposition[alphabetMassIndex] = static_cast<decomposition_value_type>(i);
00500 
00501         // this check is needed because mass could have unsigned type and after reduction on i*alphabetMass will be still be positive but huge
00502         // and that will end up in infinite loop
00503         if (mass < i * alphabet_.getWeight(alphabetMassIndex))
00504         {
00505           break;
00506         }
00507 
00508         // r: current residue class. will stay the same in the following loop
00509         value_type r = ertable_[alphabetMassIndex - 1][mass_mod_alphabet0];
00510 
00511         // TODO: if infty was std::numeric_limits<...>... the following 'if' would not be necessary
00512         if (r != infty_)
00513         {
00514           for (value_type m = mass - i * alphabet_.getWeight(alphabetMassIndex); m >= r; m -= lcm)
00515           {
00516             // the condition of the 'for' loop (m >= r) and decrementing the mass
00517             // in steps of the lcm ensures that m is decomposable. Therefore
00518             // the recursion will result in at least one witness.
00519             collectDecompositionsRecursively_(m, alphabetMassIndex - 1, decomposition, decompositionsStore);
00520             decomposition[alphabetMassIndex] += mass_in_lcm;
00521             // this check is needed because mass could have unsigned type and after reduction on i*alphabetMass will be still be positive but huge
00522             // and that will end up in infinite loop
00523             if (m < lcm)
00524             {
00525               break;
00526             }
00527           }
00528         }
00529         // subtle way of changing the modulo, instead of plain calculation it from (mass - i*currentAlphabetMass) % alphabetMass0 every time
00530         if (mass_mod_alphabet0 < mass_mod_decrement)
00531         {
00532           mass_mod_alphabet0 += alphabet_.getWeight(0) - mass_mod_decrement;
00533         }
00534         else
00535         {
00536           mass_mod_alphabet0 -= mass_mod_decrement;
00537         }
00538       }
00539 
00540     }
00541 
00550     template <typename ValueType, typename DecompositionValueType>
00551     typename IntegerMassDecomposer<ValueType, DecompositionValueType>::decomposition_value_type IntegerMassDecomposer<ValueType,
00552                                                                                                                       DecompositionValueType>::getNumberOfDecompositions(value_type mass)
00553     {
00554       return static_cast<typename IntegerMassDecomposer<ValueType, DecompositionValueType>::decomposition_value_type>(getAllDecompositions(mass).size());
00555     }
00556 
00557   } // namespace ims
00558 } // namespace OpenMS
00559 
00560 #endif // OPENMS_CHEMISTRY_MASSDECOMPOSITION_IMS_INTEGERMASSDECOMPOSER_H

OpenMS / TOPP release 1.10.0 Documentation generated on Thu Mar 7 2013 09:42:40 using doxygen 1.7.1