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 likelihood_serial_(likelihood_, calculation_is_clean_, shared_offset_)
41{
42 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, compare to original
56 // implementation in RooRealMPFE.cxx
57
58 RooArgList varList(*vars);
59
60 // Save in lists
63}
64
66{
67 if (get_manager()->process_manager().is_worker()) {
68 bool more;
69
71 assert(more);
72
73 switch (mode) {
76 assert(more);
77 auto message = get_manager()->messenger().receive_from_master_on_worker<zmq::message_t>(&more);
78 auto message_begin = message.data<update_state_t>();
79 auto message_end = message_begin + message.size() / sizeof(update_state_t);
80 std::vector<update_state_t> to_update(message_begin, message_end);
81 for (auto const &item : to_update) {
82 RooRealVar *rvar = static_cast<RooRealVar *>(vars_.at(item.var_index));
83 rvar->setVal(static_cast<double>(item.value));
84 if (rvar->isConstant() != item.is_constant) {
85 rvar->setConstant(static_cast<bool>(item.is_constant));
86 }
87 }
88
89 if (more) {
90 // offsets also incoming
92 assert(!more);
94 std::size_t N_offsets = offsets_message.size() / sizeof(ROOT::Math::KahanSum<double>);
98 }
99
100 break;
101 }
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 }
167 if (!to_update.empty() || update_offsets) {
168 ++state_id_;
169 zmq::message_t 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 zmq::message_t offsets_message(shared_offset_.offsets().begin(), shared_offset_.offsets().end());
175 std::move(message), std::move(offsets_message));
177 } else {
179 std::move(message));
180 }
181 }
182 }
183}
184
189
191{
192 if (get_manager()->process_manager().is_master()) {
193 // evaluate the serial likelihood to set the offsets
194 if (do_offset_ && shared_offset_.offsets().empty()) {
196 // note: we don't need to get the offsets from the serial likelihood, because they are already coupled through
197 // the shared_ptr
198 }
199
200 // update parameters that changed since last calculation (or creation if first time)
202
203 // master fills queue with tasks
205 for (std::size_t ix = 0; ix < N_tasks; ++ix) {
206 get_manager()->queue()->add({id_, state_id_, ix});
207 }
209
210 // wait for task results back from workers to master
212
214
215 // Note: initializing result_ to results_[0] instead of zero-initializing it makes
216 // a difference due to Kahan sum precision. This way, a single-worker run gives
217 // the same result as a run with serial likelihood. Adding the terms to a zero
218 // initial sum can cancel the carry in some cases, causing divergent values.
219 result_ = results_[0];
220 packedNaN.accumulate(results_[0].Sum());
221 for (auto item_it = results_.cbegin() + 1; item_it != results_.cend(); ++item_it) {
222 result_ += *item_it;
223 packedNaN.accumulate(item_it->Sum());
224 }
225 results_.clear();
226
227 if (packedNaN.getPayload() != 0) {
228 result_ = ROOT::Math::KahanSum<double>(packedNaN.getNaNWithPayload());
229 }
230
231 if (TMath::IsNaN(result_.Sum())) {
232 RooAbsReal::logEvalError(nullptr, GetName().c_str(), "function value is NAN");
233 }
234 }
235}
236
237// --- RESULT LOGISTICS ---
238
240{
242
243 if (numErrors) {
244 // Clear error list on local side
246 }
247
249 zmq::message_t message(sizeof(task_result_t));
250 memcpy(message.data(), &task_result, sizeof(task_result_t));
251 get_manager()->messenger().send_from_worker_to_master(std::move(message));
252}
253
254bool LikelihoodJob::receive_task_result_on_master(const zmq::message_t &message)
255{
256 auto task_result = message.data<task_result_t>();
257 results_.emplace_back(task_result->value, task_result->carry);
258 if (task_result->has_errors) {
259 RooAbsReal::logEvalError(nullptr, "LikelihoodJob", "evaluation errors at the worker processes", "no servervalue");
260 }
263 return job_completed;
264}
265
266// --- END OF RESULT LOGISTICS ---
267
269{
270 assert(get_manager()->process_manager().is_worker());
271
272 double section_first = 0;
273 double section_last = 1;
274 if (getNEventTasks() > 1) {
275 std::size_t event_task = task % getNEventTasks();
276 std::size_t N_events = likelihood_->numDataEntries();
277 if (event_task > 0) {
278 std::size_t first = N_events * event_task / getNEventTasks();
279 section_first = static_cast<double>(first) / N_events;
280 }
281 if (event_task < getNEventTasks() - 1) {
282 std::size_t last = N_events * (event_task + 1) / getNEventTasks();
283 section_last = static_cast<double>(last) / N_events;
284 }
285 }
286
287 switch (likelihood_type_) {
290 result_ = likelihood_->evaluatePartition({section_first, section_last}, 0, 0);
291 if (do_offset_ && section_last == 1) {
292 // we only subtract at the end of event sections, otherwise the offset is subtracted for each event split
294 }
295 break;
296 }
298 result_ = likelihood_->evaluatePartition({0, 1}, 0, 0);
301 }
302 break;
303 }
304 case LikelihoodType::sum: {
305 std::size_t components_first = 0;
306 std::size_t components_last = likelihood_->getNComponents();
307 if (getNComponentTasks() > 1) {
308 std::size_t component_task = task / getNEventTasks();
310 if (component_task == getNComponentTasks() - 1) {
311 components_last = likelihood_->getNComponents();
312 } else {
313 components_last = likelihood_->getNComponents() * (component_task + 1) / getNComponentTasks();
314 }
315 }
316
319 for (std::size_t comp_ix = components_first; comp_ix < components_last; ++comp_ix) {
320 auto component_result = likelihood_->evaluatePartition({section_first, section_last}, comp_ix, comp_ix + 1);
321 packedNaN.accumulate(component_result.Sum());
322 if (do_offset_ && section_last == 1 &&
324 // we only subtract at the end of event sections, otherwise the offset is subtracted for each event split
326 } else {
328 }
329 }
330 if (packedNaN.getPayload() != 0) {
331 result_ = ROOT::Math::KahanSum<double>(packedNaN.getNaNWithPayload());
332 }
333
334 break;
335 }
336 }
337}
338
340{
344 printf("WARNING: when calling MinuitFcnGrad::setOffsetting after the run has already been started the "
345 "MinuitFcnGrad::likelihood_in_gradient object (a LikelihoodSerial) on the workers can no longer be "
346 "updated! This function (LikelihoodJob::enableOffsetting) can in principle be used outside of "
347 "MinuitFcnGrad, but be aware of this limitation. To do a minimization with a different offsetting "
348 "setting, please delete all RooFit::MultiProcess based objects so that the forked processes are killed "
349 "and then set up a new RooMinimizer.\n");
351 }
352}
353
354#define PROCESS_VAL(p) \
355 case (p): s = #p; break;
356
357std::ostream &operator<<(std::ostream &out, const LikelihoodJob::update_state_mode value)
358{
359 std::string s;
360 switch (value) {
363 default: s = std::to_string(static_cast<int>(value));
364 }
365 return out << s;
366}
367
368#undef PROCESS_VAL
369
370} // namespace TestStatistics
371} // namespace RooFit
#define PROCESS_VAL(p)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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
const_iterator begin() const
const_iterator end() const
bool isConstant() const
Check if the "Constant" attribute is set.
Definition RooAbsArg.h:283
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
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:45
std::size_t state_id_
Definition Job.h:46
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
value_t receive_from_master_on_worker(bool *more=nullptr)
Definition Messenger.h:176
void send_from_worker_to_master(T &&item)
specialization that sends the final message
Definition Messenger.h:192
void publish_from_master_to_workers(T &&item)
specialization that sends the final message
Definition Messenger.h:150
virtual void add(JobTask job_task)=0
Enqueue a task.
void enableOffsetting(bool flag) override
bool receive_task_result_on_master(const zmq::message_t &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.