Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-06 09:38:32

0001 // -*- C++ -*-
0002 //
0003 // VSelector.h is a part of ThePEG - Toolkit for HEP Event Generation
0004 // Copyright (C) 1999-2019 Leif Lonnblad
0005 //
0006 // ThePEG is licenced under version 3 of the GPL, see COPYING for details.
0007 // Please respect the MCnet academic guidelines, see GUIDELINES for details.
0008 //
0009 #ifndef ThePEG_VSelector_H
0010 #define ThePEG_VSelector_H
0011 // This is the definition of the ThePEG::VSelector class.
0012 
0013 #include "ThePEG/Config/ThePEG.h"
0014 #include <stdexcept>
0015 #include <algorithm>
0016 #include <stdexcept>
0017 
0018 namespace ThePEG {
0019 
0020 template <typename T, typename WeightType = double>
0021 /**
0022  * VSelector is a templated class for storing objects associated with
0023  * probabilities in a way such that, given a flat random number
0024  * between 0 and 1, an object can be selected according to its
0025  * relative probability. Internally, the objects of class
0026  * <code>T</code> are stored in a vector parallel to a vector of the
0027  * probability of the corresponding object plus the accumulated sum of
0028  * probabilities of all objects before the current one in the
0029  * vector. This allows for fast retreival of an object according to
0030  * its probability. Where fast means that the time increases as a
0031  * logarithm of the number of objects in the selector.
0032  *
0033  * Here is an example on how to use the class:<br>
0034  * <code>double random();</code> // A random generator returning a
0035  * number between 0 and 1.<br>
0036  * <code>class foo;</code>  // Any class.<BR>
0037  * <code>VSelector<foo*> bar;</code>  // A selector.<BR>
0038  * <code>foo f1, f2;</code> <BR>
0039  * <code>bar.insert(0.5,&f1)</code>  // assign probability 0.5<BR>
0040  * <code>bar.insert(0.5,&f2)</code>  // to each of f1 and f2<BR>
0041  * <code>foo * f = bar.select(random())</code>  // randomly returns
0042  * a pointer to f1 or f2<BR>
0043  *
0044  * @see Selector
0045  */
0046 class VSelector {
0047 
0048 public:
0049 
0050   /** A vector of weights. */
0051   typedef vector<WeightType> WeightVector;
0052 
0053   /** The weight vector iterator type. */
0054   typedef typename WeightVector::const_iterator WIterator;
0055 
0056   /** A vector ob objects. */
0057   typedef vector<T> ObjectVector;
0058 
0059   /** The object vector iterator type. */
0060   typedef typename ObjectVector::iterator iterator;
0061 
0062   /** The object vector const iterator type. */
0063   typedef typename ObjectVector::const_iterator const_iterator;
0064 
0065   /** Size type of the underlying vector. */
0066   typedef typename ObjectVector::size_type size_type;
0067 
0068 public:
0069 
0070   /**
0071    * Default constructor.
0072    */
0073   VSelector(size_type reserved = 0) : theSum() 
0074   {
0075     reserve(reserved);
0076   }
0077 
0078   /**
0079    * Swap the underlying representation with the argument.
0080    */
0081   void swap(VSelector & s) {
0082     theSums.swap(s.theSums);
0083     theWeights.swap(s.theWeights);
0084     theObjects.swap(s.theObjects);
0085     std::swap(theSum, s.theSum);
0086   }
0087 
0088   /**
0089    * Insert an object given a probability for this object. If the
0090    * probability is zero or negative, the object will not be inserted
0091    * and the probability itself is returned. Otherwise the sum of
0092    * probabilities is returned.
0093    */
0094   WeightType insert(WeightType d, const T & t) {
0095     WeightType newSum = theSum + d;
0096     if ( newSum <= theSum ) return d;
0097     theSums.push_back(theSum = newSum);
0098     theWeights.push_back(d);
0099     theObjects.push_back(t);
0100     return theSum;
0101   }
0102 
0103   /**
0104    * Reweight an object previously inserted giving it a new weight. If
0105    * several equivalent objects exists, all of them will be
0106    * reweighted.
0107    */
0108   WeightType reweight(WeightType, const T &);
0109 
0110   /**
0111    * Erase an object, previously inserted. If the object had not been
0112    * inserted, nothing will happen. If several copies of the object
0113    * has been inserted, all will be removed removed. In all cases the
0114    * sum of probabilities is returned.
0115    */
0116   WeightType erase(const T &);
0117 
0118   /**
0119    * Replace all occurencies of told with tnew without changing the
0120    * probability for the entry.
0121    */
0122   void replace(const T & told, const T & tnew) {
0123     for ( iterator it = theObjects.begin(); it != theObjects.end(); ++it )
0124       if ( *it == told ) *it = tnew;
0125   }
0126 
0127   /**
0128    * Select an object randomly. Given a random number flatly
0129    * distributed in the interval ]0,1[ Select an object according to
0130    * the individual probabilities specified when they were
0131    * inserted. If rnd <= 0 or if rnd >= 1 or the Selector is empty, a
0132    * range_error will be thrown.
0133    * @param rnd a flat random number in the interval ]0,1[
0134    * @param remainder if non-zero the double pointed to will be set to
0135    * a uniform random number in the interval ]0,1[ calculated from the
0136    * fraction of rnd which was in the range of the selected object.
0137    */
0138   T & select(double rnd, double * remainder = 0) {
0139     return theObjects[iselect(rnd, remainder)];
0140   }
0141 
0142   /**
0143    * Selct an object randomly. Given a random number flatly
0144    * distributed in the interval ]0,1[ Select an object according to
0145    * the individual probabilities specified when they were
0146    * inserted. If rnd <= 0 or if rnd >= 1 or the Selector is empty, a
0147    * range_error will be thrown.
0148    */
0149   T & operator[](double rnd) {
0150     return select(rnd, 0);
0151   }
0152 
0153   /**
0154    * Selct an object randomly. Given a random number flatly
0155    * distributed in the interval ]0,1[ Select an object according to
0156    * the individual probabilities specified when they were
0157    * inserted. If rnd <= 0 or if rnd >= 1 or the Selector is empty, a
0158    * range_error will be thrown.
0159    * @param rnd a flat random number in the interval ]0,1[
0160    * @param remainder if non-zero the double pointed to will be set to
0161    * a uniform random number in the interval ]0,1[ calculated from the
0162    * fraction of rnd which was in the range of the selected object.
0163    */
0164   const T & select(double rnd, double * remainder = 0) const {
0165     return theObjects[iselect(rnd, remainder)];
0166   }
0167 
0168   /**
0169    * Selct an object randomly. Given a random number flatly
0170    * distributed in the interval ]0,1[ select an object according to
0171    * the individual probabilities specified when they were
0172    * inserted. If rnd <= 0 or if rnd >= 1 or the Selector is empty, a
0173    * range_error will be thrown.
0174    */
0175   const T & operator[](double rnd) const {
0176     return select(rnd, 0);
0177   }
0178 
0179   /**
0180    * Selct an object randomly. Given a random number generator which
0181    * generates flat random numbers in the interval ]0,1[ with the
0182    * <code>operator()()</code> function, select an object according to
0183    * the individual probabilities specified when they were
0184    * inserted. If the generated number is outside the allowed range or
0185    * the Selector is empty, a range_error will be thrown. The
0186    * generator should have a push_back function which will be used
0187    * push back a uniform random number in the interval ]0,1[
0188    * calculated from the fraction of rnd which was in the range of the
0189    * selected object.
0190    */
0191   template <typename RNDGEN>
0192   T & select(RNDGEN & rnd) {
0193     double rem = 0.0;
0194     T & t = select(rnd(), &rem);
0195     rnd.push_back(rem);
0196     return t;
0197   }
0198 
0199   /**
0200    * Selct an object randomly. Given a random number generator which
0201    * generates flat random numbers in the interval ]0,1[ with the
0202    * <code>operator()()</code> function, select an object according to
0203    * the individual probabilities specified when they were
0204    * inserted. If the generated number is outside the allowed range or
0205    * the Selector is empty, a range_error will be thrown. The
0206    * generator should have a push_back function which will be used
0207    * push back a uniform random number in the interval ]0,1[
0208    * calculated from the fraction of rnd which was in the range of the
0209    * selected object.
0210    */
0211   template <typename RNDGEN>
0212   const T & select(RNDGEN & rnd) const {
0213     double rem = 0.0;
0214     const T & t = select(rnd(), &rem);
0215     rnd.push_back(rem);
0216     return t;
0217   }
0218 
0219   /**
0220    * Return the sum of probabilities of the objects inserted. Note
0221    * that probabilities specified when objects are inserted are
0222    * rescaled with this number to give unit probability for
0223    * 'select()'.
0224    */
0225   WeightType sum() const { return theSum; }
0226 
0227   /**
0228    * Access to the <code>begin()</code> iterator of the underlying
0229    * vector of objects.
0230    */
0231   const_iterator begin() const { return theObjects.begin(); }
0232 
0233   /**
0234    * Access to the <code>end()</code> iterator in the underlying
0235    * vector of objects.
0236    */
0237   const_iterator end() const { return theObjects.end(); }
0238 
0239   /**
0240    * Returns true if the VSelector is empty.
0241    */
0242   bool empty() const { return theObjects.empty(); }
0243 
0244   /**
0245    * Returns the number of objects in the selector.
0246    */
0247   size_type size() const { return theObjects.size(); }
0248 
0249   /**
0250    * Allocate space for a number of objects in the underlying vectors.
0251    */
0252   void reserve(size_type reserved) {
0253     theSums.reserve(reserved);
0254     theWeights.reserve(reserved);
0255     theObjects.reserve(reserved);
0256   }
0257 
0258   /**
0259    * Erases all objects.
0260    */
0261   void clear() {
0262     theSums.clear();
0263     theWeights.clear();
0264     theObjects.clear();
0265     theSum = WeightType();
0266   }
0267 
0268   /**
0269    * Output to a stream.
0270    */
0271   template <typename OStream>
0272   void output(OStream &) const;
0273 
0274   /**
0275    * Input from a stream.
0276    */
0277   template <typename IStream>
0278   void input(IStream &);
0279 
0280 protected:
0281 
0282   /**
0283    * Internal selection engine.
0284    */
0285   size_type iselect(double rnd, double * remainder) const;
0286 
0287 private:
0288 
0289   /**
0290    * The vector of accumulated weights for the objects in the selector
0291    */
0292   WeightVector theSums;
0293 
0294   /**
0295    * The vector of weights for the objects in the selector
0296    */
0297   WeightVector theWeights;
0298 
0299   /**
0300    * The vector of objects in the selector.
0301    */
0302   ObjectVector theObjects;
0303 
0304   /**
0305    * The sum of all weights.
0306    */
0307   WeightType theSum;
0308 
0309 };
0310 
0311 /**
0312  * Output a VSelector to a stream.
0313  */
0314 template <typename OStream, typename T, typename WeightType>
0315 inline OStream & operator<<(OStream & os,
0316                 const VSelector<T,WeightType> & s) {
0317   s.output(os);
0318   return os;
0319 }
0320 
0321 /**
0322  * Input a VSelector from a stream.
0323  */
0324 template <typename IStream, typename T, typename WeightType>
0325 inline IStream & operator>>(IStream & is, 
0326                 VSelector<T,WeightType> & s) {
0327   s.input(is);
0328   return is;
0329 }
0330 
0331 
0332 }
0333 
0334 #include "VSelector.tcc"
0335 
0336 #endif /* ThePEG_VSelector_H */