Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
LikelihoodJob.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * PB, Patrick Bos, Netherlands eScience Center, p.bos@esciencecenter.nl
5 *
6 * Copyright (c) 2021, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13#include "LikelihoodJob.h"
14
15#include "LikelihoodSerial.h"
27#include "RooRealVar.h"
28#include "RooNaNPacker.h"
29
30#include "TMath.h" // IsNaN
31
32namespace RooFit {
33namespace TestStatistics {
34
35LikelihoodJob::LikelihoodJob(std::shared_ptr<RooAbsL> likelihood,
36 std::shared_ptr<WrapperCalculationCleanFlags> calculation_is_clean, SharedOffset offset)
37 : LikelihoodWrapper(std::move(likelihood), std::move(calculation_is_clean), std::move(offset)),
38 n_event_tasks_(MultiProcess::Config::LikelihoodJob::defaultNEventTasks),
39 n_component_tasks_(MultiProcess::Config::LikelihoodJob::defaultNComponentTasks),
40 offsets_previous_(shared_offset_.offsets()),
41 likelihood_serial_(likelihood_, calculation_is_clean_, shared_offset_)
42{
43 init_vars();
44}
45
46// This is a separate function (instead of just in ctor) for historical reasons.
48{
49 // Empty current lists
52
53 // Retrieve non-constant parameters
54 std::unique_ptr<RooArgSet> vars{likelihood_->getParameters()};
55 // TODO: make sure this is the right list of parameters
56
57 RooArgList varList(*vars);
58
59 // Save in lists
60 vars_.add(varList);
61 save_vars_.addClone(varList);
62}
63
65{
66 if (get_manager()->process_manager().is_worker()) {
67 bool more;
68
70 assert(more);
71
72 switch (mode) {
75 assert(more);
77 auto message_begin = message.data<update_state_t>();
78 auto message_end = message_begin + message.size() / sizeof(update_state_t);
79 std::vector<update_state_t> to_update(message_begin, message_end);
80 for (auto const &item : to_update) {
81 RooRealVar *rvar = static_cast<RooRealVar *>(vars_.at(item.var_index));
82 rvar->setVal(static_cast<double>(item.value));
83 if (rvar->isConstant() != item.is_constant) {
84 rvar->setConstant(static_cast<bool>(item.is_constant));
85 }
86 }
87
88 if (more) {
89 // offsets also incoming
90 auto offsets_message =
92 assert(!more);
93 auto offsets_message_begin = offsets_message.data<ROOT::Math::KahanSum<double>>();
94 std::size_t N_offsets = offsets_message.size() / sizeof(ROOT::Math::KahanSum<double>);
95 shared_offset_.offsets().resize(N_offsets);
96 auto offsets_message_end = offsets_message_begin + N_offsets;
97 std::copy(offsets_message_begin, offsets_message_end, shared_offset_.offsets().begin());
98 }
99
100 break;
101 }
103 LikelihoodWrapper::enableOffsetting(get_manager()->messenger().receive_from_master_on_worker<bool>(&more));
104 assert(!more);
105 break;
106 }
107 }
108 }
109}
110
112{
113 std::size_t val = n_event_tasks_;
115 val = 1;
116 }
117 if (val > likelihood_->getNEvents()) {
118 val = likelihood_->getNEvents();
119 }
120 return val;
121}
122
123/// \warning In automatic mode, this function can start MultiProcess (forks, starts workers, etc)!
125{
126 std::size_t val = n_component_tasks_;
128 val = get_manager()
130 .N_workers(); // get_manager() is the call that can start MultiProcess, mentioned above
131 }
132 if (val > likelihood_->getNComponents()) {
133 val = likelihood_->getNComponents();
134 }
135 return val;
136}
137
139{
140 if (get_manager()->process_manager().is_master()) {
141 bool valChanged = false;
142 bool constChanged = false;
143 std::vector<update_state_t> to_update;
144 for (std::size_t ix = 0u; ix < static_cast<std::size_t>(vars_.size()); ++ix) {
145 valChanged = !vars_[ix].isIdentical(save_vars_[ix], true);
146 constChanged = (vars_[ix].isConstant() != save_vars_[ix].isConstant());
147
148 if (valChanged || constChanged) {
149 if (constChanged) {
150 (static_cast<RooRealVar *>(&save_vars_[ix]))->setConstant(vars_[ix].isConstant());
151 }
152 // TODO: Check with Wouter why he uses copyCache in MPFE; makes it very difficult to extend, because
153 // copyCache is protected (so must be friend). Moved setting value to if-block below.
154 // _saveVars[ix].copyCache(&_vars[ix]);
155
156 // send message to queue (which will relay to workers)
157 RooAbsReal *rar_val = dynamic_cast<RooAbsReal *>(&vars_[ix]);
158 if (rar_val) {
159 double val = rar_val->getVal();
160 dynamic_cast<RooRealVar *>(&save_vars_[ix])->setVal(val);
161 bool isC = vars_[ix].isConstant();
162 to_update.push_back(update_state_t{ix, val, isC});
163 }
164 }
165 }
166 bool update_offsets = isOffsetting() && shared_offset_.offsets() != offsets_previous_;
167 if (!to_update.empty() || update_offsets) {
168 ++state_id_;
169 RooFit::MultiProcess::Message message(to_update.begin(), to_update.end());
170 // always send Job id first! This is used in worker_loop to route the
171 // update_state call to the correct Job.
172 if (update_offsets) {
173 RooFit::MultiProcess::Message offsets_message(shared_offset_.offsets().begin(),
174 shared_offset_.offsets().end());
176 std::move(message), std::move(offsets_message));
178 } else {
180 std::move(message));
181 }
182 }
183 }
184}
185
190
192{
193 if (get_manager()->process_manager().is_master()) {
194 // evaluate the serial likelihood to set the offsets
195 if (do_offset_ && shared_offset_.offsets().empty()) {
197 // note: we don't need to get the offsets from the serial likelihood, because they are already coupled through
198 // the shared_ptr
199 }
200
201 // update parameters that changed since last calculation (or creation if first time)
203
204 // master fills queue with tasks
205 auto N_tasks = getNEventTasks() * getNComponentTasks();
206 for (std::size_t ix = 0; ix < N_tasks; ++ix) {
207 get_manager()->queue()->add({id_, state_id_, ix});
208 }
209 n_tasks_at_workers_ = N_tasks;
210
211 // wait for task results back from workers to master
213
214 RooNaNPacker packedNaN;
215
216 // Note: initializing result_ to results_[0] instead of zero-initializing it makes
217 // a difference due to Kahan sum precision. This way, a single-worker run gives
218 // the same result as a run with serial likelihood. Adding the terms to a zero
219 // initial sum can cancel the carry in some cases, causing divergent values.
220 result_ = results_[0];
221 packedNaN.accumulate(results_[0].Sum());
222 for (auto item_it = results_.cbegin() + 1; item_it != results_.cend(); ++item_it) {
223 result_ += *item_it;
224 packedNaN.accumulate(item_it->Sum());
225 }
226 results_.clear();
227
228 if (packedNaN.getPayload() != 0) {
230 }
231
232 if (TMath::IsNaN(result_.Sum())) {
233 RooAbsReal::logEvalError(nullptr, GetName().c_str(), "function value is NAN");
234 }
235 }
236}
237
238// --- RESULT LOGISTICS ---
239
241{
242 int numErrors = RooAbsReal::numEvalErrors();
243
244 if (numErrors) {
245 // Clear error list on local side
247 }
248
249 task_result_t task_result{id_, result_.Result(), result_.Carry(), numErrors > 0};
251 memcpy(message.data(), &task_result, sizeof(task_result_t));
252 get_manager()->messenger().send_from_worker_to_master(std::move(message));
253}
254
256{
257 auto task_result = message.data<task_result_t>();
258 results_.emplace_back(task_result->value, task_result->carry);
259 if (task_result->has_errors) {
260 RooAbsReal::logEvalError(nullptr, "LikelihoodJob", "evaluation errors at the worker processes", "no servervalue");
261 }
263 bool job_completed = (n_tasks_at_workers_ == 0);
264 return job_completed;
265}
266
267// --- END OF RESULT LOGISTICS ---
268
269void LikelihoodJob::evaluate_task(std::size_t task)
270{
271 assert(get_manager()->process_manager().is_worker());
272
273 double section_first = 0;
274 double section_last = 1;
275 if (getNEventTasks() > 1) {
276 std::size_t event_task = task % getNEventTasks();
277 std::size_t N_events = likelihood_->numDataEntries();
278 if (event_task > 0) {
279 std::size_t first = N_events * event_task / getNEventTasks();
280 section_first = static_cast<double>(first) / N_events;
281 }
282 if (event_task < getNEventTasks() - 1) {
283 std::size_t last = N_events * (event_task + 1) / getNEventTasks();
284 section_last = static_cast<double>(last) / N_events;
285 }
286 }
287
288 switch (likelihood_type_) {
291 result_ = likelihood_->evaluatePartition({section_first, section_last}, 0, 0);
292 if (do_offset_ && section_last == 1) {
293 // we only subtract at the end of event sections, otherwise the offset is subtracted for each event split
295 }
296 break;
297 }
299 result_ = likelihood_->evaluatePartition({0, 1}, 0, 0);
302 }
303 break;
304 }
305 case LikelihoodType::sum: {
306 std::size_t components_first = 0;
307 std::size_t components_last = likelihood_->getNComponents();
308 if (getNComponentTasks() > 1) {
309 std::size_t component_task = task / getNEventTasks();
310 components_first = likelihood_->getNComponents() * component_task / getNComponentTasks();
311 if (component_task == getNComponentTasks() - 1) {
312 components_last = likelihood_->getNComponents();
313 } else {
314 components_last = likelihood_->getNComponents() * (component_task + 1) / getNComponentTasks();
315 }
316 }
317
319 RooNaNPacker packedNaN;
320 for (std::size_t comp_ix = components_first; comp_ix < components_last; ++comp_ix) {
321 auto component_result = likelihood_->evaluatePartition({section_first, section_last}, comp_ix, comp_ix + 1);
322 packedNaN.accumulate(component_result.Sum());
323 if (do_offset_ && section_last == 1 &&
325 // we only subtract at the end of event sections, otherwise the offset is subtracted for each event split
326 result_ += (component_result - shared_offset_.offsets()[comp_ix]);
327 } else {
328 result_ += component_result;
329 }
330 }
331 if (packedNaN.getPayload() != 0) {
333 }
334
335 break;
336 }
337 }
338}
339
341{
345 printf("WARNING: when calling MinuitFcnGrad::setOffsetting after the run has already been started the "
346 "MinuitFcnGrad::likelihood_in_gradient object (a LikelihoodSerial) on the workers can no longer be "
347 "updated! This function (LikelihoodJob::enableOffsetting) can in principle be used outside of "
348 "MinuitFcnGrad, but be aware of this limitation. To do a minimization with a different offsetting "
349 "setting, please delete all RooFit::MultiProcess based objects so that the forked processes are killed "
350 "and then set up a new RooMinimizer.\n");
352 }
353}
354
355#define PROCESS_VAL(p) \
356 case (p): s = #p; break;
357
358std::ostream &operator<<(std::ostream &out, const LikelihoodJob::update_state_mode value)
359{
360 std::string s;
361 switch (value) {
364 default: s = std::to_string(static_cast<int>(value));
365 }
366 return out << s;
367}
368
369#undef PROCESS_VAL
370
371} // namespace TestStatistics
372} // namespace RooFit
#define PROCESS_VAL(p)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
The Kahan summation is a compensated summation algorithm, which significantly reduces numerical error...
Definition Util.h:141
T Sum() const
Definition Util.h:259
T Result() const
Definition Util.h:264
T Carry() const
Definition Util.h:269
bool isConstant() const
Check if the "Constant" attribute is set.
Definition RooAbsArg.h:284
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
void setConstant(bool value=true)
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
static Int_t numEvalErrors()
Return the number of logged evaluation errors since the last clearing.
void logEvalError(const char *message, const char *serverValueString=nullptr) const
Log evaluation error message.
static void clearEvalErrorLog()
Clear the stack of evaluation error messages.
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
ProcessManager & process_manager() const
std::size_t id_
Definition Job.h:46
std::size_t state_id_
Definition Job.h:47
JobManager * get_manager()
Get JobManager instance; create and activate if necessary.
Definition Job.cxx:112
void gather_worker_results()
Wait for all tasks to be retrieved for the current Job.
Definition Job.cxx:126
A contiguous byte buffer used as the unit of interprocess communication.
Definition Message.h:30
std::size_t size() const
Size of the message in bytes.
Definition Message.h:65
value_t receive_from_master_on_worker(bool *more=nullptr)
Definition Messenger.h:175
void send_from_worker_to_master(T &&item)
specialization that sends the final part of a message
Definition Messenger.h:190
void publish_from_master_to_workers(T &&item)
specialization that sends the final part of a message
Definition Messenger.h:145
virtual void add(JobTask job_task)=0
Enqueue a task.
void enableOffsetting(bool flag) override
bool receive_task_result_on_master(const RooFit::MultiProcess::Message &message) override
void evaluate_task(std::size_t task) override
std::vector< ROOT::Math::KahanSum< double > > results_
void send_back_task_result_from_worker(std::size_t task) override
void update_state() override
Virtual function to update any necessary state on workers.
void evaluate() override
Triggers (possibly asynchronous) evaluation of the likelihood.
SharedOffset::OffsetVec offsets_previous_
ROOT::Math::KahanSum< double > result_
LikelihoodJob(std::shared_ptr< RooAbsL > _likelihood, std::shared_ptr< WrapperCalculationCleanFlags > calculation_is_clean, SharedOffset offset)
void evaluate() override
Triggers (possibly asynchronous) evaluation of the likelihood.
Virtual base class for implementation of likelihood calculation strategies.
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
OffsetVec & offsets()
std::size_t State
Definition types.h:23
std::ostream & operator<<(std::ostream &out, const LikelihoodJob::update_state_mode value)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73
Bool_t IsNaN(Double_t x)
Definition TMath.h:905
static constexpr std::size_t automaticNEventTasks
Definition Config.h:34
static constexpr std::size_t automaticNComponentTasks
Definition Config.h:35
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.
float getPayload() const
Retrieve packed float.
double getNaNWithPayload() const
Retrieve a NaN with the current float payload packed into the mantissa.
void accumulate(double val)
Accumulate a packed float from another NaN into this.