Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
ContourEngine.cpp
Go to the documentation of this file.
1#include "ContourEngine.h"
2#include <gsl/gsl_sf_erf.h>
3
4namespace {
5
6std::size_t count_total_points(const Contour& c) {
7 std::size_t n = 0;
8 for (const auto& path : c.paths) {
9 n += path.size();
10 }
11 return n;
12}
13
14}
15
16ContourEngine::ContourEngine(std::shared_ptr<ILikelihood> base, const ContourConfig &cfg) : cfg(cfg) {
17 // MAJ : Maybe allow to change fit backend, for now only Minuit hardcoded.
18
19 ProfilerMode profiler_mode = cfg.profile_backend;
20 std::shared_ptr<Profiler> profiler =
21 std::make_shared<Profiler>(
23 profiler_mode
24 );
25
26 std::shared_ptr<IProfilingStrategy> strategy;
28 strategy = std::make_shared<SliceProfilingStrategy>(cfg.x_id, cfg.y_id, cfg.fr);
29 } else {
30 strategy = std::make_shared<ProjectionProfilingStrategy>(cfg.x_id, cfg.y_id, cfg.fr);
31 }
32
33 std::shared_ptr<ILikelihood> maybe_constrained_base = base;
35 std::vector<std::size_t> constrained_idx;
36 for (size_t i = 0; i < cfg.fr.p_hat.size(); i++) {
37 if (i != cfg.x_id && i != cfg.y_id)
38 constrained_idx.emplace_back(i);
39 }
40
41 maybe_constrained_base = std::make_shared<WithGaussianConstraints>(
42 base,
43 this->build_constraints_distribution(),
44 constrained_idx
45 );
46 }
47
48 this->likelihood = ProfiledLikelihood2D(maybe_constrained_base, profiler, strategy);
49
50 if (cfg.fallback_contour_method.has_value()) {
51 this->extractor = std::make_shared<WithFallback>(
52 this->build_contour_extractor(cfg.primary_contour_method),
53 this->build_contour_extractor(cfg.fallback_contour_method.value())
54 );
55 } else {
56 this->extractor = this->build_contour_extractor(cfg.primary_contour_method);
57 }
58}
59
60Contour ContourEngine::compute_contour(double z, std::array<double, 4> bounds, std::size_t resolution) {
61 using clock = std::chrono::steady_clock;
62 const auto t0 = clock::now();
63
64 const double x_ref = cfg.fr.p_hat.at(cfg.x_id);
65 const double y_ref = cfg.fr.p_hat.at(cfg.y_id);
66
67 const double reference_nll =
68 this->likelihood.profiled_nll(x_ref, y_ref);
69
70 auto cache = std::make_shared<std::map<Point, double>>();
71
72 ScalarField2D field = [this, reference_nll, cache](double x, double y) {
73 Point key{x, y};
74
75 auto it = cache->find(key);
76 if (it != cache->end()) {
77 return it->second;
78 }
79
80 const double raw = this->likelihood.profiled_nll(x, y);
81 const double val = std::isfinite(raw)
82 ? std::clamp(raw - reference_nll, -1e50, 1e50)
83 : 1e50;
84
85 (*cache)[key] = val;
86 return val;
87 };
88
90 cr.bounds = bounds;
91 cr.level = z * z / 2.0;
92 cr.level = gsl_cdf_chisq_Pinv(gsl_sf_erf(z / std::sqrt(2.0)), 2) / 2.0;
93 cr.resolution = resolution;
94
95 auto defs = this->likelihood.get_param_defs();
96
97 defs[0].value = cfg.fr.p_hat.at(cfg.x_id);
98 defs[0].step_hint = std::max(cfg.fr.p_hat_std.at(cfg.x_id), 1e-3);
99 defs[0].limits = std::make_pair(bounds[0], bounds[1]);
100
101 defs[1].value = cfg.fr.p_hat.at(cfg.y_id);
102 defs[1].step_hint = std::max(cfg.fr.p_hat_std.at(cfg.y_id), 1e-3);
103 defs[1].limits = std::make_pair(bounds[2], bounds[3]);
104
105 cr.p_defs[0] = defs[0];
106 cr.p_defs[1] = defs[1];
107
108 if (cfg.on_progress) {
111 ev.level = cr.level;
112 ev.message = "Contour computation started";
113 cfg.on_progress(ev);
114 }
115
116 try {
117 Contour c = this->extractor->extract(field, cr);
118
119 if (cfg.on_progress) {
122 ev.level = c.level;
123 ev.n_paths = c.paths.size();
124 ev.n_points = count_total_points(c);
125 ev.elapsed_seconds =
126 std::chrono::duration<double>(clock::now() - t0).count();
127 ev.message = c.success ? "Contour computation finished"
128 : "Contour computation finished but contour is marked unsuccessful";
129 cfg.on_progress(ev);
130 }
131
132 return c;
133 }
134 catch (const std::exception& e) {
135 if (cfg.on_progress) {
138 ev.level = cr.level;
139 ev.elapsed_seconds =
140 std::chrono::duration<double>(clock::now() - t0).count();
141 ev.message = e.what();
142 cfg.on_progress(ev);
143 }
144 throw;
145 }
146}
147
148std::shared_ptr<JointDistribution> ContourEngine::build_constraints_distribution() {
149 std::vector<std::unique_ptr<IMarginalDistribution>> fitted_marginals;
150
151 for (size_t i = 0; i < cfg.fr.p_hat.size(); i++) {
152 if (i != cfg.x_id && i != cfg.y_id)
153 fitted_marginals.emplace_back(std::move(std::make_unique<GaussianMarginal>(cfg.fr.p_hat[i], cfg.fr.p_hat_std[i])));
154 }
155
156 RealMatrix R_fitted = cfg.fr.p_hat_correlations;
157 R_fitted.remove_row_and_column(cfg.y_id);
158 R_fitted.remove_row_and_column(cfg.x_id);
159 std::unique_ptr<ICopula> fitted_copula = std::make_unique<GaussianCopula>(std::random_device{}(), R_fitted);
160
161 return std::make_unique<JointDistribution>(std::move(fitted_marginals), std::move(fitted_copula));
162}
163
164std::shared_ptr<IContourExtractor> ContourEngine::build_contour_extractor(
166{
167 switch (ca) {
169 return std::make_shared<AMSContourExtractor>(cfg.on_progress);
171 return std::make_shared<MnContourExtractor>();
172 default:
173 throw std::invalid_argument("Unknown contouring algorithm.");
174 }
175}
High-level engine for profiled two-dimensional likelihood contours.
ContourAlgorithm
Algorithm family used to extract contour paths from the profiled field.
@ AMS
Adaptive/marching-squares contour extractor.
@ MINUIT
Minuit contour extractor.
@ SLICE
Fix all fit parameters except nuisances; scan only the selected axes.
@ PRIOR_CONSTRAINED_PROJECTION
Profile remaining parameters with Gaussian constraints around the fit result.
@ Started
Contour computation has started.
@ Finished
Contour computation finished normally.
@ Failed
Contour computation failed and no valid result was produced.
ProfilerMode
Available algorithms for profiling free parameters.
Definition Profiler.h:25
ContourEngine(std::shared_ptr< ILikelihood > base, const ContourConfig &cfg)
Constructs a contour engine for a base likelihood.
Contour compute_contour(double z, std::array< double, 4 > bounds, std::size_t resolution)
Computes a profiled 2D contour for a requested significance.
Evaluates a profiled NLL as a function of two scan coordinates.
double profiled_nll(double px, double py)
Evaluates the profiled NLL at one 2D scan point.
std::array< fit_app::ParameterDefinition, 2 > get_param_defs() const
Returns the parameter definitions for the two scan coordinates.
void remove_row_and_column(std::size_t dim_idx)
Removes one row and one column with the same index.
Definition Matrix.cpp:639
std::function< double(double, double)> ScalarField2D
Definition contour.h:17
std::pair< double, double > Point
Definition contour.h:13
std::unique_ptr< IFitBackend > make_minuit_backend()
Configuration object for ContourEngine.
ProfilerMode profile_backend
Backend used to profile nuisance/free parameters.
ProfilingMethod profiling_method
Profiling strategy used for non-displayed parameters.
ContourProgressCallback on_progress
Optional progress callback invoked during contour computation.
std::size_t x_id
std::size_t y_id
Indices of the two fit parameters displayed on the contour axes.
std::optional< ContourAlgorithm > fallback_contour_method
Optional fallback algorithm if the primary fails.
ContourAlgorithm primary_contour_method
Primary contour extraction algorithm.
FitResult fr
Global fit result used for central values, uncertainties, and correlations.
Payload passed to contour progress callbacks.
std::size_t n_points
Number of points produced so far or in total.
std::string message
Optional human-readable status message.
double level
Contour level associated with the event.
ContourProgressEventType type
Event category.
double elapsed_seconds
Wall-clock time elapsed since computation start.
std::size_t n_paths
Number of paths produced so far or in total.
Input configuration for a two-dimensional contour extraction.
std::size_t resolution
Requested grid or sampling resolution.
std::array< fit_app::ParameterDefinition, 2 > p_defs
Parameter definitions for the two scanned axes.
std::array< double, 4 > bounds
Extraction domain as {xmin, xmax, ymin, ymax}.
double level
Target scalar-field level to extract.
Output of a contour extraction algorithm.
bool success
Whether extraction produced a valid contour.
std::set< Path > paths
Extracted contour paths.
double level
Level actually targeted by the extraction.
std::vector< double > p_hat_std
Standard deviations of the parameters of interest.
std::vector< double > p_hat
Maximum-likelihood estimates for parameters of interest.
RealMatrix p_hat_correlations
Correlation matrix for the parameters of interest.