-
Notifications
You must be signed in to change notification settings - Fork 76
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
Do something sensible when running Minuit.hesse()
turns valid minimum into invalid minimum
#746
Comments
@matthewfeickert @jpivarski @henryiii Do you have thoughts on this? |
An argument to the |
@jpivarski This goes along the solution you often used in uproot, right? I think it is a good way to change behavior of the API in a soft way with a deprecation period. But I still need to decide what should be the new default. |
Some kinds of changes can have a deprecation period and others can't. Changing the name of a function, class, or method is easy: you add the new function/class/method and put a warning on the old one saying that it's going away and users should switch to the new one. An example of that is NumPy's It's much harder to change the default value of an argument to a function or method, which is what you want to do here. Suppose that you want to introduce an argument We had to do this once: among the API changes in Awkward Array (v1), " unset = object()
def fill_none(array, axis=unset):
if axis is unset:
... to be able to distinguish between existence and non-existence of the That said, not all changes in behavior require deprecation cycles. If the old behavior can be considered a bug, it can be fixed right away (unless the library has some very hard consistency guarantees, such as one that would be used in life-threatening situations, but that's not what we're talking about). What is a "bug" and what is a "feature" is a subjective thing: it depends on the author's intentions, and authors don't always know their intentions. Like a (Copenhagen) quantum state, some decisions aren't made until the question is asked. A rigorous procedure can't always be applied, and it's legitimate to consider it a judgement call. In this particular case, what I would do is introduce the argument and make the default behavior equivalent to the old behavior, so that no deprecation cycle is needed, because they're a pain. "Equivalent" can include a warning, so the new behavior can be |
Before sending edit: Yes, I wrote this before reading the middle of Jim's reply and reading that's exactly what he did. So I guess I actually had nothing to add. 🤦 I don't have much to add, just can mention that there is a procedure for nicely changing the default for an argument. You can require users provide the argument always and produce a warning if they don't. That way you can eventually safely change the default, since users should be all explicitly specifying the option anyway (assuming that they update and see the warning during the period). This can be seen in some libraries, but probably the highest-profile example: Python 3.10+ will produce a warning if Doesn't mean you want/need/should change it - Footnotes
|
I can change the API at any time by adding a warning. This is a non-breaking change and does not need a deprecation period. Changing the API so that Migrad is called after Hesse broke the fit can be considered "a fix", it is technically a breaking change on the user side, but one that rectifies a problem. We usually make such changes without a deprecation period, too. Anyway, I didn't want to discuss how to change the default for an argument, but what kind of behavior would be the best default in this case in the long-term. I was asking you not as Python programmers but as people who are familiar with Minuit. |
Yes, I am aware of that technique and would use it where appropriate. I was not asking about how to make the transition, I know how to do that. I was asking what the new default should be. Jim suggested "warning", which is the safe option, but I am not sure whether this solution is the most beneficial for the average user. To clarify further, I am not interested in what is the easiest solution now, I consider what is the best solution long-term. And that goes into fundamentals of what iminuit should be. iminuit is already not just a dumb wrapper around Minuit2. Minuit.migrad calls MnMigrad several times by default, because that improves the convergence rate. It is a silly hack that I inherited from the previous maintainers. I originally wanted to remove it, but then I got complains that iminuit's convergence rate was reduced (from pyhf, I think). So I restored that hacky feature. Since we already try to improve the user experience beyond what calling raw MnMigrad would give you, it is just consistent to also call Migrad after Hesse. However, that has potential unwanted side-effects, too. I guess it is difficult to see all the implications unless you are the long-time maintainer of iminuit. |
Oh, sorry I misunderstood. In that case, I would expect a new minimum found in HESSE to invoke another MIGRAD. I think MINOS already does that internally: I remember there being a big ASCII arrow in the log output when that happened. It's good for this to be parameterized, because there are reasons to intentionally call HESSE on non-minimal points in the space, if what you're trying to do is get a second-derivative map of it. That's not how Minuit is usually used, but it wouldn't be a bad use. |
The following is a concern when using strategy 0 or 1 (default).
In most cases, Minuit.migrad calls MnHesse automatically at the end of the minimization, but if the correlations between parameters are small, then it does not.
In the former case, calling Minuit.hesse() after Minuit.migrad() does not do anything. In the latter case, calling Minuit.hesse() can turn a valid minimum into an invalid minimum, because the EDM value depends on the Hessian matrix, which is updated by Minuit.hesse(). This can turn a EDM value below threshold into a value above threshold and Minuit then reports that the fit has failed.
There are two options for what to do here:
The second option is the most convenient for the casual user, but may irritate users that expect Minuit do to exactly what it is told and not more (people who use Minuit as a backend).
The text was updated successfully, but these errors were encountered: