ComPWA
Common Partial-Wave-Analysis Framework
Generate.cpp
Go to the documentation of this file.
1 #include <algorithm>
2 
3 #include "Core/Exceptions.hpp"
4 #include "Core/Generator.hpp"
5 #include "Core/Kinematics.hpp"
6 #include "Core/Logging.hpp"
7 #include "Core/ProgressBar.hpp"
8 #include "Core/Random.hpp"
9 #include "Data/DataSet.hpp"
10 #include "Data/Generate.hpp"
11 
12 #include "ThirdParty/parallelstl/include/pstl/algorithm"
13 #include "ThirdParty/parallelstl/include/pstl/execution"
14 
15 namespace ComPWA {
16 namespace Data {
17 
18 inline double uniform(double random, double min, double max) {
19  return random * (max - min) + min;
20 }
21 
22 std::tuple<EventCollection, double>
23 generateBunch(unsigned int EventBunchSize, const ComPWA::Kinematics &Kinematics,
25  ComPWA::UniformRealNumberGenerator &RandomGenerator,
26  double generationMaxValue,
27  std::vector<ComPWA::Event>::const_iterator PhspStartIterator,
28  std::vector<ComPWA::Event>::const_iterator PhspTrueStartIterator,
29  bool InverseIntensityWeighting = false) {
30 
31  EventCollection SelectedEvents{Kinematics.getFinalStatePIDs()};
32 
33  auto NewEvents = EventCollection{
34  Kinematics.getFinalStatePIDs(),
35  {PhspTrueStartIterator, PhspTrueStartIterator + EventBunchSize}};
36  auto TempDataSet = Kinematics.convert(NewEvents);
37 
38  // evaluate the intensity
39  auto Intensities = Intensity.evaluate(TempDataSet.Data);
40 
41  // multiply with event weights and set events outside of phase space (not
42  // finite values = nan, inf) to zero
43  std::vector<double> WeightedIntensities(Intensities.size());
44  std::transform(pstl::execution::par_unseq, Intensities.begin(),
45  Intensities.end(), TempDataSet.Weights.begin(),
46  WeightedIntensities.begin(),
47  [](double intensity, double weight) {
48  if (std::isfinite(intensity)) {
49  return intensity * weight;
50  } else {
51  return 0.0;
52  }
53  });
54 
55  // determine maximum
56  double BunchMax(*std::max_element(pstl::execution::par_unseq,
57  WeightedIntensities.begin(),
58  WeightedIntensities.end()));
59 
60  // restart generation if we got above the current maximum
61  if (BunchMax > generationMaxValue) {
62  return std::make_tuple(SelectedEvents, BunchMax);
63  }
64 
65  // do hit and miss
66  // first generate random numbers (no multithreading here, to ensure
67  // deterministic behavior independent on the number of threads)
68  std::vector<double> RandomNumbers;
69  RandomNumbers.reserve(WeightedIntensities.size());
70  std::generate_n(std::back_inserter(RandomNumbers), WeightedIntensities.size(),
71  [&RandomGenerator, generationMaxValue]() {
72  return uniform(RandomGenerator(), 0, generationMaxValue);
73  });
74 
75  if (InverseIntensityWeighting) {
76  for (unsigned int i = 0; i < WeightedIntensities.size(); ++i) {
77  if (RandomNumbers[i] < WeightedIntensities[i]) {
78  SelectedEvents.Events.push_back(
79  {PhspStartIterator->FourMomenta, 1.0 / Intensities[i]});
80  }
81  ++PhspStartIterator;
82  }
83  } else {
84  for (unsigned int i = 0; i < WeightedIntensities.size(); ++i) {
85  if (RandomNumbers[i] < WeightedIntensities[i]) {
86  SelectedEvents.Events.push_back({PhspStartIterator->FourMomenta, 1.0});
87  }
88  ++PhspStartIterator;
89  }
90  }
91 
92  return std::make_tuple(SelectedEvents, BunchMax);
93 }
94 
95 EventCollection generate(unsigned int NumberOfEvents,
97  const ComPWA::PhaseSpaceEventGenerator &Generator,
99  ComPWA::UniformRealNumberGenerator &RandomGenerator) {
100  EventCollection GeneratedEvents{Kinematics.getFinalStatePIDs()};
101  if (NumberOfEvents <= 0)
102  return GeneratedEvents;
103 
104  // initialize generator output vector
105  unsigned int EventBunchSize(5000);
106  GeneratedEvents.Events.reserve(NumberOfEvents);
107  unsigned int TotalGeneratedEvents(0);
108 
109  double SafetyMargin(0.05);
110  double generationMaxValue(0.0);
111  unsigned int initialSeed = RandomGenerator.getSeed();
112 
113  LOG(INFO) << "Generating hit-and-miss sample: [" << NumberOfEvents
114  << " events] ";
115  ComPWA::ProgressBar bar(NumberOfEvents);
116  while (true) {
117  EventCollection TempEvents =
118  Generator.generate(EventBunchSize, RandomGenerator);
119 
120  TotalGeneratedEvents += EventBunchSize;
121 
122  auto Bunch =
123  generateBunch(EventBunchSize, Kinematics, Intensity, RandomGenerator,
124  generationMaxValue, TempEvents.Events.begin(),
125  TempEvents.Events.begin());
126 
127  EventCollection BunchEvents = std::get<0>(Bunch);
128  double MaximumWeight = std::get<1>(Bunch);
129 
130  // restart generation if we got above the current maximum
131  if (MaximumWeight > generationMaxValue) {
132  generationMaxValue = (1.0 + SafetyMargin) * MaximumWeight;
133 
134  if (GeneratedEvents.Events.size() > 0) {
135  GeneratedEvents.Events.clear();
136  RandomGenerator.setSeed(initialSeed);
137  bar = ComPWA::ProgressBar(NumberOfEvents);
138  LOG(INFO) << "Tools::generate() | Error in HitMiss "
139  "procedure: Maximum value of random number generation "
140  "smaller then amplitude maximum! We raise the maximum "
141  "to "
142  << generationMaxValue << " value and restart generation!";
143  continue;
144  }
145  }
146 
147  size_t AmountToAppend(BunchEvents.Events.size());
148  if (GeneratedEvents.Events.size() + BunchEvents.Events.size() >
149  NumberOfEvents) {
150  AmountToAppend = NumberOfEvents - GeneratedEvents.Events.size();
151  }
152 
153  GeneratedEvents.Events.insert(
154  GeneratedEvents.Events.end(),
155  std::make_move_iterator(BunchEvents.Events.begin()),
156  std::make_move_iterator(BunchEvents.Events.begin() + AmountToAppend));
157  bar.next(AmountToAppend);
158 
159  if (GeneratedEvents.Events.size() == NumberOfEvents)
160  break;
161  }
162  LOG(INFO) << "Successfully generated " << NumberOfEvents
163  << " with an efficiency of "
164  << 1.0 * NumberOfEvents / TotalGeneratedEvents;
165 
166  return GeneratedEvents;
167 }
168 
169 EventCollection generate(unsigned int NumberOfEvents,
171  ComPWA::UniformRealNumberGenerator &RandomGenerator,
173  const EventCollection &PhspSample,
174  const EventCollection &PhspSampleTrue) {
175  // Doing some checks
176  if (NumberOfEvents <= 0)
177  throw std::runtime_error("Tools::generate() negative number of events: " +
178  std::to_string(NumberOfEvents));
179 
180  if (PhspSampleTrue.Events.size() != PhspSample.Events.size())
181  throw std::runtime_error(
182  "Tools::generate() | We have a sample of true "
183  "phsp events, but the sample size doesn't match that one of "
184  "the phsp sample!");
185 
186  EventCollection GeneratedEvents{Kinematics.getFinalStatePIDs()};
187  GeneratedEvents.Events.resize(NumberOfEvents);
188 
189  double SafetyMargin(0.05);
190 
191  double maxSampleWeight(ComPWA::getMaximumSampleWeight(PhspSample));
192  if (PhspSampleTrue.Events.size()) {
193  double temp_maxweight(ComPWA::getMaximumSampleWeight(PhspSampleTrue));
194  if (temp_maxweight > maxSampleWeight)
195  maxSampleWeight = temp_maxweight;
196  }
197 
198  if (maxSampleWeight <= 0.0)
199  throw std::runtime_error("Tools::generate() Sample maximum value is zero!");
200  double generationMaxValue(maxSampleWeight * (1.0 + SafetyMargin));
201  unsigned int initialSeed = RandomGenerator.getSeed();
202 
203  LOG(INFO) << "Tools::generate() | Using " << generationMaxValue
204  << " as maximum value of the intensity.";
205 
206  auto const &PhspEvents = PhspSample;
207  unsigned int LastIndex(PhspEvents.Events.size() - 1);
208 
209  unsigned int EventBunchSize(5000);
210  if (PhspEvents.Events.size() < EventBunchSize)
211  EventBunchSize = PhspEvents.Events.size();
212 
213  auto CurrentStartIterator = PhspEvents.Events.begin();
214  auto CurrentTrueStartIterator = PhspEvents.Events.begin();
215  if (PhspSampleTrue.Events.size())
216  CurrentTrueStartIterator = PhspSampleTrue.Events.begin();
217  unsigned int CurrentStartIndex(0);
218  LOG(INFO) << "Generating hit-and-miss sample: [" << NumberOfEvents
219  << " events] ";
220  ComPWA::ProgressBar bar(NumberOfEvents);
221  while (true) {
222  if (CurrentStartIndex + EventBunchSize > LastIndex)
223  EventBunchSize = LastIndex - CurrentStartIndex;
224 
225  auto Bunch = generateBunch(EventBunchSize, Kinematics, Intensity,
226  RandomGenerator, generationMaxValue,
227  CurrentStartIterator, CurrentTrueStartIterator);
228 
229  EventCollection BunchEvents = std::get<0>(Bunch);
230  double MaximumWeight = std::get<1>(Bunch);
231 
232  // restart generation if we got above the current maximum
233  if (MaximumWeight > generationMaxValue) {
234  generationMaxValue = (1.0 + SafetyMargin) * MaximumWeight;
235  LOG(INFO) << "We raise the maximum to " << generationMaxValue;
236  if (GeneratedEvents.Events.size() > 0) {
237  GeneratedEvents.Events.clear();
238  RandomGenerator.setSeed(initialSeed);
239  CurrentStartIterator = PhspEvents.Events.begin();
240  CurrentTrueStartIterator = PhspEvents.Events.begin();
241  if (PhspSampleTrue.Events.size())
242  CurrentTrueStartIterator = PhspSampleTrue.Events.begin();
243  CurrentStartIndex = 0;
244  bar = ComPWA::ProgressBar(NumberOfEvents);
245  LOG(INFO) << "Tools::generate() | Error in HitMiss "
246  "procedure: Maximum value of random number generation "
247  "smaller then amplitude maximum! Restarting generation!";
248  }
249  continue;
250  }
251 
252  size_t AmountToAppend(BunchEvents.Events.size());
253  if (GeneratedEvents.Events.size() + BunchEvents.Events.size() >
254  NumberOfEvents) {
255  AmountToAppend = NumberOfEvents - GeneratedEvents.Events.size();
256  }
257  GeneratedEvents.Events.insert(
258  GeneratedEvents.Events.end(),
259  std::make_move_iterator(BunchEvents.Events.begin()),
260  std::make_move_iterator(BunchEvents.Events.begin() + AmountToAppend));
261 
262  bar.next(AmountToAppend);
263 
264  if (GeneratedEvents.Events.size() == NumberOfEvents)
265  break;
266 
267  // increment true iterator
268  std::advance(CurrentTrueStartIterator, EventBunchSize);
269  std::advance(CurrentStartIterator, EventBunchSize);
270  CurrentStartIndex += EventBunchSize;
271 
272  if (CurrentStartIndex >= LastIndex)
273  break;
274  }
275  double gen_eff = (double)GeneratedEvents.Events.size() / NumberOfEvents;
276  if (CurrentStartIndex > NumberOfEvents) {
277  gen_eff = (double)GeneratedEvents.Events.size() / CurrentStartIndex;
278  }
279  LOG(INFO) << "Efficiency of toy MC generation: " << gen_eff;
280 
281  return GeneratedEvents;
282 }
283 
285 generatePhsp(unsigned int NumberOfEvents,
286  const ComPWA::PhaseSpaceEventGenerator &Generator,
287  ComPWA::UniformRealNumberGenerator &RandomGenerator) {
288  EventCollection GeneratedPhsp = Generator.generate(0, RandomGenerator);
289  unsigned int EventBunchSize(5000);
290 
291  LOG(INFO) << "Generating phase-space MC: [" << NumberOfEvents << " events] ";
292 
293  ComPWA::ProgressBar bar(NumberOfEvents);
294  while (true) {
295  EventCollection TmpEvents =
296  Generator.generate(EventBunchSize, RandomGenerator);
297  std::vector<double> RandomNumbers;
298  RandomNumbers.reserve(EventBunchSize);
299  std::generate_n(std::back_inserter(RandomNumbers), EventBunchSize,
300  [&RandomGenerator]() { return RandomGenerator(); });
301  for (size_t i = 0; i < RandomNumbers.size(); ++i) {
302  if (GeneratedPhsp.Events.size() == NumberOfEvents)
303  break;
304  if (RandomNumbers[i] > TmpEvents.Events[i].Weight) {
305  continue;
306  }
307 
308  // Reset weights: weights are taken into account by hit&miss. The
309  // resulting sample is therefore unweighted
310  GeneratedPhsp.Events.push_back(
311  ComPWA::Event{TmpEvents.Events[i].FourMomenta, 1.0});
312  bar.next();
313  }
314  if (GeneratedPhsp.Events.size() == NumberOfEvents)
315  break;
316  }
317  return GeneratedPhsp;
318 }
319 
321  unsigned int NumberOfEvents, const ComPWA::Kinematics &Kinematics,
322  const ComPWA::PhaseSpaceEventGenerator &Generator,
324  ComPWA::UniformRealNumberGenerator &RandomGenerator) {
325  EventCollection GeneratedEventList{Kinematics.getFinalStatePIDs()};
326  if (NumberOfEvents <= 0)
327  return GeneratedEventList;
328  // initialize generator output vector
329  unsigned int EventBunchSize(5000);
330  GeneratedEventList.Events.reserve(NumberOfEvents);
331 
332  double SafetyMargin(0.05);
333  double generationMaxValue(0.0);
334  unsigned int initialSeed = RandomGenerator.getSeed();
335 
336  LOG(INFO)
337  << "Generating phase space sample (hit-and-miss importance sampled): ["
338  << NumberOfEvents << " events] ";
339  ComPWA::ProgressBar bar(NumberOfEvents);
340  while (true) {
341  EventCollection TempEventList =
342  Generator.generate(EventBunchSize, RandomGenerator);
343 
344  EventCollection BunchEvents{Kinematics.getFinalStatePIDs()};
345  double MaximumWeight;
346 
347  std::tie(BunchEvents, MaximumWeight) =
348  generateBunch(EventBunchSize, Kinematics, Intensity, RandomGenerator,
349  generationMaxValue, TempEventList.Events.begin(),
350  TempEventList.Events.begin(), true);
351 
352  // restart generation if we got above the current maximum
353  if (MaximumWeight > generationMaxValue) {
354  generationMaxValue = (1.0 + SafetyMargin) * MaximumWeight;
355  if (GeneratedEventList.Events.size() > 0) {
356  GeneratedEventList.Events.clear();
357  RandomGenerator.setSeed(initialSeed);
358  bar = ComPWA::ProgressBar(NumberOfEvents);
359  LOG(INFO)
360  << "Tools::generateImportanceSampledPhsp() | Error in HitMiss "
361  "procedure: Maximum value of random number generation "
362  "smaller then amplitude maximum! We raise the maximum "
363  "to "
364  << generationMaxValue << " value and restart generation!";
365  continue;
366  }
367  }
368 
369  size_t AmountToAppend(BunchEvents.Events.size());
370  if (GeneratedEventList.Events.size() + BunchEvents.Events.size() >
371  NumberOfEvents) {
372  AmountToAppend = NumberOfEvents - GeneratedEventList.Events.size();
373  }
374 
375  GeneratedEventList.Events.insert(
376  GeneratedEventList.Events.end(), BunchEvents.Events.begin(),
377  BunchEvents.Events.begin() + AmountToAppend);
378 
379  bar.next(AmountToAppend);
380 
381  if (GeneratedEventList.Events.size() == NumberOfEvents)
382  break;
383  }
384  // replace with std::reduce once standard is moved to c++17
385  double WeightSum(0.0);
386  for (auto const &Event : GeneratedEventList.Events) {
387  WeightSum += Event.Weight;
388  }
389 
390  // now just rescale the event weights so that sum(event weights) = # events
391  double rescale_factor(NumberOfEvents / WeightSum);
392  for (auto &Event : GeneratedEventList.Events) {
393  Event.Weight *= rescale_factor;
394  }
395 
396  return GeneratedEventList;
397 }
398 
399 } // namespace Data
400 } // namespace ComPWA
Interface class for PHSP event generators.
Definition: Generator.hpp:17
virtual const std::vector< pid > & getFinalStatePIDs() const =0
Get a vector of PIDs of the final state.
EventCollection generatePhsp(unsigned int NumberOfEvents, const ComPWA::PhaseSpaceEventGenerator &Generator, ComPWA::UniformRealNumberGenerator &RandomGenerator)
Definition: Generate.cpp:285
virtual ComPWA::Data::DataSet convert(const EventCollection &Events) const =0
double uniform(double random, double min, double max)
Definition: Generate.cpp:18
ComPWA exceptions.
virtual EventCollection generate(unsigned int NumberOfEvents, UniformRealNumberGenerator &RandomGenerator) const =0
Some useful functions for Monte-Carlo event generation.
EventCollection generateImportanceSampledPhsp(unsigned int NumberOfEvents, const ComPWA::Kinematics &Kinematics, const ComPWA::PhaseSpaceEventGenerator &Generator, ComPWA::Intensity &Intensity, ComPWA::UniformRealNumberGenerator &RandomGenerator)
Definition: Generate.cpp:320
std::vector< Event > Events
Definition: Event.hpp:34
virtual OutputType evaluate(const InputTypes &... args) noexcept=0
double Weight
Definition: Event.hpp:22
std::tuple< EventCollection, double > generateBunch(unsigned int EventBunchSize, const ComPWA::Kinematics &Kinematics, ComPWA::Intensity &Intensity, ComPWA::UniformRealNumberGenerator &RandomGenerator, double generationMaxValue, std::vector< ComPWA::Event >::const_iterator PhspStartIterator, std::vector< ComPWA::Event >::const_iterator PhspTrueStartIterator, bool InverseIntensityWeighting=false)
Definition: Generate.cpp:23
The Kinematics interface defines the conversion of Events to a DataSet.
Definition: Kinematics.hpp:19
EventCollection generate(unsigned int NumberOfEvents, const ComPWA::Kinematics &Kinematics, const ComPWA::PhaseSpaceEventGenerator &Generator, ComPWA::Intensity &Intensity, ComPWA::UniformRealNumberGenerator &RandomGenerator)
Definition: Generate.cpp:95
void next(size_t increment=1)
indicate the next step in process
Definition: ProgressBar.cpp:23
virtual int getSeed() const =0
Data structure containing all kinematic information of a physics event.
Definition: Event.hpp:20
double getMaximumSampleWeight(const EventCollection &Sample)
Definition: Event.cpp:27
virtual void setSeed(int seed)=0
Interface template for a general Function of the form OutputType Function(InputTypes) The concept clo...
Definition: Function.hpp:24