Closed PhML closed 10 years ago
Hi,
There is a little bit of a confusing concept for whether the params object passed to leastsq() should be mutable, and whether results.params should be identical or a copy. Right now it is (or supposed to be) that the params passed in to leastsq() are changed to reflect the best values and that results.params is a copy made at output time.
That means for "fit, change param attribute, re-fit", it should be preferable to update the original params between the two runs of minimize. That way the second results "results2.params" will be different from the results of the first fit, which will be preserved, say "results1.params", for comparison of the two fits.
Probably, the doc should be clearer about this. It would also be reasonable to change to behavior to make a copy of params on input, so that the original values were not changed at all during the fit, and only results.params were changed. Opinions?
On Tue, Apr 1, 2014 at 4:51 AM, PhML notifications@github.com wrote:
Let's use the documentation examplehttp://cars9.uchicago.edu/software/python/lmfit/parameters.html#simple-example .
After running the minimize function, params seems to be updated since we use report_fit(params) that gives the same output than report_fit(result.params). So, if I change, let's say params['amp'].value, I expect this to change result.params['amp'].value also. But it does not.
In fact my use case is to use a parameter with vary set to False, run minimize, then change vary to True and run again the fit with initial values set by previous run using result.leastsq(). In order to do what I want, either I have to call again minimise, but I create a new result object, either I have to change result.params instead of params but it means between two runs, I do not look to the result at the same place.
Could you explain me what happens that I don't understand, please ?
Reply to this email directly or view it on GitHubhttps://github.com/lmfit/lmfit-py/issues/89 .
--Matt Newville
The way I see things is that we should always update original parameters or never. I mean we could have an option when we instantiate Parameters
, like copy_parameters
:
False
, we do result.params.update(params)
at output time, so any change in either params
or result.params
is reflected in the other and call to leastsq
will update original params
.True
, we do a copy and original params
is never changed. What do you think about this option?
We do always update the original parameters.
Is your proposal to add a copy_parameters
option to Parameters() that changes minimize() and Minimize.leastsq()? I'd rather add a keyword to minimize() and Minimize() for that -- otherwise there's "action at a distance". More importantly, I don't see when we ever want results.params and params to be identical and yet in two places.
I'm probably biased, but the current behavior seems to make sense to me. With a typical params = Parameters(....) result = minimize(objfunction, params, ...)
the parameters held in params
will be changed by the fit -- this is worth reconsidering, but I'm not persuaded to change this. (it's unusual for function arguments to be mutable in python, but not so unusual in numeric work, and it does follow the behavior for fitting functions in scipy.optimize and other languages, so seems natural here). I also think it's not what your suggesting, but perhaps I'm not understanding.
OK, params
is changed on output. In addition, result.params
holds a copy of the parameters, preserving the results of that fit. So (and intended to exactly meet your use case!) you can change params
and do another fit:
params['amplitude'].vary = True result2 = minimize(objfunction, params, ...)
params
will hold the latest values, and result2.params
will hold the parameters from the second fit, while result.params
will still hold the parameters from the first fit, unaltered.
Comparing two similar fits seems like an important need, and I'm reluctant to change this -- I could be persuaded, but am not yet. Without this, there's really no point in having a results.params
, and the user would have to keep making copies of results
in order to be able to compare two fits.
Perhaps I'm not understanding, or perhaps this just needs to be documented better?
Sorry if I was not clear, my only concern is that changing params
is not taken into account when running result.leastsq()
and that result.leastsq()
doesn’t update params
.
When I called result.leastsq()
I thought it would had the same effects than calling minimize
. Because (in my mind) calling result.leastsq()
it’s when you don't want to keep result of the first run. But maybe I was wrong about that?
I agree, if I do want to compare two fits, I create two results
by calling minimize
twice and store results in two different variables.
Perhaps I just misunderstood the purpose of result.leastsq
and tried to use it the wrong way?
It's always harder to understand a question without an actual working example to show the issue. Like, I somehow didn't pick up on the fact that you were running results.leastsq(), but that raises other questions, and it seems like we're not reading each others minds very well.
Can you send a short example and explain what you see and what you expect?
Thanks, --Matt
On Tue, Apr 1, 2014 at 4:26 PM, PhML notifications@github.com wrote:
Sorry if I was not clear, my only concern is that changing params is not taken into account when running result.leastsq() and that result.leastsq()doesn't update params. When I called result.leastsq() I thought it would had the same effects than calling minimize. Because (in my mind) calling result.leastsq() it's when you don't want to keep result of the first run. But maybe I was wrong about that? I agree, if I do want to compare two fits, I create two results by calling minimize twice and store results in two different variables. Perhaps I just misunderstood the purpose of result.leastsq and tried to use it the wrong way?
Reply to this email directly or view it on GitHubhttps://github.com/lmfit/lmfit-py/issues/89#issuecomment-39261237 .
--Matt Newville
just to be clear, this
params = Parameters()
params.add('x', value=10, min=0, vary=False)
fitter = Minimizer(objfun, params)
fitter.leastsq()
fitter.params['x'].vary = True
fitter.leastsq()
will change the fit parameter from fixed to varied for the second fit. Changing params['x'] will not (the fitter object uses its params attributes). Is that what you're trying to do?
Still from the example:
params = Parameters()
params.add('amp', value= 10, min=0, vary=False)
params.add('decay', value= 0.1)
params.add('shift', value= 0.0, min=-np.pi/2., max=np.pi/2)
params.add('omega', value= 3.0)
# do fit, here with leastsq model
result = minimize(fcn2min, params, args=(x, data))
# calculate final result
final = data + result.residual
# write error report
print("——————————————————")
print("report_fit(params)")
print("——————————————————")
report_fit(params, show_correl=False)
print("—————————————————————————")
print("report_fit(result.params)")
print("—————————————————————————")
report_fit(result.params, show_correl=False)
print("#####################################################################")
print("Change params['amp'].vary from False to True and run result.leastsq()")
print("#####################################################################")
params['amp'].vary = True
result.leastsq()
print("——————————————————")
print("report_fit(params)")
print("——————————————————")
report_fit(params, show_correl=False)
print("—————————————————————————")
print("report_fit(result.params)")
print("—————————————————————————")
report_fit(result.params, show_correl=False)
print("############################################################################")
print("Change result.params['amp'].vary from False to True and run result.leastsq()")
print("############################################################################")
result.params['amp'].vary = True
result.leastsq()
print("——————————————————")
print("report_fit(params)")
print("——————————————————")
report_fit(params, show_correl=False)
print("—————————————————————————")
print("report_fit(result.params)")
print("—————————————————————————")
report_fit(result.params, show_correl=False)
gives:
—————————————————— report_fit(params) —————————————————— [[Variables]] amp: 10 (fixed) decay: 0.1029322 +/- 0.006524682 (6.34%) initial = 0.1 omega: 2.069038 +/- 0.02659877 (1.29%) initial = 3 shift: -0.2771565 +/- 0.03962326 (14.30%) initial = 0 ————————————————————————— report_fit(result.params) ————————————————————————— [[Variables]] amp: 10 (fixed) decay: 0.1029322 +/- 0.006524682 (6.34%) initial = 0.1 omega: 2.069038 +/- 0.02659877 (1.29%) initial = 3 shift: -0.2771565 +/- 0.03962326 (14.30%) initial = 0 ##################################################################### Change params['amp'].vary from False to True and run result.leastsq() ##################################################################### —————————————————— report_fit(params) —————————————————— [[Variables]] amp: 10 +/- 0 (0.00%) initial = 10 decay: 0.1029322 +/- 0.006524682 (6.34%) initial = 0.1 omega: 2.069038 +/- 0.02659877 (1.29%) initial = 3 shift: -0.2771565 +/- 0.03962326 (14.30%) initial = 0 ————————————————————————— report_fit(result.params) ————————————————————————— [[Variables]] amp: 10 (fixed) decay: 0.1029217 +/- 0.006523303 (6.34%) initial = 0.1029322 omega: 2.069033 +/- 0.02659563 (1.29%) initial = 2.069038 shift: -0.2771503 +/- 0.03962212 (14.30%) initial = -0.2771565 ############################################################################ Change result.params['amp'].vary from False to True and run result.leastsq() ############################################################################ —————————————————— report_fit(params) —————————————————— [[Variables]] amp: 10 +/- 0 (0.00%) initial = 10 decay: 0.1029322 +/- 0.006524682 (6.34%) initial = 0.1 omega: 2.069038 +/- 0.02659877 (1.29%) initial = 3 shift: -0.2771565 +/- 0.03962326 (14.30%) initial = 0 ————————————————————————— report_fit(result.params) ————————————————————————— [[Variables]] amp: 4.95749 +/- 0.03897985 (0.79%) initial = 10 decay: 0.02511303 +/- 0.0004511665 (1.80%) initial = 0.1029217 omega: 2.000501 +/- 0.003232409 (0.16%) initial = 2.069033 shift: -0.09966166 +/- 0.0101004 (10.13%) initial = -0.2771503
You can see that when you call result.leastsq()
there is no more interaction with params
. I don’t know if result.leastsq()
should read params
or result.params
, but shouldn’t it update params
at least?
Sorry, I should have provided this example in my first comment.
Yes, as I said above, doing
result = minimize(fcn2min, params, args=(x, data))
params['amp'].vary = True
result.leastsq()
won't do what you want. Then again, how could it -- you changed a variable but didn't tell the result object of the change.
The minimizer() returns a Minimizer instance, which uses its own .params member in the fit. result.params is copied when the fit is done. All parameters in params and result.params are equal but not identical. If you do
result = minimize(fcn2min, params, args=(x, data))
result.params['amp'].vary = True
result.leastsq()
you will get a different fit.
I think this is a non-issue. I'm not even convinced any of the docs are wrong on this....
Let's use the documentation example.
After running the
minimize
function,params
seems to be updated since we usereport_fit(params)
that gives the same output thanreport_fit(result.params)
. So, if I change, let’s sayparams['amp'].value
, I expect this to changeresult.params['amp'].value
also. But it does not.In fact my use case is to use a parameter with
vary
set toFalse
, runminimize
, then changevary
toTrue
and run again the fit with initial values set by previous run usingresult.leastsq()
. In order to do what I want, either I have to call againminimise
, but I create a new result object, either I have to changeresult.params
instead ofparams
but it means between two runs, I do not look to the result at the same place.Could you explain me what happens that I don't understand, please ?