Skip to content

[RF] Faster Hesse in RooFit by advertising which params are independent - #16394

Merged
guitargeek merged 2 commits into
root-project:masterfrom
guitargeek:hessian_optimization
Sep 7, 2026
Merged

[RF] Faster Hesse in RooFit by advertising which params are independent#16394
guitargeek merged 2 commits into
root-project:masterfrom
guitargeek:hessian_optimization

Conversation

@guitargeek

@guitargeek guitargeek commented Sep 9, 2024

Copy link
Copy Markdown
Contributor

This reduces the time to run Hesse in the ATLAS Higgs benchmark from
123 s to 92 seconds.

Given that some models take hours for this, this is a significant
improvement for the user experience.

Further improvement is possible by analyzing the computation graph a bit
more to find more independent parameters (e.g., the different gammas for
stat uncertainties from different bins).

Benchmark results

The Higgs combination benchmark takes 31 s to minimize, plus 79 s for the Hessian with ROOT master. It was 130 seconds with ROOT master just a few days ago, before some optimization PRs got merged. This PR reduces the time for hesse() to 57 seconds.

@github-actions

github-actions Bot commented Sep 9, 2024

Copy link
Copy Markdown

Test Results

    21 files      21 suites   3d 11h 46m 15s ⏱️
 3 877 tests  3 876 ✅ 0 💤 1 ❌
71 357 runs  71 350 ✅ 6 💤 1 ❌

For more details on these failures, see this check.

Results for commit 418e525.

♻️ This comment has been updated with latest results.

@dpiparo

dpiparo commented Sep 14, 2024

Copy link
Copy Markdown
Member

The error seems an unresolved symbol on Win

2024-09-14T07:01:53.2104016Z      Creating library Z:/foobar/lib/libRooFitCore.lib and object Z:/foobar/lib/libRooFitCore.exp
2024-09-14T07:01:53.4308777Z RooXYChi2Var.obj : error LNK2001: unresolved external symbol "public: virtual void __cdecl RooAbsArg::fillVariableGroups(class RooFit::VariableGroups &)const " (?fillVariableGroups@RooAbsArg@@UEBAXAEAVVariableGroups@RooFit@@@Z) [Z:\foobar\build\roofit\roofitcore\RooFitCore.vcxproj]
2024-09-14T07:01:53.4311585Z RooAbsTestStatistic.obj : error LNK2001: unresolved external symbol "public: virtual void __cdecl RooAbsArg::fillVariableGroups(class RooFit::VariableGroups &)const " (?fillVariableGroups@RooAbsArg@@UEBAXAEAVVariableGroups@RooFit@@@Z) [Z:\foobar\build\roofit\roofitcore\RooFitCore.vcxproj]

@guitargeek
guitargeek force-pushed the hessian_optimization branch 2 times, most recently from 5a2d19a to 04afc01 Compare September 14, 2024 15:38
@dpiparo
dpiparo self-requested a review September 15, 2024 08:16

@dpiparo dpiparo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for this improvement! Let's not forget to give to this feature the right emphasis in the RNs.

@guitargeek
guitargeek force-pushed the hessian_optimization branch from 04afc01 to 9ebb89a Compare October 28, 2024 13:59
@guitargeek
guitargeek force-pushed the hessian_optimization branch 2 times, most recently from f3f761d to 5d5d765 Compare February 15, 2025 12:31
@guitargeek
guitargeek force-pushed the hessian_optimization branch from 82825d7 to 3fa8122 Compare March 11, 2025 23:27
@guitargeek
guitargeek requested a review from dpiparo July 29, 2025 07:27
@guitargeek
guitargeek marked this pull request as draft July 29, 2025 07:28
@guitargeek

Copy link
Copy Markdown
Contributor Author

Converter to draft because the main bottleneck in the Hessian computation was identified and eliminated:

It's still good to keep this PR around as a possible solution if the Hessian should become the bottleneck again.

@guitargeek
guitargeek force-pushed the hessian_optimization branch from 3fa8122 to 1505c19 Compare August 2, 2026 17:57
@guitargeek
guitargeek force-pushed the hessian_optimization branch from 1505c19 to 0c5904a Compare August 9, 2026 20:19
@guitargeek
guitargeek marked this pull request as ready for review September 6, 2026 22:17
Objective functions can now advertise pairs of parameters whose mixed
second derivative is identically zero, via the new virtual function
FCNBase::SecondDerivativeAlwaysVanishes(). The numerical Hessian
computation in MnHesse skips the finite-difference evaluations for such
parameter pairs, which can speed up Hesse significantly for likelihoods
with many mutually independent parameters.

The parameter indices in this interface refer to the FCN's own full
(external) parameter space, and the advertised information must hold for
all parameter values; see the FCNBase documentation for the complete
contract. Since MnHesse loops over Minuit-internal indices that exclude
fixed parameters, it translates them via MnUserTransformation::ExtOfInt()
before querying the predicate, so results stay correct when parameters
are fixed in the minimizer.

Clients that drive Minuit2 through the ROOT::Math interfaces can inject
the predicate with the new
Minuit2Minimizer::SetSecondDerivativeAlwaysVanishesFunc(), which follows
the same pattern as SetHessianFunction(). This keeps the feature out of
the general ROOT::Math function interfaces on purpose: it is only
consumed by Minuit2.
This reduces the time to run Hesse in the ATLAS Higgs benchmark from
123 s to 92 seconds.

Given that some models take hours for this, this is a significant
improvement for the user experience.

RooFit analyzes the computation graph of the minimized function to find
pairs of parameters that never appear in the same additive term of the
likelihood, meaning their mixed second derivative is identically zero.
The analysis is implemented privately in RooMinimizerFcn, on purpose
without adding any new public interfaces, so that the design can still
evolve: only strictly additive nodes (RooAddition, RooConstraintSum, and
the RooEvaluatorWrapper forwarding to its wrapped function) are
recursed into; everything else conservatively contributes all of its
graph leaves as a single term, which advertises no independence but is
always correct.

The resulting pairwise mask is stored as a packed bitvector and built
lazily on the first query, so that migrad-only fits and toy loops do not
pay for it. It is handed to Minuit2 in RooMinimizerFcn::initMinimizer()
via Minuit2Minimizer::SetSecondDerivativeAlwaysVanishesFunc(), which is
why RooFitCore now has a private link dependency on Minuit2.

The mask indices follow Minuit's external parameter convention, so the
result is also correct when parameters are fixed after the RooMinimizer
was constructed, which is covered by a unit test.

Further improvement is possible by analyzing the computation graph a bit
more to find more independent parameters (e.g., the different gammas for
stat uncertainties from different bins).
@guitargeek

guitargeek commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Revived the PR in the context of the Hessian work with Clad. These optimizations are obviously good for our users and can also serve as inspiration for similar optimizations in Clad. Faster numeric Hessians will also increase the pressure on the AD code path to keep up 🙂

@dpiparo, I have made this a prominent item in the release notes.

@guitargeek
guitargeek merged commit 5d9892e into root-project:master Sep 7, 2026
30 of 35 checks passed
@guitargeek
guitargeek deleted the hessian_optimization branch September 7, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants