Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // -*- C++ -*-
0002 //
0003 // CompSelector.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_CompSelector_H
0010 #define THEPEG_CompSelector_H
0011 //
0012 // This is the declaration of the CompSelector class.
0013 //
0014 
0015 #include "ThePEG/Utilities/Selector.h"
0016 
0017 namespace ThePEG {
0018 
0019 /**
0020  * The CompSelector class works like the Selector class in that it can
0021  * be used to randomly select objects according to associated
0022  * probabilities. In addition, the CompSelector class is able to
0023  * handle the case where the associated probabilities are
0024  * overestimates and the selected object will be discarded according
0025  * to some weight. If then a weight above one is encountered, this
0026  * means that the overestimated probability for the selected object
0027  * was wrong and it should in fact have been higher. If this happens,
0028  * the CompSelecteor will go into compensation mode, which means that
0029  * the selected object will be oversampled a period after the
0030  * violation to compensate for having been undersampled before. Also
0031  * the associated probability is adjusted to reflect the new
0032  * overestimate.
0033  *
0034  * The available functions are not as many as in Selector, and some of
0035  * the works somewhat differently. Before starting sampling the
0036  * objects should be added to a CompSelector object with the insert()
0037  * function. To selct an object the select() function should be
0038  * used. After that the weight with which the object should be
0039  * accepted should be presented with the reweight() function which
0040  * normally returns zero. If, however, the weight is larger than unity
0041  * the new overestimated probability is returned and the CompSelector
0042  * enters the compensating mode. Note that the weight is passed as a
0043  * reference and may be changed in by the reweight function if in the
0044  * compensating mode.
0045  */
0046 template <typename T, typename WeightType = double>
0047 class CompSelector {
0048 
0049 public:
0050 
0051   /** @name Standard constructors and destructors. */
0052   //@{
0053   /**
0054    * The default constructor. The optional argument gives the margin
0055    * used to get a new overestimated probability for an object when
0056    * entering compensation mode.
0057    */
0058   CompSelector(double newMargin = 1.1, double newTolerance = 1.0e-6)
0059     : N(0), last(), theMargin(newMargin), theTolerance(newTolerance) {}
0060   //@}
0061 
0062 public:
0063 
0064   /** @name The main function controlling the selection and compensation. */
0065   //@{
0066   /**
0067    * Insert an object given a probability for this object. If the
0068    * probability is zero or negative, the object will not be inserted
0069    * and the probability itself is returned. Otherwise the sum of
0070    * probabilities so far is returned. Note that if selection has
0071    * already started and this CompSelector is in compensating mode, it
0072    * will immediately leave this mode and the selection procedure will
0073    * start from scratch.
0074    */
0075   WeightType insert(WeightType d, const T & t) {
0076     reset();
0077     return selector.insert(d, t);
0078   }
0079 
0080   /**
0081    * Selct an object randomly. Given a random number generator which
0082    * generates flat random numbers in the interval ]0,1[ with the
0083    * <code>operator()()</code> function, select an object according to
0084    * the individual probabilities specified when they were
0085    * inserted. If the generated number is outside the allowed range or
0086    * the Selector is empty, a range_error will be thrown. The
0087    * generator should have a push_back function which will be used
0088    * push back a uniform random number in the interval ]0,1[
0089    * calculated from the fraction of rnd which was in the range of the
0090    * selected object.
0091    */
0092   template <typename RNDGEN>
0093   T & select(RNDGEN & rnd) {
0094     ++N;
0095     if ( !compensating() ) last = selector.select(rnd);
0096     return last;
0097   }
0098 
0099   /**
0100    * Report the weight associated with the last selected
0101    * object. Returns the zero if weight was below unity, otherwise the
0102    * compensation mode will be entered and the new overestimated
0103    * probabilty for the last selected object will be returned.
0104    */
0105   WeightType reweight(double & weight) {
0106     if ( abs(weight) > 1.0 + tolerance() ) {
0107       // Retrieve the old overestimate of the object by seing how much
0108       // the summed weights are decreased when removing the object.
0109       WeightType oldtot = selector.sum();
0110       WeightType oldmax = oldtot - selector.erase(last);
0111       WeightType newmax = oldmax*abs(weight)*margin();
0112       WeightType newtot = selector.insert(newmax, last);
0113       double rat = newmax/oldmax;
0114       
0115       // Setup the new compensation level.
0116       Level level;
0117       level.weight = 1.0/rat;
0118       level.lastN = long(N*newtot/oldtot);
0119       
0120       // If we are already compensating, reweight the previous
0121       // compensation levels.
0122       for ( int i = 0, M = levels.size(); i < M; ++i ) {
0123     levels[i].lastN = long(levels[i].lastN*newtot/oldtot);
0124     levels[i].weight /= rat;
0125       }
0126       levels.push_back(level);
0127       weight /= rat;
0128       return newmax;
0129     }
0130     
0131     // If we are compensating we should only accept the selection if the
0132     // weight is above the previous overestimate.
0133     if ( compensating() ) if ( abs(weight) < levels.back().weight ) weight = 0.0;
0134     
0135     return WeightType();
0136   }
0137 
0138   /**
0139    * Exit compensation mode and start selection procedure from
0140    * scratch.
0141    */
0142   void reset() {
0143     N = 0;
0144     levels.clear();
0145     last = T();
0146   }
0147 
0148   /**
0149    * Erases all objects.
0150    */
0151   void clear() {
0152     selector.clear();
0153     reset();
0154   }
0155 
0156   /**
0157    * Set the margin used to get a new overestimated probability for an
0158    * object when entering compensation mode.
0159    */
0160   void margin(double m) { theMargin = m; }
0161 
0162   /**
0163    * Set the tolerance for how much a weight is allowed to be
0164    * larger than unity before starting the compensation.
0165    */
0166   void tolerance(double t) { theTolerance = t; }
0167   //@}
0168 
0169 
0170   /** @name Simple access functions. */
0171   //@{
0172   /**
0173    * Return true if this CompSelector is in a compensating state.
0174    */
0175   bool compensating() {
0176     // Leave all levels which has reached there 'expiry date'.
0177     while ( levels.size() && levels.back().lastN < N ) levels.pop_back();
0178     return !levels.empty();
0179   }
0180 
0181   /**
0182    * If in a compensating mode, return the number of selection needed
0183    * before exiting this mode.
0184    */
0185   long compleft() const { return levels.empty()? 0: levels.back().lastN - N; }
0186 
0187   /**
0188    * Return the sum of probabilities of the objects inserted. Note
0189    * that probabilities specified when objects are inserted are
0190    * rescaled with this number to give unit probability for
0191    * 'select()'.
0192    */
0193   WeightType sum() const { return selector.sum(); }
0194 
0195   /**
0196    * Return the margin used to get a new overestimated probability for an
0197    * object when entering compensation mode.
0198    */
0199   double margin() const { return theMargin; }
0200 
0201   /**
0202    * Return the tolerance for how much a weight is allowed to be
0203    * larger than unity before starting the compensation.
0204    */
0205   double tolerance() const { return theTolerance; }
0206   //@}
0207 
0208   /** @name I/O functions. */
0209   //@{
0210   /**
0211    * Output to a stream.
0212    */
0213   template <typename OStream>
0214   void output(OStream & os) const {
0215     os << selector << N << last << theMargin << theTolerance << levels.size();
0216     for ( int i = 0, M = levels.size(); i < M; ++i )
0217       os << levels[i].lastN << levels[i].weight;
0218   }
0219 
0220   /**
0221    * Input from a stream.
0222    */
0223   template <typename IStream>
0224   void input(IStream & is) {
0225     long M;
0226     is >> selector >> N >> last >> theMargin >> theTolerance >> M;
0227     levels.resize(M);
0228     for ( int i = 0; i < M; ++i ) is >> levels[i].lastN >> levels[i].weight;
0229   }
0230   //@}
0231 
0232 private:
0233 
0234   /**
0235    * Internal struct used for bookkeeping when compensating.
0236    */
0237   struct Level {
0238 
0239     /**
0240      * The selection number at which point this level of compensation
0241      * is ended.
0242      */
0243     long lastN;
0244 
0245     /**
0246      * The minimum weight allowed when compensating on this level.
0247      */
0248     double weight;
0249 
0250   };
0251 
0252 private:
0253 
0254   /**
0255    * The underlying selector
0256    */
0257   Selector<T,WeightType> selector;
0258 
0259   /**
0260    * The number of selections so far.
0261    */
0262   long N;
0263 
0264   /**
0265    * The last selected object.
0266    */
0267   T last;
0268 
0269   /**
0270    * The margin used to get a new overestimated probability for an
0271    * object when entering compensation mode.
0272    */
0273   double theMargin;
0274 
0275   /**
0276    * Set the tolerance for how much a weight is allowed to be
0277    * larger than unity before starting the compensation.
0278    */
0279   double theTolerance;
0280 
0281   /**
0282    * The currently active compensation levels.
0283    */
0284   vector<Level> levels;
0285 
0286 };
0287 
0288 /**
0289  * Output a Selector to a stream.
0290  */
0291 template <typename OStream, typename T, typename WeightType>
0292 inline OStream & operator<<(OStream & os,
0293                 const CompSelector<T,WeightType> & s) {
0294   s.output(os);
0295   return os;
0296 }
0297 
0298 /**
0299  * Input a Selector from a stream.
0300  */
0301 template <typename IStream, typename T, typename WeightType>
0302 inline IStream & operator>>(IStream & is,
0303                 CompSelector<T,WeightType> & s) {
0304   s.input(is);
0305   return is;
0306 }
0307 
0308 }
0309 
0310 #endif /* THEPEG_CompSelector_H */