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

Wrong MinimizerResult for brute with workers != 1 #578

Closed
hombit opened this issue Aug 14, 2019 · 5 comments
Closed

Wrong MinimizerResult for brute with workers != 1 #578

hombit opened this issue Aug 14, 2019 · 5 comments

Comments

@hombit
Copy link

hombit commented Aug 14, 2019

Description

Current master version returns incorrect result report for method='brute', workers!=1, for example nfev always equals zero in this case.

A Minimal, Complete, and Verifiable example
from lmfit import Parameters, minimize, report_fit


def f(params):
    return (params['a'].value - 0.25)**2 + (params['b'].value - 0.75)**2


params = Parameters()
params.add(name='a', min=0, max=1)
params.add(name='b', min=0, max=1)
r = minimize(f, params, method='brute', Ns=10, workers=1)
report_fit(r)
r = minimize(f, params, method='brute', Ns=10, workers=-1)
report_fit(r)
r = minimize(f, params, method='brute', Ns=10, workers=2)
report_fit(r)
[[Fit Statistics]]
    # fitting method   = brute
    # function evals   = 100
    # data points      = 1
    # variables        = 2
    chi-square         = 2.3815e-06
    reduced chi-square = 2.3815e-06
    Akaike info crit   = -8.94778139
    Bayesian info crit = -12.9477814
##  Warning: uncertainties could not be estimated:
    this fitting method does not natively calculate uncertainties
    and numdifftools is not installed for lmfit to do this. Use
    `pip install numdifftools` for lmfit to estimate uncertainties
    with this fitting method.
[[Variables]]
    a:  0.22222222 (init = ?)
    b:  0.77777778 (init = ?)
[[Fit Statistics]]
    # fitting method   = brute
    # function evals   = 0
    # data points      = 1
    # variables        = 2
    chi-square         = 2.3815e-06
    reduced chi-square = 2.3815e-06
    Akaike info crit   = -8.94778139
    Bayesian info crit = -12.9477814
##  Warning: uncertainties could not be estimated:
    this fitting method does not natively calculate uncertainties
    and numdifftools is not installed for lmfit to do this. Use
    `pip install numdifftools` for lmfit to estimate uncertainties
    with this fitting method.
[[Variables]]
    a:  0.22222222 (init = ?)
    b:  0.77777778 (init = ?)
[[Fit Statistics]]
    # fitting method   = brute
    # function evals   = 0
    # data points      = 1
    # variables        = 2
    chi-square         = 2.3815e-06
    reduced chi-square = 2.3815e-06
    Akaike info crit   = -8.94778139
    Bayesian info crit = -12.9477814
##  Warning: uncertainties could not be estimated:
    this fitting method does not natively calculate uncertainties
    and numdifftools is not installed for lmfit to do this. Use
    `pip install numdifftools` for lmfit to estimate uncertainties
    with this fitting method.
[[Variables]]
    a:  0.22222222 (init = ?)
    b:  0.77777778 (init = ?)
Version information
import sys, lmfit, numpy, scipy, asteval, uncertainties, six
print('Python: {}\n\nlmfit: {}, scipy: {}, numpy: {}, asteval: {}, uncertainties: {}, six: {}'\
      .format(sys.version, lmfit.__version__, scipy.__version__, numpy.__version__, \
      asteval.__version__, uncertainties.__version__, six.__version__))
Python: 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0]

lmfit: 0.9.13+86.g41667df, scipy: 1.3.1, numpy: 1.17.0, asteval: 0.9.14, uncertainties: 3.1.2, six: 1.12.0
@newville
Copy link
Member

@hombit Do you have any evidence that any attribute other than nfev is incorrect? I don't see anything else that is obviously wrong.

Nfev is incremented internally in lmfit code - scipy.optimize.brute does not return this value or even an OptimizerResult. I can totally believe that nfev will not be meaningful when using multiprocessing -- the function evaluations will be done (and nfev counted) in other processes. Since scipy.optimize.brute does not return much other than the best solution, that information will be lost. The main process probably really did do 0 (or maybe 1) call of the objective function.

@hombit
Copy link
Author

hombit commented Aug 14, 2019

No, I've checked other attributes of MinimizerResult and haven't found other issues. But I believe that nfev can be specified correctly because lmfit knows number of grid knots.

@reneeotten
Copy link
Contributor

reneeotten commented Aug 14, 2019

@hombit @newville indeed the the brute method does not return this information, but also we do know how many evaluations there are since we know the size of the evaluation grid.

In fact, in the tests I do make sure that result.nfev == len(result.brute_Jout.ravel()); I just didn't add test for the workers options since I assumed that this was all done internally in scipy. The best thing to do is probably just set result.nfev equal to the length of the evaluation grid, then it will always be correct irrespective of the number of workers used. If everyone agrees, I will make that change and update/add the tests soon-ish.

Of note, the example doesn't run for me with the following traceback:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/renee/Library/Python/3.7/lib/python/site-packages/lmfit-0.9.13+86.g41667df.dirty-py3.7.egg/lmfit/printfuncs.py", line 326, in report_fit
  File "/Users/renee/Library/Python/3.7/lib/python/site-packages/lmfit-0.9.13+86.g41667df.dirty-py3.7.egg/lmfit/printfuncs.py", line 151, in fit_report
  File "<__array_function__ internals>", line 6, in allclose
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numpy/core/numeric.py", line 2171, in allclose
    res = all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan))
  File "<__array_function__ internals>", line 6, in isclose
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numpy/core/numeric.py", line 2270, in isclose
    yfin = isfinite(y)
TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

this likely because no value was set in the example. @hombit you don't get that message? Anyway, we can correct this situation in printfuncs.py and do there if par.init_value and np.allclose(par.value, par.init_value):. On the other hand, most likely a user will actually specify a value and, therefore, this error is not too likely to happen.

Any Thoughts?

@hombit
Copy link
Author

hombit commented Aug 14, 2019

@reneeotten I've got this error too, but only with numdifftools installed

@reneeotten
Copy link
Contributor

Yeah, okay I see... I've update my comment above already. It's because you didn't specify a value in the example. And the code that triggers this error is within and if-block that only gets executed when you have numdifftools installed but no errrobars are determined. I think we should fix that at the same time as I suggested above.

reneeotten added a commit to reneeotten/lmfit-py that referenced this issue Aug 15, 2019
…value is present

Probably this is a rare occurence as most users will actually set an
initial value when initializing the parameters. However, for algorithms
like "brute" that is actually not required and this might happen.

Added a regression test and updated the code to include the check
whether an init_value is present.

See: lmfit#578
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants