6#include "minuit-cpp/FCNBase.hh"
7#include "minuit-cpp/FunctionMinimum.hh"
8#include "minuit-cpp/MnContours.hh"
9#include "minuit-cpp/MnEigen.hh"
10#include "minuit-cpp/MnHesse.hh"
11#include "minuit-cpp/MnMigrad.hh"
12#include "minuit-cpp/MnUserParameters.hh"
14namespace M2 = MinuitCpp;
19class MinuitObjective final :
public M2::FCNBase {
21 explicit MinuitObjective(
const IObjectiveFunction& objective)
22 : objective_(objective) {}
24 double operator()(
const std::vector<double>& x)
const override {
26 const double value = objective_(x);
27 return std::isfinite(value) ? value : 1e300;
33 double Up()
const override {
34 return objective_.error_definition();
38 const IObjectiveFunction& objective_;
41class MinuitState final :
public BackendState {
43 explicit MinuitState(M2::FunctionMinimum minimum)
44 : minimum_(
std::move(minimum)) {}
46 const M2::FunctionMinimum& minimum()
const {
51 M2::FunctionMinimum minimum_;
54class MinuitCppBackend final :
public IFitBackend {
56 BackendFitResult minimize(
const IObjectiveFunction& objective,
57 const std::vector<ParameterDefinition>& parameters,
58 const FitOptions& options)
const override {
59 return do_minimize(objective, parameters, options, {}, {});
62 BackendFitResult minimize_with_fixed(
const IObjectiveFunction& objective,
63 const std::vector<ParameterDefinition>& parameters,
64 const FitOptions& options,
65 const std::vector<std::size_t>& fixed_indices,
66 const std::vector<double>& fixed_values)
const override {
67 if (fixed_indices.size() != fixed_values.size()) {
68 throw std::invalid_argument(
"fixed_indices/fixed_values size mismatch");
70 return do_minimize(objective, parameters, options, fixed_indices, fixed_values);
73 BackendContourResult contour(
const IObjectiveFunction& objective,
74 const BackendFitResult& reference_fit,
77 const ContourOptionsBackEnd& options)
const override {
78 BackendContourResult out;
80 auto state = std::dynamic_pointer_cast<const MinuitState>(reference_fit.state);
81 if (!state)
return out;
84 MinuitObjective minuit_objective(objective);
85 M2::MnContours mn_contours(minuit_objective, state->minimum(), options.strategy);
86 out.points = mn_contours(
static_cast<unsigned>(x_index),
static_cast<unsigned>(y_index), options.npoints);
87 out.success = out.points.size() >= 4;
97 static void apply_parameters(M2::MnUserParameters& upar,
98 const std::vector<ParameterDefinition>& parameters) {
99 for (
const auto& parameter : parameters) {
100 upar.Add(parameter.name.c_str(), parameter.value,
safe_step(parameter.value, parameter.step_hint));
101 if (parameter.limits.has_value()) {
102 upar.SetLimits(parameter.name.c_str(), parameter.limits->first, parameter.limits->second);
104 if (parameter.fixed) {
105 upar.Fix(parameter.name.c_str());
110 static void apply_fixed_values(M2::MnMigrad& migrad,
111 const std::vector<std::size_t>& fixed_indices,
112 const std::vector<double>& fixed_values) {
113 for (std::size_t i = 0; i < fixed_indices.size(); ++i) {
114 const unsigned idx =
static_cast<unsigned>(fixed_indices[i]);
115 migrad.SetValue(idx, fixed_values[i]);
120 static void log_summary(
const M2::FunctionMinimum& minimum) {
121 std::cout <<
"\n=== [MINUIT] FunctionMinimum ===\n";
122 std::cout <<
"IsValid = " << minimum.IsValid() <<
"\n";
123 std::cout <<
"HasValidParameters = " << minimum.HasValidParameters() <<
"\n";
124 std::cout <<
"HasValidCovariance = " << minimum.HasValidCovariance() <<
"\n";
125 std::cout <<
"HasAccurateCovar = " << minimum.HasAccurateCovar() <<
"\n";
126 std::cout <<
"HasPosDefCovar = " << minimum.HasPosDefCovar() <<
"\n";
127 std::cout <<
"HasMadePosDefCovar = " << minimum.HasMadePosDefCovar() <<
"\n";
128 std::cout <<
"HesseFailed = " << minimum.HesseFailed() <<
"\n";
129 std::cout <<
"Fval = " << minimum.Fval() <<
"\n";
130 std::cout <<
"EDM = " << minimum.Edm() <<
"\n";
131 std::cout <<
"NFcn = " << minimum.NFcn() <<
"\n";
132 std::cout <<
"Up = " << minimum.Up() <<
"\n";
135 static BackendFitResult extract_result(
const M2::FunctionMinimum& minimum,
136 const std::vector<ParameterDefinition>& parameters,
137 bool extract_full_covariance) {
138 BackendFitResult out;
139 out.values.resize(parameters.size());
140 out.errors.assign(parameters.size(), 0.0);
141 out.covariance =
RealMatrix(parameters.size(), parameters.size());
142 out.state = std::make_shared<MinuitState>(minimum);
144 out.diagnostics.fmin = minimum.Fval();
145 out.diagnostics.edm = minimum.Edm();
146 out.diagnostics.nfcn = minimum.NFcn();
147 out.diagnostics.ok = minimum.IsValid();
148 out.diagnostics.has_valid_parameters = minimum.HasValidParameters();
149 out.diagnostics.hesse_failed = minimum.HesseFailed();
150 out.diagnostics.has_valid_covar = minimum.HasValidCovariance();
151 out.diagnostics.has_posdef_covar = minimum.HasPosDefCovar();
152 out.diagnostics.has_accurate_covar = minimum.HasAccurateCovar();
153 out.diagnostics.made_posdef = minimum.HasMadePosDefCovar();
154 out.diagnostics.reached_call_limit = minimum.HasReachedCallLimit();
155 out.diagnostics.above_max_edm = minimum.IsAboveMaxEdm();
157 const auto& state = minimum.UserState();
158 for (std::size_t i = 0; i < parameters.size(); ++i) {
159 out.values[i] = state.Value(parameters[i].
name.c_str());
160 out.errors[i] = parameters[i].fixed ? 0.0 : state.Error(parameters[i].
name.c_str());
170 if (!minimum.HasValidCovariance()) {
174 if (!extract_full_covariance) {
177 const auto& cov = state.Covariance();
178 for (std::size_t i = 0; i < parameters.size(); ++i) {
179 for (std::size_t j = 0; j < parameters.size(); ++j) {
180 out.covariance.at(i, j) = cov(i, j);
185 out.diagnostics.cov_eigs = eigen(cov);
187 double min_pos = std::numeric_limits<double>::infinity();
188 double max_pos = 0.0;
189 for (
double eig : out.diagnostics.cov_eigs) {
190 if (std::isfinite(eig) && eig > 0.0) {
191 min_pos = std::min(min_pos, eig);
192 max_pos = std::max(max_pos, eig);
196 if (min_pos < std::numeric_limits<double>::infinity() && max_pos > 0.0) {
197 out.diagnostics.cond_number = max_pos / min_pos;
203 static BackendFitResult do_minimize(
const IObjectiveFunction& objective,
204 const std::vector<ParameterDefinition>& parameters,
205 const FitOptions& options,
206 const std::vector<std::size_t>& fixed_indices,
207 const std::vector<double>& fixed_values) {
208 MinuitObjective minuit_objective(objective);
210 M2::MnUserParameters upar;
211 apply_parameters(upar, parameters);
213 M2::MnMigrad migrad(minuit_objective, upar, options.strategy);
214 apply_fixed_values(migrad, fixed_indices, fixed_values);
216 M2::FunctionMinimum minimum = migrad(options.max_fcn, options.tolerance);
218 if (options.run_hesse) {
219 M2::MnHesse hesse(options.strategy);
220 hesse(minuit_objective, minimum, options.hesse_maxcalls);
223 if (options.verbose) {
224 log_summary(minimum);
227 const bool has_explicitly_fixed_parameters = !fixed_indices.empty() ||
228 std::any_of(parameters.begin(), parameters.end(), [](
const ParameterDefinition& parameter) {
229 return parameter.fixed;
232 return extract_result(minimum, parameters, !has_explicitly_fixed_parameters);
239 return std::make_unique<MinuitCppBackend>();
std::unique_ptr< IFitBackend > make_minuit_backend()
double safe_step(double value, double scale_hint)
Hash specialization for SymbolId<Tag>.