Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Guard Minuit optimizer against provided strategy of None #2278

Merged
merged 9 commits into from
Aug 16, 2023

Conversation

matthewfeickert
Copy link
Member

@matthewfeickert matthewfeickert commented Aug 15, 2023

Description

Guard Minuit optimizer strategy from None by first attempting to get a not-None result from the options kwargs and then falling back to assigning the strategy through int(not do_grad). In the process this simplifies the logic required and supersedes PR #2277.

Add tests to test_minuit_strategy_global for strategy=None and strategy=2 (2 is a valid iminuit.Minuit.strategy strategy but not used often, so done just for good measure).

Add a very brief explanation to docs of what using strategy=None means in terms of evaluation to 0 or 1 from int(pyhf.tensorlib.default_do_grad).

This extended example from @alexander-held in Issue #1785 now runs with this PR:

# issue-1785.py
import pyhf

model = pyhf.simplemodels.uncorrelated_background(
    signal=[12.0, 11.0], bkg=[50.0, 52.0], bkg_uncertainty=[3.0, 7.0]
)
data = [51, 48] + model.config.auxdata
pyhf.set_backend("numpy", "minuit")
pyhf.infer.mle.fit(data, model)

strategies = [None, 0, 1, 2]
for strategy in strategies:
    pyhf.infer.mle.fit(data, model, strategy=strategy)

Checklist Before Requesting Reviewer

  • Tests are passing
  • "WIP" removed from the title of the pull request
  • Selected an Assignee for the PR to be responsible for the log summary

Before Merging

For the PR Assignees:

  • Summarize commit messages into a comprehensive review of the PR
* Guard Minuit optimizer strategy from None by first attempting to get a
  not-None result from the options kwargs and then falling back to
  assigning the strategy through the equivalent of `int(not do_grad)`.
  In the process this simplifies the logic required and supersedes PR
  https://github.com/scikit-hep/pyhf/pull/2277.
   - Cast the truthiness of `do_grad` as an integer for human
     readability in debugging.
* Update the comments on `do_grad` strategy with definitions from
  iMinuit v2.24.0 docs.
* Add tests to test_minuit_strategy_global for strategy=None and strategy=2
  (2 is a valid iminuit.Minuit.strategy strategy but not used often).
* Add brief explanation to docs of what using strategy=None means in
  terms of evaluation to 0 or 1 from `int(pyhf.tensorlib.default_do_grad)`.

* As the iminuit.Minuit.strategy is an integer, instead of having to
  have a human determine the integer representation of the inverse of
  the truthiness of do_grad during debugging or inspection do this in
  code.
@matthewfeickert matthewfeickert added docs Documentation related tests pytest fix A bug fix need-to-backport tmp label until can be backported to patch release branch labels Aug 15, 2023
@matthewfeickert matthewfeickert self-assigned this Aug 15, 2023
src/pyhf/optimize/opt_minuit.py Outdated Show resolved Hide resolved
src/pyhf/optimize/opt_minuit.py Outdated Show resolved Hide resolved
@@ -173,27 +173,35 @@ def test_minuit_strategy_do_grad(mocker, backend):
assert spy.spy_return.minuit.strategy == 1


@pytest.mark.parametrize('strategy', [0, 1])
@pytest.mark.parametrize('strategy', [0, 1, 2])
Copy link
Member Author

Choose a reason for hiding this comment

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

Adding 2 as we can and I forgot it existed.

@matthewfeickert
Copy link
Member Author

Comment on lines +31 to +34
strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`.
Default is ``None``, which results in either
:attr:`iminuit.Minuit.strategy` ``0`` or ``1`` from the evaluation of
``int(not pyhf.tensorlib.default_do_grad)``.
Copy link
Member Author

Choose a reason for hiding this comment

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

@codecov
Copy link

codecov bot commented Aug 15, 2023

Codecov Report

Patch coverage: 100.00% and no project coverage change.

Comparison is base (b81d903) 98.30% compared to head (6cd5366) 98.30%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2278   +/-   ##
=======================================
  Coverage   98.30%   98.30%           
=======================================
  Files          69       69           
  Lines        4534     4536    +2     
  Branches      801      802    +1     
=======================================
+ Hits         4457     4459    +2     
  Misses         45       45           
  Partials       32       32           
Flag Coverage Δ
contrib 97.88% <100.00%> (+<0.01%) ⬆️
doctest 61.04% <0.00%> (-0.03%) ⬇️
unittests-3.10 96.31% <100.00%> (+<0.01%) ⬆️
unittests-3.11 96.31% <100.00%> (+<0.01%) ⬆️
unittests-3.8 96.34% <100.00%> (+<0.01%) ⬆️
unittests-3.9 96.36% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
src/pyhf/optimize/opt_minuit.py 100.00% <100.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@matthewfeickert
Copy link
Member Author

Once this is approved I'll make patch release pyhf v0.7.3 with this fix included in it.

# Guard against None from either self.strategy defaulting to None or
# passing strategy=None as options kwarg
if strategy is None:
strategy = int(not do_grad)
Copy link
Member Author

Choose a reason for hiding this comment

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

As the iminuit.Minuit.strategy is an integer, instead of having to have a human determine the integer representation of the inverse of the truthiness of do_grad during debugging or inspection do this in code.

Copy link
Member

Choose a reason for hiding this comment

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

If you're aiming for maximum readability, I like strategy = 0 if do_grad else 1 best.

Copy link
Member

@alexander-held alexander-held left a comment

Choose a reason for hiding this comment

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

This looks good to me, thanks! Just a tiny comment on what I believe is slightly more readable, but might be preference.

As a heads-up: there will also be a strategy=3 arriving eventually: root-project/root#13109 (more expensive and stable).

@matthewfeickert matthewfeickert merged commit 4cadf91 into main Aug 16, 2023
19 checks passed
@matthewfeickert matthewfeickert deleted the fix/make-minuit-strategy-explicit branch August 16, 2023 08:36
@matthewfeickert matthewfeickert removed the need-to-backport tmp label until can be backported to patch release branch label Aug 16, 2023
matthewfeickert added a commit that referenced this pull request Aug 16, 2023
matthewfeickert added a commit that referenced this pull request Aug 17, 2023
* As the bug that was fixed in PR #2278 was subtle add an example to
the pyhf v0.7.3 release notes that people can use to see if they
were affected by it.
* Amends PR #2290
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation related fix A bug fix tests pytest
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Documentation of default Minuit strategy choice
3 participants