Issue
A residue function (res
) does the sum of y values for y>thr (threshold). It returns the residue between this sum and the target.
In this example, a target
is calculated for y>70 and I would like to find, after minimization, y=70 starting from y=60.
import numpy as np
import lmfit
x=np.linspace(0,10,51)
y=(x+4)*(x-14)*-1
def res(params,y,target):
parvals = params.valuesdict()
sum=y[y>parvals['thr']].sum()
return [(target-sum)**2]
target=y[y>70].sum()
pars=lmfit.Parameters()
pars.add('thr', value=60, min=y.min(), max=y.max())
minner = lmfit.Minimizer(res, pars, fcn_args=(y, target))
result = minner.minimize()
Why does not the fit work : 70 is not returned but 60 (the initial value) is returned ?
Thanks for answer.
Solution
The fit does not work because you are using a continuous variable (pars['thr']
) as a discrete value [y>parvals['thr']]
. When the fit is running, it will try to calculate the change in the result by making small changes in the variable values.
If you add a print()
to your function:
def res(params,y,target):
parvals = params.valuesdict()
print(parvals['thr'])
sum=y[y>parvals['thr']].sum()
return [(target-sum)**2]
you will get
60.0
60.0
60.0
60.00000089406967
60.0
As the fit tries to find which way and by how much thr
should change. And it will see that changing thr
has no impact on the result.
Basically, you need to change your use of thr
from being a discrete variable into being a continuous variable. One easy way to do this is with the erf
function (or another sigmoidal function), scaling to go from ~0 to ~1 over some small but not infinitesimal interval, for example
from scipy.special import erf
def res(params,y,target):
parvals = params.valuesdict()
step = (1 + erf(y-parvals['thr']))/2.0
sum = (y*step).sum()
return target-sum # was [(target-sum)**2]
That step
array will have non-zero/non-one values near the threshold giving just enough signal for the fit to decide where to move.
Also, note that returning [(target-sum)**2]
will work, but that just returning the residual target-sum
will allow the fit to see not only the magnitude but also the sign of the misfit, and allow the fit to converge faster.
With those changes, you should get the correct value for thr
of 70 with about 11 function evaluations.
Answered By - M Newville
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.