Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/easyscience/fitting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from .fitter import Fitter # noqa: F401, E402
from .minimizers.minimizer_base import FitResults # noqa: F401, E402
from .fitter import Fitter
from .minimizers.utils import FitResults

# Causes circular import
# from .multi_fitter import MultiFitter # noqa: F401, E402

all = [Fitter, FitResults]
8 changes: 5 additions & 3 deletions src/easyscience/fitting/minimizers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# © 2021-2023 Contributors to the EasyScience project <https://github.com/easyScience/easyscience

from .minimizer_base import MinimizerBase # noqa: F401, E402
from .utils import FitError # noqa: F401, E402
from .utils import FitResults # noqa: F401, E402
from .minimizer_base import MinimizerBase
from .utils import FitError
from .utils import FitResults

__all__ = [MinimizerBase, FitError, FitResults]
85 changes: 26 additions & 59 deletions src/easyscience/fitting/minimizers/minimizer_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

import numpy as np

#causes circular import when Parameter is imported
#from easyscience.Objects.ObjectClasses import BaseObj
# causes circular import when Parameter is imported
# from easyscience.Objects.ObjectClasses import BaseObj
from easyscience.Objects.Variable import Parameter

from ..Constraints import ObjConstraint
Expand All @@ -31,7 +31,9 @@ class MinimizerBase(metaclass=ABCMeta):

wrapping: str = None

def __init__(self, obj, fit_function: Callable, method: Optional[str] = None): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501
def __init__(
self, obj, fit_function: Callable, method: Optional[str] = None
): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501
if method not in self.available_methods():
raise FitError(f'Method {method} not available in {self.__class__}')
self._object = obj
Expand Down Expand Up @@ -59,32 +61,17 @@ def add_fit_constraint(self, constraint: ObjConstraint):
def remove_fit_constraint(self, index: int) -> None:
del self._constraints[index]

@abstractmethod
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

def make_model(self, pars: List[Parameter] = None):
"""
Generate an engine model from the supplied `fit_function` and parameters in the base object.

:return: Callable model
"""

@abstractmethod
def _generate_fit_function(self) -> Callable:
"""
Using the user supplied `fit_function`, wrap it in such a way we can update `Parameter` on
iterations.
"""

@abstractmethod
def fit(
self,
x: np.ndarray,
y: np.ndarray,
weights: Optional[np.ndarray] = None,
model=None,
parameters=None,
method=None,
model: Optional[Callable] = None,
parameters: Optional[Parameter] = None,
method: Optional[str] = None,
**kwargs,
):
) -> FitResults:
"""
Perform a fit using the engine.

Expand Down Expand Up @@ -119,7 +106,7 @@ def evaluate(self, x: np.ndarray, minimizer_parameters: dict[str, float] = None,
if minimizer_parameters is None:
minimizer_parameters = {}
if not isinstance(minimizer_parameters, dict):
raise TypeError("minimizer_parameters must be a dictionary")
raise TypeError('minimizer_parameters must be a dictionary')

if self._fit_function is None:
# This will also generate self._cached_pars
Expand All @@ -129,26 +116,6 @@ def evaluate(self, x: np.ndarray, minimizer_parameters: dict[str, float] = None,

return self._fit_function(x, **minimizer_parameters, **kwargs)

def _prepare_parameters(self, parameters: dict[str, float]) -> dict[str, float]:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved below

"""
Prepare the parameters for the minimizer.

:param parameters: Dict of parameters for the minimizer with names as keys.
"""
pars = self._cached_pars

for name, item in pars.items():
parameter_name = MINIMIZER_PARAMETER_PREFIX + str(name)
if parameter_name not in parameters.keys():
## TODO clean when full move to new_variable
from easyscience.Objects.new_variable import Parameter as NewParameter

if isinstance(item, NewParameter):
parameters[parameter_name] = item.value
else:
parameters[parameter_name] = item.raw_value
return parameters

@abstractmethod
def convert_to_pars_obj(self, par_list: Optional[Union[list]] = None):
"""
Expand All @@ -170,30 +137,31 @@ def available_methods(self) -> List[str]:

@staticmethod
@abstractmethod
def convert_to_par_object(obj): # todo after constraint changes, add type hint: obj: BaseObj
def convert_to_par_object(obj): # todo after constraint changes, add type hint: obj: BaseObj
"""
Convert an `EasyScience.Objects.Base.Parameter` object to an engine Parameter object.
"""

@abstractmethod
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

def _set_parameter_fit_result(self, fit_result):
def _prepare_parameters(self, parameters: dict[str, float]) -> dict[str, float]:
"""
Update parameters to their final values and assign a std error to them.
Prepare the parameters for the minimizer.

:param fit_result: Fit object which contains info on the fit
:return: None
:rtype: noneType
:param parameters: Dict of parameters for the minimizer with names as keys.
"""
pars = self._cached_pars

@abstractmethod
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

def _gen_fit_results(self, fit_results, **kwargs) -> FitResults:
"""
Convert fit results into the unified `FitResults` format.
# TODO clean when full move to new_variable
from easyscience.Objects.new_variable import Parameter as NewParameter

:param fit_result: Fit object which contains info on the fit
:return: fit results container
:rtype: FitResults
"""
for name, item in pars.items():
parameter_name = MINIMIZER_PARAMETER_PREFIX + str(name)
if parameter_name not in parameters.keys():
# TODO clean when full move to new_variable
if isinstance(item, NewParameter):
parameters[parameter_name] = item.value
else:
parameters[parameter_name] = item.raw_value
return parameters

@staticmethod
def _error_from_jacobian(jacobian: np.ndarray, residuals: np.ndarray, confidence: float = 0.95) -> np.ndarray:
Expand All @@ -210,4 +178,3 @@ def _error_from_jacobian(jacobian: np.ndarray, residuals: np.ndarray, confidence
z = stats.norm.pdf(z)
error_matrix = z * np.sqrt(error_matrix)
return error_matrix

14 changes: 8 additions & 6 deletions src/easyscience/fitting/minimizers/minimizer_bumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from bumps.names import FitProblem
from bumps.parameter import Parameter as BumpsParameter

from easyscience.Objects.Variable import Parameter

from .minimizer_base import MINIMIZER_PARAMETER_PREFIX
from .minimizer_base import MinimizerBase
from .utils import FitError
Expand Down Expand Up @@ -43,7 +45,7 @@ def __init__(self, obj, fit_function: Callable, method: Optional[str] = None):
self._cached_pars_order = ()
self._p_0 = {}

def make_model(self, pars: Optional[List[BumpsParameter]] = None) -> Callable:
def _make_model(self, pars: Optional[List[BumpsParameter]] = None) -> Callable:
"""
Generate a bumps model from the supplied `fit_function` and parameters in the base object.
Note that this makes a callable as it needs to be initialized with *x*, *y*, *weights*
Expand Down Expand Up @@ -156,8 +158,8 @@ def fit(
x: np.ndarray,
y: np.ndarray,
weights: Optional[np.ndarray] = None,
model=None,
parameters=None,
model: Optional[Callable] = None,
parameters: Optional[Parameter] = None,
method: Optional[str] = None,
minimizer_kwargs: Optional[dict] = None,
engine_kwargs: Optional[dict] = None,
Expand Down Expand Up @@ -201,7 +203,7 @@ def fit(
minimizer_kwargs.update(engine_kwargs)

if model is None:
model = self.make_model(pars=parameters)
model = self._make_model(pars=parameters)
model = model(x, y, weights)
self._cached_model = model

Expand Down Expand Up @@ -314,8 +316,8 @@ def _gen_fit_results(self, fit_results, **kwargs) -> FitResults:
item = {}
for index, name in enumerate(self._cached_model._pnames):
dict_name = name[1:]
## TODO clean when full move to new_variable

## TODO clean when full move to new_variable
from easyscience.Objects.new_variable import Parameter

if isinstance(pars[dict_name], Parameter):
Expand Down
Loading