43 std::shared_ptr<LikelihoodContext>
ctx;
44 std::shared_ptr<BaseLikelihood>
like;
47 std::vector<double>
x0;
52static std::string param_name(
const ParamId& pid) {
53 std::ostringstream oss;
59 const ParamId& pid,
double value,
double sigma_hint)
62 out.
name = param_name(pid);
64 out.
step_hint = (std::isfinite(sigma_hint) && sigma_hint > 0.0)
66 :
std::max(1e-3, 0.01 *
std::abs(value));
68 if (out.
name.find(
"FCONST") != std::string::npos) {
69 out.
limits = std::make_pair(0.05, 0.35);
76 const ParamId& pid,
double value,
double sigma_hint)
79 out.
name = param_name(pid);
82 const double s = (std::isfinite(sigma_hint) && sigma_hint > 0.0)
83 ? std::abs(sigma_hint)
84 : std::max(1e-3, 0.01 * std::abs(value));
88 const std::string& nm = out.
name;
89 if (nm.find(
"SMINPUTS:3") != std::string::npos) {
90 out.
limits = std::make_pair(0.05, 0.30);
91 }
else if (nm.find(
"MASS:") != std::string::npos ||
92 nm.find(
"FLIFE:") != std::string::npos ||
93 nm.find(
"FCONST:") != std::string::npos ||
94 nm.find(
"FMASS:") != std::string::npos ||
95 nm.find(
"SMINPUTS:5") != std::string::npos ||
96 nm.find(
"SMINPUTS:6") != std::string::npos) {
97 out.
limits = std::make_pair(std::max(1e-12, value - 5.0 * s), value + 5.0 * s);
103static std::vector<double> ordered_prediction_vector(
104 const std::vector<ExperimentObs>& obs_ids,
105 const std::map<
ObservableId, std::vector<ObservableValue>>& pred_map)
107 std::vector<double> out;
108 out.reserve(obs_ids.size());
110 for (
const auto& bid : obs_ids) {
111 const auto& vec = pred_map.at(bid.obs.s);
113 auto it = std::find_if(vec.begin(), vec.end(), [&](
const ObservableValue& ov) {
114 auto bin = ov.bin.value_or(std::pair<double, double>{0.0, 0.0});
115 return bin == bid.obs.p;
118 if (it == vec.end()) {
119 throw std::runtime_error(
"Missing predicted observable/bin.");
122 out.push_back(it->value);
128static std::vector<fit_app::ParameterDefinition> make_parameter_definitions(
129 const std::vector<std::string>& names,
130 const std::vector<double>& values,
131 const std::vector<double>& scale_hints,
132 const std::vector<ParamLimit>& limits)
134 if (names.size() != values.size() || names.size() != scale_hints.size()) {
135 throw std::invalid_argument(
"names/values/scale_hints size mismatch");
138 std::vector<fit_app::ParameterDefinition> parameters;
139 parameters.reserve(names.size());
141 for (std::size_t i = 0; i < names.size(); ++i) {
146 parameters.push_back(std::move(p));
149 for (
const auto& lim : limits) {
150 if (lim.idx < parameters.size()) {
151 parameters[lim.idx].limits = std::make_pair(lim.low, lim.high);
158static BuiltProblem build_problem(
161 const std::shared_ptr<ObservableInterfaceProxy>& model, std::vector<ParamId> p_specs)
168 for (
const auto& [pid, _] : p_specs_map) eta_specs_real.erase(pid);
171 auto unz_p =
unzip(p_specs_map);
172 auto unz_eta =
unzip(eta_specs_real);
173 auto unz_obs =
unzip(exp_obs_map);
175 auto ctx = std::make_shared<LikelihoodContext>();
178 ctx->exp_obs_values = unz_obs.vals;
181 ctx->fp_defs.reserve(unz_p.ids.size());
182 for (std::size_t i = 0; i < unz_p.ids.size(); ++i) {
183 double sigma_hint = std::max(1e-3, std::abs(unz_p.vals[i]));
184 ctx->fp_defs.emplace_back(make_fit_param_def_local(unz_p.ids[i], unz_p.vals[i], sigma_hint));
187 const auto eta_stds = ctx->nuisance_dist->get_stds();
188 ctx->nuis_defs.reserve(unz_eta.ids.size());
189 for (std::size_t i = 0; i < unz_eta.ids.size(); ++i) {
190 double sigma_hint = (i < eta_stds.size()) ? eta_stds[i] : 1e-3;
191 ctx->nuis_defs.emplace_back(make_nuisance_param_def_local(unz_eta.ids[i], unz_eta.vals[i], sigma_hint));
194 auto model_fn = [model, obs_ids = unz_obs.ids, p_ids = unz_p.ids, eta_ids = unz_eta.ids]
195 (
const std::vector<double>& p_vec,
const std::vector<double>& eta_vec) -> std::vector<double> {
196 auto pred_map = model->predict_optimized(
zip(p_ids, p_vec),
zip(eta_ids, eta_vec));
197 return ordered_prediction_vector(obs_ids, pred_map);
200 auto like = std::make_shared<BaseLikelihood>(model_fn, ctx, unz_p.ids.size());
203 bp.p_ids = unz_p.ids;
204 bp.eta_ids = unz_eta.ids;
205 bp.obs_ids = unz_obs.ids;
210 bp.names.reserve(unz_p.ids.size() + unz_eta.ids.size());
211 bp.x0.reserve(unz_p.vals.size() + unz_eta.vals.size());
212 bp.scale_hints.reserve(unz_p.vals.size() + unz_eta.vals.size());
214 for (std::size_t i = 0; i < unz_p.ids.size(); ++i) {
216 bp.x0.push_back(unz_p.vals[i]);
217 bp.scale_hints.push_back(std::max(1e-3, std::abs(unz_p.vals[i])));
218 if (bp.names.back().find(
"FCONST") != std::string::npos) {
219 bp.limits.push_back({i, 0.05, 0.35});
223 for (std::size_t i = 0; i < unz_eta.ids.size(); ++i) {
224 const std::size_t idx = unz_p.ids.size() + i;
225 const double c = unz_eta.vals[i];
226 const double s = (i < eta_stds.size()) ? std::max(1e-12, std::abs(eta_stds[i])) : 1e-3;
230 bp.scale_hints.push_back(s);
232 const std::string& nm = bp.names.back();
233 if (nm.find(
"SMINPUTS:3") != std::string::npos) {
234 bp.limits.push_back({idx, 0.05, 0.30});
235 }
else if (nm.find(
"MASS:") != std::string::npos ||
236 nm.find(
"FLIFE:") != std::string::npos ||
237 nm.find(
"FCONST:") != std::string::npos ||
238 nm.find(
"FMASS:") != std::string::npos ||
239 nm.find(
"SMINPUTS:5") != std::string::npos ||
240 nm.find(
"SMINPUTS:6") != std::string::npos) {
241 bp.limits.push_back({idx, std::max(1e-12, c - 5.0 * s), c + 5.0 *
s});
257 const std::shared_ptr<BaseLikelihood>& like,
258 const std::vector<std::string>& names,
259 const std::vector<double>& x_start,
260 const std::vector<double>& scale_hints,
261 const std::vector<ParamLimit>& limits,
267 const auto parameters = make_parameter_definitions(names, x_start, scale_hints, limits);
270 [like](
const std::vector<double>& theta) {
271 return like->nll(theta);
281 opt.run_hesse =
false;
284 auto raw = backend.
minimize_with_fixed(objective, parameters, opt, {px, py}, {xval, yval});
286 OldProfileResult out;
291 if (raw.diagnostics.ok && raw.values.size() == x_start.size()) {
292 out.fmin = raw.diagnostics.fmin;
293 out.x_hat = raw.values;
295 out.x_hat[px] = xval;
296 out.x_hat[py] = yval;
297 out.fmin = like->nll(out.x_hat);
303static void print_theta(
const std::string& label,
const std::vector<double>& x) {
304 std::cout << label <<
" = (";
305 for (std::size_t i = 0; i < x.size(); ++i) {
306 std::cout << std::setprecision(17) << x[i];
307 if (i + 1 != x.size()) std::cout <<
", ";
320 hyp.
init(
"lha/si_input.flha", config_hyp);
322 auto oint = std::make_shared<ObservableInterface>();
330 std::vector<ParamId> p_specs = {
335 std::shared_ptr<IStatParamOptimizerProxy> spop = std::make_shared<StatParamOptimizerProxy>();
336 auto model = std::make_shared<ObservableInterfaceProxy>(oint, spop);
338 std::shared_ptr<INuisancePathsProvider> npp = std::make_shared<DefaultNuisancePathsProvider>();
343 std::make_shared<StatCorrelationProxy>(),
344 std::make_shared<StatParameterProxy>(),
345 std::make_shared<StatParamSourcesProxy>(),
346 std::make_shared<StatDependencyPruner>(),
347 std::make_shared<NuisanceReader>(npp),
351 BuiltProblem bp = build_problem(stat, config, model, p_specs);
354 auto fitter = std::make_shared<MLFitter>(bp.
ctx, [model, obs_ids = bp.
obs_ids, p_ids = bp.
p_ids, eta_ids = bp.
eta_ids]
355 (
const std::vector<double>& p_vec,
const std::vector<double>& eta_vec) -> std::vector<double> {
356 auto pred_map = model->predict_optimized(zip(p_ids, p_vec), zip(eta_ids, eta_vec));
357 return ordered_prediction_vector(obs_ids, pred_map);
360 auto fit = fitter->maximum_likelihood_fit(std::vector<double>(bp.
x0.begin(), bp.
x0.begin() + bp.
p_ids.size()));
362 std::vector<double> theta_hat = fit.p_hat;
363 theta_hat.insert(theta_hat.end(), fit.eta_hat.begin(), fit.eta_hat.end());
365 std::cout <<
"\n=== JOINT MLE ===\n";
366 print_theta(
"theta_hat", theta_hat);
367 std::cout <<
"ell_hat = " << std::setprecision(17) << fit.ell_hat <<
"\n";
370 std::vector<std::pair<double,double>> test_points = {
371 {fit.p_hat[0], fit.p_hat[1]},
380 auto backend = make_minuit_backend();
381 Profiler profiler(make_minuit_backend());
388 for (
const auto& [x, y] : test_points) {
389 std::cout <<
"\n====================================================\n";
390 std::cout <<
"Testing point (x, y) = (" << x <<
", " << y <<
")\n";
394 auto new_res_fresh = profiler.
profile(bp.
like, pr_fresh);
396 std::cout <<
"\n[NEW Profiler::profile / fresh global seed]\n";
397 std::cout <<
"ok/converged = " << new_res_fresh.
converged <<
"\n";
398 std::cout <<
"fmin = " << std::setprecision(17) << new_res_fresh.nll_hat <<
"\n";
400 std::vector<double> theta_prof_fresh(theta_hat.size(), std::numeric_limits<double>::quiet_NaN());
401 theta_prof_fresh[0] = x;
402 theta_prof_fresh[1] = y;
403 for (
const auto& [idx, val] : new_res_fresh.theta_hat) {
404 theta_prof_fresh[idx] = val;
406 print_theta(
"theta_hat_prof_fresh", theta_prof_fresh);
409 auto old_res = old_profile_at_fixed_xy(
420 std::cout <<
"\n[OLD DIRECT minimize_with_fixed]\n";
421 std::cout <<
"ok = " << old_res.ok <<
"\n";
422 std::cout <<
"fmin = " << std::setprecision(17) << old_res.fmin <<
"\n";
423 std::cout <<
"nfcn = " << old_res.raw.diagnostics.nfcn <<
"\n";
424 std::cout <<
"edm = " << old_res.raw.diagnostics.edm <<
"\n";
425 print_theta(
"x_hat", old_res.x_hat);
431 std::cout <<
"\n[NEW Profiler::profile]\n";
432 std::cout <<
"ok/converged = " << new_res.
converged <<
"\n";
433 std::cout <<
"fmin = " << std::setprecision(17) << new_res.nll_hat <<
"\n";
435 std::vector<double> theta_prof(theta_hat.size(), std::numeric_limits<double>::quiet_NaN());
438 for (
const auto& [idx, val] : new_res.theta_hat) {
439 theta_prof[idx] = val;
441 print_theta(
"theta_hat_prof", theta_prof);
443 if (new_res.converged) {
444 current = new_res.theta_hat;
447 std::cout <<
"\n[COMPARE]\n";
448 std::cout <<
"delta_f = " << std::setprecision(17) << (new_res.nll_hat - old_res.fmin) <<
"\n";
450 std::vector<double> theta_seed = theta_hat;
453 double f_seed = bp.
like->nll(theta_seed);
455 std::cout <<
"\n[SEED ONLY]\n";
456 std::cout <<
"f_seed = " << std::setprecision(17) << f_seed <<
"\n";
458 std::cout <<
"\n[COMPARE]\n";
459 std::cout <<
"old_f = " << old_res.fmin <<
"\n";
460 std::cout <<
"new_f = " << new_res.nll_hat <<
"\n";
461 std::cout <<
"seed_f = " << f_seed <<
"\n";
462 std::cout <<
"delta_new_old = " << (new_res.nll_hat - old_res.fmin) <<
"\n";
463 std::cout <<
"delta_old_seed = " << (old_res.fmin - f_seed) <<
"\n";
464 std::cout <<
"delta_new_seed = " << (new_res.nll_hat - f_seed) <<
"\n";
Concrete profileable likelihood built from a model and joint distributions.
Default nuisance-configuration path provider.
High-level maximum-likelihood fitting and confidence-contour API.
Profiling strategies used to build two-dimensional likelihood scan requests.
std::map< T, U > zip(const std::vector< T > &ids, const std::vector< U > &vals)
Builds a map from parallel id and value vectors.
UnzipResult1D< T, U > unzip(const std::map< T, U > &indexed)
Splits a map into parallel id and value vectors.
Concrete reader for nuisance-parameter definition files.
Adapter from ObservableInterface to the statistical model interface.
High-level, user-facing entry point to compute flavor observables.
Generic likelihood profiling engine.
Concrete statistical proxy forwarding correlation queries to CorrelationProvider.
Statistics-layer adapter over the core DependencyPruner service.
Statistics-layer adapter for retrieving leaf parameter sources.
Statistics-layer proxy for read-only access to parameters and observables.
High-level orchestration of statistical uncertainty propagation, likelihood construction and fit scan...
static IdOf< ObservableTag > to_id(Observables e)
Converts an enum value to an IdOf<Tag>.
High-level interface to initialize and monitor the main framework configuration.
void init(const std::string &lhaFile, HyperisoConfig config)
Initializes Hyperiso using a LHA file and a full Config object.
Minimizes a likelihood over free parameters subject to fixed constraints.
ProfileResult profile(std::shared_ptr< ILikelihood > base, const ProfileRequest &pr) const
Profiles a likelihood according to a request.
Builds slice-profile requests with fixed parameters of interest.
std::map< std::size_t, double > init_warm_start() const override
Creates the initial warm-start map for the strategy.
ProfileRequest build_request(double px, double py, const std::map< std::size_t, double > ¤t_argmin) const override
Builds a profiler request for one 2D scan point.
Coordinates statistical inputs, nuisance distributions, MLE fits and contour/scan computations.
std::unique_ptr< JointDistribution > build_nuisance_distribution()
Builds the joint nuisance distribution from cached nuisance marginals and correlations.
std::map< BinnedObservableId, GaussianSummary > compute_uncertainties()
Computes Gaussian summaries for MC-propagated observable uncertainties.
std::map< ExperimentObs, double > get_obs_exp()
std::map< ParamId, double > get_p_specs(const std::vector< ParamId > &p_specs)
Resolves initial fit-parameter values from the parameter proxy.
void update_cache(const std::vector< ParamId > &p_specs=std::vector< ParamId >())
Updates the full statistical cache for the selected fit parameters.
std::unique_ptr< JointDistribution > build_exp_data_distribution()
Builds the joint experimental-data distribution from cached observables and correlations.
std::map< ParamId, double > get_all_obss_deps()
Selects all nuisance dependencies relevant to the current observable set.
virtual BackendFitResult minimize_with_fixed(const IObjectiveFunction &objective, const std::vector< ParameterDefinition > ¶meters, const FitOptions &options, const std::vector< std::size_t > &fixed_indices, const std::vector< double > &fixed_values) const =0
std::string to_string_any(const T &x)
Hash specialization for SymbolId<Tag>.
double MLE_tol
Minimizer tolerance passed to the backend.
std::size_t MLE_max_iter
Maximum number of minimizer function calls/iterations.
Configuration object controlling model, input flags and optional MARTY resources.
Model model
Current model.
Container for a computed observable value, optionally binned.
Composite identifier for a single parameter.
Input specification for a constrained profile minimization.
bool converged
True when the profiler accepted the result as reliable.
AdvancedStatisticConfig advanced
Advanced fit/pruning/covariance configuration.
std::size_t MC_draws
Number of accepted MC draws used for uncertainty propagation.
FitDiagnostics diagnostics
std::vector< double > scale_hints
std::vector< ExperimentObs > obs_ids
std::shared_ptr< BaseLikelihood > like
std::vector< std::string > names
std::vector< ParamLimit > limits
std::shared_ptr< LikelihoodContext > ctx
std::vector< ParamId > eta_ids
std::vector< ParamId > p_ids
std::vector< double > x_hat
fit_app::BackendFitResult raw
std::optional< std::pair< double, double > > limits