Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
test_minuit_pipeline.cpp
Go to the documentation of this file.
1// #include <algorithm>
2// #include <cassert>
3// #include <chrono>
4// #include <cmath>
5// #include <functional>
6// #include <iomanip>
7// #include <iostream>
8// #include <memory>
9// #include <stdexcept>
10// #include <string>
11// #include <vector>
12
13// #include "StatisticManager.h"
14// #include "ObservableInterfaceProxy.h"
15// #include "StatCorrelationProxy.h"
16// #include "StatParameterProxy.h"
17// #include "ObservableInterface.h"
18// #include "StatParamSourcesProxy.h"
19// #include "StatDependencyPruner.h"
20// #include "BaseLikelihood.h"
21// #include "DefaultNuisancePathsProvider.h"
22
23// #include "minuit-cpp/FCNBase.hh"
24// #include "minuit-cpp/FunctionMinimum.hh"
25// #include "minuit-cpp/MnHesse.hh"
26// #include "minuit-cpp/MnMigrad.hh"
27// #include "minuit-cpp/MnUserCovariance.hh"
28// #include "minuit-cpp/MnUserParameters.hh"
29// #include "minuit-cpp/MnUserParameterState.hh"
30
31// #include "BlockProxy.h"
32// #include "NuisanceReader.h"
33
34// namespace M2 = MinuitCpp;
35
36// static void print_vec(const std::vector<double>& vec) {
37// std::cout << "[ ";
38// for (size_t i = 0; i < vec.size(); i++) {
39// std::cout << vec.at(i) << (i == vec.size() - 1 ? " " : ", ");
40// }
41// std::cout << "]\n";
42// }
43
44
45
46// static double safe_step(double scale) {
47// double s = std::fabs(scale);
48// if (!std::isfinite(s) || s <= 0.0) return 1.0;
49// return 0.1 * s;
50// }
51
52// struct MinuitJointFit {
53// Vector x_hat; // [p, eta]
54// std::vector<double> x_err;
55// RealMatrix cov;
56// double fmin = 0.0;
57// bool ok = false;
58// };
59
60// class GenericFCN final : public M2::FCNBase {
61// public:
62// explicit GenericFCN(std::function<double(const std::vector<double>&)> f) : f_(std::move(f)) {}
63
64// double operator()(const std::vector<double>& x) const override {
65// const double v = f_(x);
66// return std::isfinite(v) ? v : 1e300;
67// }
68
69// // NLL => Up=0.5
70// double Up() const override { return 0.5; }
71
72// private:
73// std::function<double(const std::vector<double>&)> f_;
74// };
75
76// static MinuitJointFit minuit_migrad_hesse(
77// const std::function<double(const std::vector<double>&)>& f,
78// const std::vector<double>& x0,
79// const std::vector<double>& scales,
80// std::size_t max_fcn,
81// double tol_edm
82// ) {
83// if (x0.size() != scales.size()) {
84// throw std::invalid_argument(
85// "x0 and scales must have same size (x0=" + std::to_string(x0.size()) +
86// ", scales=" + std::to_string(scales.size()) + ")"
87// );
88// }
89
90// GenericFCN fcn(f);
91
92// M2::MnUserParameters upar;
93// for (std::size_t i = 0; i < x0.size(); ++i) {
94// upar.Add("x" + std::to_string(i), x0[i], safe_step(scales[i]));
95// }
96
97// M2::MnMigrad migrad(fcn, upar);
98// M2::FunctionMinimum min = migrad(max_fcn, tol_edm);
99
100// M2::MnHesse hesse;
101// hesse(fcn, min);
102
103// MinuitJointFit out;
104// out.fmin = min.Fval();
105// out.ok = min.IsValid();
106
107// const auto& st = min.UserState();
108// const std::size_t n = x0.size();
109
110// out.x_hat.resize(n);
111// out.x_err.assign(n, 0.0);
112
113// for (std::size_t i = 0; i < n; ++i) {
114// out.x_hat[i] = st.Value("x" + std::to_string(i));
115// out.x_err[i] = st.Error("x" + std::to_string(i));
116// }
117
118// out.cov = RealMatrix(n, n);
119// const auto& cov = st.Covariance();
120// for (std::size_t i = 0; i < n; ++i)
121// for (std::size_t j = 0; j < n; ++j)
122// out.cov.at(i, j) = cov(i, j);
123
124// return out;
125// }
126
127
128// class MinuitMLEstimatorLocal {
129// public:
130// // using ModelFn = ProfiledLikelihood::ModelFn;
131
132// MinuitMLEstimatorLocal(LikelihoodContext ctx, ModelFn model, std::size_t max_fcn, double tol_edm)
133// : like_(std::move(ctx)), model_(std::move(model)), max_fcn_(max_fcn), tol_edm_(tol_edm) {}
134
135// FitResult fit_joint(const Vector& p0) const {
136// const std::size_t p_dim = p0.size();
137
138// // Vector eta0 = like_.nuisance_central_values;
139// Vector eta0;
140// for (auto elem : like_.nuis_defs) {
141// eta0.push_back(elem.value);
142// }
143// Vector eta_scales = like_.nuisance_dist->get_stds();
144
145// if (eta0.size() != eta_scales.size()) {
146// std::cerr << "[ERROR] eta0.size() != eta_scales.size(): "
147// << eta0.size() << " vs " << eta_scales.size() << "\n";
148// throw std::runtime_error("Nuisance central values and nuisance stds dimensions mismatch");
149// }
150
151// std::vector<double> x0;
152// x0.reserve(p_dim + eta0.size());
153// x0.insert(x0.end(), p0.begin(), p0.end());
154// x0.insert(x0.end(), eta0.begin(), eta0.end());
155
156// Vector p_scales = p0;
157// for (auto& v : p_scales) v = std::fabs(v);
158
159// std::vector<double> scales;
160// scales.reserve(p_scales.size() + eta_scales.size());
161// scales.insert(scales.end(), p_scales.begin(), p_scales.end());
162// scales.insert(scales.end(), eta_scales.begin(), eta_scales.end());
163
164// auto f = [this, p_dim](const std::vector<double>& x) -> double {
165// Vector p(x.begin(), x.begin() + p_dim);
166// Vector eta(x.begin() + p_dim, x.end());
167// return nll(p, eta);
168// };
169
170// MinuitJointFit mj = minuit_migrad_hesse(f, x0, scales, max_fcn_, tol_edm_);
171
172// FitResult fr;
173// fr.ell_hat = mj.fmin;
174
175// if (!mj.ok) {
176// fr.p_hat = p0;
177// fr.eta_hat = eta0;
178// fr.p_hat_std = Vector(p_dim, 0.0);
179// fr.p_hat_correlations = RealMatrix(p_dim, p_dim);
180// return fr;
181// }
182
183// fr.p_hat.assign(mj.x_hat.begin(), mj.x_hat.begin() + p_dim);
184// fr.eta_hat.assign(mj.x_hat.begin() + p_dim, mj.x_hat.end());
185
186// RealMatrix cov_p(p_dim, p_dim);
187// for (std::size_t i = 0; i < p_dim; ++i)
188// for (std::size_t j = 0; j < p_dim; ++j)
189// cov_p.at(i, j) = mj.cov.at(i, j);
190
191// fr.p_hat_std.assign(p_dim, 0.0);
192// for (std::size_t i = 0; i < p_dim; ++i)
193// fr.p_hat_std[i] = std::sqrt(std::max(0.0, cov_p.at(i, i)));
194
195// fr.p_hat_correlations = RealMatrix(p_dim, p_dim);
196// for (std::size_t i = 0; i < p_dim; ++i) {
197// for (std::size_t j = 0; j < p_dim; ++j) {
198// double denom = fr.p_hat_std[i] * fr.p_hat_std[j];
199// fr.p_hat_correlations.at(i, j) = (denom > 0.0) ? (cov_p.at(i, j) / denom) : 0.0;
200// }
201// }
202
203// return fr;
204// }
205
206// private:
207// double nll(const Vector& p, const Vector& eta) const {
208// Vector pred = model_(p, eta);
209
210// Vector r(pred.size());
211// for (std::size_t i = 0; i < pred.size(); ++i)
212// r[i] = pred[i] - like_.exp_obs_values[i];
213
214// double ell_obs = like_.exp_obs_dist->logpdf(r);
215// double ell_eta = like_.nuisance_dist->logpdf(eta);
216
217// return -(ell_obs + ell_eta);
218// }
219// LikelihoodContext like_;
220// ModelFn model_;
221// std::size_t max_fcn_;
222// double tol_edm_;
223// };
224
225
226int main(int argc, char** argv) {
227
228// auto F_gauss_nll = [](const Vector& p) -> double {
229// double mu0 = 1e-12, mu1 = 1e3, mu2 = 1e-4;
230// double s0 = 1e-13, s1 = 1e2, s2 = 1e-6;
231
232// RealMatrix corr({
233// {1, 0.2, -0.5},
234// {0.2, 1, 0.7},
235// {-0.5, 0.7, 1}
236// });
237
238// RealMatrix z({
239// Vector{(p[0] - mu0) / s0},
240// Vector{(p[1] - mu1) / s1},
241// Vector{(p[2] - mu2) / s2}
242// });
243
244// return 0.5 * (2 * PI * corr.slogdet().logdet + (z.transpose() * corr.inv() * z).at(0, 0));
245// };
246
247// {
248// std::vector<double> x0 = {3e-12, 250.0, 0.0002};
249// std::vector<double> scales = {1e-13, 1e2, 1e-6};
250
251// auto f = [&](const std::vector<double>& x) -> double {
252// Vector p = {x[0], x[1], x[2]};
253// return F_gauss_nll(p);
254// };
255
256// auto r = minuit_migrad_hesse(f, x0, scales, /*max_fcn*/ 50000, /*tol_edm*/ 1e-10);
257
258// std::cout << "=== Minuit test (gauss corr) ===\n";
259// std::cout << "ok=" << r.ok << " fmin=" << std::setprecision(17) << r.fmin << "\n";
260// std::cout << "xhat = "; print_vec(r.x_hat);
261// std::cout << "xerr = "; print_vec(r.x_err);
262// std::cout << "\n";
263// }
264
265// HyperisoMaster hyp;
266// HyperisoConfig config_hyp;
267// config_hyp.model = Model::SM;
268// hyp.init("lha/si_input.flha", config_hyp);
269
270// std::shared_ptr<ObservableInterface> oint = std::make_shared<ObservableInterface>();
271// oint->add_observable(ObservableMapper::to_id(Observables::BR_BS_MUMU_UNTAG), QCDOrder::LO, true)
272// .add_observable(ObservableMapper::to_id(Observables::BR_BD_MUMU), QCDOrder::LO, true);
273
274// StatisticConfig config;
275// config.MC_draws = 100;
276// config.MLE_max_iter = 10000;
277// config.MLE_tol = 1e-6;
278
279// std::vector<ParamId> p_specs = {
280// ParamId(ParameterType::WILSON, GroupMapper::str(WGroup::B, ScaleType::MATCHING), WCoefMapper::flha_full(WCoef::C10, QCDOrder::LO, ContributionType::SM))
281// };
282
283// std::shared_ptr<IStatParamOptimizerProxy> spop = std::make_shared<StatParamOptimizerProxy>();
284// auto model = std::make_shared<ObservableInterfaceProxy>(oint, spop);
285
286// std::shared_ptr<INuisancePathsProvider> npp = std::make_shared<DefaultNuisancePathsProvider>();
287
288// StatisticManager stat(
289// config,
290// model,
291// std::make_shared<StatCorrelationProxy>(),
292// std::make_shared<StatParameterProxy>(),
293// std::make_shared<StatParamSourcesProxy>(),
294// std::make_shared<StatDependencyPruner>(),
295// std::make_shared<NuisanceReader>(npp),
296// spop
297// );
298
299// LOG_INFO("YO1");
300// stat.update_cache(p_specs);
301
302// BlockProxy bp;
303// bp.log_all_blocks(ParameterType::WILSON);
304
305// auto start_u = std::chrono::steady_clock::now();
306// stat.compute_uncertainties();
307// auto stop_u = std::chrono::steady_clock::now();
308// LOG_INFO("YO2");
309// auto us_u = std::chrono::duration_cast<std::chrono::microseconds>(stop_u - start_u).count();
310// std::cout << "Uncertainty estimation time : " << us_u << " µs\n";
311
312
313// stat.update_cache(p_specs);
314
315// auto p_specs_map = stat.get_p_specs(p_specs);
316// auto eta_specs_real = stat.get_all_obss_deps();
317// for (const auto& [pid, _] : p_specs_map) eta_specs_real.erase(pid);
318// auto exp_obs_map = stat.get_obs_exp();
319
320// auto unz_p = unzip(p_specs_map);
321// auto unz_eta = unzip(eta_specs_real);
322// auto unz_obs = unzip(exp_obs_map);
323
324// std::vector<ParamId> p_ids = unz_p.ids;
325// std::vector<ParamId> eta_ids = unz_eta.ids;
326// std::vector<ExperimentObs> obs_ids = unz_obs.ids;
327
328// auto nuisance_dist = stat.build_nuisance_distribution();
329// auto exp_obs_dist = stat.build_exp_data_distribution();
330
331// if (nuisance_dist->get_stds().size() != unz_eta.vals.size()) {
332// std::cerr << "[ERROR] nuisance_dist->get_stds().size()=" << nuisance_dist->get_stds().size()
333// << " but eta central values size=" << unz_eta.vals.size() << "\n";
334// std::cerr << "=> appelle fill_cache() juste avant de construire ctx et évite de recalculer eta ailleurs.\n";
335// return 3;
336// }
337// if (exp_obs_dist->dim() != unz_obs.vals.size()) {
338// std::cerr << "[ERROR] exp_obs_dist->dim()=" << exp_obs_dist->dim()
339// << " but exp obs values size=" << unz_obs.vals.size() << "\n";
340// return 4;
341// }
342
343// LikelihoodContext ctx;
344// ctx.nuisance_dist = std::move(nuisance_dist);
345// ctx.exp_obs_dist = std::move(exp_obs_dist);
346// Vec _ = unz_eta.vals;
347// // ctx.nuisance_central_values = unz_eta.vals;
348// ctx.exp_obs_values = unz_obs.vals;
349
350// auto model_fn = [model, obs_ids, p_ids, eta_ids](const Vec& p_vec, const Vec& eta_vec) -> Vec {
351// auto pred_map = model->predict_optimized(zip(p_ids, p_vec), zip(eta_ids, eta_vec));
352
353// Vec out;
354// out.reserve(obs_ids.size());
355
356// for (const auto& bid : obs_ids) {
357// const auto& vec = pred_map.at(bid.obs.s);
358
359// auto it = std::find_if(vec.begin(), vec.end(), [&](const ObservableValue& ov){
360// auto bin = ov.bin.value_or(std::pair<double,double>{0.,0.});
361// return bin == bid.obs.p;
362// });
363// if (it == vec.end()) throw std::runtime_error("Missing predicted observable/bin");
364// out.push_back(it->value);
365// }
366// return out;
367// };
368
369// auto start_m = std::chrono::steady_clock::now();
370// MinuitMLEstimatorLocal est(std::move(ctx), model_fn, config.MLE_max_iter, config.MLE_tol);
371// FitResult fr = est.fit_joint(unz_p.vals);
372// auto stop_m = std::chrono::steady_clock::now();
373// auto us_m = std::chrono::duration_cast<std::chrono::microseconds>(stop_m - start_m).count();
374// std::cout << "MLE (Minuit) fitting time : " << us_m << " µs\n";
375
376// std::cout << "ell_hat = " << std::setprecision(17) << fr.ell_hat << "\n";
377
378// std::cout << "p_hat = ";
379// for (auto v : fr.p_hat) std::cout << v << " ";
380// std::cout << "\n";
381
382// std::cout << "p_hat_std = ";
383// for (auto v : fr.p_hat_std) std::cout << v << " ";
384// std::cout << "\n";
385
386// std::cout << "p_hat_correlations:\n" << fr.p_hat_correlations << "\n";
387
388// // std::cout << *StatParameterProxy(ParameterType::OBSERVABLE)
389// // .get_param("FOBS", LhaID("511_1_0_0_2_13_-13"))
390// // << std::endl;
391
392 return 0;
393}