From 557afc44ada0ed9764da53c8d6eb08a16e5c5316 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 10 Sep 2024 11:01:07 +0200 Subject: [PATCH 01/26] moving towards a full enum for minimizer --- .../fitting/available_minimizers.py | 35 +++++++++++-------- src/easyscience/fitting/minimizers/factory.py | 30 ++++------------ .../fitting/minimizers/minimizer_base.py | 10 +++--- .../fitting/minimizers/minimizer_bumps.py | 5 +-- .../fitting/minimizers/minimizer_dfo.py | 5 +-- .../fitting/minimizers/minimizer_lmfit.py | 5 +-- .../Fitting/minimizers/test_minimizer_base.py | 6 ++-- .../minimizers/test_minimizer_bumps.py | 7 ++-- .../Fitting/minimizers/test_minimizer_dfo.py | 4 +-- .../minimizers/test_minimizer_lmfit.py | 4 +-- 10 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/easyscience/fitting/available_minimizers.py b/src/easyscience/fitting/available_minimizers.py index 13216415..f39fc4bb 100644 --- a/src/easyscience/fitting/available_minimizers.py +++ b/src/easyscience/fitting/available_minimizers.py @@ -1,6 +1,6 @@ import warnings +from dataclasses import dataclass from enum import Enum -from enum import auto import pkg_resources @@ -32,24 +32,31 @@ warnings.warn('DFO minimization is not available. Probably dfols has not been installed.', ImportWarning, stacklevel=2) -class AvailableMinimizers(Enum): +@dataclass +class AvailableMinimizer: + package: str + method: str + enmu_id: int + + +class AvailableMinimizers(AvailableMinimizer, Enum): if lmfit_engine_available: - LMFit = auto() - LMFit_leastsq = auto() - LMFit_powell = auto() - LMFit_cobyla = auto() - LMFit_differential_evolution = auto() - LMFit_scipy_least_squares = auto() + LMFit = 'lm', 'leastsq', 11 + LMFit_leastsq = 'lm', 'leastsq', 12 + LMFit_powell = 'lm', 'powell', 13 + LMFit_cobyla = 'lm', 'cobyla', 14 + LMFit_differential_evolution = 'lm', 'differential_evolution', 15 + LMFit_scipy_least_squares = 'lm', 'least_squares', 16 if bumps_engine_available: - Bumps = auto() - Bumps_simplex = auto() - Bumps_newton = auto() - Bumps_lm = auto() + Bumps = 'bumps', 'amoeba', 21 + Bumps_simplex = 'bumps', 'amoeba', 22 + Bumps_newton = 'bumps', 'newton', 23 + Bumps_lm = 'bumps', 'lm', 24 if dfo_engine_available: - DFO = auto() - DFO_leastsq = auto() + DFO = 'dfo', 'leastsq', 31 + DFO_leastsq = 'dfo', 'leastsq', 32 # Temporary solution to convert string to enum diff --git a/src/easyscience/fitting/minimizers/factory.py b/src/easyscience/fitting/minimizers/factory.py index 5c141edf..2797f61d 100644 --- a/src/easyscience/fitting/minimizers/factory.py +++ b/src/easyscience/fitting/minimizers/factory.py @@ -13,31 +13,13 @@ def factory(minimizer_enum: AvailableMinimizers, fit_object, fit_function: Callable) -> MinimizerBase: - if minimizer_enum == AvailableMinimizers.LMFit: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='leastsq') - elif minimizer_enum == AvailableMinimizers.LMFit_leastsq: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='leastsq') - elif minimizer_enum == AvailableMinimizers.LMFit_powell: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='powell') - elif minimizer_enum == AvailableMinimizers.LMFit_cobyla: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='cobyla') - elif minimizer_enum == AvailableMinimizers.LMFit_differential_evolution: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='differential_evolution') - elif minimizer_enum == AvailableMinimizers.LMFit_scipy_least_squares: - minimizer = LMFit(obj=fit_object, fit_function=fit_function, method='least_squares') + if minimizer_enum.package == 'lm': + minimizer = LMFit(obj=fit_object, fit_function=fit_function, minimizer_enum=minimizer_enum) - elif minimizer_enum == AvailableMinimizers.Bumps: - minimizer = Bumps(obj=fit_object, fit_function=fit_function, method='amoeba') - elif minimizer_enum == AvailableMinimizers.Bumps_simplex: - minimizer = Bumps(obj=fit_object, fit_function=fit_function, method='amoeba') - elif minimizer_enum == AvailableMinimizers.Bumps_newton: - minimizer = Bumps(obj=fit_object, fit_function=fit_function, method='newton') - elif minimizer_enum == AvailableMinimizers.Bumps_lm: - minimizer = Bumps(obj=fit_object, fit_function=fit_function, method='lm') + elif minimizer_enum.package == 'bumps': + minimizer = Bumps(obj=fit_object, fit_function=fit_function, minimizer_enum=minimizer_enum) - elif minimizer_enum == AvailableMinimizers.DFO: - minimizer = DFO(obj=fit_object, fit_function=fit_function, method='leastsq') - elif minimizer_enum == AvailableMinimizers.DFO_leastsq: - minimizer = DFO(obj=fit_object, fit_function=fit_function, method='leastsq') + elif minimizer_enum.package == 'dfo': + minimizer = DFO(obj=fit_object, fit_function=fit_function, minimizer_enum=minimizer_enum) return minimizer diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index 124c377b..4e30b6dc 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -20,6 +20,7 @@ # from easyscience.Objects.ObjectClasses import BaseObj from easyscience.Objects.Variable import Parameter +from ..available_minimizers import AvailableMinimizers from ..Constraints import ObjConstraint from .utils import FitError from .utils import FitResults @@ -38,13 +39,14 @@ def __init__( self, obj, #: BaseObj, fit_function: Callable, - method: Optional[str] = None, + minimizer_enum: Optional[AvailableMinimizers] = None, ): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501 - if method not in self.supported_methods(): - raise FitError(f'Method {method} not available in {self.__class__}') + if minimizer_enum.method not in self.supported_methods(): + raise FitError(f'Method {minimizer_enum.method} not available in {self.__class__}') self._object = obj self._original_fit_function = fit_function - self._method = method + self._minimizer_enum = minimizer_enum + self._method = minimizer_enum.method self._cached_pars: Dict[str, Parameter] = {} self._cached_pars_vals: Dict[str, Tuple[float]] = {} self._cached_model = None diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index f571d717..0acf3cc8 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -18,6 +18,7 @@ # from easyscience.Objects.ObjectClasses import BaseObj from easyscience.Objects.Variable import Parameter +from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX from .minimizer_base import MinimizerBase from .utils import FitError @@ -40,7 +41,7 @@ def __init__( self, obj, #: BaseObj, fit_function: Callable, - method: Optional[str] = None, + minimizer_enum: Optional[AvailableMinimizers] = None, ): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501 """ Initialize the fitting engine with a `BaseObj` and an arbitrary fitting function. @@ -52,7 +53,7 @@ def __init__( keyword/value pairs :type fit_function: Callable """ - super().__init__(obj=obj, fit_function=fit_function, method=method) + super().__init__(obj=obj, fit_function=fit_function, minimizer_enum=minimizer_enum) self._p_0 = {} @staticmethod diff --git a/src/easyscience/fitting/minimizers/minimizer_dfo.py b/src/easyscience/fitting/minimizers/minimizer_dfo.py index 042674ff..c26419bd 100644 --- a/src/easyscience/fitting/minimizers/minimizer_dfo.py +++ b/src/easyscience/fitting/minimizers/minimizer_dfo.py @@ -14,6 +14,7 @@ # from easyscience.Objects.ObjectClasses import BaseObj from easyscience.Objects.Variable import Parameter +from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX from .minimizer_base import MinimizerBase from .utils import FitError @@ -31,7 +32,7 @@ def __init__( self, obj, #: BaseObj, fit_function: Callable, - method: Optional[str] = None, + minimizer_enum: Optional[AvailableMinimizers] = None, ): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501 """ Initialize the fitting engine with a `BaseObj` and an arbitrary fitting function. @@ -43,7 +44,7 @@ def __init__( keyword/value pairs :type fit_function: Callable """ - super().__init__(obj=obj, fit_function=fit_function, method=method) + super().__init__(obj=obj, fit_function=fit_function, minimizer_enum=minimizer_enum) self._p_0 = {} @staticmethod diff --git a/src/easyscience/fitting/minimizers/minimizer_lmfit.py b/src/easyscience/fitting/minimizers/minimizer_lmfit.py index 514a9c23..fb68d1e7 100644 --- a/src/easyscience/fitting/minimizers/minimizer_lmfit.py +++ b/src/easyscience/fitting/minimizers/minimizer_lmfit.py @@ -17,6 +17,7 @@ # from easyscience.Objects.ObjectClasses import BaseObj from easyscience.Objects.Variable import Parameter +from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX from .minimizer_base import MinimizerBase from .utils import FitError @@ -35,7 +36,7 @@ def __init__( self, obj, #: BaseObj, fit_function: Callable, - method: Optional[str] = None, + minimizer_enum: Optional[AvailableMinimizers] = None, ): # todo after constraint changes, add type hint: obj: BaseObj # noqa: E501 """ Initialize the minimizer with the `BaseObj` and the `fit_function` to be used. @@ -47,7 +48,7 @@ def __init__( :param method: Method to be used by the minimizer :type method: str """ - super().__init__(obj=obj, fit_function=fit_function, method=method) + super().__init__(obj=obj, fit_function=fit_function, minimizer_enum=minimizer_enum) @staticmethod def all_methods() -> List[str]: diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py index 3618489f..7109468b 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py @@ -19,8 +19,8 @@ def minimizer(self): minimizer = MinimizerBase( obj='obj', - fit_function='fit_function', - method='method' + fit_function='fit_function', + minimizer_enum=MagicMock(package='package', method='method') ) return minimizer @@ -34,7 +34,7 @@ def test_init_exception(self): MinimizerBase( obj='obj', fit_function='fit_function', - method='not_a_method' + minimizer_enum=MagicMock(package='package', method='not_a_method') ) def test_init(self, minimizer: MinimizerBase): diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py index bed4f6bb..791ffff6 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py @@ -4,7 +4,6 @@ import numpy as np import easyscience.fitting.minimizers.minimizer_bumps -from easyscience.Objects.new_variable import Parameter from easyscience.fitting.minimizers.minimizer_bumps import Bumps from easyscience.fitting.minimizers.utils import FitError @@ -16,7 +15,7 @@ def minimizer(self) -> Bumps: minimizer = Bumps( obj='obj', fit_function='fit_function', - method='scipy.leastsq' + minimizer_enum=MagicMock(package='bumps', method='amoeba') ) return minimizer @@ -29,7 +28,7 @@ def test_init_exception(self) -> None: Bumps( obj='obj', fit_function='fit_function', - method='not_leastsq' + minimizer_enum=MagicMock(package='bumps', method='not_amoeba') ) def test_all_methods(self, minimizer: Bumps) -> None: @@ -67,7 +66,7 @@ def test_fit(self, minimizer: Bumps, monkeypatch) -> None: # Expect assert result == 'gen_fit_results' - mock_bumps_fit.assert_called_once_with('fit_problem', method='scipy.leastsq') + mock_bumps_fit.assert_called_once_with('fit_problem', 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') diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py index 1be679c5..ba34a058 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py @@ -16,7 +16,7 @@ def minimizer(self) -> DFO: minimizer = DFO( obj='obj', fit_function='fit_function', - method='leastsq' + minimizer_enum=MagicMock(package='dfo', method='leastsq') ) return minimizer @@ -29,7 +29,7 @@ def test_init_exception(self) -> None: DFO( obj='obj', fit_function='fit_function', - method='not_leastsq' + minimizer_enum=MagicMock(package='dfo', method='not_leastsq') ) def test_supported_methods(self, minimizer: DFO) -> None: diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py index 9c055aa4..0ae7d4cd 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py @@ -15,7 +15,7 @@ def minimizer(self) -> LMFit: minimizer = LMFit( obj='obj', fit_function='fit_function', - method='leastsq' + minimizer_enum=MagicMock(package='lm', method='leastsq') ) return minimizer @@ -27,7 +27,7 @@ def test_init_exception(self) -> None: LMFit( obj='obj', fit_function='fit_function', - method='method' + minimizer_enum=MagicMock(package='dfo', method='not_leastsq') ) def test_make_model(self, minimizer: LMFit, monkeypatch) -> None: From 10060d0c700035e77324ec2eaa75b4e72590027e Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 10 Sep 2024 11:15:51 +0200 Subject: [PATCH 02/26] name property --- src/easyscience/fitting/minimizers/minimizer_base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index 4e30b6dc..ab8e5abb 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -57,6 +57,10 @@ def __init__( def all_constraints(self) -> List[ObjConstraint]: return [*self._constraints, *self._object._constraints] + @property + def name(self) -> str: + return self._minimizer_enum.name + def fit_constraints(self) -> List[ObjConstraint]: return self._constraints From e65bc46a37d0a96248a43903ad6a2f5f80a2d2d6 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 10 Sep 2024 12:04:46 +0200 Subject: [PATCH 03/26] from wrapping to package --- src/easyscience/fitting/minimizers/minimizer_base.py | 2 +- src/easyscience/fitting/minimizers/minimizer_bumps.py | 2 +- src/easyscience/fitting/minimizers/minimizer_dfo.py | 2 +- src/easyscience/fitting/minimizers/minimizer_lmfit.py | 2 +- tests/integration_tests/Fitting/test_fitter.py | 2 +- .../Fitting/test_fitter_legacy_parameter.py | 2 +- tests/unit_tests/Fitting/minimizers/test_factory.py | 6 +++--- tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py | 2 +- tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py | 2 +- tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index ab8e5abb..8ecf7199 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -33,7 +33,7 @@ class MinimizerBase(metaclass=ABCMeta): This template class is the basis for all minimizer engines in `EasyScience`. """ - wrapping: str = None + package: str = None def __init__( self, diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index 0acf3cc8..c6a7aab9 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -35,7 +35,7 @@ class Bumps(MinimizerBase): It allows for the Bumps fitting engine to use parameters declared in an `EasyScience.Objects.Base.BaseObj`. """ - wrapping = 'bumps' + package = 'bumps' def __init__( self, diff --git a/src/easyscience/fitting/minimizers/minimizer_dfo.py b/src/easyscience/fitting/minimizers/minimizer_dfo.py index c26419bd..c5edd915 100644 --- a/src/easyscience/fitting/minimizers/minimizer_dfo.py +++ b/src/easyscience/fitting/minimizers/minimizer_dfo.py @@ -26,7 +26,7 @@ class DFO(MinimizerBase): This is a wrapper to Derivative Free Optimisation for Least Square: https://numericalalgorithmsgroup.github.io/dfols/ """ - wrapping = 'dfo' + package = 'dfo' def __init__( self, diff --git a/src/easyscience/fitting/minimizers/minimizer_lmfit.py b/src/easyscience/fitting/minimizers/minimizer_lmfit.py index fb68d1e7..9a8cfadb 100644 --- a/src/easyscience/fitting/minimizers/minimizer_lmfit.py +++ b/src/easyscience/fitting/minimizers/minimizer_lmfit.py @@ -30,7 +30,7 @@ class LMFit(MinimizerBase): # noqa: S101 It allows for the lmfit fitting engine to use parameters declared in an `EasyScience.Objects.Base.BaseObj`. """ - wrapping = 'lmfit' + package = 'lmfit' def __init__( self, diff --git a/tests/integration_tests/Fitting/test_fitter.py b/tests/integration_tests/Fitting/test_fitter.py index 757c3424..c1e1b253 100644 --- a/tests/integration_tests/Fitting/test_fitter.py +++ b/tests/integration_tests/Fitting/test_fitter.py @@ -103,7 +103,7 @@ def test_basic_fit(fit_engine: AvailableMinimizers, with_errors): result = f.fit(*args, **kwargs) if fit_engine is not None: - assert result.minimizer_engine.wrapping == fit_engine.name.lower() # Special case where minimizer matches wrapping + assert result.minimizer_engine.package == fit_engine.name.lower() # Special case where minimizer matches package assert sp_sin.phase.value == pytest.approx(ref_sin.phase.value, rel=1e-3) assert sp_sin.offset.value == pytest.approx(ref_sin.offset.value, rel=1e-3) diff --git a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py b/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py index 1f43d530..2055e45e 100644 --- a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py +++ b/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py @@ -103,7 +103,7 @@ def test_basic_fit(fit_engine, with_errors): result = f.fit(*args, **kwargs) if fit_engine is not None: - assert result.minimizer_engine.wrapping == fit_engine.name.lower() + assert result.minimizer_engine.package == fit_engine.name.lower() assert sp_sin.phase.raw_value == pytest.approx(ref_sin.phase.raw_value, rel=1e-3) assert sp_sin.offset.raw_value == pytest.approx(ref_sin.offset.raw_value, rel=1e-3) diff --git a/tests/unit_tests/Fitting/minimizers/test_factory.py b/tests/unit_tests/Fitting/minimizers/test_factory.py index db749c4e..9a860955 100644 --- a/tests/unit_tests/Fitting/minimizers/test_factory.py +++ b/tests/unit_tests/Fitting/minimizers/test_factory.py @@ -16,19 +16,19 @@ def pull_minminizer(self, minimizer: AvailableMinimizers) -> MinimizerBase: def test_factory_lm_fit(self, minimizer_method, minimizer_enum): minimizer = self.pull_minminizer(minimizer_enum) assert minimizer._method == minimizer_method - assert minimizer.wrapping == 'lmfit' + assert minimizer.package == 'lmfit' @pytest.mark.parametrize('minimizer_method,minimizer_enum', [('amoeba', AvailableMinimizers.Bumps), ('amoeba', AvailableMinimizers.Bumps_simplex), ('newton', AvailableMinimizers.Bumps_newton), ('lm', AvailableMinimizers.Bumps_lm)]) def test_factory_bumps_fit(self, minimizer_method, minimizer_enum): minimizer = self.pull_minminizer(minimizer_enum) assert minimizer._method == minimizer_method - assert minimizer.wrapping == 'bumps' + assert minimizer.package == 'bumps' @pytest.mark.parametrize('minimizer_method,minimizer_enum', [('leastsq', AvailableMinimizers.DFO), ('leastsq', AvailableMinimizers.DFO_leastsq)]) def test_factory_dfo_fit(self, minimizer_method, minimizer_enum): minimizer = self.pull_minminizer(minimizer_enum) assert minimizer._method == minimizer_method - assert minimizer.wrapping == 'dfo' + assert minimizer.package == 'dfo' @pytest.mark.parametrize('minimizer_name,expected', [('LMFit', AvailableMinimizers.LMFit), ('LMFit_leastsq', AvailableMinimizers.LMFit_leastsq), ('LMFit_powell', AvailableMinimizers.LMFit_powell), ('LMFit_cobyla', AvailableMinimizers.LMFit_cobyla), ('LMFit_differential_evolution', AvailableMinimizers.LMFit_differential_evolution), ('LMFit_scipy_least_squares', AvailableMinimizers.LMFit_scipy_least_squares) ]) diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py index 791ffff6..42050a86 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py @@ -21,7 +21,7 @@ def minimizer(self) -> Bumps: def test_init(self, minimizer: Bumps) -> None: assert minimizer._p_0 == {} - assert minimizer.wrapping == 'bumps' + assert minimizer.package == 'bumps' def test_init_exception(self) -> None: with pytest.raises(FitError): diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py index ba34a058..ae620f11 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py @@ -22,7 +22,7 @@ def minimizer(self) -> DFO: def test_init(self, minimizer: DFO) -> None: assert minimizer._p_0 == {} - assert minimizer.wrapping == 'dfo' + assert minimizer.package == 'dfo' def test_init_exception(self) -> None: with pytest.raises(FitError): diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py index 0ae7d4cd..b2f7b08e 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py @@ -20,7 +20,7 @@ def minimizer(self) -> LMFit: return minimizer def test_init(self, minimizer: LMFit) -> None: - assert minimizer.wrapping == 'lmfit' + assert minimizer.package == 'lmfit' def test_init_exception(self) -> None: with pytest.raises(FitError): From 7e79f11630db85e8c60b521e2ab55b4568594592 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Wed, 11 Sep 2024 11:14:58 +0200 Subject: [PATCH 04/26] To new Descriptor and Paramter, from @id to unique_name --- src/easyscience/Objects/Groups.py | 10 +- .../Objects/new_variable/descriptor_number.py | 133 +++++++++--------- src/easyscience/Utils/io/template.py | 10 +- tests/unit_tests/Objects/test_Groups.py | 42 +++--- tests/unit_tests/utils/io_tests/test_core.py | 32 ++--- tests/unit_tests/utils/io_tests/test_dict.py | 60 ++++---- tests/unit_tests/utils/io_tests/test_json.py | 22 +-- tests/unit_tests/utils/io_tests/test_xml.py | 12 +- 8 files changed, 151 insertions(+), 170 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index eaec36b5..f011dc1a 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -18,6 +18,7 @@ from typing import Union from easyscience.global_object.undo_redo import NotarizedDict +from easyscience.Objects.new_variable.descriptor_base import DescriptorBase from easyscience.Objects.ObjectClasses import BasedBase from easyscience.Objects.ObjectClasses import Descriptor @@ -40,6 +41,7 @@ def __init__( name: str, *args: Union[B, V], interface: Optional[iF] = None, + unique_name: Optional[str] = None, **kwargs, ): """ @@ -51,7 +53,7 @@ def __init__( :param _kwargs: Fields which this class should contain :type _kwargs: dict """ - BasedBase.__init__(self, name) + BasedBase.__init__(self, name, unique_name=unique_name) kwargs = {key: kwargs[key] for key in kwargs.keys() if kwargs[key] is not None} _args = [] for item in args: @@ -67,7 +69,7 @@ def __init__( _kwargs[key] = item kwargs = _kwargs for item in list(kwargs.values()) + _args: - if not issubclass(type(item), (Descriptor, BasedBase)): + if not issubclass(type(item), (Descriptor, DescriptorBase, BasedBase)): raise AttributeError('A collection can only be formed from easyscience objects.') args = _args _kwargs = {} @@ -104,7 +106,7 @@ def insert(self, index: int, value: Union[V, B]) -> None: :rtype: None """ t_ = type(value) - if issubclass(t_, (BasedBase, Descriptor)): + if issubclass(t_, (BasedBase, Descriptor, DescriptorBase)): update_key = list(self._kwargs.keys()) values = list(self._kwargs.values()) # Update the internal dict @@ -165,7 +167,7 @@ def __setitem__(self, key: int, value: Union[B, V]) -> None: if isinstance(value, Number): # noqa: S3827 item = self.__getitem__(key) item.value = value - elif issubclass(type(value), BasedBase) or issubclass(type(value), Descriptor): + elif issubclass(type(value), (BasedBase, Descriptor, DescriptorBase)): update_key = list(self._kwargs.keys()) values = list(self._kwargs.values()) old_item = values[key] diff --git a/src/easyscience/Objects/new_variable/descriptor_number.py b/src/easyscience/Objects/new_variable/descriptor_number.py index 95c6fc5e..44c5cb13 100644 --- a/src/easyscience/Objects/new_variable/descriptor_number.py +++ b/src/easyscience/Objects/new_variable/descriptor_number.py @@ -3,6 +3,7 @@ import numbers from typing import Any from typing import Dict +from typing import List from typing import Optional from typing import Union @@ -89,7 +90,6 @@ def from_scipp(cls, name: str, full_value: Variable, **kwargs) -> DescriptorNumb raise TypeError(f'{full_value=} must be a scipp scalar') return cls(name=name, value=full_value.value, unit=full_value.unit, variance=full_value.variance, **kwargs) - @property def full_value(self) -> Variable: """ @@ -101,8 +101,10 @@ def full_value(self) -> Variable: @full_value.setter def full_value(self, full_value: Variable) -> None: - raise AttributeError(f'Full_value is read-only. Change the value and variance seperately. Or create a new {self.__class__.__name__}.') # noqa: E501 - + raise AttributeError( + f'Full_value is read-only. Change the value and variance seperately. Or create a new {self.__class__.__name__}.' + ) # noqa: E501 + @property def value(self) -> numbers.Number: """ @@ -135,7 +137,9 @@ def unit(self) -> str: @unit.setter def unit(self, unit_str: str) -> None: - raise AttributeError(f'Unit is read-only. Use convert_unit to change the unit between allowed types or create a new {self.__class__.__name__} with the desired unit.') # noqa: E501 + raise AttributeError( + f'Unit is read-only. Use convert_unit to change the unit between allowed types or create a new {self.__class__.__name__} with the desired unit.' + ) # noqa: E501 @property def variance(self) -> float: @@ -162,7 +166,6 @@ def variance(self, variance_float: float) -> None: variance_float = float(variance_float) self._scalar.variance = variance_float - @property def error(self) -> float: """ @@ -205,94 +208,92 @@ def convert_unit(self, unit_str: str): # Just to get return type right def __copy__(self) -> DescriptorNumber: return super().__copy__() - + def __repr__(self) -> str: """Return printable representation.""" - string='<' - string+= self.__class__.__name__+' ' - string+=f"'{self._name}': " - string+= f'{self._scalar.value:.4f}' + string = '<' + string += self.__class__.__name__ + ' ' + string += f"'{self._name}': " + string += f'{self._scalar.value:.4f}' if self.variance: - string += f' \u00B1 {self.error:.4f}' + string += f' \u00b1 {self.error:.4f}' obj_unit = self._scalar.unit if obj_unit == 'dimensionless': obj_unit = '' else: obj_unit = f' {obj_unit}' - string+= obj_unit - string+='>' + string += obj_unit + string += '>' return string # return f"<{class_name} '{obj_name}': {obj_value:0.04f}{obj_unit}>" - def as_dict(self) -> Dict[str, Any]: - raw_dict = super().as_dict() + def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + raw_dict = super().as_dict(skip=skip) raw_dict['value'] = self._scalar.value raw_dict['unit'] = str(self._scalar.unit) raw_dict['variance'] = self._scalar.variance return raw_dict - + def __add__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be added to dimensionless values") - new_value = self.full_value + other + raise UnitError('Numbers can only be added to dimensionless values') + new_value = self.full_value + other elif type(other) is DescriptorNumber: original_unit = other.unit try: other.convert_unit(self.unit) except UnitError: - raise UnitError(f"Values with units {self.unit} and {other.unit} cannot be added") from None + raise UnitError(f'Values with units {self.unit} and {other.unit} cannot be added') from None new_value = self.full_value + other.full_value other.convert_unit(original_unit) else: return NotImplemented - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - - def __radd__(self, other: numbers.Number) -> DescriptorNumber: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be added to dimensionless values") + raise UnitError('Numbers can only be added to dimensionless values') new_value = other + self.full_value else: return NotImplemented - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number def __sub__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be subtracted from dimensionless values") + raise UnitError('Numbers can only be subtracted from dimensionless values') new_value = self.full_value - other elif type(other) is DescriptorNumber: original_unit = other.unit try: other.convert_unit(self.unit) except UnitError: - raise UnitError(f"Values with units {self.unit} and {other.unit} cannot be subtracted") from None + raise UnitError(f'Values with units {self.unit} and {other.unit} cannot be subtracted') from None new_value = self.full_value - other.full_value other.convert_unit(original_unit) else: return NotImplemented - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __rsub__(self, other: numbers.Number) -> DescriptorNumber: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be subtracted from dimensionless values") + raise UnitError('Numbers can only be subtracted from dimensionless values') new_value = other - self.full_value else: return NotImplemented - descriptor= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor.name=descriptor.unique_name + descriptor = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor.name = descriptor.unique_name return descriptor - + def __mul__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber: if isinstance(other, numbers.Number): new_value = self.full_value * other @@ -302,46 +303,46 @@ def __mul__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorN return NotImplemented descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) descriptor_number.convert_unit(descriptor_number._base_unit()) - descriptor_number.name=descriptor_number.unique_name + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __rmul__(self, other: numbers.Number) -> DescriptorNumber: if isinstance(other, numbers.Number): new_value = other * self.full_value else: return NotImplemented - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __truediv__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber: if isinstance(other, numbers.Number): original_other = other if other == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') new_value = self.full_value / other elif type(other) is DescriptorNumber: original_other = other.value if original_other == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') new_value = self.full_value / other.full_value other.value = original_other else: return NotImplemented descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) descriptor_number.convert_unit(descriptor_number._base_unit()) - descriptor_number.name=descriptor_number.unique_name + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __rtruediv__(self, other: numbers.Number) -> DescriptorNumber: if isinstance(other, numbers.Number): if self.value == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') new_value = other / self.full_value else: return NotImplemented - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number def __pow__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber: @@ -349,51 +350,51 @@ def __pow__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorN exponent = other elif type(other) is DescriptorNumber: if other.unit != 'dimensionless': - raise UnitError("Exponents must be dimensionless") + raise UnitError('Exponents must be dimensionless') if other.variance is not None: - raise ValueError("Exponents must not have variance") + raise ValueError('Exponents must not have variance') exponent = other.value else: return NotImplemented try: - new_value = self.full_value ** exponent + new_value = self.full_value**exponent except Exception as message: raise message from None if np.isnan(new_value.value): - raise ValueError("The result of the exponentiation is not a number") - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + raise ValueError('The result of the exponentiation is not a number') + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __rpow__(self, other: numbers.Number) -> numbers.Number: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Exponents must be dimensionless") + raise UnitError('Exponents must be dimensionless') if self.variance is not None: - raise ValueError("Exponents must not have variance") - new_value = other ** self.value + raise ValueError('Exponents must not have variance') + new_value = other**self.value else: return NotImplemented return new_value def __neg__(self) -> DescriptorNumber: new_value = -self.full_value - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - + def __abs__(self) -> DescriptorNumber: new_value = abs(self.full_value) - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number def _base_unit(self) -> str: string = str(self._scalar.unit) for i, letter in enumerate(string): - if letter == "e": - if string[i:i+2] not in ["e+", "e-"]: + if letter == 'e': + if string[i : i + 2] not in ['e+', 'e-']: return string[i:] - elif letter not in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "+", "-"]: + elif letter not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', '-']: return string[i:] - return "" \ No newline at end of file + return '' diff --git a/src/easyscience/Utils/io/template.py b/src/easyscience/Utils/io/template.py index e5e6bbb0..f122a614 100644 --- a/src/easyscience/Utils/io/template.py +++ b/src/easyscience/Utils/io/template.py @@ -222,8 +222,8 @@ def runner(o): d.update({'value': runner(obj.value)}) # pylint: disable=E1101 if hasattr(obj, '_convert_to_dict'): d = obj._convert_to_dict(d, self, skip=skip, **kwargs) - if hasattr(obj, '_global_object') and '@id' not in d: - d['@id'] = obj.unique_name + if hasattr(obj, '_global_object') and 'unique_name' not in d: + d['unique_name'] = obj.unique_name return d @staticmethod @@ -257,7 +257,11 @@ def _convert_from_dict(d): mod = __import__(modname, globals(), locals(), [classname], 0) if hasattr(mod, classname): cls_ = getattr(mod, classname) - data = {k: BaseEncoderDecoder._convert_from_dict(v) for k, v in d.items() if not k.startswith('@')} + data = { + k: BaseEncoderDecoder._convert_from_dict(v) + for k, v in d.items() + if not (k.startswith('@') or k == 'unique_name') + } return cls_(**data) elif np is not None and modname == 'numpy' and classname == 'array': if d['dtype'].startswith('complex'): diff --git a/tests/unit_tests/Objects/test_Groups.py b/tests/unit_tests/Objects/test_Groups.py index dc4b99ff..31297729 100644 --- a/tests/unit_tests/Objects/test_Groups.py +++ b/tests/unit_tests/Objects/test_Groups.py @@ -12,28 +12,28 @@ import easyscience from easyscience.Objects.Groups import BaseCollection from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.ObjectClasses import Descriptor -from easyscience.Objects.ObjectClasses import Parameter +from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.new_variable import Parameter from easyscience import global_object test_dict = { "@module": "easyscience.Objects.Groups", "@class": "BaseCollection", - "@version": "0.1.0", + "@version": easyscience.__version__, "name": "testing", "data": [ { - "@module": Descriptor.__module__, - "@class": Descriptor.__name__, + "@module": DescriptorNumber.__module__, + "@class": DescriptorNumber.__name__, "@version": easyscience.__version__, "name": "par1", - "value": 1, - "units": "dimensionless", + "value": 1.0, + "unit": "dimensionless", + "variance": None, "unique_name": "BaseCollection_0", "description": "", "url": "", "display_name": "par1", - "enabled": True, } ], } @@ -51,9 +51,9 @@ def setup_pars(): d = { "name": "test", "par1": Parameter("p1", 0.1, fixed=True), - "des1": Descriptor("d1", 0.1), + "des1": DescriptorNumber("d1", 0.1), "par2": Parameter("p2", 0.1), - "des2": Descriptor("d2", 0.1), + "des2": DescriptorNumber("d2", 0.1), "par3": Parameter("p3", 0.1), } return d @@ -114,7 +114,7 @@ def test_baseCollection_create_fail(cls, setup_pars, value): def test_baseCollection_create_fail2(cls, setup_pars, key): name = setup_pars["name"] del setup_pars["name"] - setup_pars[key] = Descriptor("fail_name", 0) + setup_pars[key] = DescriptorNumber("fail_name", 0) with pytest.raises(AttributeError): coll = cls(name, **setup_pars) @@ -299,7 +299,7 @@ def test_baseCollection_get_fit_parameters_nested(cls, setup_pars): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_dir(cls): name = "testing" - kwargs = {"p1": Descriptor("par1", 1)} + kwargs = {"p1": DescriptorNumber("par1", 1)} obj = cls(name, **kwargs) d = set(dir(obj)) @@ -338,17 +338,13 @@ def test_baseCollection_dir(cls): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_as_dict(cls): name = "testing" - kwargs = {"p1": Descriptor("par1", 1)} + kwargs = {"p1": DescriptorNumber("par1", 1)} obj = cls(name, **kwargs) d = obj.as_dict() def check_dict(dict_1: dict, dict_2: dict): keys_1 = list(dict_1.keys()) keys_2 = list(dict_2.keys()) - if "@id" in keys_1: - del keys_1[keys_1.index("@id")] - if "@id" in keys_2: - del keys_2[keys_2.index("@id")] if "unique_name" in keys_1: del keys_1[keys_1.index("unique_name")] if "unique_name" in keys_2: @@ -365,6 +361,8 @@ def testit(item1, item2): else: if isinstance(item1, str) and isinstance(item2, str): assert item1 == item2 + elif isinstance(item1, float) and isinstance(item2, float): + assert item1 == item2 else: assert item1 is item2 @@ -384,7 +382,7 @@ def testit(item1, item2): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_from_dict(cls): name = "testing" - kwargs = {"p1": Descriptor("par1", 1)} + kwargs = {"p1": DescriptorNumber("par1", 1)} expected = cls.from_dict(test_dict) ref = cls(name, **kwargs) @@ -453,7 +451,7 @@ def test_baseCollection_iterator_dict(cls): obj2 = cls.from_dict(d) for index, item in enumerate(obj2): - assert item.raw_value == l_object[index].raw_value + assert item.value == l_object[index].value @pytest.mark.parametrize("cls", class_constructors) @@ -528,7 +526,7 @@ def test_baseCollection_sort(cls): v = [1, 4, 3, 2, 5] expected = [1, 2, 3, 4, 5] d = cls(name, *[Parameter(f"p{i}", v[i]) for i in range(len(v))]) - d.sort(lambda x: x.raw_value) + d.sort(lambda x: x.value) for i, item in enumerate(d): assert item.value == expected[i] @@ -540,9 +538,9 @@ def test_baseCollection_sort_reverse(cls): expected = [1, 2, 3, 4, 5] expected.reverse() d = cls(name, *[Parameter(f"p{i}", v[i]) for i in range(len(v))]) - d.sort(lambda x: x.raw_value, reverse=True) + d.sort(lambda x: x.value, reverse=True) for i, item in enumerate(d): - assert item.raw_value == expected[i] + assert item.value == expected[i] class Beta(BaseObj): diff --git a/tests/unit_tests/utils/io_tests/test_core.py b/tests/unit_tests/utils/io_tests/test_core.py index 76661df8..9cb58409 100644 --- a/tests/unit_tests/utils/io_tests/test_core.py +++ b/tests/unit_tests/utils/io_tests/test_core.py @@ -8,26 +8,26 @@ import easyscience from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.ObjectClasses import Descriptor -from easyscience.Objects.ObjectClasses import Parameter +from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.new_variable import Parameter dp_param_dict = { "argnames": "dp_kwargs, dp_cls", "argvalues": ( [ { - "@module": Descriptor.__module__, - "@class": Descriptor.__name__, + "@module": DescriptorNumber.__module__, + "@class": DescriptorNumber.__name__, "@version": easyscience.__version__, "name": "test", - "value": 1, - "units": "dimensionless", + "value": 1.0, + "variance": None, + "unit": "dimensionless", "description": "", "url": "", "display_name": "test", - "enabled": True, }, - Descriptor, + DescriptorNumber, ], [ { @@ -35,9 +35,9 @@ "@class": Parameter.__name__, "@version": easyscience.__version__, "name": "test", - "units": "kilometer", + "unit": "km", "value": 1.0, - "error": 0.0, + "variance": 0.0, "min": -easyscience.np.inf, "max": easyscience.np.inf, "fixed": False, @@ -49,7 +49,7 @@ Parameter, ], ), - "ids": ["Descriptor", "Parameter"], + "ids": ["DescriptorNumber", "Parameter"], } _skip_opt = [[], None] + [ @@ -65,8 +65,6 @@ def check_dict(check, item): if isinstance(check, dict) and isinstance(item, dict): for key in check.keys(): - if key == "@id": - continue assert key in item.keys() check_dict(check[key], item[key]) else: @@ -76,7 +74,7 @@ def check_dict(check, item): @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_as_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_as_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -103,7 +101,7 @@ def test_variable_as_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor], ski @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_as_data_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_as_data_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -138,7 +136,7 @@ def __init__(self, a, b): @pytest.mark.parametrize(**dp_param_dict) -def test_custom_class_as_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_custom_class_as_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} a_kw = {data_dict["name"]: dp_cls(**data_dict)} @@ -165,7 +163,7 @@ def test_custom_class_as_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor]) @pytest.mark.parametrize(**dp_param_dict) -def test_custom_class_as_data_dict_methods(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_custom_class_as_data_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} a_kw = {data_dict["name"]: dp_cls(**data_dict)} diff --git a/tests/unit_tests/utils/io_tests/test_dict.py b/tests/unit_tests/utils/io_tests/test_dict.py index 40286e27..476298d2 100644 --- a/tests/unit_tests/utils/io_tests/test_dict.py +++ b/tests/unit_tests/utils/io_tests/test_dict.py @@ -10,11 +10,11 @@ from easyscience.Utils.io.dict import DataDictSerializer from easyscience.Utils.io.dict import DictSerializer +from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.ObjectClasses import BaseObj from .test_core import A from .test_core import B -from .test_core import BaseObj -from .test_core import Descriptor from .test_core import check_dict from .test_core import dp_param_dict from .test_core import skip_dict @@ -42,7 +42,7 @@ def recursive_remove(d, remove_keys: list) -> dict: ######################################################################################################################## @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -69,7 +69,7 @@ def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -97,7 +97,7 @@ def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], ) @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_encode_data(dp_kwargs: dict, dp_cls: Type[Descriptor], skip, encoder): +def test_variable_encode_data(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip, encoder): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -123,7 +123,7 @@ def test_variable_encode_data(dp_kwargs: dict, dp_cls: Type[Descriptor], skip, e @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) def test_custom_class_DictSerializer_encode( - dp_kwargs: dict, dp_cls: Type[Descriptor], skip + dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip ): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} @@ -158,7 +158,7 @@ def test_custom_class_DictSerializer_encode( @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) def test_custom_class_DataDictSerializer( - dp_kwargs: dict, dp_cls: Type[Descriptor], skip + dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip ): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} @@ -185,7 +185,7 @@ def test_custom_class_DataDictSerializer( "encoder", [None, DataDictSerializer], ids=["Default", "DataDictSerializer"] ) @pytest.mark.parametrize(**dp_param_dict) -def test_custom_class_encode_data(dp_kwargs: dict, dp_cls: Type[Descriptor], encoder): +def test_custom_class_encode_data(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], encoder): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} a_kw = {data_dict["name"]: dp_cls(**data_dict)} @@ -216,12 +216,13 @@ def __init__(self, a, b): except metadata.PackageNotFoundError: version = '0.0.0' - obj = B(Descriptor("a", 1.0, unique_name="a"), np.array([1.0, 2.0, 3.0])) + obj = B(DescriptorNumber("a", 1.0, unique_name="a"), np.array([1.0, 2.0, 3.0])) full_enc = obj.encode(encoder=DictSerializer, full_encode=True) expected = { "@module": "tests.unit_tests.utils.io_tests.test_dict", "@class": "B", "@version": None, + "unique_name": "B_0", "b": { "@module": "numpy", "@class": "array", @@ -229,15 +230,15 @@ def __init__(self, a, b): "data": [1.0, 2.0, 3.0], }, "a": { - "@module": "easyscience.Objects.Variable", - "@class": "Descriptor", + "@module": "easyscience.Objects.new_variable.descriptor_number", + "@class": "DescriptorNumber", "@version": version, "description": "", - "units": "dimensionless", + "unit": "dimensionless", "display_name": "a", "name": "a", - "enabled": True, "value": 1.0, + "variance": None, "unique_name": "a", "url": "", }, @@ -246,13 +247,14 @@ def __init__(self, a, b): def test_custom_class_full_decode_with_numpy(): - - obj = B(Descriptor("a", 1.0), np.array([1.0, 2.0, 3.0])) + global_object.map._clear() + obj = B(DescriptorNumber("a", 1.0), np.array([1.0, 2.0, 3.0])) full_enc = obj.encode(encoder=DictSerializer, full_encode=True) global_object.map._clear() obj2 = B.decode(full_enc, decoder=DictSerializer) assert obj.name == obj2.name - assert obj.a.raw_value == obj2.a.raw_value + assert obj.unique_name == obj2.unique_name + assert obj.a.value == obj2.a.value assert np.all(obj.b == obj2.b) @@ -260,14 +262,10 @@ def test_custom_class_full_decode_with_numpy(): # TESTING DECODING ######################################################################################################################## @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=DictSerializer) global_object.map._clear() @@ -281,14 +279,10 @@ def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DictSerializer_from_dict(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_DictSerializer_from_dict(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=DictSerializer) global_object.map._clear() @@ -302,14 +296,10 @@ def test_variable_DictSerializer_from_dict(dp_kwargs: dict, dp_cls: Type[Descrip @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DataDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_DataDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=DataDictSerializer) with pytest.raises(NotImplementedError): @@ -317,8 +307,8 @@ def test_variable_DataDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descri def test_group_encode(): - d0 = Descriptor("a", 0) - d1 = Descriptor("b", 1) + d0 = DescriptorNumber("a", 0) + d1 = DescriptorNumber("b", 1) from easyscience.Objects.Groups import BaseCollection @@ -328,8 +318,8 @@ def test_group_encode(): def test_group_encode2(): - d0 = Descriptor("a", 0) - d1 = Descriptor("b", 1) + d0 = DescriptorNumber("a", 0) + d1 = DescriptorNumber("b", 1) from easyscience.Objects.Groups import BaseCollection diff --git a/tests/unit_tests/utils/io_tests/test_json.py b/tests/unit_tests/utils/io_tests/test_json.py index 17442c4a..e5d25c03 100644 --- a/tests/unit_tests/utils/io_tests/test_json.py +++ b/tests/unit_tests/utils/io_tests/test_json.py @@ -9,9 +9,9 @@ from easyscience.Utils.io.json import JsonDataSerializer from easyscience.Utils.io.json import JsonSerializer +from easyscience.Objects.new_variable import DescriptorNumber from .test_core import A -from .test_core import Descriptor from .test_core import check_dict from .test_core import dp_param_dict from .test_core import skip_dict @@ -39,7 +39,7 @@ def recursive_remove(d, remove_keys: list) -> dict: ######################################################################################################################## @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -69,7 +69,7 @@ def test_variable_DictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -97,7 +97,7 @@ def test_variable_DataDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) def test_custom_class_DictSerializer_encode( - dp_kwargs: dict, dp_cls: Type[Descriptor], skip + dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip ): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} @@ -137,7 +137,7 @@ def test_custom_class_DictSerializer_encode( @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) def test_custom_class_DataDictSerializer( - dp_kwargs: dict, dp_cls: Type[Descriptor], skip + dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip ): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} @@ -169,14 +169,10 @@ def test_custom_class_DataDictSerializer( # # TESTING DECODING # ######################################################################################################################## @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=JsonSerializer) global_object.map._clear() @@ -191,14 +187,10 @@ def test_variable_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor @pytest.mark.parametrize(**dp_param_dict) -def test_variable_DataDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_DataDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=JsonDataSerializer) global_object.map._clear() diff --git a/tests/unit_tests/utils/io_tests/test_xml.py b/tests/unit_tests/utils/io_tests/test_xml.py index 562ceae1..9f908a99 100644 --- a/tests/unit_tests/utils/io_tests/test_xml.py +++ b/tests/unit_tests/utils/io_tests/test_xml.py @@ -9,9 +9,9 @@ import pytest from easyscience.Utils.io.xml import XMLSerializer +from easyscience.Objects.new_variable import DescriptorNumber from .test_core import A -from .test_core import Descriptor from .test_core import dp_param_dict from .test_core import skip_dict from easyscience import global_object @@ -45,7 +45,7 @@ def recursive_test(testing_obj, reference_obj): ######################################################################################################################## @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_XMLDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], skip): +def test_variable_XMLDictSerializer(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) @@ -69,7 +69,7 @@ def test_variable_XMLDictSerializer(dp_kwargs: dict, dp_cls: Type[Descriptor], s @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) def test_custom_class_XMLDictSerializer_encode( - dp_kwargs: dict, dp_cls: Type[Descriptor], skip + dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip ): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} @@ -102,14 +102,10 @@ def test_custom_class_XMLDictSerializer_encode( # # TESTING DECODING # ######################################################################################################################## @pytest.mark.parametrize(**dp_param_dict) -def test_variable_XMLDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): +def test_variable_XMLDictSerializer_decode(dp_kwargs: dict, dp_cls: Type[DescriptorNumber]): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != "@"} obj = dp_cls(**data_dict) - if "units" in data_dict.keys(): - data_dict["unit"] = data_dict.pop("units") - if "value" in data_dict.keys(): - data_dict["raw_value"] = data_dict.pop("value") enc = obj.encode(encoder=XMLSerializer) assert isinstance(enc, str) From dce225ee7329b0acc9e482ba475a3d1e474c2e3b Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Wed, 11 Sep 2024 11:17:04 +0200 Subject: [PATCH 05/26] path in map --- src/easyscience/Utils/classTools.py | 2 +- src/easyscience/global_object/map.py | 23 ++++++----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/easyscience/Utils/classTools.py b/src/easyscience/Utils/classTools.py index 5c09711f..0bb60886 100644 --- a/src/easyscience/Utils/classTools.py +++ b/src/easyscience/Utils/classTools.py @@ -68,5 +68,5 @@ def generatePath(model_obj: B, skip_first: bool = False) -> Tuple[List[int], Lis objs = [getattr(global_object.map.get_item_by_key(r), 'name') for r in route] objs.reverse() names.append('.'.join(objs[start_idx:])) - ids.append(elem.int) + ids.append(elem) return ids, names diff --git a/src/easyscience/global_object/map.py b/src/easyscience/global_object/map.py index 2e9d6857..7eb25ff3 100644 --- a/src/easyscience/global_object/map.py +++ b/src/easyscience/global_object/map.py @@ -9,6 +9,7 @@ import sys import weakref from typing import List +from typing import Optional class _EntryList(list): @@ -183,17 +184,10 @@ def find_isolated_vertices(self) -> list: isolated += [vertex] return isolated - def find_path(self, start_obj, end_obj, path=[]) -> list: + def find_path(self, start_vertex: str, end_vertex: str, path=[]) -> list: """find a path from start_vertex to end_vertex in map""" - try: - start_vertex = start_obj.unique_name - end_vertex = end_obj.unique_name - except AttributeError: - start_vertex = start_obj - end_vertex = end_obj - graph = self.__type_dict path = path + [start_vertex] if start_vertex == end_vertex: @@ -207,13 +201,10 @@ def find_path(self, start_obj, end_obj, path=[]) -> list: return extended_path return [] - def find_all_paths(self, start_obj, end_obj, path=[]) -> list: + def find_all_paths(self, start_vertex: str, end_vertex: str, path=[]) -> list: """find all paths from start_vertex to end_vertex in map""" - start_vertex = start_obj.unique_name - end_vertex = end_obj.unique_name - graph = self.__type_dict path = path + [start_vertex] if start_vertex == end_vertex: @@ -228,7 +219,7 @@ def find_all_paths(self, start_obj, end_obj, path=[]) -> list: paths.append(p) return paths - def reverse_route(self, end_obj, start_obj=None) -> List: + def reverse_route(self, end_vertex: str, start_vertex: Optional[str] = None) -> List: """ In this case we have an object and want to know the connections to get to another in reverse. We might not know the start_object. In which case we follow the shortest path to a base vertex. @@ -239,11 +230,9 @@ def reverse_route(self, end_obj, start_obj=None) -> List: :return: :rtype: """ - end_vertex = end_obj.unique_name - path_length = sys.maxsize optimum_path = [] - if start_obj is None: + if start_vertex is None: # We now have to find where to begin..... for possible_start, vertices in self.__type_dict.items(): if end_vertex in vertices: @@ -252,7 +241,7 @@ def reverse_route(self, end_obj, start_obj=None) -> List: path_length = len(temp_path) optimum_path = temp_path else: - optimum_path = self.find_path(start_obj, end_obj) + optimum_path = self.find_path(start_vertex, end_vertex) optimum_path.reverse() return optimum_path From 2f2e301bd148b63bc3df8db47faad5cb0841611c Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Thu, 12 Sep 2024 09:43:52 +0200 Subject: [PATCH 06/26] unique_name adjustments --- src/easyscience/Objects/new_variable/descriptor_base.py | 3 +-- src/easyscience/Utils/io/template.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/easyscience/Objects/new_variable/descriptor_base.py b/src/easyscience/Objects/new_variable/descriptor_base.py index ae5572ce..e7c4db08 100644 --- a/src/easyscience/Objects/new_variable/descriptor_base.py +++ b/src/easyscience/Objects/new_variable/descriptor_base.py @@ -205,7 +205,6 @@ def __repr__(self) -> str: def __copy__(self) -> DescriptorBase: """Return a copy of the object.""" - temp = self.as_dict() - temp['unique_name'] = None + temp = self.as_dict(skip=['unique_name']) new_obj = self.__class__.from_dict(temp) return new_obj diff --git a/src/easyscience/Utils/io/template.py b/src/easyscience/Utils/io/template.py index f122a614..3424ddf7 100644 --- a/src/easyscience/Utils/io/template.py +++ b/src/easyscience/Utils/io/template.py @@ -222,7 +222,7 @@ def runner(o): d.update({'value': runner(obj.value)}) # pylint: disable=E1101 if hasattr(obj, '_convert_to_dict'): d = obj._convert_to_dict(d, self, skip=skip, **kwargs) - if hasattr(obj, '_global_object') and 'unique_name' not in d: + if hasattr(obj, '_global_object') and 'unique_name' not in d and 'unique_name' not in skip: d['unique_name'] = obj.unique_name return d From 61fa213bcd805c1c5c2c016a93e926b35260df26 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Thu, 12 Sep 2024 13:22:28 +0200 Subject: [PATCH 07/26] added copy method --- src/easyscience/Objects/ObjectClasses.py | 12 +- tests/unit_tests/Objects/test_BaseObj.py | 83 ++- .../Objects/test_BaseObj_legacy_parameter.py | 503 ++++++++++++++++++ 3 files changed, 567 insertions(+), 31 deletions(-) create mode 100644 tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 52ebdc90..d7a7c005 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -186,7 +186,7 @@ def _get_linkable_attributes(self) -> List[V]: for key, item in self._kwargs.items(): if hasattr(item, '_get_linkable_attributes'): item_list = [*item_list, *item._get_linkable_attributes()] - elif issubclass(type(item), Descriptor) or issubclass(type(item), DescriptorBase): + elif issubclass(type(item), (Descriptor, DescriptorBase)): item_list.append(item) return item_list @@ -215,6 +215,12 @@ def __dir__(self) -> Iterable[str]: new_class_objs = list(k for k in dir(self.__class__) if not k.startswith('_')) return sorted(new_class_objs) + def __copy__(self) -> BasedBase: + """Return a copy of the object.""" + temp = self.as_dict(skip=['unique_name']) + new_obj = self.__class__.from_dict(temp) + return new_obj + if TYPE_CHECKING: B = TypeVar('B', bound=BasedBase) @@ -346,7 +352,9 @@ def getter(obj: BV) -> BV: @staticmethod def __setter(key: str) -> Callable[[BV], None]: def setter(obj: BV, value: float) -> None: - if issubclass(obj._kwargs[key].__class__, Descriptor) and not issubclass(value.__class__, Descriptor): + if issubclass(obj._kwargs[key].__class__, (Descriptor, DescriptorBase)) and not issubclass( + value.__class__, (Descriptor, DescriptorBase) + ): obj._kwargs[key].value = value else: obj._kwargs[key] = value diff --git a/tests/unit_tests/Objects/test_BaseObj.py b/tests/unit_tests/Objects/test_BaseObj.py index 5fdb087e..15d12fdf 100644 --- a/tests/unit_tests/Objects/test_BaseObj.py +++ b/tests/unit_tests/Objects/test_BaseObj.py @@ -6,6 +6,8 @@ # © 2021-2023 Contributors to the EasyScience project +# SPDX-License-Identifier: BSD-3-Clause +# © 2021-2023 Contributors to the EasyScience project Date: Fri, 13 Sep 2024 13:35:57 +0200 Subject: [PATCH 08/26] handle when empty group --- src/easyscience/Objects/Groups.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index f011dc1a..75fa8e12 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -69,7 +69,7 @@ def __init__( _kwargs[key] = item kwargs = _kwargs for item in list(kwargs.values()) + _args: - if not issubclass(type(item), (Descriptor, DescriptorBase, BasedBase)): + if item and not issubclass(type(item), (Descriptor, DescriptorBase, BasedBase)): raise AttributeError('A collection can only be formed from easyscience objects.') args = _args _kwargs = {} @@ -85,10 +85,11 @@ def __init__( for key in kwargs.keys(): if key in self.__dict__.keys() or key in self.__slots__: raise AttributeError(f'Given kwarg: `{key}`, is an internal attribute. Please rename.') - self._global_object.map.add_edge(self, kwargs[key]) - self._global_object.map.reset_type(kwargs[key], 'created_internal') - if interface is not None: - kwargs[key].interface = interface + if kwargs[key]: # Might be an empty tuple + self._global_object.map.add_edge(self, kwargs[key]) + self._global_object.map.reset_type(kwargs[key], 'created_internal') + if interface is not None: + kwargs[key].interface = interface # TODO wrap getter and setter in Logger if interface is not None: self.interface = interface From 045c865c880e9f13478e2ed182e6f8da635b368a Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Fri, 13 Sep 2024 14:09:45 +0200 Subject: [PATCH 09/26] typo and revert change --- src/easyscience/Objects/Groups.py | 2 +- src/easyscience/fitting/available_minimizers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index 75fa8e12..a6b64eb0 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -69,7 +69,7 @@ def __init__( _kwargs[key] = item kwargs = _kwargs for item in list(kwargs.values()) + _args: - if item and not issubclass(type(item), (Descriptor, DescriptorBase, BasedBase)): + if not issubclass(type(item), (Descriptor, DescriptorBase, BasedBase)): raise AttributeError('A collection can only be formed from easyscience objects.') args = _args _kwargs = {} diff --git a/src/easyscience/fitting/available_minimizers.py b/src/easyscience/fitting/available_minimizers.py index f39fc4bb..9567317d 100644 --- a/src/easyscience/fitting/available_minimizers.py +++ b/src/easyscience/fitting/available_minimizers.py @@ -36,7 +36,7 @@ class AvailableMinimizer: package: str method: str - enmu_id: int + enum_id: int class AvailableMinimizers(AvailableMinimizer, Enum): From 68a01a0f44be0e0617463ba47d306cffcd2a7c7f Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Fri, 13 Sep 2024 14:35:45 +0200 Subject: [PATCH 10/26] ruff --- src/easyscience/Objects/new_variable/descriptor_number.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/easyscience/Objects/new_variable/descriptor_number.py b/src/easyscience/Objects/new_variable/descriptor_number.py index 44c5cb13..e8306f17 100644 --- a/src/easyscience/Objects/new_variable/descriptor_number.py +++ b/src/easyscience/Objects/new_variable/descriptor_number.py @@ -138,7 +138,10 @@ def unit(self) -> str: @unit.setter def unit(self, unit_str: str) -> None: raise AttributeError( - f'Unit is read-only. Use convert_unit to change the unit between allowed types or create a new {self.__class__.__name__} with the desired unit.' + ( + f'Unit is read-only. Use convert_unit to change the unit between allowed types ' + f'or create a new {self.__class__.__name__} with the desired unit.' + ) ) # noqa: E501 @property From 824f4c0d8f777b5065a52e6061841e0b61f6cf12 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Fri, 13 Sep 2024 14:52:00 +0200 Subject: [PATCH 11/26] code cleaning --- src/easyscience/Objects/Groups.py | 2 +- .../Objects/new_variable/descriptor_number.py | 2 +- src/easyscience/Utils/classTools.py | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index a6b64eb0..cbbecb55 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -85,7 +85,7 @@ def __init__( for key in kwargs.keys(): if key in self.__dict__.keys() or key in self.__slots__: raise AttributeError(f'Given kwarg: `{key}`, is an internal attribute. Please rename.') - if kwargs[key]: # Might be an empty tuple + if kwargs[key]: # Might be None (empty tuple or list) self._global_object.map.add_edge(self, kwargs[key]) self._global_object.map.reset_type(kwargs[key], 'created_internal') if interface is not None: diff --git a/src/easyscience/Objects/new_variable/descriptor_number.py b/src/easyscience/Objects/new_variable/descriptor_number.py index e8306f17..c21c52e2 100644 --- a/src/easyscience/Objects/new_variable/descriptor_number.py +++ b/src/easyscience/Objects/new_variable/descriptor_number.py @@ -103,7 +103,7 @@ def full_value(self) -> Variable: def full_value(self, full_value: Variable) -> None: raise AttributeError( f'Full_value is read-only. Change the value and variance seperately. Or create a new {self.__class__.__name__}.' - ) # noqa: E501 + ) @property def value(self) -> numbers.Number: diff --git a/src/easyscience/Utils/classTools.py b/src/easyscience/Utils/classTools.py index 0bb60886..056998f9 100644 --- a/src/easyscience/Utils/classTools.py +++ b/src/easyscience/Utils/classTools.py @@ -59,14 +59,13 @@ def removeProp(inst: BV, name: str) -> None: def generatePath(model_obj: B, skip_first: bool = False) -> Tuple[List[int], List[str]]: pars = model_obj.get_parameters() start_idx = 0 + int(skip_first) - ids = [] + unique_names = [] names = [] - model_id = model_obj.unique_name for par in pars: - elem = par.unique_name - route = global_object.map.reverse_route(elem, model_id) + par_unique_name = par.unique_name + route = global_object.map.reverse_route(par_unique_name, model_obj.unique_name) objs = [getattr(global_object.map.get_item_by_key(r), 'name') for r in route] objs.reverse() names.append('.'.join(objs[start_idx:])) - ids.append(elem) - return ids, names + unique_names.append(par_unique_name) + return unique_names, names From 2d6b4a8907024ad0f7b36c31a1652db6d79ec371 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 10:04:40 +0200 Subject: [PATCH 12/26] try to build on mocos-12 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 53379663..fcd14dc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,7 +138,7 @@ python = [gh-actions:env] PLATFORM = ubuntu-latest: linux - macos-latest: macos + macos-latest: macos-12 windows-latest: windows [testenv] passenv = From fbf918da22b5fc38e62088436d46cd8ac8a18b23 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 10:17:18 +0200 Subject: [PATCH 13/26] macos-12 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fcd14dc0..ea8ea3b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,7 +138,7 @@ python = [gh-actions:env] PLATFORM = ubuntu-latest: linux - macos-latest: macos-12 + macos-12: macos windows-latest: windows [testenv] passenv = From 2252f781fd4a07aac763be430d7cebacca4813e3 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 10:20:31 +0200 Subject: [PATCH 14/26] macox-12 --- .github/workflows/python-ci.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 6d02c1cf..c0366a7e 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -31,7 +31,7 @@ jobs: max-parallel: 4 matrix: python-version: ['3.9', '3.10', '3.11'] - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-12, windows-latest] runs-on: ${{ matrix.os }} if: "!contains(github.event.head_commit.message, '[ci skip]')" diff --git a/pyproject.toml b/pyproject.toml index ea8ea3b8..53379663 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,7 +138,7 @@ python = [gh-actions:env] PLATFORM = ubuntu-latest: linux - macos-12: macos + macos-latest: macos windows-latest: windows [testenv] passenv = From 14433f673e7b16d334a1d0d8e1d86a02f46f282b Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 10:27:44 +0200 Subject: [PATCH 15/26] back to macos lastest --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index c0366a7e..6d02c1cf 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -31,7 +31,7 @@ jobs: max-parallel: 4 matrix: python-version: ['3.9', '3.10', '3.11'] - os: [ubuntu-latest, macos-12, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} if: "!contains(github.event.head_commit.message, '[ci skip]')" From 19f9da3282381dfbcc4099aa7338fbf4ce5aa739 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 10:38:46 +0200 Subject: [PATCH 16/26] added 3.12 to tests --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 6d02c1cf..ac9967bd 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -30,7 +30,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} From a1ab8604cfe0a17a92c26b1c52021840dc43e131 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Mon, 16 Sep 2024 13:40:14 +0200 Subject: [PATCH 17/26] pyproject python version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 53379663..5984f52f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Development Status :: 3 - Alpha" ] -requires-python = ">=3.9,<3.12" +requires-python = ">=3.9,<3.13" dependencies = [ "asteval", "bumps", From d98c8cb3ca4c1c1066fbec24f8514e5802d15774 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 17 Sep 2024 07:00:27 +0200 Subject: [PATCH 18/26] moving Constraint to break circular dependency, using new_variable more consistently --- Examples/fitting/plot_constraints.py | 2 +- src/easyscience/{fitting => }/Constraints.py | 0 src/easyscience/Objects/ObjectClasses.py | 2 +- src/easyscience/Objects/Variable.py | 4 +- .../Objects/new_variable/__init__.py | 15 +- .../Objects/new_variable/descriptor_base.py | 10 +- .../Objects/new_variable/parameter.py | 210 +++++++++--------- src/easyscience/Objects/virtual.py | 2 +- .../fitting/minimizers/minimizer_base.py | 17 +- .../fitting/minimizers/minimizer_bumps.py | 8 +- .../fitting/minimizers/minimizer_dfo.py | 15 +- .../fitting/minimizers/minimizer_lmfit.py | 8 +- .../integration_tests/Fitting/test_fitter.py | 2 +- .../Fitting/test_fitter_legacy_parameter.py | 2 +- .../Fitting/test_multi_fitter.py | 2 +- .../test_multi_fitter_legacy_parameter.py | 2 +- tests/unit_tests/Fitting/test_constraints.py | 4 +- .../test_constraints_legacy_parameter.py | 4 +- .../new_variable/test_descriptor_base.py | 8 +- tests/unit_tests/Objects/test_Groups.py | 2 +- 20 files changed, 155 insertions(+), 164 deletions(-) rename src/easyscience/{fitting => }/Constraints.py (100%) diff --git a/Examples/fitting/plot_constraints.py b/Examples/fitting/plot_constraints.py index 70d2db5e..b150bc82 100644 --- a/Examples/fitting/plot_constraints.py +++ b/Examples/fitting/plot_constraints.py @@ -4,7 +4,7 @@ This example shows the usages of the different constraints. """ -from easyscience.fitting import Constraints +from easyscience import Constraints from easyscience.Objects.ObjectClasses import Parameter p1 = Parameter('p1', 1) diff --git a/src/easyscience/fitting/Constraints.py b/src/easyscience/Constraints.py similarity index 100% rename from src/easyscience/fitting/Constraints.py rename to src/easyscience/Constraints.py diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index d7a7c005..4956c72c 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -28,7 +28,7 @@ from .Variable import Parameter if TYPE_CHECKING: - from easyscience.fitting.Constraints import C + from easyscience.Constraints import C from easyscience.Objects.Inferface import iF from easyscience.Objects.Variable import V diff --git a/src/easyscience/Objects/Variable.py b/src/easyscience/Objects/Variable.py index 2cf0ed6d..10362ba9 100644 --- a/src/easyscience/Objects/Variable.py +++ b/src/easyscience/Objects/Variable.py @@ -30,14 +30,14 @@ from easyscience import global_object from easyscience import pint from easyscience import ureg -from easyscience.fitting.Constraints import SelfConstraint +from easyscience.Constraints import SelfConstraint from easyscience.global_object.undo_redo import property_stack_deco from easyscience.Objects.core import ComponentSerializer from easyscience.Utils.classTools import addProp from easyscience.Utils.Exceptions import CoreSetException if TYPE_CHECKING: - from easyscience.fitting.Constraints import C + from easyscience.Constraints import C Q_ = ureg.Quantity M_ = ureg.Measurement diff --git a/src/easyscience/Objects/new_variable/__init__.py b/src/easyscience/Objects/new_variable/__init__.py index b46d4b95..37b95714 100644 --- a/src/easyscience/Objects/new_variable/__init__.py +++ b/src/easyscience/Objects/new_variable/__init__.py @@ -1,4 +1,11 @@ -from .descriptor_bool import DescriptorBool # noqa: F401 -from .descriptor_number import DescriptorNumber # noqa: F401 -from .descriptor_str import DescriptorStr # noqa: F401 -from .parameter import Parameter # noqa: F401 +from .descriptor_bool import DescriptorBool +from .descriptor_number import DescriptorNumber +from .descriptor_str import DescriptorStr +from .parameter import Parameter + +__all__ = [ + DescriptorBool, + DescriptorNumber, + DescriptorStr, + Parameter, +] diff --git a/src/easyscience/Objects/new_variable/descriptor_base.py b/src/easyscience/Objects/new_variable/descriptor_base.py index e7c4db08..4c9ea678 100644 --- a/src/easyscience/Objects/new_variable/descriptor_base.py +++ b/src/easyscience/Objects/new_variable/descriptor_base.py @@ -27,7 +27,7 @@ class DescriptorBase(ComponentSerializer, metaclass=abc.ABCMeta): # Used by serializer _REDIRECT = {'parent': None} - _global_object = global_object + # _global_object = global_object def __init__( self, @@ -55,7 +55,7 @@ def __init__( """ if unique_name is None: - unique_name = self._global_object.generate_unique_name(self.__class__.__name__) + unique_name = global_object.generate_unique_name(self.__class__.__name__) self._unique_name = unique_name if not isinstance(name, str): @@ -80,10 +80,10 @@ def __init__( # Let the collective know we've been assimilated self._parent = parent - self._global_object.map.add_vertex(self, obj_type='created') + global_object.map.add_vertex(self, obj_type='created') # Make the connection between self and parent if parent is not None: - self._global_object.map.add_edge(parent, self) + global_object.map.add_edge(parent, self) @property def name(self) -> str: @@ -187,7 +187,7 @@ def unique_name(self, new_unique_name: str): if not isinstance(new_unique_name, str): raise TypeError('Unique name has to be a string.') self._unique_name = new_unique_name - self._global_object.map.add_vertex(self) + global_object.map.add_vertex(self) @property @abc.abstractmethod diff --git a/src/easyscience/Objects/new_variable/parameter.py b/src/easyscience/Objects/new_variable/parameter.py index 66626cdc..26992c04 100644 --- a/src/easyscience/Objects/new_variable/parameter.py +++ b/src/easyscience/Objects/new_variable/parameter.py @@ -21,8 +21,8 @@ from scipp import Variable from easyscience import global_object -from easyscience.fitting.Constraints import ConstraintBase -from easyscience.fitting.Constraints import SelfConstraint +from easyscience.Constraints import ConstraintBase +from easyscience.Constraints import SelfConstraint from easyscience.global_object.undo_redo import property_stack_deco from easyscience.Utils.Exceptions import CoreSetException @@ -91,7 +91,7 @@ def __init__( raise ValueError('The min and max bounds cannot be identical. Please use fixed=True instead to fix the value.') if not isinstance(fixed, bool): raise TypeError('`fixed` must be either True or False') - + self._min = sc.scalar(float(min), unit=unit) self._max = sc.scalar(float(max), unit=unit) @@ -122,7 +122,6 @@ def __init__( } self._constraints = Constraints(builtin=builtin_constraint, user={}, virtual={}) - @property def value_no_call_back(self) -> numbers.Number: """ @@ -148,7 +147,9 @@ def full_value(self) -> Variable: @full_value.setter def full_value(self, scalar: Variable) -> None: - raise AttributeError(f'Full_value is read-only. Change the value and variance seperately. Or create a new {self.__class__.__name__}.') # noqa: E501 + raise AttributeError( + f'Full_value is read-only. Change the value and variance seperately. Or create a new {self.__class__.__name__}.' + ) # noqa: E501 @property def value(self) -> numbers.Number: @@ -189,13 +190,13 @@ def value(self, value: numbers.Number) -> None: # Deals with user constraints # Changes should not be registrered in the undo/redo stack - stack_state = self._global_object.stack.enabled + stack_state = global_object.stack.enabled if stack_state: - self._global_object.stack.force_state(False) + global_object.stack.force_state(False) try: value = self._constraint_runner(self.user_constraints, value) finally: - self._global_object.stack.force_state(stack_state) + global_object.stack.force_state(stack_state) value = self._constraint_runner(self._constraints.virtual, value) @@ -290,9 +291,9 @@ def fixed(self, fixed: bool) -> None: :param fixed: True = fixed, False = can vary """ if not self.enabled: - if self._global_object.stack.enabled: + if global_object.stack.enabled: # Remove the recorded change from the stack - self._global_object.stack.pop() + global_object.stack.pop() if global_object.debug: raise CoreSetException(f'{str(self)} is not enabled.') return @@ -415,66 +416,66 @@ def __repr__(self) -> str: # Seems redundant # def __float__(self) -> float: # return float(self._scalar.value) - + def __add__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be added to dimensionless values") + raise UnitError('Numbers can only be added to dimensionless values') new_full_value = self.full_value + other min_value = self.min + other max_value = self.max + other - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here other_unit = other.unit try: other.convert_unit(self.unit) except UnitError: - raise UnitError(f"Values with units {self.unit} and {other.unit} cannot be added") from None + raise UnitError(f'Values with units {self.unit} and {other.unit} cannot be added') from None new_full_value = self.full_value + other.full_value min_value = self.min + other.min if isinstance(other, Parameter) else self.min + other.value max_value = self.max + other.max if isinstance(other, Parameter) else self.max + other.value other.convert_unit(other_unit) - else: + else: return NotImplemented - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter def __radd__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be added to dimensionless values") + raise UnitError('Numbers can only be added to dimensionless values') new_full_value = self.full_value + other min_value = self.min + other max_value = self.max + other - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here original_unit = self.unit try: self.convert_unit(other.unit) except UnitError: - raise UnitError(f"Values with units {other.unit} and {self.unit} cannot be added") from None + raise UnitError(f'Values with units {other.unit} and {self.unit} cannot be added') from None new_full_value = self.full_value + other.full_value min_value = self.min + other.value max_value = self.max + other.value self.convert_unit(original_unit) else: return NotImplemented - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter def __sub__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be subtracted from dimensionless values") + raise UnitError('Numbers can only be subtracted from dimensionless values') new_full_value = self.full_value - other min_value = self.min - other max_value = self.max - other - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here other_unit = other.unit try: other.convert_unit(self.unit) except UnitError: - raise UnitError(f"Values with units {self.unit} and {other.unit} cannot be subtracted") from None + raise UnitError(f'Values with units {self.unit} and {other.unit} cannot be subtracted') from None new_full_value = self.full_value - other.full_value if isinstance(other, Parameter): min_value = self.min - other.max if other.max != np.inf else -np.inf @@ -483,52 +484,59 @@ def __sub__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> min_value = self.min - other.value max_value = self.max - other.value other.convert_unit(other_unit) - else: + else: return NotImplemented - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter - + def __rsub__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): if self.unit != 'dimensionless': - raise UnitError("Numbers can only be subtracted from dimensionless values") + raise UnitError('Numbers can only be subtracted from dimensionless values') new_full_value = other - self.full_value min_value = other - self.max max_value = other - self.min - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here original_unit = self.unit try: self.convert_unit(other.unit) except UnitError: - raise UnitError(f"Values with units {other.unit} and {self.unit} cannot be subtracted") from None + raise UnitError(f'Values with units {other.unit} and {self.unit} cannot be subtracted') from None new_full_value = other.full_value - self.full_value - min_value = other.value - self.max - max_value = other.value - self.min + min_value = other.value - self.max + max_value = other.value - self.min self.convert_unit(original_unit) else: return NotImplemented - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter - + def __mul__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): new_full_value = self.full_value * other if other == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number combinations = [self.min * other, self.max * other] - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here new_full_value = self.full_value * other.full_value - if other.value == 0 and type(other) is DescriptorNumber: # Only return DescriptorNumber if other is strictly 0, i.e. not a parameter # noqa: E501 - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + if ( + other.value == 0 and type(other) is DescriptorNumber + ): # Only return DescriptorNumber if other is strictly 0, i.e. not a parameter # noqa: E501 + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number if isinstance(other, Parameter): combinations = [] - for first, second in [(self.min, other.min), (self.min, other.max), (self.max, other.min), (self.max, other.max)]: # noqa: E501 + for first, second in [ + (self.min, other.min), + (self.min, other.max), + (self.max, other.min), + (self.max, other.max), + ]: # noqa: E501 if first == 0 and np.isinf(second): combinations.append(0) elif second == 0 and np.isinf(first): @@ -537,28 +545,28 @@ def __mul__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> combinations.append(first * second) else: combinations = [self.min * other.value, self.max * other.value] - else: + else: return NotImplemented min_value = min(combinations) max_value = max(combinations) parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) parameter.convert_unit(parameter._base_unit()) - parameter.name=parameter.unique_name + parameter.name = parameter.unique_name return parameter - + def __rmul__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): new_full_value = other * self.full_value if other == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number combinations = [other * self.min, other * self.max] - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here new_full_value = other.full_value * self.full_value if other.value == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number combinations = [self.min * other.value, self.max * other.value] else: @@ -567,39 +575,39 @@ def __rmul__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: max_value = max(combinations) parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) parameter.convert_unit(parameter._base_unit()) - parameter.name=parameter.unique_name + parameter.name = parameter.unique_name return parameter - + def __truediv__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): if other == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') new_full_value = self.full_value / other combinations = [self.min / other, self.max / other] elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here other_value = other.value if other_value == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') new_full_value = self.full_value / other.full_value if isinstance(other, Parameter): - if (other.min < 0 and other.max > 0): + if other.min < 0 and other.max > 0: combinations = [-np.inf, np.inf] elif other.min == 0: - if (self.min < 0 and self.max > 0): + if self.min < 0 and self.max > 0: combinations = [-np.inf, np.inf] elif self.min >= 0: - combinations = [self.min/other.max, np.inf] + combinations = [self.min / other.max, np.inf] elif self.max <= 0: - combinations = [-np.inf, self.max/other.max] + combinations = [-np.inf, self.max / other.max] elif other.max == 0: - if (self.min < 0 and self.max > 0): + if self.min < 0 and self.max > 0: combinations = [-np.inf, np.inf] elif self.min >= 0: - combinations = [-np.inf, self.min/other.min] + combinations = [-np.inf, self.min / other.min] elif self.max <= 0: - combinations = [self.max/other.min, np.inf] + combinations = [self.max / other.min, np.inf] else: - combinations = [self.min/other.min, self.max/other.max, self.min/other.max, self.max/other.min] + combinations = [self.min / other.min, self.max / other.max, self.min / other.max, self.max / other.min] else: combinations = [self.min / other.value, self.max / other.value] other.value = other_value @@ -609,85 +617,85 @@ def __truediv__(self, other: Union[DescriptorNumber, Parameter, numbers.Number]) max_value = max(combinations) parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) parameter.convert_unit(parameter._base_unit()) - parameter.name=parameter.unique_name + parameter.name = parameter.unique_name return parameter - + def __rtruediv__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: original_self = self.value if original_self == 0: - raise ZeroDivisionError("Cannot divide by zero") + raise ZeroDivisionError('Cannot divide by zero') if isinstance(other, numbers.Number): new_full_value = other / self.full_value other_value = other if other_value == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number - elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here + elif isinstance(other, DescriptorNumber): # Parameter inherits from DescriptorNumber and is also handled here new_full_value = other.full_value / self.full_value other_value = other.value if other_value == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name return descriptor_number else: return NotImplemented - if (self.min < 0 and self.max > 0): + if self.min < 0 and self.max > 0: combinations = [-np.inf, np.inf] elif self.min == 0: if other_value > 0: - combinations = [other_value/self.max, np.inf] + combinations = [other_value / self.max, np.inf] elif other_value < 0: - combinations = [-np.inf, other_value/self.max] + combinations = [-np.inf, other_value / self.max] elif self.max == 0: if other_value > 0: - combinations = [-np.inf, other_value/self.min] + combinations = [-np.inf, other_value / self.min] elif other_value < 0: - combinations = [other_value/self.min, np.inf] + combinations = [other_value / self.min, np.inf] else: combinations = [other_value / self.min, other_value / self.max] min_value = min(combinations) max_value = max(combinations) parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) parameter.convert_unit(parameter._base_unit()) - parameter.name=parameter.unique_name + parameter.name = parameter.unique_name self.value = original_self return parameter - + def __pow__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: if isinstance(other, numbers.Number): exponent = other - elif type(other) is DescriptorNumber: # Strictly a DescriptorNumber, We can't raise to the power of a Parameter + elif type(other) is DescriptorNumber: # Strictly a DescriptorNumber, We can't raise to the power of a Parameter if other.unit != 'dimensionless': - raise UnitError("Exponents must be dimensionless") + raise UnitError('Exponents must be dimensionless') if other.variance is not None: - raise ValueError("Exponents must not have variance") + raise ValueError('Exponents must not have variance') exponent = other.value else: return NotImplemented - + try: - new_full_value = self.full_value ** exponent + new_full_value = self.full_value**exponent except Exception as message: raise message from None - + if np.isnan(new_full_value.value): - raise ValueError("The result of the exponentiation is not a number") + raise ValueError('The result of the exponentiation is not a number') if exponent == 0: - descriptor_number= DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) - descriptor_number.name=descriptor_number.unique_name - return descriptor_number + descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_full_value) + descriptor_number.name = descriptor_number.unique_name + return descriptor_number elif exponent < 0: if self.min < 0 and self.max > 0: combinations = [-np.inf, np.inf] elif self.min == 0: - combinations = [self.max ** exponent, np.inf] + combinations = [self.max**exponent, np.inf] elif self.max == 0: - combinations = [-np.inf, self.min ** exponent] + combinations = [-np.inf, self.min**exponent] else: - combinations = [self.min ** exponent, self.max ** exponent] + combinations = [self.min**exponent, self.max**exponent] else: - combinations = [self.min ** exponent, self.max ** exponent] + combinations = [self.min**exponent, self.max**exponent] if exponent % 2 == 0: if self.min < 0 and self.max > 0: combinations.append(0) @@ -698,18 +706,18 @@ def __pow__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter: combinations = [combination for combination in combinations if combination >= 0] min_value = min(combinations) max_value = max(combinations) - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter - + def __neg__(self) -> Parameter: new_full_value = -self.full_value min_value = -self.max max_value = -self.min - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter - + def __abs__(self) -> Parameter: new_full_value = abs(self.full_value) combinations = [abs(self.min), abs(self.max)] @@ -717,6 +725,6 @@ def __abs__(self) -> Parameter: combinations.append(0) min_value = min(combinations) max_value = max(combinations) - parameter=Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) - parameter.name=parameter.unique_name + parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value) + parameter.name = parameter.unique_name return parameter diff --git a/src/easyscience/Objects/virtual.py b/src/easyscience/Objects/virtual.py index b33d8406..eb4e7623 100644 --- a/src/easyscience/Objects/virtual.py +++ b/src/easyscience/Objects/virtual.py @@ -15,7 +15,7 @@ from typing import MutableSequence from easyscience import global_object -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import ObjConstraint if TYPE_CHECKING: from easyscience.Objects.ObjectClasses import BV diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index 8ecf7199..5435b39c 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -16,12 +16,13 @@ import numpy as np +from easyscience.Constraints import ObjConstraint + # causes circular import when Parameter is imported # from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.new_variable import Parameter from ..available_minimizers import AvailableMinimizers -from ..Constraints import ObjConstraint from .utils import FitError from .utils import FitResults @@ -184,14 +185,11 @@ def _prepare_parameters(self, parameters: dict[str, float]) -> dict[str, float]: """ pars = self._cached_pars - # TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter as NewParameter - 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): + if isinstance(item, Parameter): parameters[parameter_name] = item.value else: parameters[parameter_name] = item.raw_value @@ -227,8 +225,6 @@ def _fit_function(x: np.ndarray, **kwargs): """ # Update the `Parameter` values and the callback if needed # TODO THIS IS NOT THREAD SAFE :-( - # TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter for name, value in kwargs.items(): par_name = name[1:] @@ -265,12 +261,9 @@ def _create_signature(parameters: Dict[int, Parameter]) -> Signature: wrapped_parameters = [] wrapped_parameters.append(InspectParameter('x', InspectParameter.POSITIONAL_OR_KEYWORD, annotation=_empty)) - ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter as NewParameter - for name, parameter in parameters.items(): ## TODO clean when full move to new_variable - if isinstance(parameter, NewParameter): + if isinstance(parameter, Parameter): default_value = parameter.value else: default_value = parameter.raw_value diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index c6a7aab9..a2142584 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -16,7 +16,7 @@ # causes circular import when Parameter is imported # from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.new_variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX @@ -117,8 +117,6 @@ def fit( self._cached_model = model ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(self._cached_pars[list(self._cached_pars.keys())[0]], Parameter): self._p_0 = {f'p{key}': self._cached_pars[key].value for key in self._cached_pars.keys()} else: @@ -167,8 +165,6 @@ def convert_to_par_object(obj) -> BumpsParameter: """ ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(obj, Parameter): value = obj.value else: @@ -252,8 +248,6 @@ def _gen_fit_results(self, fit_results, **kwargs) -> FitResults: dict_name = name[len(MINIMIZER_PARAMETER_PREFIX) :] ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(pars[dict_name], Parameter): item[name] = pars[dict_name].value else: diff --git a/src/easyscience/fitting/minimizers/minimizer_dfo.py b/src/easyscience/fitting/minimizers/minimizer_dfo.py index c5edd915..7534e2fc 100644 --- a/src/easyscience/fitting/minimizers/minimizer_dfo.py +++ b/src/easyscience/fitting/minimizers/minimizer_dfo.py @@ -12,7 +12,7 @@ # causes circular import when Parameter is imported # from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.new_variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX @@ -99,8 +99,6 @@ def fit( self._cached_model.y = y ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(self._cached_pars[list(self._cached_pars.keys())[0]], Parameter): self._p_0 = {f'p{key}': self._cached_pars[key].value for key in self._cached_pars.keys()} else: @@ -148,19 +146,18 @@ def _make_model(self, parameters: Optional[List[Parameter]] = None) -> Callable: def _outer(obj: DFO): def _make_func(x, y, weights): ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter as NewParameter dfo_pars = {} if not parameters: for name, par in obj._cached_pars.items(): - if isinstance(par, NewParameter): + if isinstance(par, Parameter): dfo_pars[MINIMIZER_PARAMETER_PREFIX + str(name)] = par.value else: dfo_pars[MINIMIZER_PARAMETER_PREFIX + str(name)] = par.raw_value else: for par in parameters: - if isinstance(par, NewParameter): + if isinstance(par, Parameter): dfo_pars[MINIMIZER_PARAMETER_PREFIX + par.unique_name] = par.value else: dfo_pars[MINIMIZER_PARAMETER_PREFIX + par.unique_name] = par.raw_value @@ -221,8 +218,6 @@ def _gen_fit_results(self, fit_results, weights, **kwargs) -> FitResults: pars = {} for p_name, par in self._cached_pars.items(): ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(par, Parameter): pars[f'p{p_name}'] = par.value else: @@ -256,9 +251,7 @@ def _dfo_fit(pars: Dict[str, Parameter], model: Callable, **kwargs): """ ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter as NewParameter - - if isinstance(list(pars.values())[0], NewParameter): + if isinstance(list(pars.values())[0], Parameter): pars_values = np.array([par.value for par in pars.values()]) else: pars_values = np.array([par.raw_value for par in pars.values()]) diff --git a/src/easyscience/fitting/minimizers/minimizer_lmfit.py b/src/easyscience/fitting/minimizers/minimizer_lmfit.py index 9a8cfadb..8550ffc0 100644 --- a/src/easyscience/fitting/minimizers/minimizer_lmfit.py +++ b/src/easyscience/fitting/minimizers/minimizer_lmfit.py @@ -15,7 +15,7 @@ # causes circular import when Parameter is imported # from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.new_variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX @@ -165,9 +165,7 @@ def convert_to_par_object(parameter: Parameter) -> LMParameter: :rtype: LMParameter """ ## TODO clean when full move to - from easyscience.Objects.new_variable import Parameter as NewParameter - - if isinstance(parameter, NewParameter): + if isinstance(parameter, Parameter): value = parameter.value else: value = parameter.raw_value @@ -208,8 +206,6 @@ def _make_model(self, pars: Optional[LMParameters] = None) -> LMModel: value = item.value else: ## TODO clean when full move to new_variable - from easyscience.Objects.new_variable import Parameter - if isinstance(item, Parameter): value = item.value else: diff --git a/tests/integration_tests/Fitting/test_fitter.py b/tests/integration_tests/Fitting/test_fitter.py index c1e1b253..a3e12519 100644 --- a/tests/integration_tests/Fitting/test_fitter.py +++ b/tests/integration_tests/Fitting/test_fitter.py @@ -8,7 +8,7 @@ import pytest import numpy as np -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import ObjConstraint from easyscience.fitting.fitter import Fitter from easyscience.fitting.minimizers import FitError from easyscience.fitting.available_minimizers import AvailableMinimizers diff --git a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py b/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py index 2055e45e..f9554062 100644 --- a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py +++ b/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py @@ -8,7 +8,7 @@ import pytest import numpy as np -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import ObjConstraint from easyscience.fitting.fitter import Fitter from easyscience.fitting.minimizers import FitError from easyscience.fitting.available_minimizers import AvailableMinimizers diff --git a/tests/integration_tests/Fitting/test_multi_fitter.py b/tests/integration_tests/Fitting/test_multi_fitter.py index 66092dce..ba59f953 100644 --- a/tests/integration_tests/Fitting/test_multi_fitter.py +++ b/tests/integration_tests/Fitting/test_multi_fitter.py @@ -8,7 +8,7 @@ import pytest import numpy as np -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import ObjConstraint from easyscience.fitting.multi_fitter import MultiFitter from easyscience.fitting.minimizers import FitError from easyscience.Objects.ObjectClasses import BaseObj diff --git a/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py b/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py index ed869302..0f9d70a2 100644 --- a/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py +++ b/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py @@ -8,7 +8,7 @@ import pytest import numpy as np -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import ObjConstraint from easyscience.fitting.multi_fitter import MultiFitter from easyscience.fitting.minimizers import FitError from easyscience.Objects.ObjectClasses import BaseObj diff --git a/tests/unit_tests/Fitting/test_constraints.py b/tests/unit_tests/Fitting/test_constraints.py index b20247e5..566da085 100644 --- a/tests/unit_tests/Fitting/test_constraints.py +++ b/tests/unit_tests/Fitting/test_constraints.py @@ -11,8 +11,8 @@ import pytest from unittest.mock import MagicMock -from easyscience.fitting.Constraints import NumericConstraint -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import NumericConstraint +from easyscience.Constraints import ObjConstraint from easyscience.Objects.new_variable.parameter import Parameter diff --git a/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py b/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py index b33f4dfb..b20cd0f2 100644 --- a/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py +++ b/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py @@ -10,8 +10,8 @@ import pytest -from easyscience.fitting.Constraints import NumericConstraint -from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Constraints import NumericConstraint +from easyscience.Constraints import ObjConstraint from easyscience.Objects.Variable import Parameter diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_base.py b/tests/unit_tests/Objects/new_variable/test_descriptor_base.py index 346a383d..7a93b4f2 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_base.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_base.py @@ -53,7 +53,7 @@ def test_init(self, descriptor: DescriptorBase): assert descriptor._description == "description" assert descriptor._url == "url" assert descriptor._display_name == "display_name" - assert len(descriptor._global_object.map.created_objs) - self.objs_before_new_descriptor == 1 + assert len(global_object.map.created_objs) - self.objs_before_new_descriptor == 1 def test_display_name(self, descriptor: DescriptorBase): @@ -119,15 +119,15 @@ def test_url_setter_type_error(self, descriptor: DescriptorBase, url): def test_set_display_name_without_global_object_stack(self, descriptor: DescriptorBase, stack_enabled, stack_elements): # When descriptor.__repr__ = lambda x: "DescriptorBase" - descriptor._global_object.stack.clear() - descriptor._global_object.stack._enabled = stack_enabled + global_object.stack.clear() + global_object.stack._enabled = stack_enabled # Then descriptor.display_name = "new_display_name" # Expect assert descriptor.display_name == "new_display_name" - assert len(descriptor._global_object.stack.history) == stack_elements + assert len(global_object.stack.history) == stack_elements def test_copy(self, descriptor: DescriptorBase): # When Then diff --git a/tests/unit_tests/Objects/test_Groups.py b/tests/unit_tests/Objects/test_Groups.py index 31297729..eb9ce042 100644 --- a/tests/unit_tests/Objects/test_Groups.py +++ b/tests/unit_tests/Objects/test_Groups.py @@ -399,7 +399,7 @@ def test_baseCollection_constraints(cls): p1 = Parameter("p1", 1) p2 = Parameter("p2", 2) - from easyscience.fitting.Constraints import ObjConstraint + from easyscience.Constraints import ObjConstraint p2.user_constraints["testing"] = ObjConstraint(p2, "2*", p1) From 170dbeae81d5bb8c49c96d366a1faf0fdce1df85 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 17 Sep 2024 08:29:18 +0200 Subject: [PATCH 19/26] AvailableMinimizers in init of repo root --- src/easyscience/__init__.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/easyscience/__init__.py b/src/easyscience/__init__.py index b658a714..7e7c6c7b 100644 --- a/src/easyscience/__init__.py +++ b/src/easyscience/__init__.py @@ -1,23 +1,26 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project Date: Tue, 17 Sep 2024 08:58:11 +0200 Subject: [PATCH 20/26] fix test --- tests/unit_tests/utils/io_tests/test_core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/utils/io_tests/test_core.py b/tests/unit_tests/utils/io_tests/test_core.py index 9cb58409..fd0153b5 100644 --- a/tests/unit_tests/utils/io_tests/test_core.py +++ b/tests/unit_tests/utils/io_tests/test_core.py @@ -1,6 +1,7 @@ __author__ = "github.com/wardsimon" __version__ = "0.0.1" +import numpy as np from copy import deepcopy from typing import Type @@ -38,8 +39,8 @@ "unit": "km", "value": 1.0, "variance": 0.0, - "min": -easyscience.np.inf, - "max": easyscience.np.inf, + "min": -np.inf, + "max": np.inf, "fixed": False, "url": "https://www.boo.com", "description": "", From af50b6704c67272b81d565cc92d51b47f478434f Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 17 Sep 2024 12:50:59 +0200 Subject: [PATCH 21/26] typo --- src/easyscience/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/easyscience/__init__.py b/src/easyscience/__init__.py index 7e7c6c7b..4913e0a9 100644 --- a/src/easyscience/__init__.py +++ b/src/easyscience/__init__.py @@ -12,11 +12,11 @@ from .__version__ import __version__ as __version__ # noqa: E402 -from .fitting.available_minimizers import AvailableMinimizer # noqa: E402 +from .fitting.available_minimizers import AvailableMinimizers # noqa: E402 __all__ = [ __version__, - AvailableMinimizer, + AvailableMinimizers, global_object, ] From a521e7649de39bf2ceb3c02609b537efc153cafb Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Tue, 17 Sep 2024 14:54:37 +0200 Subject: [PATCH 22/26] assume minimizer modules are always available --- src/easyscience/fitting/available_minimizers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/easyscience/fitting/available_minimizers.py b/src/easyscience/fitting/available_minimizers.py index 9567317d..2a79b29f 100644 --- a/src/easyscience/fitting/available_minimizers.py +++ b/src/easyscience/fitting/available_minimizers.py @@ -4,7 +4,7 @@ import pkg_resources -installed_packages = {pkg.key for pkg in pkg_resources.working_set} +installed_packages = {pkg.key.lower() for pkg in pkg_resources.working_set} # Change to importlib.metadata when Python 3.10 is the minimum version # import importlib.metadata @@ -32,6 +32,11 @@ warnings.warn('DFO minimization is not available. Probably dfols has not been installed.', ImportWarning, stacklevel=2) +lmfit_engine_available = True +bumps_engine_available = True +dfo_engine_available = True + + @dataclass class AvailableMinimizer: package: str From ee998dfe443be45e7c0cf4a0aa1cd55e44ef84d7 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Wed, 18 Sep 2024 06:26:03 +0200 Subject: [PATCH 23/26] moved back to try except structure to determine installed minimizer modules --- .../fitting/available_minimizers.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/easyscience/fitting/available_minimizers.py b/src/easyscience/fitting/available_minimizers.py index 2a79b29f..1022902f 100644 --- a/src/easyscience/fitting/available_minimizers.py +++ b/src/easyscience/fitting/available_minimizers.py @@ -11,32 +11,30 @@ # installed_packages = [x.name for x in importlib.metadata.distributions()] lmfit_engine_available = False -if 'lmfit' in installed_packages: +try: + import lmfit # noqa: F401 lmfit_engine_available = True -else: +except ImportError: # TODO make this a proper message (use logging?) warnings.warn('LMFit minimization is not available. Probably lmfit has not been installed.', ImportWarning, stacklevel=2) bumps_engine_available = False -if 'bumps' in installed_packages: +try: + import bumps # noqa: F401 bumps_engine_available = True -else: +except ImportError: # TODO make this a proper message (use logging?) warnings.warn('Bumps minimization is not available. Probably bumps has not been installed.', ImportWarning, stacklevel=2) dfo_engine_available = False -if 'dfo-ls' in installed_packages: +try: + import dfols # noqa: F401 dfo_engine_available = True -else: +except ImportError: # TODO make this a proper message (use logging?) warnings.warn('DFO minimization is not available. Probably dfols has not been installed.', ImportWarning, stacklevel=2) -lmfit_engine_available = True -bumps_engine_available = True -dfo_engine_available = True - - @dataclass class AvailableMinimizer: package: str From 19bfdcb4fa25c9ca64c26ab7add0dbeb5ee46d79 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Wed, 18 Sep 2024 13:39:31 +0200 Subject: [PATCH 24/26] PR response --- src/easyscience/Utils/classTools.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/easyscience/Utils/classTools.py b/src/easyscience/Utils/classTools.py index 056998f9..382abe4d 100644 --- a/src/easyscience/Utils/classTools.py +++ b/src/easyscience/Utils/classTools.py @@ -62,10 +62,9 @@ def generatePath(model_obj: B, skip_first: bool = False) -> Tuple[List[int], Lis unique_names = [] names = [] for par in pars: - par_unique_name = par.unique_name - route = global_object.map.reverse_route(par_unique_name, model_obj.unique_name) + route = global_object.map.reverse_route(par.unique_name, model_obj.unique_name) objs = [getattr(global_object.map.get_item_by_key(r), 'name') for r in route] objs.reverse() names.append('.'.join(objs[start_idx:])) - unique_names.append(par_unique_name) + unique_names.append(par.unique_name) return unique_names, names From cf374bc96cfd302380d57720a36c5ff716b9e47a Mon Sep 17 00:00:00 2001 From: Andreas Pedersen Date: Wed, 18 Sep 2024 13:42:34 +0200 Subject: [PATCH 25/26] PR response --- src/easyscience/fitting/available_minimizers.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/easyscience/fitting/available_minimizers.py b/src/easyscience/fitting/available_minimizers.py index 1022902f..2d1fb717 100644 --- a/src/easyscience/fitting/available_minimizers.py +++ b/src/easyscience/fitting/available_minimizers.py @@ -2,10 +2,6 @@ from dataclasses import dataclass from enum import Enum -import pkg_resources - -installed_packages = {pkg.key.lower() for pkg in pkg_resources.working_set} - # Change to importlib.metadata when Python 3.10 is the minimum version # import importlib.metadata # installed_packages = [x.name for x in importlib.metadata.distributions()] @@ -13,6 +9,7 @@ lmfit_engine_available = False try: import lmfit # noqa: F401 + lmfit_engine_available = True except ImportError: # TODO make this a proper message (use logging?) @@ -21,6 +18,7 @@ bumps_engine_available = False try: import bumps # noqa: F401 + bumps_engine_available = True except ImportError: # TODO make this a proper message (use logging?) @@ -29,6 +27,7 @@ dfo_engine_available = False try: import dfols # noqa: F401 + dfo_engine_available = True except ImportError: # TODO make this a proper message (use logging?) From 148cfe429042ba9ab929774b9f89510b1ff83ce6 Mon Sep 17 00:00:00 2001 From: Andreas Pedersen <48797331+andped10@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:58:15 +0200 Subject: [PATCH 26/26] PR response Co-authored-by: henrikjacobsenfys --- src/easyscience/Objects/new_variable/descriptor_base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/easyscience/Objects/new_variable/descriptor_base.py b/src/easyscience/Objects/new_variable/descriptor_base.py index 4c9ea678..9f144f8f 100644 --- a/src/easyscience/Objects/new_variable/descriptor_base.py +++ b/src/easyscience/Objects/new_variable/descriptor_base.py @@ -27,7 +27,6 @@ class DescriptorBase(ComponentSerializer, metaclass=abc.ABCMeta): # Used by serializer _REDIRECT = {'parent': None} - # _global_object = global_object def __init__( self,