lmfit Python Library

repository·master·Indexed 22 days ago

https://github.com/lmfit/lmfit-py

A Python library for non-linear least-squares minimization and curve fitting. It features a flexible system for managing optimization variables as named parameters with bounds, constraints, and fixed/varying status. The library includes a comprehensive `models` module with built-in functional forms (e.g., Gaussian, Lorentzian, SplineModel), support for composite models via arithmetic operators, and tools for automated parameter guessing and confidence interval calculation.

Tokens
35.6K
Snippets
50
Records
223
Agent score
77%

What's inside lmfit

  1. Overview of lmfit for non-linear optimization and curve-fitting

    master

    lmfit is a high-level Python interface for non-linear optimization and curve-fitting. It extends scipy.optimize by providing several key enhancements:

    • Parameter Objects: Instead of using plain floats, lmfit uses lmfit.parameter.Parameter objects. These allow you to:
      • Vary values during a fit or keep them fixed.
      • Set upper and/or lower bounds.
      • Constrain a parameter using algebraic expressions of other parameters.
      • Access attributes like standard error after a fit to estimate uncertainties.
    • Flexible Algorithms: You can switch fitting algorithms without modifying your objective function once a model is set up.
    • Advanced Uncertainty Estimation: lmfit provides tools to explicitly explore parameter space for confidence intervals and can use the numdifftools package (if installed) to estimate uncertainties for algorithms that don't natively support it in SciPy.
    • Model-Based Curve Fitting: The lmfit.model.Model class turns modeling functions into Python classes, making it easier to parametrize and fit data compared to scipy.optimize.curve_fit.
    • Built-in Models: Includes many pre-defined models for common lineshapes.
  2. Use built-in models from the `models` module

    master

    The lmfit.models module provides a variety of pre-defined, well-known functional forms (e.g., Gaussian, Lorentzian, Exponential) that subclass lmfit.model.Model. These models wrap simple Python functions from lmfit.lineshapes.

    A key feature of these models is the .guess() method, which can be used to provide reasonable starting values for parameters based on an input data array.

  3. Explore parameter space with Minimizer.emcee

    master

    The emcee method is used to obtain the posterior probability distribution of parameters given experimental data.

    Important: emcee is not a fitting method. It does not iteratively refine a solution to a minimization problem. Instead, it should be used after a successful fit has been performed to thoroughly explore the parameter space around the best-fit values and gain a better understanding of the parameter probability distributions.

    To use it effectively:

    1. Perform an initial fit using a standard minimization method (e.g., nelder or lm).
    2. Use the resulting parameters as the starting point for emcee.

    If your objective function returns an array of unweighted residuals (i.e., data - model), use the is_weighted=False argument. In this mode, emcee will automatically use/add a __lnsigma parameter to estimate the true uncertainty in the data.

    import lmfit
    import numpy as np
    
    # Assuming 'residual' is your objective function and 'mi.params' are results from a previous fit
    res = lmfit.minimize(residual, method='emcee', nan_policy='omit', burn=300, steps=1000, thin=20, 
                         params=mi.params, is_weighted=False, progress=False)
  4. How Parameter and Parameters work together

    master

    In lmfit, optimization is driven by Parameter objects rather than plain floating-point numbers.

    • A Parameter represents a single quantity to be optimized. It tracks a value, whether it is fixed or varied, and can have lower (min) and/or upper (max) bounds. It can also be constrained by an algebraic expression (expr) of other parameters.
    • A Parameters object is an ordered collection (acting like an ordered dictionary) of Parameter objects. This object is what is passed to optimization routines. It manages the group of variables that the underlying optimizer (like scipy.optimize) requires.

    By using these abstractions, your objective function does not need to be modified every time you change which parameters are being varied or add bounds; the Parameter objects handle that state externally.

    def fcn2min(params, x, data):
        """Model a decaying sine wave and subtract data."""
        v = params.valuesdict()
    
        model = v['amp'] * np.sin(x*v['omega'] + v['shift']) * np.exp(-x*x*v['decay'])
        return model - data
  5. Curve fitting with the Model class

    master

    The lmfit.model.Model class provides a flexible, class-based approach to curve-fitting. Unlike scipy.optimize.curve_fit, which is functional, Model wraps a model function and automatically handles residual function generation and parameter name extraction from the function's signature.

    Key advantages include:

    • Automatic Parameter Discovery: It uses the function signature to determine parameter names and independent variables.
    • Parameter Management: It integrates with lmfit.parameter.Parameters for advanced control (bounds, constraints, etc.).
    • Composability: Models can be easily combined into composite models.
    • Rich Results: The .fit() method returns a ModelResult object containing detailed fit statistics and best-fit data.
    from lmfit import Model
    
    def gaussian(x, amp, cen, wid):
        return amp * exp(-(x-cen)**2 / wid)
    
    gmodel = Model(gaussian)
  6. Assess emcee sampling success

    master

    The success of an emcee run can be assessed by checking the acceptance fraction of the walkers.

    A good rule of thumb is that the mean acceptance fraction should be between 0.2 and 0.5. You can access these values via the acceptance_fraction attribute of the MinimizerResult object.

    import matplotlib.pyplot as plt
    
    # res is the MinimizerResult from lmfit.minimize(..., method='emcee', ...)
    plt.plot(res.acceptance_fraction, 'o')
    plt.xlabel('walker')
    plt.ylabel('acceptance fraction')
    plt.show()
  7. Create composite models using algebraic operations

    master

    You can combine multiple Model objects into a single CompositeModel using standard Python algebraic operators: addition (+), subtraction (-), multiplication (*), and division (/). The resulting composite model contains all parameters from its component models, allowing them to collectively influence the fit. This is useful for building complex models (e.g., a peak with a background) from simpler, pre-defined sub-models.

    If component models have overlapping parameter names, you should use the prefix argument when initializing the Model to ensure each parameter is uniquely identified within the composite model.

  8. The Model class overview

    master
    The Model class is a general-purpose wrapper for pre-defined Python functions, turning them into fitting models. It handles the distinction between Parameters (values to be optimized) and independent variables (values required for evaluation, like x in $y(x)$).
  9. Accessing fit results with MinimizerResult

    master

    When you run an optimization using minimize or Minimizer.minimize, it returns a MinimizerResult object. This object acts as a container for all the results of the minimization.

    Important: The original Parameters object passed into the minimization is not modified. To access the best-fit values, uncertainties, and other updated attributes, you must use the result.params attribute of the returned MinimizerResult object.

    You can use the pretty_print() method on the params attribute to display the fitted values, bounds, and other attributes in a formatted table.

  10. Propagate uncertainties using ModelResult.uvars

    master

    To perform post-fit calculations that account for parameter uncertainties and correlations, use the ModelResult.uvars attribute.

    uvars is a dictionary where keys are variable parameter names and values are uvalues from the uncertainties package. When used with standard Python operators or numpy functions, these values automatically propagate uncertainties while accounting for the full covariance matrix (correlations between parameters).

    Tip: You can automate these calculations by overriding the Model.post_fit method. This method is called automatically after a fit completes but before Model.fit returns, allowing you to add 'derived parameters' to the ModelResult.

  11. Access fit results using MinimizerResult

    master

    Starting from version 0.9.0, minimize and Minimizer.minimize return a MinimizerResult object instead of modifying the input Parameters object in place.

    To access the optimized parameter values after a fit, you must use the .params attribute of the returned result object. The original Parameters object passed into the function remains unchanged and will still hold the initial starting values.

  12. Specify maximum function evaluations with max_nfev

    master

    Starting in version 1.0.1, a new argument max_nfev was introduced to uniformly specify the maximum number of function evaluations across different solvers.

    Important: When using max_nfev, all other solver-specific arguments (such as maxfev or maxiter) will no longer be passed to the underlying SciPy solver, and a warning will be emitted. You should transition to using max_nfev for consistent behavior.