Source code for utilipy.data_utils.fitting.lmfit_decorator

# -*- coding: utf-8 -*-

# ----------------------------------------------------------------------------
#
# TITLE   : lmfitsss
# PROJECT : utilipy
#
# ----------------------------------------------------------------------------

"""Decorators for converting scipy residual functions to lmfit functions."""

__author__ = "Nathaniel Starkman"


##############################################################################
# IMPORTS

# GENERAL

from typing import Any, Callable, Optional
from wrapt import ObjectProxy

try:
    from lmfit import Parameters
except ImportError:
    Parameters = Any


##############################################################################
# CODE


[docs]class scipy_residual_to_lmfit(ObjectProxy): """decorator to make scipy residual functions compatible with lmfit. (see https://lmfit.github.io/lmfit-py/fitting.html) Parameters ---------- var_order : list of strs the variable order used by lmfit the strings are the names of the lmfit parameters must be in the same order as the scipy residual function Returns ------- scipy_residual_to_lmfit : class internally constructed class Notes ----- the function can be called as normal add a .lmfit function for use in lmfit minimizations see https://lmfit.github.io/lmfit-py/fitting.html >>> @scipy_residual_to_lmfit(var_order=['amp', 'phase', 'freq', 'decay']) ... def residual(vars, x, data, eps_data): ... amp, phase, freq, decay = vars ... # calculate residual here ... return res TODO ---- since using ObjectProxy, make it compatible with bound functions see https://wrapt.readthedocs.io/en/latest/wrappers.html#function-wrappers """ def __new__( cls, func: Callable = None, var_order: Optional[list] = None ) -> object: """Create Proxy.""" if var_order is None: raise ValueError("var_order cannot be None") self = super().__new__(cls) # inherit class information # assigning documentation as function documentation self.__doc__ = func.__doc__ # allowing scipy_residual_to_lmfit to act as a decorator if func is None: return self.decorator(var_order) return self # /def
[docs] @classmethod def decorator(cls, var_order: list) -> Callable: """Decorator.""" # @functools.wraps(cls) # not needed when using ObjectProxy def wrapper(func: Callable): """scipy_residual_to_lmfit wrapper.""" return cls(func, var_order=var_order) # /def return wrapper
# /def def __init__(self, func: Callable, var_order: list) -> None: """Initialize Proxy.""" super().__init__(func) # inializing function into wrapt.ObjectProxy self.var_order = var_order return # /def
[docs] def lmfit(self, params: Parameters, *args: Any, **kwargs: Any) -> Any: """`lmfit` version of function.""" vars = [params[n].value for n in self.var_order] return self.__wrapped__(vars, *args, **kwargs)
# /def # /class ############################################################################## # END