Note that there are some explanatory texts on larger screens.

plurals
  1. POPython parameter transformation according to MINUIT
    primarykey
    data
    text
    <p>I am writing an automated curve fitting routine for 2D data based on scipy's optimize.leastsq, and it works. However when adding many curves with starting values slighly off I get non-physical results (negative amplitude, for example).</p> <p>I found this post <a href="https://stackoverflow.com/questions/7409694/scipy-bounds-for-fitting-parameters-when-using-optimize-leastsq">Scipy: bounds for fitting parameter(s) when using optimize.leastsq</a> and was trying to use the parameter transformation according to Minuit from Cern. In the above mentioned question somebody provided a link to some python code.</p> <pre><code>code.google.com/p/nmrglue/source/browse/trunk/nmrglue/analysis/leastsqbound.py </code></pre> <p>I wrote this minimal working example (extending the code)</p> <pre><code>""" http://code.google.com/p/nmrglue/source/browse/trunk/nmrglue/analysis/leastsqbound.py Constrained multivariate Levenberg-Marquardt optimization """ from scipy.optimize import leastsq import numpy as np import matplotlib.pyplot as plt #new def internal2external_grad(xi, bounds): """ Calculate the internal to external gradiant Calculates the partial of external over internal """ ge = np.empty_like(xi) for i, (v, bound) in enumerate(zip(xi, bounds)): a = bound[0] # minimum b = bound[1] # maximum if a == None and b == None: # No constraints ge[i] = 1.0 elif b == None: # only min ge[i] = v / np.sqrt(v ** 2 + 1) elif a == None: # only max ge[i] = -v / np.sqrt(v ** 2 + 1) else: # both min and max ge[i] = (b - a) * np.cos(v) / 2. return ge def i2e_cov_x(xi, bounds, cov_x): grad = internal2external_grad(xi, bounds) grad = grad = np.atleast_2d(grad) return np.dot(grad.T, grad) * cov_x def internal2external(xi, bounds): """ Convert a series of internal variables to external variables""" xe = np.empty_like(xi) for i, (v, bound) in enumerate(zip(xi, bounds)): a = bound[0] # minimum b = bound[1] # maximum if a == None and b == None: # No constraints xe[i] = v elif b == None: # only min xe[i] = a - 1. + np.sqrt(v ** 2. + 1.) elif a == None: # only max xe[i] = b + 1. - np.sqrt(v ** 2. + 1.) else: # both min and max xe[i] = a + ((b - a) / 2.) * (np.sin(v) + 1.) return xe def external2internal(xe, bounds): """ Convert a series of external variables to internal variables""" xi = np.empty_like(xe) for i, (v, bound) in enumerate(zip(xe, bounds)): a = bound[0] # minimum b = bound[1] # maximum if a == None and b == None: # No constraints xi[i] = v elif b == None: # only min xi[i] = np.sqrt((v - a + 1.) ** 2. - 1) elif a == None: # only max xi[i] = np.sqrt((b - v + 1.) ** 2. - 1) else: # both min and max xi[i] = np.arcsin((2.*(v - a) / (b - a)) - 1.) return xi def err(p, bounds, efunc, args): pe = internal2external(p, bounds) # convert to external variables return efunc(pe, *args) def calc_cov_x(infodic, p): """ Calculate cov_x from fjac, ipvt and p as is done in leastsq """ fjac = infodic['fjac'] ipvt = infodic['ipvt'] n = len(p) # adapted from leastsq function in scipy/optimize/minpack.py perm = np.take(np.eye(n), ipvt - 1, 0) r = np.triu(np.transpose(fjac)[:n, :]) R = np.dot(r, perm) try: cov_x = np.linalg.inv(np.dot(np.transpose(R), R)) except LinAlgError: cov_x = None return cov_x def leastsqbound(func, x0, bounds, args = (), **kw): """ Constrained multivariant Levenberg-Marquard optimization Minimize the sum of squares of a given function using the Levenberg-Marquard algorithm. Contraints on parameters are inforced using variable transformations as described in the MINUIT User's Guide by Fred James and Matthias Winkler. Parameters: * func functions to call for optimization. * x0 Starting estimate for the minimization. * bounds (min,max) pair for each element of x, defining the bounds on that parameter. Use None for one of min or max when there is no bound in that direction. * args Any extra arguments to func are places in this tuple. Returns: (x,{cov_x,infodict,mesg},ier) Return is described in the scipy.optimize.leastsq function. x and con_v are corrected to take into account the parameter transformation, infodic is not corrected. Additional keyword arguments are passed directly to the scipy.optimize.leastsq algorithm. """ # check for full output if "full_output" in kw and kw["full_output"]: full = True else: full = False # convert x0 to internal variables i0 = external2internal(x0, bounds) # perfrom unconstrained optimization using internal variables r = leastsq(err, i0, args = (bounds, func, args), **kw) # unpack return convert to external variables and return if full: xi, cov_xi, infodic, mesg, ier = r xe = internal2external(xi, bounds) cov_xe = i2e_cov_x(xi, bounds, cov_xi) # XXX correct infodic 'fjac','ipvt', and 'qtf' return xe, cov_xe, infodic, mesg, ier else: xi, ier = r xe = internal2external(xi, bounds) return xe, ier # new below def _evaluate(x, p): ''' Linear plus Lorentzian curve p = list with three parameters ([a, b, I, Pos, FWHM]) ''' return p[0] + p[1] * x + p[2] / (1 + np.power((x - p[3]) / (p[4] / 2), 2)) def residuals(p, y, x): err = _evaluate(x, p) - y return err if __name__ == '__main__': data = np.loadtxt('constraint.dat') # read data p0 = [5000., 0., 500., 2450., 3] #Start values for a, b, I, Pos, FWHM constraints = [(4000., None), (-50., 20.), (0., 2000.), (2400., 2451.), (None, None)] p, res = leastsqbound(residuals, p0, constraints, args = (data[:, 1], data[:, 0]), maxfev = 20000) print p, res plt.plot(data[:, 0], data[:, 1]) # plot data plt.plot(data[:, 0], _evaluate(data[:, 0], p0)) # plot start values plt.plot(data[:, 0], _evaluate(data[:, 0], p)) # plot fit values plt.show() </code></pre> <p>Thats the plot output, where green is the starting conditions and red the fit result: <img src="https://i.stack.imgur.com/VisvI.png" alt="plot results"></p> <p>Is this the correct usage? External2internal conversion just throws a nan if outside the bounds. leastsq seems to be able to handle this?</p> <p>I uploaded the fitting data <a href="http://pastebin.com/cn6kR6Lf" rel="nofollow noreferrer">here</a>. Just paste into a text file named constraint.dat.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload