30 const std::vector<double>& p,
31 const std::vector<double>& eta,
39 for (std::size_t i = 0; i < p.size(); ++i) {
43 for (std::size_t a = 0; a < eta.size(); ++a) {
51 const std::shared_ptr<ILikelihood>& base,
58 std::vector<double> theta = pr.
start;
60 theta.assign(base->dim(), 0.0);
62 if (theta.size() != base->dim()) {
63 throw std::runtime_error(
"direct_profile_no_free: bad start dimension");
66 for (
const auto& [idx, val] : pr.fixed_params) {
67 if (idx >= theta.size()) {
68 throw std::runtime_error(
"direct_profile_no_free: fixed index out of range");
76 for (std::size_t i = 0; i < theta.size(); ++i) {
86 std::shared_ptr<fit_app::IFitBackend> minimizer,
89 : minimizer(
std::move(minimizer)),
94 std::shared_ptr<ILikelihood> base,
98 return direct_profile_no_free(base, pr);
103 return profile_laplace_nuisance(base, pr);
104 }
catch (
const std::exception& e) {
105 std::cout <<
"[LAPLACE PROFILE] failed, falling back to Minuit: "
106 << e.what() << std::endl;
107 return profile_minuit(base, pr);
111 return profile_minuit(base, pr);
115 std::shared_ptr<ILikelihood> base,
118 auto like = std::dynamic_pointer_cast<IProfileableLikelihood>(base);
121 throw std::runtime_error(
"Likelihood does not implement IProfileableLikelihood");
124 const std::size_t p_dim = like->p_dimension();
125 const std::size_t eta_dim = like->eta_dimension();
127 if (p_dim + eta_dim != like->dim()) {
128 throw std::runtime_error(
"Inconsistent p/eta dimensions in likelihood");
131 for (std::size_t i : pr.free_params) {
133 throw std::runtime_error(
134 "Laplace nuisance profiler only supports fixed fit parameters; "
135 "free fit parameters require Minuit fallback"
140 std::vector<double> p = like->central_p();
142 for (
const auto& [idx, val] : pr.fixed_params) {
157 laplace_profile_eta_refined(*like, p, opts);
159 return full_theta_result_from_split(
170 LOG_ERROR(
"InvalidArgument",
"Dimension mismatch in profiler. Fit dimension is", base->dim(),
175 std::vector<std::size_t> fixed_idx = unzipped.ids;
176 std::vector<double> fixed_vals = unzipped.vals;
179 [base](std::vector<double> theta) {
return base->nll(theta); },
183 auto make_defs_from = [&](
const std::vector<double>& start) {
184 auto defs = base->get_param_defs();
185 if (!start.empty()) {
186 if (start.size() != defs.size()) {
187 LOG_ERROR(
"InvalidArgument",
"ProfileRequest::start has wrong dimension.");
189 for (std::size_t i = 0; i < defs.size(); ++i) {
190 defs[i].value = start[i];
193 for (std::size_t k = 0; k < fixed_idx.size(); ++k) {
194 defs[fixed_idx[k]].value = fixed_vals[k];
199 auto central_seed = [&]() {
200 auto defs = base->get_param_defs();
201 std::vector<double> out(defs.size());
202 for (std::size_t i = 0; i < defs.size(); ++i) out[i] = defs[i].value;
203 for (std::size_t k = 0; k < fixed_idx.size(); ++k) out[fixed_idx[k]] = fixed_vals[k];
215 bool best_raw_set =
false;
217 auto try_one = [&](
const std::vector<double>&
seed,
221 auto defs = make_defs_from(seed);
226 return minimizer->minimize_with_fixed(
f, defs, local_opt, fixed_idx, fixed_vals);
233 if (!acceptable_for_profile(r1)) {
240 if (!acceptable_for_profile(best_raw)) {
248 const bool accepted = acceptable_for_profile(best_raw);
251 std::cout <<
"\n=== [PROFILE FAIL] ===\n";
252 std::cout <<
"fixed_idx = [ ";
253 for (
auto i : fixed_idx)
std::cout << i <<
" ";
256 std::cout <<
"fixed_vals = [ ";
257 for (
auto v : fixed_vals)
std::cout <<
std::setprecision(17) <<
v <<
" ";
274 for (std::size_t i : pr.free_params) {
UnzipResult1D< T, U > unzip(const std::map< T, U > &indexed)
Splits a map into parallel id and value vectors.
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
Generic likelihood profiling engine.
ProfilerMode
Available algorithms for profiling free parameters.
@ LAPLACE_NUISANCE
Use the Laplace nuisance approximation when possible, with Minuit fallback.
Profiler(std::shared_ptr< fit_app::IFitBackend > minimizer, ProfilerMode mode=ProfilerMode::MINUIT)
Constructs a profiler.
ProfileResult profile(std::shared_ptr< ILikelihood > base, const ProfileRequest &pr) const
Profiles a likelihood according to a request.
Hash specialization for SymbolId<Tag>.
double f(double x)
Wilson special function f depending on x.
Result of a Laplace nuisance-profile computation.
bool ok
True when the computation produced a finite, usable result.
double nll_hat
Profiled or approximate profiled NLL value.
std::vector< double > eta_hat
Estimated profiled nuisance vector.
Numerical controls for the hybrid Laplace/Newton nuisance profiler.
std::size_t max_refinement_iters
Maximum number of outer correction cycles.
double max_newton_step_in_sigma
Maximum Newton displacement measured in nuisance standard deviations.
double stationarity_threshold
Threshold on above which a direction is refined.
std::size_t max_refined_eta
Maximum number of nuisance directions corrected by Newton refinement.
bool use_direct_nll_for_final_value
If true, use the direct NLL at the final profiled point as the reported value.
Input specification for a constrained profile minimization.
std::vector< std::size_t > free_params
Global indices of parameters released during profiling.
std::map< std::size_t, double > fixed_params
Fixed parameter values keyed by global parameter index.
std::vector< double > start
Initial full parameter vector used as a warm start.
Output of a constrained profile minimization.
double nll_hat
Minimum or approximate profiled NLL value.
bool converged
True when the profiler accepted the result as reliable.
std::map< std::size_t, double > theta_hat
Profiled parameter values keyed by global parameter index.
std::vector< double > values
FitDiagnostics diagnostics
bool has_valid_parameters