From d5aabbaa008a9d6bc84f3318018fb23cbeeecd03 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Sat, 23 Aug 2025 12:15:33 +0200 Subject: [PATCH 1/5] some modules are now lowercase --- src/easyscience/base_classes/based_base.py | 1 + src/easyscience/base_classes/obj_base.py | 2 +- src/easyscience/global_object/global_object.py | 2 +- src/easyscience/global_object/hugger/hugger.py | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/easyscience/base_classes/based_base.py b/src/easyscience/base_classes/based_base.py index 0af389e..7925306 100644 --- a/src/easyscience/base_classes/based_base.py +++ b/src/easyscience/base_classes/based_base.py @@ -158,6 +158,7 @@ def _get_linkable_attributes(self) -> List[DescriptorBase]: :return: List of `Descriptor`/`Parameter` objects. """ item_list = [] + from ..variable.descriptor_base import DescriptorBase for key, item in self._kwargs.items(): if hasattr(item, '_get_linkable_attributes'): item_list = [*item_list, *item._get_linkable_attributes()] diff --git a/src/easyscience/base_classes/obj_base.py b/src/easyscience/base_classes/obj_base.py index 3331625..611ddcb 100644 --- a/src/easyscience/base_classes/obj_base.py +++ b/src/easyscience/base_classes/obj_base.py @@ -7,7 +7,7 @@ from typing import Callable from typing import Optional -from ..utils.classTools import addLoggedProp +from ..Utils.classTools import addLoggedProp from ..variable.descriptor_base import DescriptorBase from .based_base import BasedBase diff --git a/src/easyscience/global_object/global_object.py b/src/easyscience/global_object/global_object.py index 63726d0..218f517 100644 --- a/src/easyscience/global_object/global_object.py +++ b/src/easyscience/global_object/global_object.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause # © 2021-2025 Contributors to the EasyScience project Date: Tue, 26 Aug 2025 10:36:11 +0200 Subject: [PATCH 2/5] more Utils->utils changes --- src/easyscience/base_classes/obj_base.py | 2 +- src/easyscience/global_object/global_object.py | 2 +- src/easyscience/global_object/hugger/hugger.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/easyscience/base_classes/obj_base.py b/src/easyscience/base_classes/obj_base.py index 611ddcb..3331625 100644 --- a/src/easyscience/base_classes/obj_base.py +++ b/src/easyscience/base_classes/obj_base.py @@ -7,7 +7,7 @@ from typing import Callable from typing import Optional -from ..Utils.classTools import addLoggedProp +from ..utils.classTools import addLoggedProp from ..variable.descriptor_base import DescriptorBase from .based_base import BasedBase diff --git a/src/easyscience/global_object/global_object.py b/src/easyscience/global_object/global_object.py index 218f517..63726d0 100644 --- a/src/easyscience/global_object/global_object.py +++ b/src/easyscience/global_object/global_object.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause # © 2021-2025 Contributors to the EasyScience project Date: Wed, 27 Aug 2025 10:22:48 +0200 Subject: [PATCH 3/5] fixes for bumps==1.0.2 (required by some dependencies) --- pyproject.toml | 2 +- src/easyscience/fitting/minimizers/minimizer_bumps.py | 6 +++--- .../unit_tests/fitting/minimizers/test_minimizer_bumps.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0f4ff28..6ce612e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ classifiers = [ requires-python = ">=3.11" dependencies = [ "asteval", - "bumps<1.0.0", + "bumps", "DFO-LS", "lmfit", "numpy", diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index ed2b140..23e21e9 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -63,7 +63,7 @@ def all_methods() -> List[str]: @staticmethod def supported_methods() -> List[str]: # only a small subset - methods = ['scipy.leastsq', 'amoeba', 'newton', 'lm'] + methods = ['amoeba', 'newton', 'lm'] return methods def fit( @@ -219,7 +219,7 @@ def _set_parameter_fit_result(self, fit_result, stack_status: bool): global_object.stack.enabled = True global_object.stack.beginMacro('Fitting routine') - for index, name in enumerate(self._cached_model._pnames): + for index, name in enumerate(self._cached_model.pars.keys()): dict_name = name[len(MINIMIZER_PARAMETER_PREFIX) :] pars[dict_name].value = fit_result.x[index] pars[dict_name].error = fit_result.dx[index] @@ -242,7 +242,7 @@ def _gen_fit_results(self, fit_results, **kwargs) -> FitResults: results.success = fit_results.success pars = self._cached_pars item = {} - for index, name in enumerate(self._cached_model._pnames): + for index, name in enumerate(self._cached_model.pars.keys()): dict_name = name[len(MINIMIZER_PARAMETER_PREFIX) :] item[name] = pars[dict_name].value diff --git a/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py b/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py index 7feaf8e..e1a5e37 100644 --- a/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py +++ b/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py @@ -33,11 +33,11 @@ def test_init_exception(self) -> None: def test_all_methods(self, minimizer: Bumps) -> None: # When Then Expect - assert minimizer.all_methods() == ['amoeba', 'de', 'dream', 'newton', 'scipy.leastsq', 'lm'] + assert minimizer.all_methods() == ['amoeba', 'de', 'dream', 'newton', 'lm'] def test_supported_methods(self, minimizer: Bumps) -> None: # When Then Expect - assert set(minimizer.supported_methods()) == set(['scipy.leastsq','newton', 'lm', 'amoeba']) + assert set(minimizer.supported_methods()) == set(['newton', 'lm', 'amoeba']) def test_fit(self, minimizer: Bumps, monkeypatch) -> None: # When @@ -107,7 +107,7 @@ def test_set_parameter_fit_result_no_stack_status(self, minimizer: Bumps): minimizer._cached_pars['b'].value = 'b' mock_cached_model = MagicMock() - mock_cached_model._pnames = ['pa', 'pb'] + mock_cached_model.pars = {'pa':0, 'pb': 0} minimizer._cached_model = mock_cached_model mock_fit_result = MagicMock() @@ -136,7 +136,7 @@ def test_gen_fit_results(self, minimizer: Bumps, monkeypatch): mock_cached_model.x = 'x' mock_cached_model.y = 'y' mock_cached_model.dy = 'dy' - mock_cached_model._pnames = ['ppar_1', 'ppar_2'] + mock_cached_model.pars = {'ppar_1': 0, 'ppar_2': 0} minimizer._cached_model = mock_cached_model mock_cached_par_1 = MagicMock() From 89695ef6976b3b01fb90dfad7b418342c801ca48 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 12 Sep 2025 15:02:23 +0200 Subject: [PATCH 4/5] fixed minor issue with bumps parameter order. Modified tests --- .../fitting/minimizers/minimizer_bumps.py | 6 ++-- .../minimizers/test_minimizer_bumps.py | 29 +++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index 23e21e9..195114a 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -133,7 +133,7 @@ def fit( try: model_results = bumps_fit(problem, **method_dict, **minimizer_kwargs, **kwargs) - self._set_parameter_fit_result(model_results, stack_status) + self._set_parameter_fit_result(model_results, stack_status, problem._parameters) results = self._gen_fit_results(model_results) except Exception as e: for key in self._cached_pars.keys(): @@ -200,7 +200,7 @@ def _make_func(x, y, weights): return _outer(self) - def _set_parameter_fit_result(self, fit_result, stack_status: bool): + def _set_parameter_fit_result(self, fit_result, stack_status: bool, par_list: List[BumpsParameter]): """ Update parameters to their final values and assign a std error to them. @@ -219,7 +219,7 @@ def _set_parameter_fit_result(self, fit_result, stack_status: bool): global_object.stack.enabled = True global_object.stack.beginMacro('Fitting routine') - for index, name in enumerate(self._cached_model.pars.keys()): + for index, name in enumerate([par.name for par in par_list]): dict_name = name[len(MINIMIZER_PARAMETER_PREFIX) :] pars[dict_name].value = fit_result.x[index] pars[dict_name].error = fit_result.dx[index] diff --git a/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py b/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py index e1a5e37..fc10017 100644 --- a/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py +++ b/tests/unit_tests/fitting/minimizers/test_minimizer_bumps.py @@ -47,13 +47,18 @@ def test_fit(self, minimizer: Bumps, monkeypatch) -> None: mock_bumps_fit = MagicMock(return_value='fit') monkeypatch.setattr(easyscience.fitting.minimizers.minimizer_bumps, "bumps_fit", mock_bumps_fit) - mock_FitProblem = MagicMock(return_value='fit_problem') + # Prepare a mock parameter with .name = 'pmock_parm_1' + mock_bumps_param = MagicMock() + mock_bumps_param.name = 'pmock_parm_1' + # Patch FitProblem to have _parameters attribute as expected + mock_FitProblem_instance = MagicMock() + mock_FitProblem_instance._parameters = [mock_bumps_param] + mock_FitProblem = MagicMock(return_value=mock_FitProblem_instance) monkeypatch.setattr(easyscience.fitting.minimizers.minimizer_bumps, "FitProblem", mock_FitProblem) mock_model = MagicMock() mock_model_function = MagicMock(return_value=mock_model) minimizer._make_model = MagicMock(return_value=mock_model_function) - minimizer._set_parameter_fit_result = MagicMock() minimizer._gen_fit_results = MagicMock(return_value='gen_fit_results') cached_par = MagicMock() @@ -61,14 +66,21 @@ def test_fit(self, minimizer: Bumps, monkeypatch) -> None: cached_pars = {'mock_parm_1': cached_par} minimizer._cached_pars = cached_pars + # Patch _set_parameter_fit_result to a real function that will not raise KeyError + def fake_set_parameter_fit_result(fit_result, stack_status, par_list): + # Simulate what the real function does: update _cached_pars + for index, name in enumerate([par.name for par in par_list]): + dict_name = name[len('p'):] # Remove prefix 'p' + minimizer._cached_pars[dict_name].value = 42 # Arbitrary value + minimizer._set_parameter_fit_result = fake_set_parameter_fit_result + # Then result = minimizer.fit(x=1.0, y=2.0) # Expect assert result == 'gen_fit_results' - mock_bumps_fit.assert_called_once_with('fit_problem', method='amoeba') + mock_bumps_fit.assert_called_once_with(mock_FitProblem_instance, method='amoeba') minimizer._make_model.assert_called_once_with(parameters=None) - minimizer._set_parameter_fit_result.assert_called_once_with('fit', False) minimizer._gen_fit_results.assert_called_once_with('fit') mock_model_function.assert_called_once_with(1.0, 2.0, 1.4142135623730951) mock_FitProblem.assert_called_once_with(mock_model) @@ -114,8 +126,15 @@ def test_set_parameter_fit_result_no_stack_status(self, minimizer: Bumps): mock_fit_result.x = [1.0, 2.0] mock_fit_result.dx = [0.1, 0.2] + # The new argument: par_list (list of mock parameters) + mock_par_a = MagicMock() + mock_par_a.name = 'pa' + mock_par_b = MagicMock() + mock_par_b.name = 'pb' + par_list = [mock_par_a, mock_par_b] + # Then - minimizer._set_parameter_fit_result(mock_fit_result, False) + minimizer._set_parameter_fit_result(mock_fit_result, False, par_list) # Expect assert minimizer._cached_pars['a'].value == 1.0 From 8bcb21ce19e667ad4a1525d8f4556f0c1b12a63c Mon Sep 17 00:00:00 2001 From: rozyczko Date: Wed, 17 Sep 2025 15:08:17 +0200 Subject: [PATCH 5/5] move import to top. CR comment --- src/easyscience/base_classes/based_base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/easyscience/base_classes/based_base.py b/src/easyscience/base_classes/based_base.py index 7925306..b401d04 100644 --- a/src/easyscience/base_classes/based_base.py +++ b/src/easyscience/base_classes/based_base.py @@ -14,10 +14,10 @@ from ..io import SerializerComponent from ..variable import Parameter +from ..variable.descriptor_base import DescriptorBase if TYPE_CHECKING: from ..fitting.calculators import InterfaceFactoryTemplate - from ..variable.descriptor_base import DescriptorBase class BasedBase(SerializerComponent): @@ -158,7 +158,6 @@ def _get_linkable_attributes(self) -> List[DescriptorBase]: :return: List of `Descriptor`/`Parameter` objects. """ item_list = [] - from ..variable.descriptor_base import DescriptorBase for key, item in self._kwargs.items(): if hasattr(item, '_get_linkable_attributes'): item_list = [*item_list, *item._get_linkable_attributes()]