Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README/ReleaseNotes/v638/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ The following people have contributed to this new version:
### Minuit2

* Behavior change: building ROOT using `minuit2_omp=ON` option no longer enables OpenMP parallelization by default. One has to call now additionally GradientCalculator::SetParallelOMP().
* The functionality of the `FCNGradAdapter` got absorbed into the `FCNAdapter`. This also means that the `FCNGradAdapter.h` header is gone.
* The `ROOT::Minuit2::FCNAdapter` is no longer templated and now handle only functions with this exact signatures:
* `double(double const *)` for the wrapped function
* `void(double const *, double *)` for the gradient
The advantage of this restriction is that the `FCNAdapter` can now also wrap any Python function that respects this interface.

## RooFit

Expand Down
13 changes: 0 additions & 13 deletions math/mathcore/inc/Math/IFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ namespace ROOT {
// if it inherits from ROOT::Math::IGradientFunctionMultiDim.
virtual bool HasGradient() const { return false; }

virtual bool returnsInMinuit2ParameterSpace() const { return false; }

/// Evaluate all the vector of function derivatives (gradient) at a point x.
/// Derived classes must re-implement it if more efficient than evaluating one at a time
virtual void Gradient(const T *x, T *grad) const
Expand All @@ -103,17 +101,6 @@ namespace ROOT {
}
}

/// In some cases, the gradient algorithm will use information from the previous step, these can be passed
/// in with this overload. The `previous_*` arrays can also be used to return second derivative and step size
/// so that these can be passed forward again as well at the call site, if necessary.
virtual void GradientWithPrevResult(const T *x, T *grad, T *previous_grad, T *previous_g2, T *previous_gstep) const
{
unsigned int ndim = NDim();
for (unsigned int icoord = 0; icoord < ndim; ++icoord) {
grad[icoord] = Derivative(x, icoord, previous_grad, previous_g2, previous_gstep);
}
}

/// Optimized method to evaluate at the same time the function value and derivative at a point x.
/// Often both value and derivatives are needed and it is often more efficient to compute them at the same time.
/// Derived class should implement this method if performances play an important role and if it is faster to
Expand Down
1 change: 0 additions & 1 deletion math/minuit2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ if(CMAKE_PROJECT_NAME STREQUAL ROOT)
Minuit2/ExternalInternalGradientCalculator.h
Minuit2/FCNAdapter.h
Minuit2/FCNBase.h
Minuit2/FCNGradAdapter.h
Minuit2/FCNGradientBase.h
Minuit2/FumiliBuilder.h
Minuit2/FumiliChi2FCN.h
Expand Down
177 changes: 151 additions & 26 deletions math/minuit2/inc/Minuit2/FCNAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,166 @@
#include <ROOT/RSpan.hxx>

#include <vector>

namespace ROOT {

namespace Minuit2 {

/**


template wrapped class for adapting to FCNBase signature

@author Lorenzo Moneta

@ingroup Minuit

*/

template <class Function>
#include <functional>

namespace ROOT::Minuit2 {

/// Adapter class to wrap user-provided functions into the FCNBase interface.
///
/// This class allows users to supply their function (and optionally its gradient,
/// diagonal second derivatives, and Hessian) in the form of `std::function` objects.
/// It adapts these functions so that they can be used transparently with the MINUIT
/// minimizers via the `FCNBase` interface.
///
/// Typical usage:
/// - Pass the function to minimize to the constructor.
/// - Optionally set the gradient, G2 (second derivative diagonal), or Hessian
/// functions using the provided setter methods.
/// - MINUIT will then query these functions if available, or fall back to numerical
/// approximations if they are not provided.
///
/// \ingroup Minuit
class FCNAdapter : public FCNBase {

public:
FCNAdapter(const Function &f, double up = 1.) : fFunc(f), fUp(up) {}

double operator()(std::vector<double> const& v) const override { return fFunc.operator()(&v[0]); }
double operator()(const double *v) const { return fFunc.operator()(v); }
/// Construct an adapter around a user-provided function.
///
/// @param f Function to minimize. It must take a pointer to the parameter array
/// (`double const*`) and return the function value.
/// @param up Error definition parameter (defaults to 1.0).
FCNAdapter(std::function<double(double const *)> f, double up = 1.) : fUp(up), fFunc(std::move(f)) {}

/// Indicate whether an analytic gradient has been provided.
///
/// @return `true` if a gradient function was set, otherwise `false`.
bool HasGradient() const override { return bool(fGradFunc); }

/// Indicate whether analytic second derivatives (diagonal of the Hessian) are available.
///
/// @return `true` if a G2 function or a Hessian function has been set, otherwise `false`.
bool HasG2() const override { return bool(fG2Func); }

/// Indicate whether an analytic Hessian has been provided.
///
/// @return `true` if a Hessian function was set, otherwise `false`.
bool HasHessian() const override { return bool(fHessianFunc); }

/// Evaluate the function at the given parameter vector.
///
/// @param v Parameter vector.
/// @return Function value at the specified parameters.
double operator()(std::vector<double> const &v) const override { return fFunc(v.data()); }

/// Return the error definition parameter (`up`).
///
/// @return Current error definition value.
double Up() const override { return fUp; }

/// Evaluate the gradient of the function at the given parameter vector.
///
/// @param v Parameter vector.
/// @return Gradient vector (∂f/∂xᵢ) at the specified parameters.
std::vector<double> Gradient(std::vector<double> const &v) const override
{
std::vector<double> output(v.size());
fGradFunc(v.data(), output.data());
return output;
}

/// Return the diagonal elements of the Hessian (second derivatives).
///
/// If a G2 function is set, it is used directly. If only a Hessian function
/// is available, the diagonal is extracted from the full Hessian.
///
/// @param x Parameter vector.
/// @return Vector of second derivatives (one per parameter).
std::vector<double> G2(std::vector<double> const &x) const override
{
std::vector<double> output;
if (fG2Func)
return fG2Func(x);
if (fHessianFunc) {
std::size_t n = x.size();
output.resize(n);
if (fHessian.empty())
fHessian.resize(n * n);
fHessianFunc(x, fHessian.data());
if (!fHessian.empty()) {
// Extract diagonal elements of Hessian
for (unsigned int i = 0; i < n; i++)
output[i] = fHessian[i * n + i];
}
}
return output;
}

/// Return the full Hessian matrix.
///
/// If a Hessian function is available, it is used to fill the matrix.
/// If the Hessian function fails, it is cleared and not used again.
///
/// @param x Parameter vector.
/// @return Flattened Hessian matrix in row-major order.
std::vector<double> Hessian(std::vector<double> const &x) const override
{
std::vector<double> output;
if (fHessianFunc) {
std::size_t n = x.size();
output.resize(n * n);
bool ret = fHessianFunc(x, output.data());
if (!ret) {
output.clear();
fHessianFunc = nullptr;
}
}

return output;
}

/// Set the analytic gradient function.
///
/// @param f Gradient function of type `void(double const*, double*)`.
/// The first argument is the parameter array, the second is
/// the output array for the gradient values.
void SetGradientFunction(std::function<void(double const *, double *)> f) { fGradFunc = std::move(f); }

/// Set the function providing diagonal second derivatives (G2).
///
/// @param f Function taking a parameter vector and returning the
/// diagonal of the Hessian matrix as a vector.
void SetG2Function(std::function<std::vector<double>(std::vector<double> const &)> f) { fG2Func = std::move(f); }

/// Set the function providing the full Hessian matrix.
///
/// @param f Function of type `bool(std::vector<double> const&, double*)`.
/// The first argument is the parameter vector, the second is
/// the output buffer (flattened matrix). The return value
/// should be `true` on success, `false` on failure.
void SetHessianFunction(std::function<bool(std::vector<double> const &, double *)> f)
{
fHessianFunc = std::move(f);
}

/// Update the error definition parameter.
///
/// @param up New error definition value.
void SetErrorDef(double up) override { fUp = up; }

private:
const Function &fFunc;
double fUp;
using Function = std::function<double(double const *)>;
using GradFunction = std::function<void(double const *, double *)>;
using G2Function = std::function<std::vector<double>(std::vector<double> const &)>;
using HessianFunction = std::function<bool(std::vector<double> const &, double *)>;

double fUp = 1.; ///< Error definition parameter.
mutable std::vector<double> fHessian; ///< Storage for intermediate Hessian values.

Function fFunc; ///< Wrapped function to minimize.
GradFunction fGradFunc; ///< Optional gradient function.
G2Function fG2Func; ///< Optional diagonal second-derivative function.
mutable HessianFunction fHessianFunc; ///< Optional Hessian function.
};

} // end namespace Minuit2

} // end namespace ROOT
} // namespace ROOT::Minuit2

#endif // ROOT_Minuit2_FCNAdapter
Loading
Loading