# Connection between BFGS and ROOT.Minuit. Stopping criteria

**URL:** <https://discourse.julialang.org/t/connection-between-bfgs-and-root-minuit-stopping-criteria/48729>\
**Category:** Optimization (Mathematical)\
**Tags:** optimization, hep\
**Created:** [October 21, 2020, 8:19am UTC](https://discourse.julialang.org/t/connection-between-bfgs-and-root-minuit-stopping-criteria/48729 "2020-10-21T08:19:55Z")\
**Posts on this page:** 1\
**Showing post:** 4

<div class="post-metadata">

**Author:** ![Yuan-Ru-Lin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuan-ru-lin/32/46068_2.png) [@Yuan-Ru-Lin](https://discourse.julialang.org/u/Yuan-Ru-Lin)\
**Post date:** [January 21, 2023, 6:26pm UTC](https://discourse.julialang.org/t/connection-between-bfgs-and-root-minuit-stopping-criteria/48729/4 "2023-01-21T18:26:35Z")

</div>

The stopping criteria can be implemented as follows.

```julia
"""
See also MIGrad in Chapter 4: Minuit Commands, https://root.cern.ch/download/minuit.pdf
"""
function miniutestop(state)
    mt = state.metadata
    edm = mt["g(x)"]' * mt["~inv(H)"] * mt["g(x)"] / 2
    edm < 1e-3 * 0.1 * 1.0
end

```

Here is a minimal working example:

```julia
using Distributions, Optim

"""
See also MIGrad in Chapter 4: Minuit Commands, https://root.cern.ch/download/minuit.pdf
""" 
function miunitstop(state)
    mt = state.metadata
    edm = mt["g(x)"]' * mt["~inv(H)"] * mt["g(x)"] / 2
    edm < 1e-3 * 0.1 * 1.0
end

data = randn(1000)
res = optimize([-Inf, 0.0], [Inf, Inf], [0.0, 1.0], Fminbox(BFGS()), Optim.Options(extended_trace=true, callback=miunitstop)) do pars
    -loglikelihood(Normal(pars...), data)
end

```

Please note that the callback has to be added in `Optim.Options` along with `extended_trace=true` for those metadata to be accessible.

Note also that the inverted hessian used in the calculation of EDM is just an approximate one that happens to be used in BFGS. So, after the minimum is found, you may want to calculate an exact one with `Zygote.hessian(pars -> -loglikelihood(Normal(pars...), data), res.minimizer)` (especially when you need to invert the hessian matrix to get the covariance matrix).

* * *

## Demo

The `miunitstop` can reduce the number of calls by ~100x, with effectively the same results:

```julia
julia> res = optimize([-Inf, 0.0], [Inf, Inf], [0.0, 1.0], Fminbox(BFGS()), Optim.Options(extended_trace=true, callback=miunitstop)) do pars
           -loglikelihood(Normal(pars...), data)
       end
 * Status: failure

 * Candidate solution
    Final objective value: 1.421423e+03

 * Found with
    Algorithm: Fminbox with BFGS

 * Convergence measures
    |x - x'| = 1.34e-02 ≰ 0.0e+00
    |x - x'|/|x'| = 1.34e-02 ≰ 0.0e+00
    |f(x) - f(x')| = 0.00e+00 ≤ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 0.00e+00 ≤ 0.0e+00
    |g(x)| = 2.06e-02 ≰ 1.0e-08

 * Work counters
    Seconds run: 0 (vs limit Inf)
    Iterations: 1
    f(x) calls: 9
    ∇f(x) calls: 9

julia> res2 = optimize([-Inf, 0.0], [Inf, Inf], [0.0, 1.0], Fminbox(BFGS())) do pars
           -loglikelihood(Normal(pars...), data)
       end
 * Status: success (objective increased between iterations)

 * Candidate solution
    Final objective value: 1.421423e+03

 * Found with
    Algorithm: Fminbox with BFGS

 * Convergence measures
    |x - x'| = 9.17e-11 ≰ 0.0e+00
    |x - x'|/|x'| = 9.15e-11 ≰ 0.0e+00
    |f(x) - f(x')| = 0.00e+00 ≤ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 0.00e+00 ≤ 0.0e+00
    |g(x)| = 3.75e-08 ≰ 1.0e-08

 * Work counters
    Seconds run: 0 (vs limit Inf)
    Iterations: 4
    f(x) calls: 713
    ∇f(x) calls: 713

julia> res.minimizer
2-element Vector{Float64}:
 0.013204458490624406
 1.002498042266284

julia> res2.minimizer
2-element Vector{Float64}:
 0.013199480602918739
 1.002487701716321

```

---

_[View the full topic](https://discourse.julialang.org/t/connection-between-bfgs-and-root-minuit-stopping-criteria/48729)._
