Custom Function Class
Custom Function Class
This guide explains how to create a custom function class for use with the ffit
package.
To contribute a new class, create a new file in the ffit/funcs
directory. Name it custom_func.py
and include two classes: CustomFuncParam
and CustomFunc
.
Create a Param Class
Start by defining the function parameters in a dataclass that inherits from ParamDataclass
.
The std
attribute should have the type Optional[CustomFuncParam]
and default to None
.
from dataclasses import dataclass
from ffit.utils import ParamDataclass
@dataclass(frozen=True)
class CustomFuncParam(ParamDataclass):
"""CustomFunc function parameters.
Attributes
----------
- attr1: float
First attribute of the function.
- attr2: float
Second attribute of the function.
Additional attributes
----------------------
- param1: float
The param1 of the function.
Methods
-------
- meth1: float
The meth1 of the function.
"""
attr1: float
attr2: float
std: "Optional[FuncParam]" = None
@property
def param1(self):
return self.attr1 ** 2
def meth1(self):
return self.attr1 * self.attr2
Define the Function
Define the function to be used for fitting.
The first argument is the x data (type NDARRAY
), followed by the parameters in the same order as defined in CustomFuncParam
. The return type should be NDARRAY
.
from ffit.utils import _NDARRAY
def custom_func(x: _NDARRAY, attr1: float, attr2: float) -> _NDARRAY:
return attr1 * attr2
Define the Guess Function
The guess function helps determine initial parameters.
The first two arguments are the x and y data (type NDARRAY
), followed by **kwargs
. The return type should be NDARRAY
.
You can pass kwargs
to the fit
methods to improve guesses. For example, you might specify whether an exponential function is increasing or decreasing.
from ffit.utils import _NDARRAY
import numpy as np
def custom_func_guess(x: _NDARRAY, y: _NDARRAY, **kwargs) -> _NDARRAY:
sign = kwargs.get("func_sign", np.sign(np.mean(y)))
return np.array([...])
Normalize the Result
Normalize results to avoid stochastic behavior in the fit function.
For example, ensure phases are \(2\pi\) periodic or set one parameter to always be positive.
from typing import Sequence
import numpy as np
def normalize_res_list(x: Sequence[float]) -> _NDARRAY:
return np.array([
abs(x[0]),
np.sign(x[0]) * x[1],
x[2] % (2 * np.pi),
x[3]
])
Define the Class
The main class should inherit from FitLogic[CustomFuncParam]
. Set CustomFuncParam
correctly to provide users with accurate typing.
Include a minimalistic docstring with LaTeX and Python representations of the function, as well as a reference to the final parameters class.
from ffit.fit_logic import FitLogic
import typing as _t
class CustomFunc(FitLogic[CustomFuncParam]): # type: ignore
r"""CustomFunc function.
---
$$
f(x)^2 = \cos(\omega)
$$
f(x) = sqrt(cos(2 * pi * frequency))
Final Parameters
-----------------
The final parameters are given by [`CustomFuncParam`](../custom_func_param/) dataclass.
"""
param: _t.Type[CustomFuncParam] = CustomFuncParam
func = staticmethod(custom_func)
normalize_res = staticmethod(normalize_res_list)
_guess = staticmethod(custom_func_guess)
Additional Information
Example Parameters
Provide example parameters for documentation plots:
_example_param = (1, 1, 1.0, 1.0)
# Additionally you can setup the x axis:
_example_x_min: float
_example_x_max: float
_example_x_points: int
_example_std: float
Mask Signature
Add a mask method signature for better autocompletion.
Ensure the attributes match those in CustomFuncParam
.
@overload
@classmethod
def mask( # type: ignore # pylint: disable=W0221
cls,
*,
attr1: float = None, # type: ignore
attr2: float = None, # type: ignore
) -> "CustomFunc": ...
@classmethod
def mask(cls, **kwargs) -> "CustomFunc":
return super().mask(**kwargs)
Testing Range
Specify attribute testing ranges to ensure proper function behavior.
If not set, the default range is (-100, 100).
_test_range = {
"attr1": (0, 1),
"attr2": None,
}