From deb64469ab54ab2c0db745b47b004a510e9e0d73 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Thu, 2 Jan 2025 15:28:37 +0100 Subject: [PATCH 01/41] Update test to new_parameter First step to remove old parameter --- tests/unit_tests/Objects/test_BaseObj.py | 14 +- .../Objects/test_BaseObj_legacy_parameter.py | 503 ------------------ 2 files changed, 7 insertions(+), 510 deletions(-) delete mode 100644 tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py diff --git a/tests/unit_tests/Objects/test_BaseObj.py b/tests/unit_tests/Objects/test_BaseObj.py index c545e6bc..126479da 100644 --- a/tests/unit_tests/Objects/test_BaseObj.py +++ b/tests/unit_tests/Objects/test_BaseObj.py @@ -357,7 +357,7 @@ def test_subclassing(): from typing import ClassVar from easyscience.models.polynomial import Line - from easyscience.Objects.Variable import Parameter + from easyscience.Objects.new_variable import Parameter class L2(Line): diff: ClassVar[Parameter] @@ -375,23 +375,23 @@ def from_pars(cls, m, c, diff): return cls(m, c, diff) def __call__(self, *args, **kwargs): - return super(L2, self).__call__(*args, **kwargs) + self.diff.raw_value + return super(L2, self).__call__(*args, **kwargs) + self.diff.value l2 = L2.from_pars(1, 2, 3) - assert l2.m.raw_value == 1 - assert l2.c.raw_value == 2 - assert l2.diff.raw_value == 3 + assert l2.m.value == 1 + assert l2.c.value == 2 + assert l2.diff.value == 3 l2.diff = 4 assert isinstance(l2.diff, Parameter) - assert l2.diff.raw_value == 4 + assert l2.diff.value == 4 l2.foo = "foo" assert l2.foo == "foo" x = np.linspace(0, 10, 100) - y = l2.m.raw_value * x + l2.c.raw_value + l2.diff.raw_value + y = l2.m.value * x + l2.c.value + l2.diff.value assert np.allclose(l2(x), y) diff --git a/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py b/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py deleted file mode 100644 index 5fdb087e..00000000 --- a/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py +++ /dev/null @@ -1,503 +0,0 @@ -__author__ = "github.com/wardsimon" -__version__ = "0.1.0" - -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project Date: Thu, 2 Jan 2025 16:05:52 +0100 Subject: [PATCH 02/41] Add check if parameter is a number --- .../Objects/new_variable/parameter.py | 4 +- src/easyscience/models/polynomial.py | 14 +- .../Objects/test_BaseObj_legacy_parameter.py | 503 ++++++++++++++++++ 3 files changed, 513 insertions(+), 8 deletions(-) create mode 100644 tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py diff --git a/src/easyscience/Objects/new_variable/parameter.py b/src/easyscience/Objects/new_variable/parameter.py index c2c21793..e60c2dde 100644 --- a/src/easyscience/Objects/new_variable/parameter.py +++ b/src/easyscience/Objects/new_variable/parameter.py @@ -81,7 +81,9 @@ def __init__( if not isinstance(min, numbers.Number): raise TypeError('`min` must be a number') if not isinstance(max, numbers.Number): - raise TypeError('`max` must be a number') + raise TypeError('`max` must be a number') + if not isinstance(value, numbers.Number): + raise TypeError('`value` must be a number') if value < min: raise ValueError(f'{value=} can not be less than {min=}') if value > max: diff --git a/src/easyscience/models/polynomial.py b/src/easyscience/models/polynomial.py index 4086e80c..76689c01 100644 --- a/src/easyscience/models/polynomial.py +++ b/src/easyscience/models/polynomial.py @@ -23,7 +23,7 @@ def designate_calc_fn(func): @functools.wraps(func) def wrapper(obj, *args, **kwargs): for name in list(obj.__annotations__.keys()): - func.__globals__['_' + name] = getattr(obj, name).raw_value + func.__globals__['_' + name] = getattr(obj, name).value return func(obj, *args, **kwargs) return wrapper @@ -64,16 +64,16 @@ def __init__( raise TypeError('coefficients must be a list or a BaseCollection') def __call__(self, x: np.ndarray, *args, **kwargs) -> np.ndarray: - return np.polyval([c.raw_value for c in self.coefficients], x) + return np.polyval([c.value for c in self.coefficients], x) def __repr__(self): s = [] if len(self.coefficients) >= 1: - s += [f'{self.coefficients[0].raw_value}'] + s += [f'{self.coefficients[0].value}'] if len(self.coefficients) >= 2: - s += [f'{self.coefficients[1].raw_value}x'] + s += [f'{self.coefficients[1].value}x'] if len(self.coefficients) >= 3: - s += [f'{c.raw_value}x^{i+2}' for i, c in enumerate(self.coefficients[2:]) if c.raw_value != 0] + s += [f'{c.value}x^{i+2}' for i, c in enumerate(self.coefficients[2:]) if c.value != 0] s.reverse() s = ' + '.join(s) return 'Polynomial({}, {})'.format(self.name, s) @@ -94,9 +94,9 @@ def __init__( if c is not None: self.c = c - # @designate_calc_fn can be used to inject parameters into the calculation function. i.e. _m = m.raw_value + # @designate_calc_fn can be used to inject parameters into the calculation function. i.e. _m = m.value def __call__(self, x: np.ndarray, *args, **kwargs) -> np.ndarray: - return self.m.raw_value * x + self.c.raw_value + return self.m.value * x + self.c.value def __repr__(self): return '{}({}, {})'.format(self.__class__.__name__, self.m, self.c) diff --git a/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py b/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py new file mode 100644 index 00000000..5fdb087e --- /dev/null +++ b/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py @@ -0,0 +1,503 @@ +__author__ = "github.com/wardsimon" +__version__ = "0.1.0" + +# SPDX-FileCopyrightText: 2023 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +# © 2021-2023 Contributors to the EasyScience project Date: Fri, 3 Jan 2025 15:01:28 +0100 Subject: [PATCH 03/41] Remove old Variable, move new_variable to variable --- src/easyscience/Constraints.py | 22 +++++++++---------- src/easyscience/Objects/Groups.py | 2 +- src/easyscience/Objects/Inferface.py | 4 ++-- src/easyscience/Objects/ObjectClasses.py | 4 ++-- .../Objects/{Variable.py => old_Variable.py} | 0 .../{new_variable => variable}/__init__.py | 0 .../descriptor_base.py | 0 .../descriptor_bool.py | 0 .../descriptor_number.py | 0 .../descriptor_str.py | 0 .../{new_variable => variable}/parameter.py | 0 .../fitting/minimizers/minimizer_base.py | 2 +- .../fitting/minimizers/minimizer_bumps.py | 2 +- .../fitting/minimizers/minimizer_dfo.py | 2 +- .../fitting/minimizers/minimizer_lmfit.py | 2 +- .../integration_tests/Fitting/test_fitter.py | 2 +- .../Fitting/test_multi_fitter.py | 2 +- .../Fitting/minimizers/test_minimizer_base.py | 2 +- .../Fitting/minimizers/test_minimizer_dfo.py | 2 +- .../minimizers/test_minimizer_lmfit.py | 2 +- tests/unit_tests/Fitting/test_constraints.py | 2 +- .../new_variable/test_descriptor_base.py | 2 +- .../new_variable/test_descriptor_bool.py | 2 +- .../test_descriptor_from_legacy.py | 6 ++--- .../new_variable/test_descriptor_number.py | 2 +- .../new_variable/test_descriptor_str.py | 2 +- .../Objects/new_variable/test_parameter.py | 4 ++-- .../test_parameter_from_legacy.py | 2 +- tests/unit_tests/Objects/test_BaseObj.py | 6 ++--- tests/unit_tests/Objects/test_Groups.py | 4 ++-- .../global_object/test_global_object.py | 2 +- tests/unit_tests/utils/io_tests/test_core.py | 4 ++-- tests/unit_tests/utils/io_tests/test_dict.py | 2 +- tests/unit_tests/utils/io_tests/test_json.py | 2 +- tests/unit_tests/utils/io_tests/test_xml.py | 2 +- 35 files changed, 47 insertions(+), 47 deletions(-) rename src/easyscience/Objects/{Variable.py => old_Variable.py} (100%) rename src/easyscience/Objects/{new_variable => variable}/__init__.py (100%) rename src/easyscience/Objects/{new_variable => variable}/descriptor_base.py (100%) rename src/easyscience/Objects/{new_variable => variable}/descriptor_bool.py (100%) rename src/easyscience/Objects/{new_variable => variable}/descriptor_number.py (100%) rename src/easyscience/Objects/{new_variable => variable}/descriptor_str.py (100%) rename src/easyscience/Objects/{new_variable => variable}/parameter.py (100%) diff --git a/src/easyscience/Constraints.py b/src/easyscience/Constraints.py index 69644085..e181dcf4 100644 --- a/src/easyscience/Constraints.py +++ b/src/easyscience/Constraints.py @@ -195,9 +195,9 @@ def __init__(self, dependent_obj: V, operator: str, value: Number): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter - if isinstance(obj, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): value = obj.value_no_call_back else: value = obj.raw_value @@ -259,9 +259,9 @@ def __init__(self, dependent_obj: V, operator: str, value: str): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter - if isinstance(obj, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): value = obj.value_no_call_back else: value = obj.raw_value @@ -323,9 +323,9 @@ def __init__(self, dependent_obj: V, operator: str, independent_obj: V): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter - if isinstance(obj, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): value = obj.value_no_call_back else: value = obj.raw_value @@ -417,13 +417,13 @@ def __init__( self.external = True def _parse_operator(self, independent_objs: List[V], *args, **kwargs) -> Number: - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter in_str = '' value = None for idx, obj in enumerate(independent_objs): ## TODO clean when full move to new_variable - if isinstance(obj, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.value_no_call_back else: self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.raw_value @@ -485,14 +485,14 @@ def __init__( self.external = True def _parse_operator(self, obj: V, *args, **kwargs) -> Number: - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter self.aeval.symtable[f'f{id(self.function)}'] = self.function value_str = f'r_value = f{id(self.function)}(' if isinstance(obj, list): for o in obj: ## TODO clean when full move to new_variable - if isinstance(o, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(o, easyscience.Objects.variable.parameter.Parameter): value_str += f'{o.value_no_call_back},' else: value_str += f'{o.raw_value},' @@ -500,7 +500,7 @@ def _parse_operator(self, obj: V, *args, **kwargs) -> Number: value_str = value_str[:-1] else: ## TODO clean when full move to new_variable - if isinstance(obj, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): value_str += f'{obj.value_no_call_back}' else: value_str += f'{obj.raw_value}' diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index cbbecb55..20eceba8 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -18,7 +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.variable.descriptor_base import DescriptorBase from easyscience.Objects.ObjectClasses import BasedBase from easyscience.Objects.ObjectClasses import Descriptor diff --git a/src/easyscience/Objects/Inferface.py b/src/easyscience/Objects/Inferface.py index c3e7a7c9..e1d324b7 100644 --- a/src/easyscience/Objects/Inferface.py +++ b/src/easyscience/Objects/Inferface.py @@ -152,7 +152,7 @@ def generate_bindings(self, model, *args, ifun=None, **kwargs): :return: binding property :rtype: property """ - import easyscience.Objects.new_variable.parameter + import easyscience.Objects.variable.parameter class_links = self.__interface_obj.create(model) props = model._get_linkable_attributes() @@ -165,7 +165,7 @@ def generate_bindings(self, model, *args, ifun=None, **kwargs): prop = props[idx] ## TODO clean when full move to new_variable - if isinstance(prop, easyscience.Objects.new_variable.parameter.Parameter): + if isinstance(prop, easyscience.Objects.variable.parameter.Parameter): # Should be fetched this way to ensure we don't get value from callback prop_value = prop.value_no_call_back else: diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 4956c72c..e0acd599 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -22,8 +22,8 @@ from easyscience.Utils.classTools import addLoggedProp from .core import ComponentSerializer -from .new_variable import Parameter as NewParameter -from .new_variable.descriptor_base import DescriptorBase +from .variable import Parameter as NewParameter +from .variable.descriptor_base import DescriptorBase from .Variable import Descriptor from .Variable import Parameter diff --git a/src/easyscience/Objects/Variable.py b/src/easyscience/Objects/old_Variable.py similarity index 100% rename from src/easyscience/Objects/Variable.py rename to src/easyscience/Objects/old_Variable.py diff --git a/src/easyscience/Objects/new_variable/__init__.py b/src/easyscience/Objects/variable/__init__.py similarity index 100% rename from src/easyscience/Objects/new_variable/__init__.py rename to src/easyscience/Objects/variable/__init__.py diff --git a/src/easyscience/Objects/new_variable/descriptor_base.py b/src/easyscience/Objects/variable/descriptor_base.py similarity index 100% rename from src/easyscience/Objects/new_variable/descriptor_base.py rename to src/easyscience/Objects/variable/descriptor_base.py diff --git a/src/easyscience/Objects/new_variable/descriptor_bool.py b/src/easyscience/Objects/variable/descriptor_bool.py similarity index 100% rename from src/easyscience/Objects/new_variable/descriptor_bool.py rename to src/easyscience/Objects/variable/descriptor_bool.py diff --git a/src/easyscience/Objects/new_variable/descriptor_number.py b/src/easyscience/Objects/variable/descriptor_number.py similarity index 100% rename from src/easyscience/Objects/new_variable/descriptor_number.py rename to src/easyscience/Objects/variable/descriptor_number.py diff --git a/src/easyscience/Objects/new_variable/descriptor_str.py b/src/easyscience/Objects/variable/descriptor_str.py similarity index 100% rename from src/easyscience/Objects/new_variable/descriptor_str.py rename to src/easyscience/Objects/variable/descriptor_str.py diff --git a/src/easyscience/Objects/new_variable/parameter.py b/src/easyscience/Objects/variable/parameter.py similarity index 100% rename from src/easyscience/Objects/new_variable/parameter.py rename to src/easyscience/Objects/variable/parameter.py diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index e0bd6771..2d8bb2fa 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -20,7 +20,7 @@ # causes circular import when Parameter is imported # from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter from ..available_minimizers import AvailableMinimizers from .utils import FitError diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index ee9f75f5..ae26a751 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.new_variable import Parameter +from easyscience.Objects.variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX diff --git a/src/easyscience/fitting/minimizers/minimizer_dfo.py b/src/easyscience/fitting/minimizers/minimizer_dfo.py index 8bf09ef5..e45b491f 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.new_variable import Parameter +from easyscience.Objects.variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX diff --git a/src/easyscience/fitting/minimizers/minimizer_lmfit.py b/src/easyscience/fitting/minimizers/minimizer_lmfit.py index a4094ea8..3662f580 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.new_variable import Parameter +from easyscience.Objects.variable import Parameter from ..available_minimizers import AvailableMinimizers from .minimizer_base import MINIMIZER_PARAMETER_PREFIX diff --git a/tests/integration_tests/Fitting/test_fitter.py b/tests/integration_tests/Fitting/test_fitter.py index cf17a79d..19e0f876 100644 --- a/tests/integration_tests/Fitting/test_fitter.py +++ b/tests/integration_tests/Fitting/test_fitter.py @@ -13,7 +13,7 @@ from easyscience.fitting.minimizers import FitError from easyscience.fitting.available_minimizers import AvailableMinimizers from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter class AbsSin(BaseObj): diff --git a/tests/integration_tests/Fitting/test_multi_fitter.py b/tests/integration_tests/Fitting/test_multi_fitter.py index ba59f953..1c1735a0 100644 --- a/tests/integration_tests/Fitting/test_multi_fitter.py +++ b/tests/integration_tests/Fitting/test_multi_fitter.py @@ -12,7 +12,7 @@ from easyscience.fitting.multi_fitter import MultiFitter from easyscience.fitting.minimizers import FitError from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter class Line(BaseObj): diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py index 39993455..d89935a7 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py @@ -8,7 +8,7 @@ from easyscience.fitting.minimizers.minimizer_base import MinimizerBase from easyscience.fitting.minimizers.utils import FitError -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter class TestMinimizerBase(): @pytest.fixture diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py index ae620f11..30006198 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py @@ -4,7 +4,7 @@ import numpy as np import easyscience.fitting.minimizers.minimizer_dfo -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter from easyscience.fitting.minimizers.minimizer_dfo import DFO from easyscience.fitting.minimizers.utils import FitError diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py index e17b7401..f981436e 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_lmfit.py @@ -4,7 +4,7 @@ import easyscience.fitting.minimizers.minimizer_lmfit from easyscience.fitting.minimizers.minimizer_lmfit import LMFit -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter from lmfit import Parameter as LMParameter from easyscience.fitting.minimizers.utils import FitError diff --git a/tests/unit_tests/Fitting/test_constraints.py b/tests/unit_tests/Fitting/test_constraints.py index 566da085..47a5792b 100644 --- a/tests/unit_tests/Fitting/test_constraints.py +++ b/tests/unit_tests/Fitting/test_constraints.py @@ -13,7 +13,7 @@ from easyscience.Constraints import NumericConstraint from easyscience.Constraints import ObjConstraint -from easyscience.Objects.new_variable.parameter import Parameter +from easyscience.Objects.variable.parameter import Parameter @pytest.fixture 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 7a93b4f2..38140a34 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_base.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_base.py @@ -1,7 +1,7 @@ import pytest from easyscience import global_object -from easyscience.Objects.new_variable.descriptor_base import DescriptorBase +from easyscience.Objects.variable.descriptor_base import DescriptorBase class TestDesciptorBase: diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_bool.py b/tests/unit_tests/Objects/new_variable/test_descriptor_bool.py index 0f0e9fa9..63bf484a 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_bool.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_bool.py @@ -1,6 +1,6 @@ import pytest -from easyscience.Objects.new_variable.descriptor_bool import DescriptorBool +from easyscience.Objects.variable.descriptor_bool import DescriptorBool from easyscience import global_object class TestDescriptorBool: diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py b/tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py index d559a709..edccaf5f 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py @@ -12,9 +12,9 @@ import scipp as sc import easyscience -from easyscience.Objects.new_variable import DescriptorBool -from easyscience.Objects.new_variable import DescriptorNumber -from easyscience.Objects.new_variable import DescriptorStr +from easyscience.Objects.variable import DescriptorBool +from easyscience.Objects.variable import DescriptorNumber +from easyscience.Objects.variable import DescriptorStr @pytest.fixture diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_number.py b/tests/unit_tests/Objects/new_variable/test_descriptor_number.py index 75edc6dd..df47a9f6 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_number.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_number.py @@ -3,7 +3,7 @@ import scipp as sc from scipp import UnitError -from easyscience.Objects.new_variable.descriptor_number import DescriptorNumber +from easyscience.Objects.variable.descriptor_number import DescriptorNumber from easyscience import global_object class TestDescriptorNumber: diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_str.py b/tests/unit_tests/Objects/new_variable/test_descriptor_str.py index c087a527..71c50715 100644 --- a/tests/unit_tests/Objects/new_variable/test_descriptor_str.py +++ b/tests/unit_tests/Objects/new_variable/test_descriptor_str.py @@ -1,6 +1,6 @@ import pytest -from easyscience.Objects.new_variable.descriptor_str import DescriptorStr +from easyscience.Objects.variable.descriptor_str import DescriptorStr from easyscience import global_object class TestDescriptorStr: diff --git a/tests/unit_tests/Objects/new_variable/test_parameter.py b/tests/unit_tests/Objects/new_variable/test_parameter.py index f6958dc2..e00350b6 100644 --- a/tests/unit_tests/Objects/new_variable/test_parameter.py +++ b/tests/unit_tests/Objects/new_variable/test_parameter.py @@ -5,8 +5,8 @@ from scipp import UnitError -from easyscience.Objects.new_variable.parameter import Parameter -from easyscience.Objects.new_variable.descriptor_number import DescriptorNumber +from easyscience.Objects.variable.parameter import Parameter +from easyscience.Objects.variable.descriptor_number import DescriptorNumber from easyscience import global_object class TestParameter: diff --git a/tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py b/tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py index 867c103d..f41ff09e 100644 --- a/tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py +++ b/tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py @@ -15,7 +15,7 @@ import easyscience from easyscience.Objects.Variable import CoreSetException -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import Parameter from easyscience.Objects.Variable import global_object diff --git a/tests/unit_tests/Objects/test_BaseObj.py b/tests/unit_tests/Objects/test_BaseObj.py index 126479da..11260e3b 100644 --- a/tests/unit_tests/Objects/test_BaseObj.py +++ b/tests/unit_tests/Objects/test_BaseObj.py @@ -19,8 +19,8 @@ import easyscience from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import DescriptorNumber -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import DescriptorNumber +from easyscience.Objects.variable import Parameter from easyscience.Utils.io.dict import DictSerializer from easyscience import global_object @@ -357,7 +357,7 @@ def test_subclassing(): from typing import ClassVar from easyscience.models.polynomial import Line - from easyscience.Objects.new_variable import Parameter + from easyscience.Objects.variable import Parameter class L2(Line): diff: ClassVar[Parameter] diff --git a/tests/unit_tests/Objects/test_Groups.py b/tests/unit_tests/Objects/test_Groups.py index eb9ce042..e61d5e70 100644 --- a/tests/unit_tests/Objects/test_Groups.py +++ b/tests/unit_tests/Objects/test_Groups.py @@ -12,8 +12,8 @@ import easyscience from easyscience.Objects.Groups import BaseCollection from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import DescriptorNumber -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import DescriptorNumber +from easyscience.Objects.variable import Parameter from easyscience import global_object test_dict = { diff --git a/tests/unit_tests/global_object/test_global_object.py b/tests/unit_tests/global_object/test_global_object.py index d52c817a..2997b523 100644 --- a/tests/unit_tests/global_object/test_global_object.py +++ b/tests/unit_tests/global_object/test_global_object.py @@ -1,6 +1,6 @@ import easyscience from easyscience.global_object.global_object import GlobalObject -from easyscience.Objects.new_variable.descriptor_bool import DescriptorBool +from easyscience.Objects.variable.descriptor_bool import DescriptorBool class TestGlobalObject: def test_init(self): diff --git a/tests/unit_tests/utils/io_tests/test_core.py b/tests/unit_tests/utils/io_tests/test_core.py index c49f5f1d..3e87d539 100644 --- a/tests/unit_tests/utils/io_tests/test_core.py +++ b/tests/unit_tests/utils/io_tests/test_core.py @@ -9,8 +9,8 @@ import easyscience from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.new_variable import DescriptorNumber -from easyscience.Objects.new_variable import Parameter +from easyscience.Objects.variable import DescriptorNumber +from easyscience.Objects.variable import Parameter dp_param_dict = { "argnames": "dp_kwargs, dp_cls", diff --git a/tests/unit_tests/utils/io_tests/test_dict.py b/tests/unit_tests/utils/io_tests/test_dict.py index 6a456581..fa54fcbb 100644 --- a/tests/unit_tests/utils/io_tests/test_dict.py +++ b/tests/unit_tests/utils/io_tests/test_dict.py @@ -10,7 +10,7 @@ from easyscience.Utils.io.dict import DataDictSerializer from easyscience.Utils.io.dict import DictSerializer -from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.variable import DescriptorNumber from easyscience.Objects.ObjectClasses import BaseObj from .test_core import A diff --git a/tests/unit_tests/utils/io_tests/test_json.py b/tests/unit_tests/utils/io_tests/test_json.py index e5d25c03..cec6e4c0 100644 --- a/tests/unit_tests/utils/io_tests/test_json.py +++ b/tests/unit_tests/utils/io_tests/test_json.py @@ -9,7 +9,7 @@ from easyscience.Utils.io.json import JsonDataSerializer from easyscience.Utils.io.json import JsonSerializer -from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.variable import DescriptorNumber from .test_core import A from .test_core import check_dict diff --git a/tests/unit_tests/utils/io_tests/test_xml.py b/tests/unit_tests/utils/io_tests/test_xml.py index 9f908a99..2edb761e 100644 --- a/tests/unit_tests/utils/io_tests/test_xml.py +++ b/tests/unit_tests/utils/io_tests/test_xml.py @@ -9,7 +9,7 @@ import pytest from easyscience.Utils.io.xml import XMLSerializer -from easyscience.Objects.new_variable import DescriptorNumber +from easyscience.Objects.variable import DescriptorNumber from .test_core import A from .test_core import dp_param_dict From 1e1b7af20a34397459f3d6d3778de9475a250d6b Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:29:03 +0100 Subject: [PATCH 04/41] fixed _get_linkable_attributes and get_fit_parameters --- src/easyscience/Objects/ObjectClasses.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index e0acd599..4046cfa7 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -22,15 +22,17 @@ from easyscience.Utils.classTools import addLoggedProp from .core import ComponentSerializer -from .variable import Parameter as NewParameter +from .variable import Parameter from .variable.descriptor_base import DescriptorBase -from .Variable import Descriptor -from .Variable import Parameter + +# from .Variable import Descriptor +# from .Variable import Parameter if TYPE_CHECKING: from easyscience.Constraints import C from easyscience.Objects.Inferface import iF - from easyscience.Objects.Variable import V + V = TypeVar('V', bound=DescriptorBase) + # from easyscience.Objects.Variable import V class BasedBase(ComponentSerializer): @@ -175,7 +177,7 @@ def get_parameters(self) -> Union[List[Parameter], List[NewParameter]]: par_list.append(item) return par_list - ## TODO clean when full move to new_variable + ## TODO clean when full move to new_variable. Should he fixed def _get_linkable_attributes(self) -> List[V]: """ Get all objects which can be linked against as a list. @@ -186,12 +188,12 @@ 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, DescriptorBase)): + elif issubclass(type(item), (DescriptorBase)): item_list.append(item) return item_list - ## TODO clean when full move to new_variable - def get_fit_parameters(self) -> Union[List[Parameter], List[NewParameter]]: + ## TODO clean when full move to new_variable. Should be fixed + def get_fit_parameters(self) -> List[Parameter]: """ Get all objects which can be fitted (and are not fixed) as a list. @@ -201,7 +203,7 @@ def get_fit_parameters(self) -> Union[List[Parameter], List[NewParameter]]: for key, item in self._kwargs.items(): if hasattr(item, 'get_fit_parameters'): fit_list = [*fit_list, *item.get_fit_parameters()] - elif isinstance(item, Parameter) or isinstance(item, NewParameter): + elif isinstance(item, Parameter): if item.enabled and not item.fixed: fit_list.append(item) return fit_list From b5e59e32586f978af83b687cb27ba3627eccbe28 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:30:26 +0100 Subject: [PATCH 05/41] Fx __init__ --- src/easyscience/Objects/ObjectClasses.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 4046cfa7..798e288f 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -177,7 +177,7 @@ def get_parameters(self) -> Union[List[Parameter], List[NewParameter]]: par_list.append(item) return par_list - ## TODO clean when full move to new_variable. Should he fixed + ## TODO clean when full move to new_variable. Is fixed def _get_linkable_attributes(self) -> List[V]: """ Get all objects which can be linked against as a list. @@ -192,7 +192,7 @@ def _get_linkable_attributes(self) -> List[V]: item_list.append(item) return item_list - ## TODO clean when full move to new_variable. Should be fixed + ## TODO clean when full move to new_variable. Is fixed def get_fit_parameters(self) -> List[Parameter]: """ Get all objects which can be fitted (and are not fixed) as a list. @@ -237,7 +237,7 @@ class BaseObj(BasedBase): cheat with `BaseObj(*[Descriptor(...), Parameter(...), ...])`. """ - ## TODO clean when full move to new_variable + ## TODO clean when full move to new_variable. Is fixed def __init__( self, name: str, @@ -255,7 +255,7 @@ def __init__( super(BaseObj, self).__init__(name=name, unique_name=unique_name) # If Parameter or Descriptor is given as arguments... for arg in args: - if issubclass(type(arg), (BaseObj, Descriptor, DescriptorBase)): + if issubclass(type(arg), (BaseObj, DescriptorBase)): kwargs[getattr(arg, 'name')] = arg # Set kwargs, also useful for serialization known_keys = self.__dict__.keys() @@ -263,7 +263,7 @@ def __init__( for key in kwargs.keys(): if key in known_keys: raise AttributeError('Kwargs cannot overwrite class attributes in BaseObj.') - if issubclass(type(kwargs[key]), (BasedBase, Descriptor, DescriptorBase)) or 'BaseCollection' in [ + if issubclass(type(kwargs[key]), (BasedBase, DescriptorBase)) or 'BaseCollection' in [ c.__name__ for c in type(kwargs[key]).__bases__ ]: self._global_object.map.add_edge(self, kwargs[key]) From 2b553f336c7669aaecf3e22265f7985a2be9eec6 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:31:03 +0100 Subject: [PATCH 06/41] fix __setattr__ --- src/easyscience/Objects/ObjectClasses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 798e288f..9fd18a3f 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -312,7 +312,7 @@ def __init__(self, foo: Parameter, bar: Parameter): test_class=BaseObj, ) - ## TODO clean when full move to new_variable + ## TODO clean when full move to new_variable. Is fixed def __setattr__(self, key: str, value: BV) -> None: # Assume that the annotation is a ClassVar old_obj = None @@ -325,12 +325,12 @@ def __setattr__(self, key: str, value: BV) -> None: self.__class__.__annotations__[key].__args__, ) ): - if issubclass(type(getattr(self, key, None)), (BasedBase, Descriptor, DescriptorBase)): + if issubclass(type(getattr(self, key, None)), (BasedBase, DescriptorBase)): old_obj = self.__getattribute__(key) self._global_object.map.prune_vertex_from_edge(self, old_obj) self._add_component(key, value) else: - if hasattr(self, key) and issubclass(type(value), (BasedBase, Descriptor, DescriptorBase)): + if hasattr(self, key) and issubclass(type(value), (BasedBase, DescriptorBase)): old_obj = self.__getattribute__(key) self._global_object.map.prune_vertex_from_edge(self, old_obj) self._global_object.map.add_edge(self, value) From 5c3dd604b88a268dc97adb920378d3b9f07222af Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:32:22 +0100 Subject: [PATCH 07/41] Fixed get_parameters --- src/easyscience/Objects/ObjectClasses.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 9fd18a3f..13fa094f 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -163,7 +163,7 @@ def constraints(self) -> List[C]: return constraints ## TODO clean when full move to new_variable - def get_parameters(self) -> Union[List[Parameter], List[NewParameter]]: + def get_parameters(self) -> List[Parameter]: """ Get all parameter objects as a list. @@ -173,7 +173,7 @@ def get_parameters(self) -> Union[List[Parameter], List[NewParameter]]: for key, item in self._kwargs.items(): if hasattr(item, 'get_parameters'): par_list = [*par_list, *item.get_parameters()] - elif isinstance(item, Parameter) or isinstance(item, NewParameter): + elif isinstance(item, Parameter): par_list.append(item) return par_list @@ -354,8 +354,8 @@ 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, DescriptorBase)) and not issubclass( - value.__class__, (Descriptor, DescriptorBase) + if issubclass(obj._kwargs[key].__class__, (DescriptorBase)) and not issubclass( + value.__class__, (DescriptorBase) ): obj._kwargs[key].value = value else: From 99d52e05e280f98417a90dc956c0cfec3cc28050 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:34:03 +0100 Subject: [PATCH 08/41] remove unused imports, ObjectClasses is finished --- src/easyscience/Objects/ObjectClasses.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 13fa094f..8e819cd5 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -16,7 +16,6 @@ from typing import Optional from typing import Set from typing import TypeVar -from typing import Union from easyscience import global_object from easyscience.Utils.classTools import addLoggedProp @@ -25,14 +24,11 @@ from .variable import Parameter from .variable.descriptor_base import DescriptorBase -# from .Variable import Descriptor -# from .Variable import Parameter if TYPE_CHECKING: from easyscience.Constraints import C from easyscience.Objects.Inferface import iF V = TypeVar('V', bound=DescriptorBase) - # from easyscience.Objects.Variable import V class BasedBase(ComponentSerializer): @@ -162,7 +158,7 @@ def constraints(self) -> List[C]: constraints.append(con[key]) return constraints - ## TODO clean when full move to new_variable + ## TODO clean when full move to new_variable. Is fixed def get_parameters(self) -> List[Parameter]: """ Get all parameter objects as a list. From 9ee9dddcf1daf1f541c2286b006ef358159371fb Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:48:23 +0100 Subject: [PATCH 09/41] Update Groups.py to new parameter --- src/easyscience/Objects/Groups.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index 20eceba8..69e3ead5 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 not issubclass(type(item), (DescriptorBase, BasedBase)): raise AttributeError('A collection can only be formed from easyscience objects.') args = _args _kwargs = {} @@ -102,12 +102,12 @@ def insert(self, index: int, value: Union[V, B]) -> None: :param index: Index for EasyScience object to be inserted. :type index: int :param value: Object to be inserted. - :type value: Union[BasedBase, Descriptor] + :type value: Union[BasedBase, DescriptorBase] :return: None :rtype: None """ t_ = type(value) - if issubclass(t_, (BasedBase, Descriptor, DescriptorBase)): + if issubclass(t_, (BasedBase, DescriptorBase)): update_key = list(self._kwargs.keys()) values = list(self._kwargs.values()) # Update the internal dict @@ -124,7 +124,7 @@ def insert(self, index: int, value: Union[V, B]) -> None: def __getitem__(self, idx: Union[int, slice]) -> Union[V, B]: """ - Get an item in the collection based on it's index. + Get an item in the collection based on its index. :param idx: index or slice of the collection. :type idx: Union[int, slice] @@ -168,7 +168,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, Descriptor, DescriptorBase)): + elif issubclass(type(value), (BasedBase, DescriptorBase)): update_key = list(self._kwargs.keys()) values = list(self._kwargs.values()) old_item = values[key] From d4c50d6c79803c675d51c5a6d5e53508c50c7709 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:49:30 +0100 Subject: [PATCH 10/41] fix type checking --- src/easyscience/Objects/Groups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index 69e3ead5..dfe5a177 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -16,16 +16,16 @@ from typing import Optional from typing import Tuple from typing import Union +from typing import TypeVar from easyscience.global_object.undo_redo import NotarizedDict from easyscience.Objects.variable.descriptor_base import DescriptorBase from easyscience.Objects.ObjectClasses import BasedBase -from easyscience.Objects.ObjectClasses import Descriptor if TYPE_CHECKING: from easyscience.Objects.Inferface import iF from easyscience.Objects.ObjectClasses import B - from easyscience.Objects.Variable import V + V = TypeVar('V', bound=DescriptorBase) class BaseCollection(BasedBase, MutableSequence): From 9c297fbc5802af6350e3b6ee4c413484964685c3 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:51:46 +0100 Subject: [PATCH 11/41] Fix test referring to new_variable --- tests/unit_tests/utils/io_tests/test_dict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/utils/io_tests/test_dict.py b/tests/unit_tests/utils/io_tests/test_dict.py index fa54fcbb..6835ebef 100644 --- a/tests/unit_tests/utils/io_tests/test_dict.py +++ b/tests/unit_tests/utils/io_tests/test_dict.py @@ -230,7 +230,7 @@ def __init__(self, a, b, unique_name): "data": [1.0, 2.0, 3.0], }, "a": { - "@module": "easyscience.Objects.new_variable.descriptor_number", + "@module": "easyscience.Objects.variable.descriptor_number", "@class": "DescriptorNumber", "@version": version, "description": "", From f0fbecd06fc19654ef45f2f3670ab1d73542ecdc Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:52:31 +0100 Subject: [PATCH 12/41] Added TODO on outcommented test --- tests/unit_tests/utils/io_tests/test_dict.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit_tests/utils/io_tests/test_dict.py b/tests/unit_tests/utils/io_tests/test_dict.py index 6835ebef..5c68b14d 100644 --- a/tests/unit_tests/utils/io_tests/test_dict.py +++ b/tests/unit_tests/utils/io_tests/test_dict.py @@ -328,6 +328,7 @@ def test_group_encode2(): assert isinstance(d["b"], dict) +#TODO: do we need/want this test? # # @pytest.mark.parametrize(**dp_param_dict) # def test_custom_class_DictSerializer_decode(dp_kwargs: dict, dp_cls: Type[Descriptor]): From 4281e1c1fed57d6aca23ab2c9443d0b6cc1398f5 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 3 Jan 2025 17:55:12 +0100 Subject: [PATCH 13/41] rename tests from new_variable to variable --- .../Objects/{new_variable => variable}/test_descriptor_base.py | 0 .../Objects/{new_variable => variable}/test_descriptor_bool.py | 0 .../{new_variable => variable}/test_descriptor_from_legacy.py | 0 .../Objects/{new_variable => variable}/test_descriptor_number.py | 0 .../Objects/{new_variable => variable}/test_descriptor_str.py | 0 .../Objects/{new_variable => variable}/test_parameter.py | 0 .../{new_variable => variable}/test_parameter_from_legacy.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename tests/unit_tests/Objects/{new_variable => variable}/test_descriptor_base.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_descriptor_bool.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_descriptor_from_legacy.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_descriptor_number.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_descriptor_str.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_parameter.py (100%) rename tests/unit_tests/Objects/{new_variable => variable}/test_parameter_from_legacy.py (100%) diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_base.py b/tests/unit_tests/Objects/variable/test_descriptor_base.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_descriptor_base.py rename to tests/unit_tests/Objects/variable/test_descriptor_base.py diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_bool.py b/tests/unit_tests/Objects/variable/test_descriptor_bool.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_descriptor_bool.py rename to tests/unit_tests/Objects/variable/test_descriptor_bool.py diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py b/tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_descriptor_from_legacy.py rename to tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_number.py b/tests/unit_tests/Objects/variable/test_descriptor_number.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_descriptor_number.py rename to tests/unit_tests/Objects/variable/test_descriptor_number.py diff --git a/tests/unit_tests/Objects/new_variable/test_descriptor_str.py b/tests/unit_tests/Objects/variable/test_descriptor_str.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_descriptor_str.py rename to tests/unit_tests/Objects/variable/test_descriptor_str.py diff --git a/tests/unit_tests/Objects/new_variable/test_parameter.py b/tests/unit_tests/Objects/variable/test_parameter.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_parameter.py rename to tests/unit_tests/Objects/variable/test_parameter.py diff --git a/tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py b/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py similarity index 100% rename from tests/unit_tests/Objects/new_variable/test_parameter_from_legacy.py rename to tests/unit_tests/Objects/variable/test_parameter_from_legacy.py From ad79069a1bf33e55671b254d8f2c5cc0904f7d6b Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Jan 2025 11:43:57 +0100 Subject: [PATCH 14/41] Remove old tests --- .../Fitting/test_fitter_legacy_parameter.py | 335 ------------------ .../test_multi_fitter_legacy_parameter.py | 279 --------------- 2 files changed, 614 deletions(-) delete mode 100644 tests/integration_tests/Fitting/test_fitter_legacy_parameter.py delete mode 100644 tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py diff --git a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py b/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py deleted file mode 100644 index ca10fae9..00000000 --- a/tests/integration_tests/Fitting/test_fitter_legacy_parameter.py +++ /dev/null @@ -1,335 +0,0 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project 0 % This does not work as some methods don't calculate error - assert item1.error == pytest.approx(0, abs=1e-1) - assert item1.raw_value == pytest.approx(item2.raw_value, abs=5e-3) - y_calc_ref = ref_sin(x) - assert result.y_calc == pytest.approx(y_calc_ref, abs=1e-2) - assert result.residual == pytest.approx(sp_sin(x) - y_calc_ref, abs=1e-2) - - -@pytest.mark.parametrize("with_errors", [False, True]) -@pytest.mark.parametrize("fit_engine", [None, AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_basic_fit(fit_engine, with_errors): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - if fit_engine is not None: - try: - f.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - args = [x, y] - kwargs = {} - if with_errors: - kwargs["weights"] = 1 / np.sqrt(y) - result = f.fit(*args, **kwargs) - - if fit_engine is not None: - 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) - - -@pytest.mark.parametrize("fit_engine", [None, AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_basic_max_evaluations(fit_engine): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - if fit_engine is not None: - try: - f.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - args = [x, y] - kwargs = {} - f.max_evaluations = 3 - try: - result = f.fit(*args, **kwargs) - # Result should not be the same as the reference - 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) - except FitError as e: - # DFO throws a different error - assert "Objective has been called MAXFUN times" in str(e) - - -@pytest.mark.parametrize("fit_engine,tolerance", [(None, 10), (AvailableMinimizers.LMFit, 10), (AvailableMinimizers.Bumps, 0.1), (AvailableMinimizers.DFO, 0.1)]) -def test_basic_tolerance(fit_engine, tolerance): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - if fit_engine is not None: - try: - f.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - args = [x, y] - kwargs = {} - f.tolerance = tolerance - result = f.fit(*args, **kwargs) - # Result should not be the same as the reference - 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) - - -@pytest.mark.parametrize("fit_engine", [None, AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_fit_result(fit_engine): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - sp_ref1 = { - f"p{item1.unique_name}": item1.raw_value - for item1, item2 in zip(sp_sin._kwargs.values(), ref_sin._kwargs.values()) - } - sp_ref2 = { - f"p{item1.unique_name}": item2.raw_value - for item1, item2 in zip(sp_sin._kwargs.values(), ref_sin._kwargs.values()) - } - - f = Fitter(sp_sin, sp_sin) - - if fit_engine is not None: - try: - f.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - - result = f.fit(x, y) - check_fit_results(result, sp_sin, ref_sin, x, sp_ref1=sp_ref1, sp_ref2=sp_ref2) - - -@pytest.mark.parametrize("fit_method", ["leastsq", "powell", "cobyla"]) -def test_lmfit_methods(fit_method): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - assert fit_method in f._minimizer.supported_methods() - result = f.fit(x, y, method=fit_method) - check_fit_results(result, sp_sin, ref_sin, x) - - -#@pytest.mark.xfail(reason="known bumps issue") -@pytest.mark.parametrize("fit_method", ["newton", "lm"]) -def test_bumps_methods(fit_method): - ref_sin = AbsSin(0.2, np.pi) - sp_sin = AbsSin(0.354, 3.05) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.offset.fixed = False - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - f.switch_minimizer("Bumps") - assert fit_method in f._minimizer.supported_methods() - result = f.fit(x, y, method=fit_method) - check_fit_results(result, sp_sin, ref_sin, x) - - -@pytest.mark.parametrize("fit_engine", [AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_fit_constraints(fit_engine): - ref_sin = AbsSin(np.pi * 0.45, 0.45 * np.pi * 0.5) - sp_sin = AbsSin(1, 0.5) - - x = np.linspace(0, 5, 200) - y = ref_sin(x) - - sp_sin.phase.fixed = False - - f = Fitter(sp_sin, sp_sin) - - assert len(f.fit_constraints()) == 0 - c = ObjConstraint(sp_sin.offset, "2*", sp_sin.phase) - f.add_fit_constraint(c) - - if fit_engine is not None: - try: - f.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - - result = f.fit(x, y) - check_fit_results(result, sp_sin, ref_sin, x) - assert len(f.fit_constraints()) == 1 - f.remove_fit_constraint(0) - assert len(f.fit_constraints()) == 0 - - -@pytest.mark.parametrize("with_errors", [False, True]) -@pytest.mark.parametrize("fit_engine", [None, AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_2D_vectorized(fit_engine, with_errors): - x = np.linspace(0, 5, 200) - mm = AbsSin2D(0.3, 1.6) - m2 = AbsSin2D( - 0.1, 1.8 - ) # The fit is quite sensitive to the initial values :-( - X, Y = np.meshgrid(x, x) - XY = np.stack((X, Y), axis=2) - ff = Fitter(m2, m2) - if fit_engine is not None: - try: - ff.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - try: - args = [XY, mm(XY)] - kwargs = {"vectorized": True} - if with_errors: - kwargs["weights"] = 1 / np.sqrt(args[1]) - result = ff.fit(*args, **kwargs) - except FitError as e: - if "Unable to allocate" in str(e): - pytest.skip(msg="MemoryError - Matrix too large") - else: - raise e - assert result.n_pars == len(m2.get_fit_parameters()) - assert result.reduced_chi == pytest.approx(0, abs=1.5e-3) - assert result.success - assert np.all(result.x == XY) - y_calc_ref = m2(XY) - assert result.y_calc == pytest.approx(y_calc_ref, abs=1e-2) - assert result.residual == pytest.approx(mm(XY) - y_calc_ref, abs=1e-2) - - -@pytest.mark.parametrize("with_errors", [False, True]) -@pytest.mark.parametrize("fit_engine", [None, AvailableMinimizers.LMFit, AvailableMinimizers.Bumps, AvailableMinimizers.DFO]) -def test_2D_non_vectorized(fit_engine, with_errors): - x = np.linspace(0, 5, 200) - mm = AbsSin2DL(0.3, 1.6) - m2 = AbsSin2DL( - 0.1, 1.8 - ) # The fit is quite sensitive to the initial values :-( - X, Y = np.meshgrid(x, x) - XY = np.stack((X, Y), axis=2) - ff = Fitter(m2, m2) - if fit_engine is not None: - try: - ff.switch_minimizer(fit_engine) - except AttributeError: - pytest.skip(msg=f"{fit_engine} is not installed") - try: - args = [XY, mm(XY.reshape(-1, 2))] - kwargs = {"vectorized": False} - if with_errors: - kwargs["weights"] = 1 / np.sqrt(args[1]) - result = ff.fit(*args, **kwargs) - except FitError as e: - if "Unable to allocate" in str(e): - pytest.skip(msg="MemoryError - Matrix too large") - else: - raise e - assert result.n_pars == len(m2.get_fit_parameters()) - assert result.reduced_chi == pytest.approx(0, abs=1.5e-3) - assert result.success - assert np.all(result.x == XY) - y_calc_ref = m2(XY.reshape(-1, 2)) - assert result.y_calc == pytest.approx(y_calc_ref, abs=1e-2) - assert result.residual == pytest.approx( - mm(XY.reshape(-1, 2)) - y_calc_ref, abs=1e-2 - ) diff --git a/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py b/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py deleted file mode 100644 index 0f9d70a2..00000000 --- a/tests/integration_tests/Fitting/test_multi_fitter_legacy_parameter.py +++ /dev/null @@ -1,279 +0,0 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project Date: Mon, 13 Jan 2025 12:06:28 +0100 Subject: [PATCH 15/41] remove old variable, make ruff happy --- src/easyscience/Objects/ObjectClasses.py | 3 +- src/easyscience/Objects/old_Variable.py | 889 ----------------------- 2 files changed, 1 insertion(+), 891 deletions(-) delete mode 100644 src/easyscience/Objects/old_Variable.py diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 8e819cd5..35c6937e 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -21,10 +21,9 @@ from easyscience.Utils.classTools import addLoggedProp from .core import ComponentSerializer -from .variable import Parameter +from .variable import Parameter from .variable.descriptor_base import DescriptorBase - if TYPE_CHECKING: from easyscience.Constraints import C from easyscience.Objects.Inferface import iF diff --git a/src/easyscience/Objects/old_Variable.py b/src/easyscience/Objects/old_Variable.py deleted file mode 100644 index 1f9bf555..00000000 --- a/src/easyscience/Objects/old_Variable.py +++ /dev/null @@ -1,889 +0,0 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project Set[str]: - base_cls = getattr(self, '__old_class__', self.__class__) - mro = base_cls.__mro__ - idx = mro.index(ComponentSerializer) - names = set() - for i in range(idx): - cls = mro[i] - if hasattr(cls, '_CORE'): - spec = getfullargspec(cls.__init__) - names = names.union(set(spec.args[1:])) - return names - - def __reduce__(self): - """ - Make the class picklable. Due to the nature of the dynamic class definitions special measures need to be taken. - - :return: Tuple consisting of how to make the object - :rtype: tuple - """ - state = self.encode() - cls = self.__class__ - if hasattr(self, '__old_class__'): - cls = self.__old_class__ - return cls.from_dict, (state,) - - @property - def unique_name(self) -> str: - """ - Get the unique name of this object. - - :return: Unique name of this object - """ - return self._unique_name - - @unique_name.setter - def unique_name(self, new_unique_name: str): - """Set a new unique name for the object. The old name is still kept in the map. - - :param new_unique_name: New unique name for the object""" - 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) - - @property - def display_name(self) -> str: - """ - Get a pretty display name. - - :return: The pretty display name. - """ - # TODO This might be better implementing fancy f-strings where we can return html,latex, markdown etc - display_name = self._display_name - if display_name is None: - display_name = self.name - return display_name - - @display_name.setter - @property_stack_deco - def display_name(self, name_str: str): - """ - Set the pretty display name. - - :param name_str: Pretty display name of the object. - :return: None - """ - self._display_name = name_str - - @property - def unit(self) -> pint.UnitRegistry: - """ - Get the unit associated with the object. - - :return: Unit associated with self in `pint` form. - """ - return self._units.units - - @unit.setter - @property_stack_deco - def unit(self, unit_str: str): - """ - Set the unit to a new one. - - :param unit_str: String representation of the unit required. i.e `m/s` - :return: None - """ - if not isinstance(unit_str, str): - unit_str = str(unit_str) - new_unit = ureg.parse_expression(unit_str) - self._units = new_unit - self._args['units'] = str(new_unit) - self._value = self.__class__._constructor(**self._args) - - @property - def value(self) -> Any: - """ - Get the value of self as a pint. This is should be usable for most cases. If a pint - is not acceptable then the raw value can be obtained through `obj.raw_value`. - - :return: Value of self with unit. - """ - # Cached property? Should reference callback. - # Also should reference for undo/redo - if self._callback.fget is not None: - try: - value = self._callback.fget() - if hasattr(self._value, 'magnitude'): - if value != self._value.magnitude: - self.__deepValueSetter(value) - elif value != self._value: - self.__deepValueSetter(value) - - except Exception as e: - raise ValueError(f'Unable to return value:\n{e}') - r_value = self._value - if self.__isBooleanValue: - r_value = bool(r_value) - return r_value - - def __deepValueSetter(self, value: Any): - """ - Set the value of self. This creates a pint with a unit. - - :param value: New value of self - :return: None - """ - # TODO there should be a callback to the collective, logging this as a return(if from a non `EasyScience` class) - if hasattr(value, 'magnitude'): - value = value.magnitude - if hasattr(value, 'nominal_value'): - value = value.nominal_value - self._type = type(value) - self.__isBooleanValue = isinstance(value, bool) - if self.__isBooleanValue: - value = int(value) - self._args['value'] = value - self._value = self.__class__._constructor(**self._args) - - @value.setter - @property_stack_deco - def value(self, value: Any): - """ - Set the value of self. This creates a pint with a unit. - - :param value: New value of self - :return: None - """ - self.__deepValueSetter(value) - if self._callback.fset is not None: - try: - self._callback.fset(value) - except Exception as e: - raise CoreSetException(e) - - @property - def raw_value(self) -> Any: - """ - Return the raw value of self without a unit. - - :return: The raw value of self - """ - value = self._value - if hasattr(value, 'magnitude'): - value = value.magnitude - if hasattr(value, 'nominal_value'): - value = value.nominal_value - if self.__isBooleanValue: - value = bool(value) - return value - - @property - def enabled(self) -> bool: - """ - Logical property to see if the objects value can be directly set. - - :return: Can the objects value be set - """ - return self._enabled - - @enabled.setter - @property_stack_deco - def enabled(self, value: bool): - """ - Enable and disable the direct setting of an objects value field. - - :param value: True - objects value can be set, False - the opposite - """ - self._enabled = value - - def convert_unit(self, unit_str: str): - """ - Convert the value from one unit system to another. You will should use - `compatible_units` to see if your new unit is compatible. - - :param unit_str: New unit in string form - """ - new_unit = ureg.parse_expression(unit_str) - self._value = self._value.to(new_unit) - self._units = new_unit - self._args['value'] = self.raw_value - self._args['units'] = str(self.unit) - - # @cached_property - @property - def compatible_units(self) -> List[str]: - """ - Returns all possible units for which the current unit can be converted. - - :return: Possible conversion units - """ - return [str(u) for u in self.unit.compatible_units()] - - def __repr__(self): - """Return printable representation of a Descriptor/Parameter object.""" - class_name = self.__class__.__name__ - obj_name = self.name - if self.__isBooleanValue: - obj_value = self.raw_value - else: - obj_value = self._value.magnitude - if isinstance(obj_value, float): - obj_value = '{:0.04f}'.format(obj_value) - obj_units = '' - if not self.unit.dimensionless: - obj_units = ' {:~P}'.format(self.unit) - out_str = f"<{class_name} '{obj_name}': {obj_value}{obj_units}>" - return out_str - - def to_obj_type(self, data_type: Type[Parameter], *kwargs): - """ - Convert between a `Parameter` and a `Descriptor`. - - :param data_type: class constructor of what we want to be - :param kwargs: Additional keyword/value pairs for conversion - :return: self as a new type - """ - pickled_obj = self.encode() - pickled_obj.update(kwargs) - if '@class' in pickled_obj.keys(): - pickled_obj['@class'] = data_type.__name__ - return data_type.from_dict(pickled_obj) - - def __copy__(self): - return self.__class__.from_dict(self.as_dict()) - - -V = TypeVar('V', bound=Descriptor) - - -class ComboDescriptor(Descriptor): - """ - This class is an extension of a ``EasyScience.Object.Base.Descriptor``. This class has a selection of values which can - be checked against. For example, combo box styling. - """ - - def __init__(self, *args, available_options: list = None, **kwargs): - super(ComboDescriptor, self).__init__(*args, **kwargs) - if available_options is None: - available_options = [] - self._available_options = available_options - - # We have initialized from the Descriptor class where value has it's own undo/redo decorator - # This needs to be bypassed to use the Parameter undo/redo stack - fun = self.__class__.value.fset - if hasattr(fun, 'func'): - fun = getattr(fun, 'func') - self.__previous_set: Callable[[V, Union[numbers.Number, np.ndarray]], Union[numbers.Number, np.ndarray]] = fun - - # Monkey patch the unit and the value to take into account the new max/min situation - addProp( - self, - 'value', - fget=self.__class__.value.fget, - fset=self.__class__._property_value.fset, - fdel=self.__class__.value.fdel, - ) - - @property - def _property_value(self) -> Union[numbers.Number, np.ndarray]: - return self.value - - @_property_value.setter - @property_stack_deco - def _property_value(self, set_value: Union[numbers.Number, np.ndarray, Q_]): - """ - Verify value against constraints. This hasn't really been implemented as fitting is tricky. - - :param set_value: value to be verified - :return: new value from constraint - """ - if isinstance(set_value, Q_): - set_value = set_value.magnitude - # Save the old state and create the new state - old_value = self._value - state = self._global_object.stack.enabled - if state: - self._global_object.stack.force_state(False) - try: - new_value = old_value - if set_value in self.available_options: - new_value = set_value - finally: - self._global_object.stack.force_state(state) - - # Restore to the old state - self.__previous_set(self, new_value) - - @property - def available_options(self) -> List[Union[numbers.Number, np.ndarray, Q_]]: - return self._available_options - - @available_options.setter - @property_stack_deco - def available_options(self, available_options: List[Union[numbers.Number, np.ndarray, Q_]]) -> None: - self._available_options = available_options - - def as_dict(self, **kwargs) -> Dict[str, Any]: - import json - - d = super().as_dict(**kwargs) - d['name'] = self.name - d['available_options'] = json.dumps(self.available_options) - return d - - -class Parameter(Descriptor): - """ - This class is an extension of a ``EasyScience.Object.Base.Descriptor``. Where the descriptor was for static objects, - a `Parameter` is for dynamic objects. A parameter has the ability to be used in fitting and - has additional fields to facilitate this. - """ - - _constructor = M_ - - def __init__( - self, - name: str, - value: Union[numbers.Number, np.ndarray], - error: Optional[Union[numbers.Number, np.ndarray]] = 0.0, - min: Optional[numbers.Number] = -np.inf, - max: Optional[numbers.Number] = np.inf, - fixed: Optional[bool] = False, - **kwargs, - ): - """ - This class is an extension of a ``EasyScience.Object.Base.Descriptor``. Where the descriptor was for static - objects, a `Parameter` is for dynamic objects. A parameter has the ability to be used in fitting and has - additional fields to facilitate this. - - :param name: Name of this obj - :param value: Value of this object - :param error: Error associated as sigma for this parameter - :param min: Minimum value for fitting - :param max: Maximum value for fitting - :param fixed: Should this parameter vary when fitting? - :param kwargs: Key word arguments for the `Descriptor` class. - - .. code-block:: python - - from easyscience.Objects.Base import Parameter - # Describe a phase - phase_basic = Parameter('phase', 3) - # Describe a phase with a unit - phase_unit = Parameter('phase', 3, units,='rad/s') - - .. note:: - Undo/Redo functionality is implemented for the attributes `value`, `error`, `min`, `max`, `fixed` - """ - # Set the error - self._args = {'value': value, 'units': '', 'error': error} - - if not isinstance(value, numbers.Number) or isinstance(value, np.ndarray): - raise ValueError('In a parameter the `value` must be numeric') - if value < min: - raise ValueError('`value` can not be less than `min`') - if value > max: - raise ValueError('`value` can not be greater than `max`') - if error < 0: - raise ValueError('Standard deviation `error` must be positive') - - super().__init__(name=name, value=value, **kwargs) - self._args['units'] = str(self.unit) - - # Warnings if we are given a boolean - if isinstance(self._type, bool): - warnings.warn( - 'Boolean values are not officially supported in Parameter. Use a Descriptor instead', - UserWarning, - ) - - # Create additional fitting elements - self._min: numbers.Number = min - self._max: numbers.Number = max - self._fixed: bool = fixed - self.initial_value = self.value - self._constraints: dict = { - 'user': {}, - 'builtin': { - 'min': SelfConstraint(self, '>=', '_min'), - 'max': SelfConstraint(self, '<=', '_max'), - }, - 'virtual': {}, - } - # This is for the serialization. Otherwise we wouldn't catch the values given to `super()` - self._kwargs = kwargs - - # We have initialized from the Descriptor class where value has it's own undo/redo decorator - # This needs to be bypassed to use the Parameter undo/redo stack - fun = self.__class__.value.fset - if hasattr(fun, 'func'): - fun = getattr(fun, 'func') - self.__previous_set: Callable[ - [V, Union[numbers.Number, np.ndarray]], - Union[numbers.Number, np.ndarray], - ] = fun - - # Monkey patch the unit and the value to take into account the new max/min situation - addProp( - self, - 'value', - fget=self.__class__.value.fget, - fset=self.__class__._property_value.fset, - fdel=self.__class__.value.fdel, - ) - - @property - def _property_value(self) -> Union[numbers.Number, np.ndarray, M_]: - return self.value - - @_property_value.setter - @property_stack_deco - def _property_value(self, set_value: Union[numbers.Number, np.ndarray, M_]) -> None: - """ - Verify value against constraints. This hasn't really been implemented as fitting is tricky. - - :param set_value: value to be verified - :return: new value from constraint - """ - if isinstance(set_value, M_): - set_value = set_value.magnitude.nominal_value - # Save the old state and create the new state - old_value = self._value - self._value = self.__class__._constructor(value=set_value, units=self._args['units'], error=self._args['error']) - - # First run the built in constraints. i.e. min/max - constraint_type: MappingProxyType[str, C] = self.builtin_constraints - new_value = self.__constraint_runner(constraint_type, set_value) - # Then run any user constraints. - constraint_type: dict = self.user_constraints - state = self._global_object.stack.enabled - if state: - self._global_object.stack.force_state(False) - try: - new_value = self.__constraint_runner(constraint_type, new_value) - finally: - self._global_object.stack.force_state(state) - - # And finally update any virtual constraints - constraint_type: dict = self._constraints['virtual'] - _ = self.__constraint_runner(constraint_type, new_value) - - # Restore to the old state - self._value = old_value - self.__previous_set(self, new_value) - - def convert_unit(self, new_unit: str): # noqa: S1144 - """ - Perform unit conversion. The value, max and min can change on unit change. - - :param new_unit: new unit - :return: None - """ - old_unit = str(self._args['units']) - super().convert_unit(new_unit) - # Deal with min/max. Error is auto corrected - if not self.value.unitless and old_unit != 'dimensionless': - self._min = Q_(self.min, old_unit).to(self._units).magnitude - self._max = Q_(self.max, old_unit).to(self._units).magnitude - # Log the new converted error - self._args['error'] = self.value.error.magnitude - - @property - def min(self) -> numbers.Number: - """ - Get the minimum value for fitting. - - :return: minimum value - """ - return self._min - - @min.setter - @property_stack_deco - def min(self, value: numbers.Number): - """ - Set the minimum value for fitting. - - implements undo/redo functionality. - - :param value: new minimum value - :return: None - """ - if value <= self.raw_value: - self._min = value - else: - raise ValueError(f'The current set value ({self.raw_value}) is less than the desired min value ({value}).') - - @property - def max(self) -> numbers.Number: - """ - Get the maximum value for fitting. - - :return: maximum value - """ - return self._max - - @max.setter - @property_stack_deco - def max(self, value: numbers.Number): - """ - Get the maximum value for fitting. - - implements undo/redo functionality. - - :param value: new maximum value - :return: None - """ - if value >= self.raw_value: - self._max = value - else: - raise ValueError(f'The current set value ({self.raw_value}) is greater than the desired max value ({value}).') - - @property - def fixed(self) -> bool: - """ - Can the parameter vary while fitting? - - :return: True = fixed, False = can vary - :rtype: bool - """ - return self._fixed - - @fixed.setter - @property_stack_deco - def fixed(self, value: bool): - """ - Change the parameter vary while fitting state. - - implements undo/redo functionality. - - :param value: True = fixed, False = can vary - :return: None - """ - if not self.enabled: - if self._global_object.stack.enabled: - self._global_object.stack.pop() - if global_object.debug: - raise CoreSetException(f'{str(self)} is not enabled.') - return - # TODO Should we try and cast value to bool rather than throw ValueError? - if not isinstance(value, bool): - raise ValueError - self._fixed = value - - @property - def free(self) -> bool: - return not self.fixed - - @free.setter - def free(self, value: bool) -> None: - self.fixed = not value - - @property - def error(self) -> float: - """ - The error associated with the parameter. - - :return: Error associated with parameter - """ - return float(self._value.error.magnitude) - - @error.setter - @property_stack_deco - def error(self, value: float): - """ - Set the error associated with the parameter. - - implements undo/redo functionality. - - :param value: New error value - :return: None - """ - if value < 0: - raise ValueError - self._args['error'] = value - self._value = self.__class__._constructor(**self._args) - - def __repr__(self) -> str: - """ - Return printable representation of a Parameter object. - """ - super_str = super().__repr__() - super_str = super_str[:-1] - s = [] - if self.fixed: - super_str += ' (fixed)' - s.append(super_str) - s.append('bounds=[%s:%s]' % (repr(self.min), repr(self.max))) - return '%s>' % ', '.join(s) - - def __float__(self) -> float: - return float(self.raw_value) - - @property - def builtin_constraints(self) -> MappingProxyType[str, C]: - """ - Get the built in constrains of the object. Typically these are the min/max - - :return: Dictionary of constraints which are built into the system - """ - return MappingProxyType(self._constraints['builtin']) - - @property - def user_constraints(self) -> Dict[str, C]: - """ - Get the user specified constrains of the object. - - :return: Dictionary of constraints which are user supplied - """ - return self._constraints['user'] - - @user_constraints.setter - def user_constraints(self, constraints_dict: Dict[str, C]) -> None: - self._constraints['user'] = constraints_dict - - def _quick_set( - self, - set_value: float, - run_builtin_constraints: bool = False, - run_user_constraints: bool = False, - run_virtual_constraints: bool = False, - ) -> None: - """ - This is a quick setter for the parameter. It bypasses all the checks and constraints, - just setting the value and issuing the interface callbacks. - - WARNING: This is a dangerous function and should only be used when you know what you are doing. - """ - # First run the built-in constraints. i.e. min/max - if run_builtin_constraints: - constraint_type: MappingProxyType = self.builtin_constraints - set_value = self.__constraint_runner(constraint_type, set_value) - # Then run any user constraints. - if run_user_constraints: - constraint_type: dict = self.user_constraints - state = self._global_object.stack.enabled - if state: - self._global_object.stack.force_state(False) - try: - set_value = self.__constraint_runner(constraint_type, set_value) - finally: - self._global_object.stack.force_state(state) - if run_virtual_constraints: - # And finally update any virtual constraints - constraint_type: dict = self._constraints['virtual'] - _ = self.__constraint_runner(constraint_type, set_value) - - # Finally set the value - self._property_value._magnitude._nominal_value = set_value - self._args['value'] = set_value - if self._callback.fset is not None: - self._callback.fset(set_value) - - def __constraint_runner( - self, - this_constraint_type: Union[dict, MappingProxyType[str, C]], - newer_value: numbers.Number, - ) -> float: - for constraint in this_constraint_type.values(): - if constraint.external: - constraint() - continue - this_new_value = constraint(no_set=True) - if this_new_value != newer_value: - if global_object.debug: - print(f'Constraint `{constraint}` has been applied') - self._value = self.__class__._constructor( - value=this_new_value, - units=self._args['units'], - error=self._args['error'], - ) - newer_value = this_new_value - return newer_value - - @property - def bounds(self) -> Tuple[numbers.Number, numbers.Number]: - """ - Get the bounds of the parameter. - - :return: Tuple of the parameters minimum and maximum values - """ - return self._min, self._max - - @bounds.setter - def bounds(self, new_bound: Union[Tuple[numbers.Number, numbers.Number], numbers.Number]) -> None: - """ - Set the bounds of the parameter. *This will also enable the parameter*. - - :param new_bound: New bounds. This can be a tuple of (min, max) or a single number (min). - For changing the max use (None, max_value). - """ - # Macro checking and opening for undo/redo - close_macro = False - if self._global_object.stack.enabled: - self._global_object.stack.beginMacro('Setting bounds') - close_macro = True - # Have we only been given a single number (MIN)? - if isinstance(new_bound, numbers.Number): - self.min = new_bound - # Have we been given a tuple? - if isinstance(new_bound, tuple): - new_min, new_max = new_bound - # Are there any None values? - if isinstance(new_min, numbers.Number): - self.min = new_min - if isinstance(new_max, numbers.Number): - self.max = new_max - # Enable the parameter if needed - if not self.enabled: - self.enabled = True - # This parameter is now not fixed. - self.fixed = False - # Close the macro if we opened it - if close_macro: - self._global_object.stack.endMacro() From e6931237807eccb595b898e7d85484d6f3bc6b8c Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Jan 2025 12:13:20 +0100 Subject: [PATCH 16/41] Remove old tests --- .../test_constraints_legacy_parameter.py | 131 ----- .../Objects/test_BaseObj_legacy_parameter.py | 503 ------------------ .../variable/test_descriptor_from_legacy.py | 216 -------- .../variable/test_parameter_from_legacy.py | 424 --------------- 4 files changed, 1274 deletions(-) delete mode 100644 tests/unit_tests/Fitting/test_constraints_legacy_parameter.py delete mode 100644 tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py delete mode 100644 tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py delete mode 100644 tests/unit_tests/Objects/variable/test_parameter_from_legacy.py diff --git a/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py b/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py deleted file mode 100644 index b20cd0f2..00000000 --- a/tests/unit_tests/Fitting/test_constraints_legacy_parameter.py +++ /dev/null @@ -1,131 +0,0 @@ -__author__ = "github.com/wardsimon" -__version__ = "0.1.0" - -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project Tuple[List[Parameter], List[int]]: - return [Parameter("a", 1), Parameter("b", 2)], [1, 2] - - -@pytest.fixture -def threePars(twoPars) -> Tuple[List[Parameter], List[int]]: - ps, vs = twoPars - ps.append(Parameter("c", 3)) - vs.append(3) - return ps, vs - - -def test_NumericConstraints_Equals(twoPars): - - value = 1 - - # Should skip - c = NumericConstraint(twoPars[0][0], "==", value) - c() - assert twoPars[0][0].value == twoPars[1][0] - - # Should update to new value - c = NumericConstraint(twoPars[0][1], "==", value) - c() - assert twoPars[0][1].value == value - - -def test_NumericConstraints_Greater(twoPars): - value = 1.5 - - # Should update to new value - c = NumericConstraint(twoPars[0][0], ">", value) - c() - assert twoPars[0][0].value == value - - # Should skip - c = NumericConstraint(twoPars[0][1], ">", value) - c() - assert twoPars[0][1].value == twoPars[1][1] - - -def test_NumericConstraints_Less(twoPars): - value = 1.5 - - # Should skip - c = NumericConstraint(twoPars[0][0], "<", value) - c() - assert twoPars[0][0].value == twoPars[1][0] - - # Should update to new value - c = NumericConstraint(twoPars[0][1], "<", value) - c() - assert twoPars[0][1].value == value - - -@pytest.mark.parametrize("multiplication_factor", [None, 1, 2, 3, 4.5]) -def test_ObjConstraintMultiply(twoPars, multiplication_factor): - if multiplication_factor is None: - multiplication_factor = 1 - operator_str = "" - else: - operator_str = f"{multiplication_factor}*" - c = ObjConstraint(twoPars[0][0], operator_str, twoPars[0][1]) - c() - assert twoPars[0][0].value == multiplication_factor * twoPars[1][1] - - -@pytest.mark.parametrize("division_factor", [1, 2, 3, 4.5]) -def test_ObjConstraintDivide(twoPars, division_factor): - operator_str = f"{division_factor}/" - c = ObjConstraint(twoPars[0][0], operator_str, twoPars[0][1]) - c() - assert twoPars[0][0].value == division_factor / twoPars[1][1] - - -def test_ObjConstraint_Multiple(threePars): - - p0 = threePars[0][0] - p1 = threePars[0][1] - p2 = threePars[0][2] - - value = 1.5 - - p0.user_constraints["num_1"] = ObjConstraint(p1, "", p0) - p0.user_constraints["num_2"] = ObjConstraint(p2, "", p0) - - p0.value = value - assert p0.value == value - assert p1.value == value - assert p2.value == value - - -def test_ConstraintEnable_Disable(twoPars): - - assert twoPars[0][0].enabled - assert twoPars[0][1].enabled - - c = ObjConstraint(twoPars[0][0], "", twoPars[0][1]) - twoPars[0][0].user_constraints["num_1"] = c - - assert c.enabled - assert twoPars[0][1].enabled - assert not twoPars[0][0].enabled - - c.enabled = False - assert not c.enabled - assert twoPars[0][1].enabled - assert twoPars[0][0].enabled - - c.enabled = True - assert c.enabled - assert twoPars[0][1].enabled - assert not twoPars[0][0].enabled diff --git a/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py b/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py deleted file mode 100644 index 5fdb087e..00000000 --- a/tests/unit_tests/Objects/test_BaseObj_legacy_parameter.py +++ /dev/null @@ -1,503 +0,0 @@ -__author__ = "github.com/wardsimon" -__version__ = "0.1.0" - -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project " - d = DescriptorNumber("test", 1, unit="cm") - assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 cm>" - - -def test_descriptor_number_as_dict(): - d = DescriptorNumber("test", 1) - result = d.as_dict() - expected = { - "@module": DescriptorNumber.__module__, - "@class": DescriptorNumber.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1, - "unit": "dimensionless", - "description": "", - "url": "", - "display_name": "test", - "callback": None, - } - for key in expected.keys(): - if key == "callback": - continue - assert result[key] == expected[key] - - -@pytest.mark.parametrize( - "reference, constructor", - ( - [ - { - "@module": DescriptorBool.__module__, - "@class": DescriptorBool.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": False, - "description": "", - "url": "", - "display_name": "test", - }, - DescriptorBool, - ], - [ - { - "@module": DescriptorNumber.__module__, - "@class": DescriptorNumber.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1, - "unit": "dimensionless", - "variance": 0.0, - "description": "", - "url": "", - "display_name": "test", - }, - DescriptorNumber, - ], - [ - { - "@module": DescriptorStr.__module__, - "@class": DescriptorStr.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": "string", - "description": "", - "url": "", - "display_name": "test", - }, - DescriptorStr, - ], - ), - ids=["DescriptorBool", "DescriptorNumber", "DescriptorStr"], -) -def test_item_from_dict(reference, constructor): - d = constructor.from_dict(reference) - for key, item in reference.items(): - if key.startswith("@"): - continue - obtained = getattr(d, key) - assert obtained == item - - -@pytest.mark.parametrize("value", ("This is ", "a fun ", "test")) -def test_parameter_display_name(value): - p = DescriptorNumber("test", 1, display_name=value) - assert p.display_name == value - - -def test_item_boolean_value(): - item = DescriptorBool("test", True) - assert item.value is True - item.value = False - assert item.value is False - - item = DescriptorBool("test", False) - assert item.value is False - item.value = True - assert item.value is True - diff --git a/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py b/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py deleted file mode 100644 index f41ff09e..00000000 --- a/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py +++ /dev/null @@ -1,424 +0,0 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project " - d = Parameter("test", 1, unit="cm") - assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 cm, bounds=[-inf:inf]>" - d = Parameter("test", 1, variance=0.1) - assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 ± 0.3162, bounds=[-inf:inf]>" - - d = Parameter("test", 1, fixed=True) - assert ( - repr(d) - == f"<{d.__class__.__name__} 'test': 1.0000 (fixed), bounds=[-inf:inf]>" - ) - d = Parameter("test", 1, unit="cm", variance=0.1, fixed=True) - assert ( - repr(d) - == f"<{d.__class__.__name__} 'test': 1.0000 ± 0.3162 cm (fixed), bounds=[-inf:inf]>" - ) - - -def test_parameter_as_dict(): - d = Parameter("test", 1) - result = d.as_dict() - expected = { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1.0, - "variance": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "unit": "dimensionless", - } - for key in expected.keys(): - assert result[key] == expected[key] - - # Check that additional arguments work - d = Parameter("test", 1, unit="km", url="https://www.boo.com") - result = d.as_dict() - expected = { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "unit": "km", - "value": 1.0, - "variance": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - } - for key in expected.keys(): - assert result[key] == expected[key] - - -def test_item_from_dict(): - reference = { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "unit": "km", - "value": 1.0, - "variance": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - } - constructor = Parameter - d = constructor.from_dict(reference) - for key, item in reference.items(): - if key == "callback" or key.startswith("@"): - continue - obtained = getattr(d, key) - assert obtained == item - - -@pytest.mark.parametrize( - "construct", - ( - { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "unit": "km", - "value": 1.0, - "variance": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - }, - ), - ids=["Parameter"], -) -def test_item_from_Decoder(construct): - - from easyscience.Utils.io.dict import DictSerializer - - d = DictSerializer().decode(construct) - assert d.__class__.__name__ == construct["@class"] - for key, item in construct.items(): - if key == "callback" or key.startswith("@"): - continue - obtained = getattr(d, key) - assert obtained == item - - -@pytest.mark.parametrize("value", (-np.inf, 0, 1.0, 2147483648, np.inf)) -def test_parameter_min(value): - d = Parameter("test", -0.1) - if d.value < value: - with pytest.raises(ValueError): - d.min = value - else: - d.min = value - assert d.min == value - - -@pytest.mark.parametrize("value", [-np.inf, 0, 1.1, 2147483648, np.inf]) -def test_parameter_max(value): - d = Parameter("test", 2147483649) - if d.value > value: - with pytest.raises(ValueError): - d.max = value - else: - d.max = value - assert d.max == value - - -@pytest.mark.parametrize("value", [True, False, 5]) -def test_parameter_fixed(value): - d = Parameter("test", -np.inf) - if isinstance(value, bool): - d.fixed = value - assert d.fixed == value - else: - with pytest.raises(ValueError): - d.fixed = value - - -@pytest.mark.parametrize("value", (-np.inf, -0.1, 0, 1.0, 2147483648, np.inf)) -def test_parameter_error(value): - d = Parameter("test", 1) - if value >= 0: - d.error = value - assert d.error == value - else: - with pytest.raises(ValueError): - d.error = value - - -def _generate_advanced_inputs(): - temp = _generate_inputs() - # These will be the optional parameters - advanced = {"variance": 1.0, "min": -0.1, "max": 2147483648, "fixed": False} - advanced_result = { - "variance": {"name": "variance", "value": advanced["variance"]}, - "min": {"name": "min", "value": advanced["min"]}, - "max": {"name": "max", "value": advanced["max"]}, - "fixed": {"name": "fixed", "value": advanced["fixed"]}, - } - - def create_entry(base, key, value, ref, ref_key=None): - this_temp = deepcopy(base) - for item in base: - test, res = item - new_opt = deepcopy(test[1]) - new_res = deepcopy(res) - if ref_key is None: - ref_key = key - new_res[ref_key] = ref - new_opt[key] = value - this_temp.append(([test[0], new_opt], new_res)) - return this_temp - - for add_opt in advanced.keys(): - if isinstance(advanced[add_opt], list): - for idx, item in enumerate(advanced[add_opt]): - temp = create_entry( - temp, - add_opt, - item, - advanced_result[add_opt]["value"][idx], - ref_key=advanced_result[add_opt]["name"], - ) - else: - temp = create_entry( - temp, - add_opt, - advanced[add_opt], - advanced_result[add_opt]["value"], - ref_key=advanced_result[add_opt]["name"], - ) - return temp - - -@pytest.mark.parametrize("element, expected", _generate_advanced_inputs()) -def test_parameter_advanced_creation(element, expected): - if len(element[0]) > 0: - value = element[0][1] - else: - value = element[1]["value"] - if "min" in element[1].keys(): - if element[1]["min"] > value: - with pytest.raises(ValueError): - d = Parameter(*element[0], **element[1]) - elif "max" in element[1].keys(): - if element[1]["max"] < value: - with pytest.raises(ValueError): - d = Parameter(*element[0], **element[1]) - else: - d = Parameter(*element[0], **element[1]) - for field in expected.keys(): - ref = expected[field] - obtained = getattr(d, field) - assert obtained == ref - - -@pytest.mark.parametrize("value", ("This is ", "a fun ", "test")) -def test_parameter_display_name(value): - p = Parameter("test", 1, display_name=value) - assert p.display_name == value - - -@pytest.mark.parametrize("value", (True, False)) -def test_parameter_bounds(value): - for fixed in (True, False): - p = Parameter("test", 1, enabled=value, fixed=fixed) - assert p.min == -np.inf - assert p.max == np.inf - assert p.fixed == fixed - assert p.bounds == (-np.inf, np.inf) - - p.bounds = (0, 2) - assert p.min == 0 - assert p.max == 2 - assert p.bounds == (0, 2) - assert p.enabled is True - assert p.fixed is False From e62769098405ff0b9388a17a6b9cd40ad21dffa4 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Jan 2025 12:19:35 +0100 Subject: [PATCH 17/41] Update test of polynomial --- .../Objects/test_Descriptor_Parameter.py | 579 ------------------ tests/unit_tests/models/test_polynomial.py | 16 +- 2 files changed, 8 insertions(+), 587 deletions(-) delete mode 100644 tests/unit_tests/Objects/test_Descriptor_Parameter.py diff --git a/tests/unit_tests/Objects/test_Descriptor_Parameter.py b/tests/unit_tests/Objects/test_Descriptor_Parameter.py deleted file mode 100644 index 88a04c65..00000000 --- a/tests/unit_tests/Objects/test_Descriptor_Parameter.py +++ /dev/null @@ -1,579 +0,0 @@ -# SPDX-FileCopyrightText: 2023 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -# © 2021-2023 Contributors to the EasyScience project " - d = Descriptor("test", 1, units="cm") - assert repr(d) == f"<{d.__class__.__name__} 'test': 1 cm>" - - -def test_parameter_repr(): - d = Parameter("test", 1) - assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0+/-0, bounds=[-inf:inf]>" - d = Parameter("test", 1, units="cm") - assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0+/-0 cm, bounds=[-inf:inf]>" - - d = Parameter("test", 1, fixed=True) - assert ( - repr(d) - == f"<{d.__class__.__name__} 'test': 1.0+/-0 (fixed), bounds=[-inf:inf]>" - ) - d = Parameter("test", 1, units="cm", fixed=True) - assert ( - repr(d) - == f"<{d.__class__.__name__} 'test': 1.0+/-0 cm (fixed), bounds=[-inf:inf]>" - ) - - -def test_descriptor_as_dict(): - d = Descriptor("test", 1) - result = d.as_dict() - expected = { - "@module": Descriptor.__module__, - "@class": Descriptor.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1, - "units": "dimensionless", - "description": "", - "url": "", - "display_name": "test", - "callback": None, - } - for key in expected.keys(): - if key == "callback": - continue - assert result[key] == expected[key] - - -def test_parameter_as_dict(): - d = Parameter("test", 1) - result = d.as_dict() - expected = { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1.0, - "error": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "units": "dimensionless", - } - for key in expected.keys(): - if key == "callback": - continue - assert result[key] == expected[key] - - # Check that additional arguments work - d = Parameter("test", 1, units="km", url="https://www.boo.com") - result = d.as_dict() - expected = { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "units": "kilometer", - "value": 1.0, - "error": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - } - for key in expected.keys(): - if key == "callback": - continue - assert result[key] == expected[key] - - -@pytest.mark.parametrize( - "reference, constructor", - ( - [ - { - "@module": Descriptor.__module__, - "@class": Descriptor.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1, - "units": "dimensionless", - "description": "", - "url": "", - "display_name": "test", - "callback": None, - }, - Descriptor, - ], - [ - { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "units": "kilometer", - "value": 1.0, - "error": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - }, - Parameter, - ], - ), - ids=["Descriptor", "Parameter"], -) -def test_item_from_dict(reference, constructor): - d = constructor.from_dict(reference) - for key, item in reference.items(): - if key == "callback" or key.startswith("@"): - continue - if key == "units": - key = "unit" - if key == "value": - key = "raw_value" - obtained = getattr(d, key) - if isinstance(obtained, (ureg.Unit, Q_)): - obtained = str(obtained) - assert obtained == item - - -@pytest.mark.parametrize( - "construct", - ( - { - "@module": Descriptor.__module__, - "@class": Descriptor.__name__, - "@version": easyscience.__version__, - "name": "test", - "value": 1, - "units": "dimensionless", - "description": "", - "url": "", - "display_name": "test", - "callback": None, - }, - { - "@module": Parameter.__module__, - "@class": Parameter.__name__, - "@version": easyscience.__version__, - "name": "test", - "units": "kilometer", - "value": 1.0, - "error": 0.0, - "min": -np.inf, - "max": np.inf, - "fixed": False, - "url": "https://www.boo.com", - }, - ), - ids=["Descriptor", "Parameter"], -) -def test_item_from_Decoder(construct): - from easyscience.Utils.io.dict import DictSerializer - - d = DictSerializer().decode(construct) - assert d.__class__.__name__ == construct["@class"] - for key, item in construct.items(): - if key == "callback" or key.startswith("@"): - continue - if key == "units": - key = "unit" - if key == "value": - key = "raw_value" - obtained = getattr(d, key) - if isinstance(obtained, (ureg.Unit, Q_)): - obtained = str(obtained) - assert obtained == item - - -@pytest.mark.parametrize("value", (-np.inf, 0, 1.0, 2147483648, np.inf)) -def test_parameter_min(value): - d = Parameter("test", -0.1) - if d.raw_value < value: - with pytest.raises(ValueError): - d.min = value - else: - d.min = value - assert d.min == value - - -@pytest.mark.parametrize("value", [-np.inf, 0, 1.1, 2147483648, np.inf]) -def test_parameter_max(value): - d = Parameter("test", 2147483649) - if d.raw_value > value: - with pytest.raises(ValueError): - d.max = value - else: - d.max = value - assert d.max == value - - -@pytest.mark.parametrize("value", [True, False, 5]) -def test_parameter_fixed(value): - d = Parameter("test", -np.inf) - if isinstance(value, bool): - d.fixed = value - assert d.fixed == value - else: - with pytest.raises(ValueError): - d.fixed = value - - -@pytest.mark.parametrize("value", (-np.inf, -0.1, 0, 1.0, 2147483648, np.inf)) -def test_parameter_error(value): - d = Parameter("test", 1) - if value >= 0: - d.error = value - assert d.error == value - else: - with pytest.raises(ValueError): - d.error = value - - -def _generate_advanced_inputs(): - temp = _generate_inputs() - # These will be the optional parameters - advanced = {"error": 1.0, "min": -0.1, "max": 2147483648, "fixed": False} - advanced_result = { - "error": {"name": "error", "value": advanced["error"]}, - "min": {"name": "min", "value": advanced["min"]}, - "max": {"name": "max", "value": advanced["max"]}, - "fixed": {"name": "fixed", "value": advanced["fixed"]}, - } - - def create_entry(base, key, value, ref, ref_key=None): - this_temp = deepcopy(base) - for item in base: - test, res = item - new_opt = deepcopy(test[1]) - new_res = deepcopy(res) - if ref_key is None: - ref_key = key - new_res[ref_key] = ref - new_opt[key] = value - this_temp.append(([test[0], new_opt], new_res)) - return this_temp - - for add_opt in advanced.keys(): - if isinstance(advanced[add_opt], list): - for idx, item in enumerate(advanced[add_opt]): - temp = create_entry( - temp, - add_opt, - item, - advanced_result[add_opt]["value"][idx], - ref_key=advanced_result[add_opt]["name"], - ) - else: - temp = create_entry( - temp, - add_opt, - advanced[add_opt], - advanced_result[add_opt]["value"], - ref_key=advanced_result[add_opt]["name"], - ) - return temp - - -@pytest.mark.parametrize("element, expected", _generate_advanced_inputs()) -def test_parameter_advanced_creation(element, expected): - if len(element[0]) > 0: - value = element[0][1] - else: - value = element[1]["value"] - if "min" in element[1].keys(): - if element[1]["min"] > value: - with pytest.raises(ValueError): - d = Parameter(*element[0], **element[1]) - elif "max" in element[1].keys(): - if element[1]["max"] < value: - with pytest.raises(ValueError): - d = Parameter(*element[0], **element[1]) - else: - d = Parameter(*element[0], **element[1]) - for field in expected.keys(): - ref = expected[field] - obtained = getattr(d, field) - if isinstance(obtained, (ureg.Unit, Q_)): - obtained = str(obtained) - assert obtained == ref - - -@pytest.mark.parametrize("value", ("This is ", "a fun ", "test")) -def test_parameter_display_name(value): - p = Parameter("test", 1, display_name=value) - assert p.display_name == value - - p = Descriptor("test", 1, display_name=value) - assert p.display_name == value - - -@pytest.mark.parametrize("instance", (Descriptor, Parameter), indirect=True) -def test_item_boolean_value(instance): - def creator(value): - if instance == Parameter: - with pytest.warns(UserWarning): - item = instance("test", value) - else: - item = instance("test", value) - return item - - def setter(item, value): - if instance == Parameter: - with pytest.warns(UserWarning): - item.value = value - else: - item.value = value - - item = creator(True) - assert item.value is True - setter(item, False) - assert item.value is False - - item = creator(False) - assert item.value is False - setter(item, True) - assert item.value is True - - -@pytest.mark.parametrize("value", (True, False)) -def test_parameter_bounds(value): - for fixed in (True, False): - p = Parameter("test", 1, enabled=value, fixed=fixed) - assert p.min == -np.inf - assert p.max == np.inf - assert p.fixed == fixed - assert p.bounds == (-np.inf, np.inf) - - p.bounds = (0, 2) - assert p.min == 0 - assert p.max == 2 - assert p.bounds == (0, 2) - assert p.enabled is True - assert p.fixed is False diff --git a/tests/unit_tests/models/test_polynomial.py b/tests/unit_tests/models/test_polynomial.py index 9e7a5afd..adccb9e1 100644 --- a/tests/unit_tests/models/test_polynomial.py +++ b/tests/unit_tests/models/test_polynomial.py @@ -10,7 +10,7 @@ from easyscience.models.polynomial import Line from easyscience.models.polynomial import Polynomial -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.variable.parameter import Parameter line_test_cases = ((1, 2), (-1, -2), (0.72, 6.48)) poly_test_cases = ( @@ -29,11 +29,11 @@ def test_Line_pars(m, c): line = Line(m, c) - assert line.m.raw_value == m - assert line.c.raw_value == c + assert line.m.value == m + assert line.c.value == c x = np.linspace(0, 10, 100) - y = line.m.raw_value * x + line.c.raw_value + y = line.m.value * x + line.c.value assert np.allclose(line(x), y) @@ -43,11 +43,11 @@ def test_Line_constructor(m, c): c_ = Parameter("c", c) line = Line(m_, c_) - assert line.m.raw_value == m - assert line.c.raw_value == c + assert line.m.value == m + assert line.c.value == c x = np.linspace(0, 10, 100) - y = line.m.raw_value * x + line.c.raw_value + y = line.m.value * x + line.c.value assert np.allclose(line(x), y) @@ -55,7 +55,7 @@ def test_Line_constructor(m, c): def test_Polynomial_pars(coo): poly = Polynomial(coefficients=coo) - vals = {coo.raw_value for coo in poly.coefficients} + vals = {coo.value for coo in poly.coefficients} assert len(vals.difference(set(coo))) == 0 x = np.linspace(0, 10, 100) From d8ddac4966006751a81e3be9cee50e39cbd0f9dc Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Jan 2025 13:46:38 +0100 Subject: [PATCH 18/41] Update imports in tests to make the appear in list of tests --- src/easyscience/Objects/virtual.py | 2 +- tests/unit_tests/Objects/test_Virtual.py | 2 +- tests/unit_tests/global_object/test_map.py | 2 +- tests/unit_tests/global_object/test_undo_redo.py | 8 ++++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/easyscience/Objects/virtual.py b/src/easyscience/Objects/virtual.py index eb4e7623..29cee336 100644 --- a/src/easyscience/Objects/virtual.py +++ b/src/easyscience/Objects/virtual.py @@ -148,7 +148,7 @@ def virtualizer(obj: BV) -> BV: 'relalize_component': component_realizer, } - import easyscience.Objects.Variable as ec_var + import easyscience.Objects.variable as ec_var if klass in ec_var.__dict__.values(): # is_variable check virtual_options['fixed'] = property( diff --git a/tests/unit_tests/Objects/test_Virtual.py b/tests/unit_tests/Objects/test_Virtual.py index 7c610887..b0a33795 100644 --- a/tests/unit_tests/Objects/test_Virtual.py +++ b/tests/unit_tests/Objects/test_Virtual.py @@ -9,7 +9,7 @@ from easyscience.models.polynomial import Line from easyscience.Objects import virtual as Virtual -from easyscience.Objects.Variable import Parameter +from easyscience.Objects.variable.parameter import Parameter @pytest.mark.parametrize( diff --git a/tests/unit_tests/global_object/test_map.py b/tests/unit_tests/global_object/test_map.py index bc7958d9..b3eae678 100644 --- a/tests/unit_tests/global_object/test_map.py +++ b/tests/unit_tests/global_object/test_map.py @@ -3,7 +3,7 @@ # © 2021-2023 Contributors to the EasyScience project Date: Mon, 13 Jan 2025 14:50:18 +0100 Subject: [PATCH 19/41] Updated virtual --- src/easyscience/Objects/virtual.py | 32 +++++++++------- tests/unit_tests/Objects/test_Virtual.py | 38 ++++++++++--------- .../global_object/test_undo_redo.py | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/easyscience/Objects/virtual.py b/src/easyscience/Objects/virtual.py index 29cee336..03fed121 100644 --- a/src/easyscience/Objects/virtual.py +++ b/src/easyscience/Objects/virtual.py @@ -16,6 +16,12 @@ from easyscience import global_object from easyscience.Constraints import ObjConstraint +from easyscience.Objects.variable.descriptor_base import DescriptorBase +from easyscience.Objects.variable.descriptor_bool import DescriptorBool +from easyscience.Objects.variable.descriptor_number import DescriptorNumber +from easyscience.Objects.variable.descriptor_str import DescriptorStr +from easyscience.Objects.variable.parameter import Constraints +from easyscience.Objects.variable.parameter import Parameter if TYPE_CHECKING: from easyscience.Objects.ObjectClasses import BV @@ -31,8 +37,8 @@ def _remover(a_obj_id: str, v_obj_id: str): a_obj = global_object.map.get_item_by_key(a_obj_id) except ValueError: return - if a_obj._constraints['virtual'].get(v_obj_id, False): - del a_obj._constraints['virtual'][v_obj_id] + if a_obj._constraints.virtual.get(v_obj_id, False): + del a_obj._constraints.virtual[v_obj_id] def realizer(obj: BV): @@ -43,10 +49,9 @@ def realizer(obj: BV): """ if getattr(obj, '_is_virtual', False): klass = getattr(obj, '__non_virtual_class__') - import easyscience.Objects.Variable as ec_var args = [] - if klass in ec_var.__dict__.values(): # is_variable check + if klass in [DescriptorBool, DescriptorNumber, DescriptorStr, Parameter]: # is_variable check kwargs = obj.encode_data() kwargs['unique_name'] = None return klass(**kwargs) @@ -73,8 +78,6 @@ def component_realizer(obj: BV, component: str, recursive: bool = True): :param recursive: Should we realize all sub-components of the component """ - import easyscience.Objects.Variable as ec_var - done_mapping = True if not isinstance(obj, Iterable) or not issubclass(obj.__class__, MutableSequence): old_component = obj._kwargs[component] @@ -95,7 +98,7 @@ def component_realizer(obj: BV, component: str, recursive: bool = True): else: value = key key = value.unique_name - if getattr(value, '__old_class__', value.__class__) in ec_var.__dict__.values(): + if getattr(value, '__old_class__', value.__class__) in [DescriptorBool, DescriptorNumber, DescriptorStr, Parameter]: # noqa: E501 continue component._global_object.map.prune_vertex_from_edge(component, component._kwargs[key]) component._global_object.map.add_edge(component, old_component._kwargs[key]) @@ -148,9 +151,7 @@ def virtualizer(obj: BV) -> BV: 'relalize_component': component_realizer, } - import easyscience.Objects.variable as ec_var - - if klass in ec_var.__dict__.values(): # is_variable check + if klass in [DescriptorBool, DescriptorNumber, DescriptorStr, Parameter]: # is_variable check virtual_options['fixed'] = property( fget=lambda self: self._fixed, fset=lambda self, value: raise_(AttributeError('Virtual parameters cannot be fixed')), @@ -160,7 +161,8 @@ def virtualizer(obj: BV) -> BV: # Determine what to do next. args = [] # If `obj` is a parameter or descriptor etc, then simple mods. - if hasattr(obj, '_constructor'): + # if hasattr(obj, '_constructor'): + if isinstance(obj, DescriptorBase): # All Variables are based on the Descriptor. d = obj.encode_data() if hasattr(d, 'fixed'): @@ -170,8 +172,12 @@ def virtualizer(obj: BV) -> BV: v_p._enabled = False constraint = ObjConstraint(v_p, '', obj) constraint.external = True - obj._constraints['virtual'][v_p.unique_name] = constraint - v_p._constraints['builtin'] = dict() + obj._constraints.virtual[v_p.unique_name] = constraint + v_p._constraints = Constraints( + user=v_p._constraints.user, + builtin=dict(), # Set the new value for 'builtin' + virtual=v_p._constraints.virtual + ) setattr(v_p, '__previous_set', getattr(obj, '__previous_set', None)) weakref.finalize( v_p, diff --git a/tests/unit_tests/Objects/test_Virtual.py b/tests/unit_tests/Objects/test_Virtual.py index b0a33795..04d331b9 100644 --- a/tests/unit_tests/Objects/test_Virtual.py +++ b/tests/unit_tests/Objects/test_Virtual.py @@ -33,7 +33,7 @@ def test_virtual_variable(cls): assert hasattr(v_obj, attr) assert obj.name == v_obj.name - assert obj.raw_value == v_obj.raw_value + assert obj.value == v_obj.value @pytest.mark.parametrize( @@ -43,35 +43,37 @@ def test_virtual_variable(cls): ], ) def test_virtual_variable_modify(cls): + import gc obj = cls(name="test", value=1) v_obj = Virtual.virtualizer(obj) assert obj.name == v_obj.name - assert obj.raw_value == v_obj.raw_value + assert obj.value == v_obj.value new_value = 2.0 obj.value = new_value - assert obj.raw_value == v_obj.raw_value + assert obj.value == v_obj.value id_vobj = v_obj.unique_name - assert id_vobj in list(obj._constraints["virtual"].keys()) + assert id_vobj in list(obj._constraints.virtual.keys()) del v_obj - # assert id_vobj not in list(obj._constraints["virtual"].keys()) + gc.collect() # Force garbage collection + assert id_vobj not in list(obj._constraints.virtual.keys()) def test_Base_obj(): l = Line(2, 1) v_l = Virtual.virtualizer(l) assert l.name == v_l.name - assert l.m.raw_value == v_l.m.raw_value - assert l.c.raw_value == v_l.c.raw_value + assert l.m.value == v_l.m.value + assert l.c.value == v_l.c.value m = 4.0 l.m = m - assert l.m.raw_value == m - assert l.m.raw_value == v_l.m.raw_value - assert l.c.raw_value == v_l.c.raw_value + assert l.m.value == m + assert l.m.value == v_l.m.value + assert l.c.value == v_l.c.value def test_Base_obj(): @@ -79,19 +81,19 @@ def test_Base_obj(): l = Line(old_m, 1) v_l = Virtual.virtualizer(l) assert l.name == v_l.name - assert l.m.raw_value == v_l.m.raw_value - assert l.c.raw_value == v_l.c.raw_value + assert l.m.value == v_l.m.value + assert l.c.value == v_l.c.value Virtual.component_realizer(v_l, "m") m = 4.0 l.m = m - assert l.m.raw_value == m - assert v_l.m.raw_value == old_m - assert l.c.raw_value == v_l.c.raw_value + assert l.m.value == m + assert v_l.m.value == old_m + assert l.c.value == v_l.c.value m_other = 5.0 v_l.m = m_other - assert l.m.raw_value == m - assert v_l.m.raw_value == m_other - assert l.c.raw_value == v_l.c.raw_value + assert l.m.value == m + assert v_l.m.value == m_other + assert l.c.value == v_l.c.value diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index c4d0a457..ff5f43df 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -15,7 +15,7 @@ # from easyscience.Objects.Variable import Descriptor # from easyscience.Objects.Variable import Parameter from easyscience.Objects.variable.parameter import Parameter -# from easyscience.Objects.variable.descripter_str import DescriptorStr +from easyscience.Objects.variable.descripter_str import DescriptorStr # from easyscience.Objects.variable.descripter_number import DescriptorNumber from easyscience.fitting import Fitter From 14b22a32d083cf717402f57f852bdbbe0982b48a Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Jan 2025 15:26:21 +0100 Subject: [PATCH 20/41] Updated bounds in parameter and added _global_object to DescriptorBase to allow bounds to be undone in one step --- .../Objects/variable/descriptor_base.py | 2 +- src/easyscience/Objects/variable/parameter.py | 41 ++++++++++++++++++- .../global_object/test_undo_redo.py | 32 +++++++-------- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/easyscience/Objects/variable/descriptor_base.py b/src/easyscience/Objects/variable/descriptor_base.py index 9f144f8f..b525d4f1 100644 --- a/src/easyscience/Objects/variable/descriptor_base.py +++ b/src/easyscience/Objects/variable/descriptor_base.py @@ -23,7 +23,7 @@ class DescriptorBase(ComponentSerializer, metaclass=abc.ABCMeta): A `Descriptor` is typically something which describes part of a model and is non-fittable and generally changes the state of an object. """ - + _global_object = global_object # Used by serializer _REDIRECT = {'parent': None} diff --git a/src/easyscience/Objects/variable/parameter.py b/src/easyscience/Objects/variable/parameter.py index e60c2dde..2eb8e69d 100644 --- a/src/easyscience/Objects/variable/parameter.py +++ b/src/easyscience/Objects/variable/parameter.py @@ -320,6 +320,31 @@ def bounds(self) -> Tuple[numbers.Number, numbers.Number]: """ return self.min, self.max + # @bounds.setter + # def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: + # """ + # Set the bounds of the parameter. *This will also enable the parameter*. + + # :param new_bound: New bounds. This should be a tuple of (min, max). + # """ + # old_min = self.min + # old_max = self.max + # new_min, new_max = new_bound + + # try: + # self.min = new_min + # self.max = new_max + # except ValueError: + # self.min = old_min + # self.max = old_max + # raise ValueError(f'Current paramter value: {self._scalar.value} must be within {new_bound=}') + + # # Enable the parameter if needed + # if not self.enabled: + # self.enabled = True + # # Free parameter if needed + # if self.fixed: + # self.fixed = False @bounds.setter def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: """ @@ -331,13 +356,23 @@ def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: old_max = self.max new_min, new_max = new_bound + # Begin macro operation for undo/redo + close_macro = False + if self._global_object.stack.enabled: + self._global_object.stack.beginMacro('Setting bounds') + close_macro = True + try: + # Update bounds self.min = new_min self.max = new_max except ValueError: + # Rollback on failure self.min = old_min self.max = old_max - raise ValueError(f'Current paramter value: {self._scalar.value} must be within {new_bound=}') + if close_macro: + self._global_object.stack.endMacro() + raise ValueError(f'Current parameter value: {self._scalar.value} must be within {new_bound=}') # Enable the parameter if needed if not self.enabled: @@ -346,6 +381,10 @@ def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: if self.fixed: self.fixed = False + # End macro operation + if close_macro: + self._global_object.stack.endMacro() + @property def builtin_constraints(self) -> Dict[str, SelfConstraint]: """ diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index ff5f43df..9eaa27cf 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -15,8 +15,8 @@ # from easyscience.Objects.Variable import Descriptor # from easyscience.Objects.Variable import Parameter from easyscience.Objects.variable.parameter import Parameter -from easyscience.Objects.variable.descripter_str import DescriptorStr -# from easyscience.Objects.variable.descripter_number import DescriptorNumber +from easyscience.Objects.variable.descriptor_str import DescriptorStr +from easyscience.Objects.variable.descriptor_number import DescriptorNumber from easyscience.fitting import Fitter @@ -28,7 +28,7 @@ def createSingleObjs(idx): if idx % 2: return Parameter(name, idx) else: - return Descriptor(name, idx) + return DescriptorNumber(name, idx) def createParam(option): @@ -81,7 +81,7 @@ def getter(_obj, _attr): ], ) @pytest.mark.parametrize( - "idx", [pytest.param(0, id="Descriptor"), pytest.param(1, id="Parameter")] + "idx", [pytest.param(0, id="DescriptorNumber"), pytest.param(1, id="Parameter")] ) def test_SinglesUndoRedo(idx, test): obj = createSingleObjs(idx) @@ -129,7 +129,7 @@ def test_BaseObjUndoRedo(): # Test setting value for b_obj in objs.values(): - e = doUndoRedo(obj, b_obj.name, b_obj.raw_value + 1, "raw_value") + e = doUndoRedo(obj, b_obj.name, b_obj.value + 1, "value") if e: raise e @@ -213,25 +213,25 @@ def test_UndoRedoMacros(): global_object.stack.enabled = True global_object.stack.beginMacro(undo_text) - values = [item.raw_value for item in items] + values = [item.value for item in items] for item, value in zip(items, values): item.value = value + offset global_object.stack.endMacro() for item, old_value in zip(items, values): - assert item.raw_value == old_value + offset + assert item.value == old_value + offset assert global_object.stack.undoText() == undo_text global_object.stack.undo() for item, old_value in zip(items, values): - assert item.raw_value == old_value + assert item.value == old_value assert global_object.stack.redoText() == undo_text global_object.stack.redo() for item, old_value in zip(items, values): - assert item.raw_value == old_value + offset + assert item.value == old_value + offset @pytest.mark.parametrize("fit_engine", ["LMFit", "Bumps", "DFO"]) @@ -258,7 +258,7 @@ def from_pars(cls, m_value: float, c_value: float): return cls(m=m, c=c) def __call__(self, x: np.ndarray) -> np.ndarray: - return self.m.raw_value * x + self.c.raw_value + return self.m.value * x + self.c.value l1 = Line.default() m_sp = 4 @@ -281,18 +281,18 @@ def __call__(self, x: np.ndarray) -> np.ndarray: global_object.stack.enabled = True res = f.fit(x, y) - # assert l1.c.raw_value == pytest.approx(l2.c.raw_value, rel=l2.c.error * 3) - # assert l1.m.raw_value == pytest.approx(l2.m.raw_value, rel=l2.m.error * 3) + # assert l1.c.value == pytest.approx(l2.c.value, rel=l2.c.error * 3) + # assert l1.m.value == pytest.approx(l2.m.value, rel=l2.m.error * 3) assert global_object.stack.undoText() == "Fitting routine" global_object.stack.undo() - assert l2.m.raw_value == m_sp - assert l2.c.raw_value == c_sp + assert l2.m.value == m_sp + assert l2.c.value == c_sp assert global_object.stack.redoText() == "Fitting routine" global_object.stack.redo() - assert l2.m.raw_value == res.p[f"p{l2.m.unique_name}"] - assert l2.c.raw_value == res.p[f"p{l2.c.unique_name}"] + assert l2.m.value == res.p[f"p{l2.m.unique_name}"] + assert l2.c.value == res.p[f"p{l2.c.unique_name}"] # @pytest.mark.parametrize('math_funcs', [pytest.param([Parameter.__iadd__, float.__add__], id='Addition'), From ce2f5a5841dbd86f8f6a34ac497694c9bc8d3e19 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 09:55:54 +0100 Subject: [PATCH 21/41] Updated error setter to handle None as input --- src/easyscience/Objects/variable/descriptor_number.py | 6 +++++- tests/unit_tests/global_object/test_undo_redo.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/easyscience/Objects/variable/descriptor_number.py b/src/easyscience/Objects/variable/descriptor_number.py index c21c52e2..b8e2997b 100644 --- a/src/easyscience/Objects/variable/descriptor_number.py +++ b/src/easyscience/Objects/variable/descriptor_number.py @@ -176,6 +176,8 @@ def error(self) -> float: :return: Error associated with parameter """ + if self._scalar.variance is None: + return None return float(np.sqrt(self._scalar.variance)) @error.setter @@ -192,7 +194,9 @@ def error(self, value: float) -> None: if value < 0: raise ValueError(f'{value=} must be positive') value = float(value) - self._scalar.variance = value**2 + self._scalar.variance = value**2 + else: + self._scalar.variance = None def convert_unit(self, unit_str: str): """ diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 9eaa27cf..5adbf6b9 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -83,6 +83,7 @@ def getter(_obj, _attr): @pytest.mark.parametrize( "idx", [pytest.param(0, id="DescriptorNumber"), pytest.param(1, id="Parameter")] ) + def test_SinglesUndoRedo(idx, test): obj = createSingleObjs(idx) attr = test[0] From 37f4fd0a2f07106716f40a5a91c45080f07222bf Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 11:19:32 +0100 Subject: [PATCH 22/41] Update tests to handle units --- tests/unit_tests/global_object/test_undo_redo.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 5adbf6b9..8c4b6b7b 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -26,9 +26,9 @@ def createSingleObjs(idx): reps = math.floor(idx / len(alphabet)) + 1 name = alphabet[idx % len(alphabet)] * reps if idx % 2: - return Parameter(name, idx) + return Parameter(name, idx,unit="m/s") else: - return DescriptorNumber(name, idx) + return DescriptorNumber(name, idx,unit="m/s") def createParam(option): @@ -49,7 +49,10 @@ def getter(_obj, _attr): try: previous = getter(obj, attr) - setattr(obj, attr, future) + if attr == "unit" and hasattr(obj, "convert_unit"): + obj.convert_unit(future) + else: + setattr(obj, attr, future) assert getter(obj, attr) == future assert global_object.stack.canUndo() global_object.stack.undo() @@ -72,7 +75,7 @@ def getter(_obj, _attr): ("value", 500), ("error", 5), ("enabled", False), - ("unit", "meter / second"), + ("unit", "m/s"), ("display_name", "boom"), ("fixed", False), ("max", 505), From f97fe4f865279718ea469b0994b15f68380fedd1 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 12:58:17 +0100 Subject: [PATCH 23/41] investigated tests of undo --- src/easyscience/Objects/variable/parameter.py | 25 ---- .../global_object/test_undo_redo.py | 117 +++++++++--------- 2 files changed, 59 insertions(+), 83 deletions(-) diff --git a/src/easyscience/Objects/variable/parameter.py b/src/easyscience/Objects/variable/parameter.py index 2eb8e69d..10baf5b6 100644 --- a/src/easyscience/Objects/variable/parameter.py +++ b/src/easyscience/Objects/variable/parameter.py @@ -320,31 +320,6 @@ def bounds(self) -> Tuple[numbers.Number, numbers.Number]: """ return self.min, self.max - # @bounds.setter - # def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: - # """ - # Set the bounds of the parameter. *This will also enable the parameter*. - - # :param new_bound: New bounds. This should be a tuple of (min, max). - # """ - # old_min = self.min - # old_max = self.max - # new_min, new_max = new_bound - - # try: - # self.min = new_min - # self.max = new_max - # except ValueError: - # self.min = old_min - # self.max = old_max - # raise ValueError(f'Current paramter value: {self._scalar.value} must be within {new_bound=}') - - # # Enable the parameter if needed - # if not self.enabled: - # self.enabled = True - # # Free parameter if needed - # if self.fixed: - # self.fixed = False @bounds.setter def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: """ diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 8c4b6b7b..a2c78741 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -298,65 +298,66 @@ def __call__(self, x: np.ndarray) -> np.ndarray: assert l2.m.value == res.p[f"p{l2.m.unique_name}"] assert l2.c.value == res.p[f"p{l2.c.unique_name}"] - -# @pytest.mark.parametrize('math_funcs', [pytest.param([Parameter.__iadd__, float.__add__], id='Addition'), -# pytest.param([Parameter.__isub__, float.__sub__], id='Subtraction')]) +# TODO: Check if this test is needed +# @pytest.mark.parametrize('math_funcs', [pytest.param([Parameter.__add__, float.__add__], id='Addition'), +# pytest.param([Parameter.__sub__, float.__sub__], id='Subtraction')]) # def test_parameter_maths_basic(math_funcs): # a = 1.0 # b = 2.0 # sa = 0.1 # sb = 0.2 -# + # p_fun = math_funcs[0] # f_fun = math_funcs[1] -# + # result_value = f_fun(a, b) # result_error = (sa ** 2 + sb ** 2) ** 0.5 -# + # from easyscience import global_object # global_object.stack.enabled = True -# + # # Perform basic test # p1 = Parameter('a', a) # p2 = Parameter('b', b) -# -# p1 = p_fun(p1, p2) -# assert float(p1) == result_value -# global_object.stack.undo() -# assert float(p1) == a -# global_object.stack.redo() -# assert float(p1) == result_value -# -# # Perform basic + error -# p1 = Parameter('a', a, error=sa) -# p2 = Parameter('b', b, error=sb) -# p1 = p_fun(p1, p2) -# assert float(p1) == result_value -# assert p1.error == result_error -# global_object.stack.undo() -# assert float(p1) == a -# assert p1.error == sa -# global_object.stack.redo() -# assert float(p1) == result_value -# assert p1.error == result_error -# -# # Perform basic + units -# p1 = Parameter('a', a, error=sa, units='m/s') -# p2 = Parameter('b', b, error=sb, units='m/s') + # p1 = p_fun(p1, p2) -# assert float(p1) == result_value -# assert p1.error == result_error -# assert str(p1.unit) == 'meter / second' + +# assert p1.value == result_value # global_object.stack.undo() -# assert float(p1) == a -# assert p1.error == sa -# assert str(p1.unit) == 'meter / second' +# assert p1.value == a # global_object.stack.redo() -# assert float(p1) == result_value -# assert p1.error == result_error -# assert str(p1.unit) == 'meter / second' -# -# +# assert p1.value == result_value + + # # Perform basic + error + # p1 = Parameter('a', a, error=sa) + # p2 = Parameter('b', b, error=sb) + # p1 = p_fun(p1, p2) + # assert p1.value == result_value + # assert p1.error == result_error + # global_object.stack.undo() + # assert p1.value == a + # assert p1.error == sa + # global_object.stack.redo() + # assert p1.value == result_value + # assert p1.error == result_error + + # # Perform basic + units + # p1 = Parameter('a', a, error=sa, units='m/s') + # p2 = Parameter('b', b, error=sb, units='m/s') + # p1 = p_fun(p1, p2) + # assert p1.value == result_value + # assert p1.error == result_error + # assert str(p1.unit) == 'meter / second' + # global_object.stack.undo() + # assert p1.value == a + # assert p1.error == sa + # assert str(p1.unit) == 'meter / second' + # global_object.stack.redo() + # assert p1.value == result_value + # assert p1.error == result_error + # assert str(p1.unit) == 'meter / second' + + # @pytest.mark.parametrize('math_funcs', [pytest.param([Parameter.__imul__, float.__mul__, # 'meter ** 2 / second ** 2'], id='Multiplication'), # pytest.param([Parameter.__itruediv__, float.__truediv__, @@ -367,53 +368,53 @@ def __call__(self, x: np.ndarray) -> np.ndarray: # sa = 0.1 # sb = 0.2 # unit = 'meter / second' -# + # p_fun = math_funcs[0] # f_fun = math_funcs[1] # u_str = math_funcs[2] -# + # result_value = f_fun(a, b) # result_error = ((sa / a) ** 2 + (sb / b) ** 2) ** 0.5 * result_value -# + # from easyscience import global_object # global_object.stack.enabled = True -# + # # Perform basic test # p1 = Parameter('a', a) # p2 = Parameter('b', b) -# + # p1 = p_fun(p1, p2) -# assert float(p1) == result_value +# assert p1.value == result_value # global_object.stack.undo() -# assert float(p1) == a +# assert p1.value == a # global_object.stack.redo() -# assert float(p1) == result_value -# +# assert p1.value == result_value + # # Perform basic + error # p1 = Parameter('a', a, error=sa) # p2 = Parameter('b', b, error=sb) # p1 = p_fun(p1, p2) -# assert float(p1) == result_value +# assert p1.value == result_value # assert p1.error == result_error # global_object.stack.undo() -# assert float(p1) == a +# assert p1.value == a # assert p1.error == sa # global_object.stack.redo() -# assert float(p1) == result_value +# assert p1.value == result_value # assert p1.error == result_error -# + # # Perform basic + units # p1 = Parameter('a', a, error=sa, units=unit) # p2 = Parameter('b', b, error=sb, units=unit) # p1 = p_fun(p1, p2) -# assert float(p1) == result_value +# assert p1.value == result_value # assert p1.error == result_error # assert str(p1.unit) == u_str # global_object.stack.undo() -# assert float(p1) == a +# assert p1.value == a # assert p1.error == sa # assert str(p1.unit) == unit # global_object.stack.redo() -# assert float(p1) == result_value +# assert p1.value == result_value # assert p1.error == result_error # assert str(p1.unit) == u_str From 26085948c3d041d5788f0607a88aeb63bd8fa391 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 14:12:39 +0100 Subject: [PATCH 24/41] Added undo/redo functionality to convert_unit --- .../Objects/variable/descriptor_number.py | 43 +++++++++++++-- src/easyscience/Objects/variable/parameter.py | 1 - .../global_object/test_undo_redo.py | 52 +++++++++++++------ 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/easyscience/Objects/variable/descriptor_number.py b/src/easyscience/Objects/variable/descriptor_number.py index b8e2997b..bf68afc9 100644 --- a/src/easyscience/Objects/variable/descriptor_number.py +++ b/src/easyscience/Objects/variable/descriptor_number.py @@ -12,6 +12,7 @@ from scipp import UnitError from scipp import Variable +from easyscience.global_object.undo_redo import PropertyStack from easyscience.global_object.undo_redo import property_stack_deco from .descriptor_base import DescriptorBase @@ -198,19 +199,55 @@ def error(self, value: float) -> None: else: self._scalar.variance = None - def convert_unit(self, unit_str: str): + # def convert_unit(self, unit_str: str): + # """ + # Convert the value from one unit system to another. + + # :param unit_str: New unit in string form + # """ + # if not isinstance(unit_str, str): + # raise TypeError(f'{unit_str=} must be a string representing a valid scipp unit') + # try: + # new_unit = sc.Unit(unit_str) + # except UnitError as message: + # raise UnitError(message) from None + # self._scalar = self._scalar.to(unit=new_unit) + + def convert_unit(self, unit_str: str) -> None: """ - Convert the value from one unit system to another. + Convert the value from one unit system to another with undo/redo functionality. :param unit_str: New unit in string form """ if not isinstance(unit_str, str): raise TypeError(f'{unit_str=} must be a string representing a valid scipp unit') + try: new_unit = sc.Unit(unit_str) except UnitError as message: raise UnitError(message) from None - self._scalar = self._scalar.to(unit=new_unit) + + # Save the current state for undo/redo + old_scalar = self._scalar + + # Perform the unit conversion + try: + new_scalar = self._scalar.to(unit=new_unit) + except Exception as e: + raise UnitError(f"Failed to convert unit: {e}") from e + + # Define the setter function for the undo stack + def set_scalar(obj, scalar): + obj._scalar = scalar + + # Push to undo stack + self._global_object.stack.push( + PropertyStack(self, set_scalar, old_scalar, new_scalar, text=f"Convert unit to {unit_str}") + ) + + # Update the scalar + self._scalar = new_scalar + # Just to get return type right def __copy__(self) -> DescriptorNumber: diff --git a/src/easyscience/Objects/variable/parameter.py b/src/easyscience/Objects/variable/parameter.py index 10baf5b6..fd09c18f 100644 --- a/src/easyscience/Objects/variable/parameter.py +++ b/src/easyscience/Objects/variable/parameter.py @@ -319,7 +319,6 @@ def bounds(self) -> Tuple[numbers.Number, numbers.Number]: :return: Tuple of the parameters minimum and maximum values """ return self.min, self.max - @bounds.setter def bounds(self, new_bound: Tuple[numbers.Number, numbers.Number]) -> None: """ diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index a2c78741..651139c9 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -12,11 +12,10 @@ from easyscience.Objects.Groups import BaseCollection from easyscience.Objects.ObjectClasses import BaseObj -# from easyscience.Objects.Variable import Descriptor -# from easyscience.Objects.Variable import Parameter from easyscience.Objects.variable.parameter import Parameter from easyscience.Objects.variable.descriptor_str import DescriptorStr from easyscience.Objects.variable.descriptor_number import DescriptorNumber +from easyscience.Objects.variable.descriptor_bool import DescriptorBool from easyscience.fitting import Fitter @@ -67,6 +66,38 @@ def getter(_obj, _attr): return e +# @pytest.mark.parametrize( +# "test", +# [ +# createParam(option) +# for option in [ +# ("value", 500), +# ("error", 5), +# ("enabled", False), +# ("unit", "m/s"), +# ("display_name", "boom"), +# ("fixed", False), +# ("max", 505), +# ("min", -1), +# ] +# ], +# ) +# @pytest.mark.parametrize( +# "idx", [pytest.param(0, id="DescriptorNumber"), pytest.param(1, id="Parameter")] +# ) + +# def test_SinglesUndoRedo(idx, test): +# obj = createSingleObjs(idx) +# attr = test[0] +# value = test[1] + +# if not hasattr(obj, attr): +# pytest.skip(f"Not applicable: {obj} does not have field {attr}") +# e = doUndoRedo(obj, attr, value) +# if e: +# raise e + + @pytest.mark.parametrize( "test", [ @@ -74,31 +105,20 @@ def getter(_obj, _attr): for option in [ ("value", 500), ("error", 5), - ("enabled", False), - ("unit", "m/s"), + ("unit", "km/s"), ("display_name", "boom"), - ("fixed", False), - ("max", 505), - ("min", -1), ] ], ) -@pytest.mark.parametrize( - "idx", [pytest.param(0, id="DescriptorNumber"), pytest.param(1, id="Parameter")] -) - -def test_SinglesUndoRedo(idx, test): - obj = createSingleObjs(idx) +def test_DescriptorNumberUndoRedo(test): + obj = DescriptorNumber('DescriptorNumber',1,unit='m/s') attr = test[0] value = test[1] - if not hasattr(obj, attr): - pytest.skip(f"Not applicable: {obj} does not have field {attr}") e = doUndoRedo(obj, attr, value) if e: raise e - @pytest.mark.parametrize("value", (True, False)) def test_Parameter_Bounds_UndoRedo(value): from easyscience import global_object From 988f0bff567d0fe67a64f699e928695f21504e9d Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 14:30:21 +0100 Subject: [PATCH 25/41] Added test of undo/redo for Parameter --- .../Objects/variable/descriptor_number.py | 6 ++-- src/easyscience/Objects/variable/parameter.py | 6 ++-- .../global_object/test_undo_redo.py | 33 ++++++++++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/easyscience/Objects/variable/descriptor_number.py b/src/easyscience/Objects/variable/descriptor_number.py index bf68afc9..16e7521d 100644 --- a/src/easyscience/Objects/variable/descriptor_number.py +++ b/src/easyscience/Objects/variable/descriptor_number.py @@ -45,8 +45,7 @@ def __init__( param url: URL of the descriptor param display_name: Display name of the descriptor param parent: Parent of the descriptor - - .. note:: Undo/Redo functionality is implemented for the attributes `variance` and `value`. + .. note:: Undo/Redo functionality is implemented for the attributes `variance`, `error`, `unit` and `value`. """ if not isinstance(value, numbers.Number) or isinstance(value, bool): raise TypeError(f'{value=} must be a number') @@ -215,13 +214,12 @@ def error(self, value: float) -> None: def convert_unit(self, unit_str: str) -> None: """ - Convert the value from one unit system to another with undo/redo functionality. + Convert the value from one unit system to another. :param unit_str: New unit in string form """ if not isinstance(unit_str, str): raise TypeError(f'{unit_str=} must be a string representing a valid scipp unit') - try: new_unit = sc.Unit(unit_str) except UnitError as message: diff --git a/src/easyscience/Objects/variable/parameter.py b/src/easyscience/Objects/variable/parameter.py index fd09c18f..03b3c230 100644 --- a/src/easyscience/Objects/variable/parameter.py +++ b/src/easyscience/Objects/variable/parameter.py @@ -76,8 +76,8 @@ def __init__( :param parent: The object which is the parent to this one .. note:: - Undo/Redo functionality is implemented for the attributes `value`, `error`, `min`, `max`, `fixed` - """ + Undo/Redo functionality is implemented for the attributes `value`, `variance`, `error`, `min`, `max`, `bounds`, `fixed`, `unit` + """ # noqa: E501 if not isinstance(min, numbers.Number): raise TypeError('`min` must be a number') if not isinstance(max, numbers.Number): @@ -94,6 +94,7 @@ def __init__( if not isinstance(fixed, bool): raise TypeError('`fixed` must be either True or False') + self._fixed = fixed # For fitting, but must be initialized before super().__init__ self._min = sc.scalar(float(min), unit=unit) self._max = sc.scalar(float(max), unit=unit) @@ -114,7 +115,6 @@ def __init__( weakref.finalize(self, self._callback.fdel) # Create additional fitting elements - self._fixed = fixed self._enabled = enabled self._initial_scalar = copy.deepcopy(self._scalar) builtin_constraint = { diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 651139c9..4fe70141 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -96,7 +96,10 @@ def getter(_obj, _attr): # e = doUndoRedo(obj, attr, value) # if e: # raise e - +# ("enabled", False), +# ("fixed", False), +# ("max", 505), +# ("min", -1), @pytest.mark.parametrize( "test", @@ -110,6 +113,7 @@ def getter(_obj, _attr): ] ], ) + def test_DescriptorNumberUndoRedo(test): obj = DescriptorNumber('DescriptorNumber',1,unit='m/s') attr = test[0] @@ -119,6 +123,33 @@ def test_DescriptorNumberUndoRedo(test): if e: raise e + +@pytest.mark.parametrize( + "test", + [ + createParam(option) + for option in [ + ("value", 500), + ("error", 5), + ("unit", "km/s"), + ("display_name", "boom"), + ("enabled", False), + ("fixed", False), + ("max", 505), + ("min", -1), + ] + ], +) + +def test_ParameterUndoRedo(test): + obj = Parameter('Parameter',1,unit='m/s') + attr = test[0] + value = test[1] + + e = doUndoRedo(obj, attr, value) + if e: + raise e + @pytest.mark.parametrize("value", (True, False)) def test_Parameter_Bounds_UndoRedo(value): from easyscience import global_object From d10f3f8c2be7345679b1eb0aeda035d7777524bd Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 14:35:26 +0100 Subject: [PATCH 26/41] Added test of undo/redo for DescriptorBool --- tests/unit_tests/global_object/test_undo_redo.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 4fe70141..2c546e9f 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -123,7 +123,23 @@ def test_DescriptorNumberUndoRedo(test): if e: raise e +def test_DescriptorBoolUndoRedo(): + obj = DescriptorBool('DescriptorBool',False) + attr = 'value' + value = True + e = doUndoRedo(obj, attr, value) + if e: + raise e + + obj = DescriptorBool('DescriptorBool',True) + attr = 'value' + value = False + + e = doUndoRedo(obj, attr, value) + if e: + raise e + @pytest.mark.parametrize( "test", [ From 22964713d9868ddf54d03209831fea56665a4398 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 14:37:24 +0100 Subject: [PATCH 27/41] Added test of undo/redo for DescriptorStr --- .../global_object/test_undo_redo.py | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index 2c546e9f..f660ade1 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -65,42 +65,6 @@ def getter(_obj, _attr): global_object.stack.enabled = False return e - -# @pytest.mark.parametrize( -# "test", -# [ -# createParam(option) -# for option in [ -# ("value", 500), -# ("error", 5), -# ("enabled", False), -# ("unit", "m/s"), -# ("display_name", "boom"), -# ("fixed", False), -# ("max", 505), -# ("min", -1), -# ] -# ], -# ) -# @pytest.mark.parametrize( -# "idx", [pytest.param(0, id="DescriptorNumber"), pytest.param(1, id="Parameter")] -# ) - -# def test_SinglesUndoRedo(idx, test): -# obj = createSingleObjs(idx) -# attr = test[0] -# value = test[1] - -# if not hasattr(obj, attr): -# pytest.skip(f"Not applicable: {obj} does not have field {attr}") -# e = doUndoRedo(obj, attr, value) -# if e: -# raise e -# ("enabled", False), -# ("fixed", False), -# ("max", 505), -# ("min", -1), - @pytest.mark.parametrize( "test", [ @@ -139,7 +103,16 @@ def test_DescriptorBoolUndoRedo(): e = doUndoRedo(obj, attr, value) if e: raise e - + +def test_DescriptorStrUndoRedo(): + obj = DescriptorStr('DescriptorStr','Foo') + attr = 'value' + value = 'Bar' + + e = doUndoRedo(obj, attr, value) + if e: + raise e + @pytest.mark.parametrize( "test", [ From b6dcc2063dc2574d2c46d4b2422349047edc1e8c Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 15:13:51 +0100 Subject: [PATCH 28/41] Fix failing tests --- tests/unit_tests/Objects/test_Groups.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit_tests/Objects/test_Groups.py b/tests/unit_tests/Objects/test_Groups.py index e61d5e70..3850b5f3 100644 --- a/tests/unit_tests/Objects/test_Groups.py +++ b/tests/unit_tests/Objects/test_Groups.py @@ -381,6 +381,7 @@ def testit(item1, item2): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_from_dict(cls): + global_object.map._clear() #TODO: figure out why this test fails without this line name = "testing" kwargs = {"p1": DescriptorNumber("par1", 1)} expected = cls.from_dict(test_dict) @@ -437,6 +438,7 @@ def test_baseCollection_iterator(cls): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_iterator_dict(cls): + global_object.map._clear() #TODO: figure out why this test fails without this line name = "test" p1 = Parameter("p1", 1) p2 = Parameter("p2", 2) @@ -456,6 +458,7 @@ def test_baseCollection_iterator_dict(cls): @pytest.mark.parametrize("cls", class_constructors) def test_baseCollection_sameName(cls): + global_object.map._clear() #TODO: figure out why this test fails without this line name = "test" p1 = Parameter("p1", 1) p2 = Parameter("p1", 2) From a99c40571c3f7cb93f670464da2b08ee3282e6b7 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 15:18:41 +0100 Subject: [PATCH 29/41] removed old TODOs --- src/easyscience/Objects/ObjectClasses.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/easyscience/Objects/ObjectClasses.py b/src/easyscience/Objects/ObjectClasses.py index 35c6937e..e6a159ad 100644 --- a/src/easyscience/Objects/ObjectClasses.py +++ b/src/easyscience/Objects/ObjectClasses.py @@ -157,7 +157,6 @@ def constraints(self) -> List[C]: constraints.append(con[key]) return constraints - ## TODO clean when full move to new_variable. Is fixed def get_parameters(self) -> List[Parameter]: """ Get all parameter objects as a list. @@ -172,7 +171,6 @@ def get_parameters(self) -> List[Parameter]: par_list.append(item) return par_list - ## TODO clean when full move to new_variable. Is fixed def _get_linkable_attributes(self) -> List[V]: """ Get all objects which can be linked against as a list. @@ -187,7 +185,6 @@ def _get_linkable_attributes(self) -> List[V]: item_list.append(item) return item_list - ## TODO clean when full move to new_variable. Is fixed def get_fit_parameters(self) -> List[Parameter]: """ Get all objects which can be fitted (and are not fixed) as a list. @@ -232,7 +229,6 @@ class BaseObj(BasedBase): cheat with `BaseObj(*[Descriptor(...), Parameter(...), ...])`. """ - ## TODO clean when full move to new_variable. Is fixed def __init__( self, name: str, @@ -307,7 +303,6 @@ def __init__(self, foo: Parameter, bar: Parameter): test_class=BaseObj, ) - ## TODO clean when full move to new_variable. Is fixed def __setattr__(self, key: str, value: BV) -> None: # Assume that the annotation is a ClassVar old_obj = None From 13565b134ac2b6917cb4423b2f1fafa0cf37151c Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Wed, 15 Jan 2025 15:30:18 +0100 Subject: [PATCH 30/41] More cleanup when move to new variable --- src/easyscience/Constraints.py | 37 ++++++---------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/src/easyscience/Constraints.py b/src/easyscience/Constraints.py index e181dcf4..4dcad12a 100644 --- a/src/easyscience/Constraints.py +++ b/src/easyscience/Constraints.py @@ -195,12 +195,8 @@ def __init__(self, dependent_obj: V, operator: str, value: Number): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.variable.parameter - if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): - value = obj.value_no_call_back - else: - value = obj.raw_value + value = obj.value_no_call_back if isinstance(value, list): value = np.array(value) @@ -259,12 +255,7 @@ def __init__(self, dependent_obj: V, operator: str, value: str): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.variable.parameter - - if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): - value = obj.value_no_call_back - else: - value = obj.raw_value + value = obj.value_no_call_back self.aeval.symtable['value1'] = value self.aeval.symtable['value2'] = getattr(obj, self.value) @@ -323,12 +314,7 @@ def __init__(self, dependent_obj: V, operator: str, independent_obj: V): def _parse_operator(self, obj: V, *args, **kwargs) -> Number: ## TODO clean when full move to new_variable - import easyscience.Objects.variable.parameter - - if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): - value = obj.value_no_call_back - else: - value = obj.raw_value + value = obj.value_no_call_back self.aeval.symtable['value1'] = value try: @@ -417,16 +403,12 @@ def __init__( self.external = True def _parse_operator(self, independent_objs: List[V], *args, **kwargs) -> Number: - import easyscience.Objects.variable.parameter in_str = '' value = None for idx, obj in enumerate(independent_objs): ## TODO clean when full move to new_variable - if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): - self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.value_no_call_back - else: - self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.raw_value + self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.value_no_call_back in_str += ' p' + str(self.independent_obj_ids[idx]) if idx < len(self.operator): @@ -485,25 +467,18 @@ def __init__( self.external = True def _parse_operator(self, obj: V, *args, **kwargs) -> Number: - import easyscience.Objects.variable.parameter self.aeval.symtable[f'f{id(self.function)}'] = self.function value_str = f'r_value = f{id(self.function)}(' if isinstance(obj, list): for o in obj: ## TODO clean when full move to new_variable - if isinstance(o, easyscience.Objects.variable.parameter.Parameter): - value_str += f'{o.value_no_call_back},' - else: - value_str += f'{o.raw_value},' + value_str += f'{o.value_no_call_back},' value_str = value_str[:-1] else: ## TODO clean when full move to new_variable - if isinstance(obj, easyscience.Objects.variable.parameter.Parameter): - value_str += f'{obj.value_no_call_back}' - else: - value_str += f'{obj.raw_value}' + value_str += f'{obj.value_no_call_back}' value_str += ')' try: From d05d8ec9afc5198f97ff441eccb51b6eb39e68d1 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:13:00 +0100 Subject: [PATCH 31/41] clean minimizer_base --- .../fitting/minimizers/minimizer_base.py | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_base.py b/src/easyscience/fitting/minimizers/minimizer_base.py index 2d8bb2fa..02130a6e 100644 --- a/src/easyscience/fitting/minimizers/minimizer_base.py +++ b/src/easyscience/fitting/minimizers/minimizer_base.py @@ -194,11 +194,7 @@ def _prepare_parameters(self, parameters: dict[str, float]) -> dict[str, float]: 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, Parameter): - parameters[parameter_name] = item.value - else: - parameters[parameter_name] = item.raw_value + parameters[parameter_name] = item.value return parameters def _generate_fit_function(self) -> Callable: @@ -235,15 +231,9 @@ def _fit_function(x: np.ndarray, **kwargs): for name, value in kwargs.items(): par_name = name[1:] if par_name in self._cached_pars.keys(): - # TODO clean when full move to new_variable - if isinstance(self._cached_pars[par_name], Parameter): # This will take into account constraints - if self._cached_pars[par_name].value != value: - self._cached_pars[par_name].value = value - else: - # This will take into account constraints - if self._cached_pars[par_name].raw_value != value: - self._cached_pars[par_name].value = value + if self._cached_pars[par_name].value != value: + self._cached_pars[par_name].value = value # Since we are calling the parameter fset will be called. # TODO Pre processing here @@ -268,11 +258,7 @@ def _create_signature(parameters: Dict[int, Parameter]) -> Signature: wrapped_parameters.append(InspectParameter('x', InspectParameter.POSITIONAL_OR_KEYWORD, annotation=_empty)) for name, parameter in parameters.items(): - ## TODO clean when full move to new_variable - if isinstance(parameter, Parameter): - default_value = parameter.value - else: - default_value = parameter.raw_value + default_value = parameter.value wrapped_parameters.append( InspectParameter( From 2302e7af17381d0e8afd3b5d91442761b9990093 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:14:00 +0100 Subject: [PATCH 32/41] clean minimizer_bumps --- .../fitting/minimizers/minimizer_bumps.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_bumps.py b/src/easyscience/fitting/minimizers/minimizer_bumps.py index ae26a751..14df1d0f 100644 --- a/src/easyscience/fitting/minimizers/minimizer_bumps.py +++ b/src/easyscience/fitting/minimizers/minimizer_bumps.py @@ -122,11 +122,7 @@ def fit( model = model_function(x, y, weights) self._cached_model = model - ## TODO clean when full move to new_variable - 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: - self._p_0 = {f'p{key}': self._cached_pars[key].raw_value for key in self._cached_pars.keys()} + self._p_0 = {f'p{key}': self._cached_pars[key].value for key in self._cached_pars.keys()} problem = FitProblem(model) # Why do we do this? Because a fitting template has to have global_object instantiated outside pre-runtime @@ -170,11 +166,7 @@ def convert_to_par_object(obj) -> BumpsParameter: :rtype: BumpsParameter """ - ## TODO clean when full move to new_variable - if isinstance(obj, Parameter): - value = obj.value - else: - value = obj.raw_value + value = obj.value return BumpsParameter( name=MINIMIZER_PARAMETER_PREFIX + obj.unique_name, @@ -253,11 +245,7 @@ def _gen_fit_results(self, fit_results, **kwargs) -> FitResults: for index, name in enumerate(self._cached_model._pnames): dict_name = name[len(MINIMIZER_PARAMETER_PREFIX) :] - ## TODO clean when full move to new_variable - if isinstance(pars[dict_name], Parameter): - item[name] = pars[dict_name].value - else: - item[name] = pars[dict_name].raw_value + item[name] = pars[dict_name].value results.p0 = self._p_0 results.p = item From 5c9b641d60917238318cd0035c0364f8de64835b Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:15:49 +0100 Subject: [PATCH 33/41] clean minimize_dfo --- .../fitting/minimizers/minimizer_dfo.py | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_dfo.py b/src/easyscience/fitting/minimizers/minimizer_dfo.py index e45b491f..27f7eba4 100644 --- a/src/easyscience/fitting/minimizers/minimizer_dfo.py +++ b/src/easyscience/fitting/minimizers/minimizer_dfo.py @@ -96,11 +96,7 @@ def fit( self._cached_model.x = x self._cached_model.y = y - ## TODO clean when full move to new_variable - 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: - self._p_0 = {f'p{key}': self._cached_pars[key].raw_value for key in self._cached_pars.keys()} + self._p_0 = {f'p{key}': self._cached_pars[key].value for key in self._cached_pars.keys()} # Why do we do this? Because a fitting template has to have global_object instantiated outside pre-runtime from easyscience import global_object @@ -145,22 +141,14 @@ 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 dfo_pars = {} if not parameters: for name, par in obj._cached_pars.items(): - if isinstance(par, Parameter): - dfo_pars[MINIMIZER_PARAMETER_PREFIX + str(name)] = par.value - else: - dfo_pars[MINIMIZER_PARAMETER_PREFIX + str(name)] = par.raw_value - + dfo_pars[MINIMIZER_PARAMETER_PREFIX + str(name)] = par.value else: for par in parameters: - 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 + dfo_pars[MINIMIZER_PARAMETER_PREFIX + par.unique_name] = par.value def _residuals(pars_values: List[float]) -> np.ndarray: for idx, par_name in enumerate(dfo_pars.keys()): @@ -217,11 +205,7 @@ 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 - if isinstance(par, Parameter): - pars[f'p{p_name}'] = par.value - else: - pars[f'p{p_name}'] = par.raw_value + pars[f'p{p_name}'] = par.value results.p = pars results.p0 = self._p_0 @@ -254,11 +238,7 @@ def _dfo_fit( :return: dfols fit results container """ - ## TODO clean when full move to new_variable - 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()]) + pars_values = np.array([par.value for par in pars.values()]) bounds = ( np.array([par.min for par in pars.values()]), From 84390b8cf1507f1c1507ce87276312a9edef5549 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:16:23 +0100 Subject: [PATCH 34/41] clean minimizer_lmfit --- .../fitting/minimizers/minimizer_lmfit.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/easyscience/fitting/minimizers/minimizer_lmfit.py b/src/easyscience/fitting/minimizers/minimizer_lmfit.py index 3662f580..bb29e8de 100644 --- a/src/easyscience/fitting/minimizers/minimizer_lmfit.py +++ b/src/easyscience/fitting/minimizers/minimizer_lmfit.py @@ -180,11 +180,7 @@ def convert_to_par_object(parameter: Parameter) -> LMParameter: :return: lmfit Parameter compatible object. :rtype: LMParameter """ - ## TODO clean when full move to - if isinstance(parameter, Parameter): - value = parameter.value - else: - value = parameter.raw_value + value = parameter.value return LMParameter( MINIMIZER_PARAMETER_PREFIX + parameter.unique_name, @@ -221,11 +217,7 @@ def _make_model(self, pars: Optional[LMParameters] = None) -> LMModel: if isinstance(item, LMParameter): value = item.value else: - ## TODO clean when full move to new_variable - if isinstance(item, Parameter): - value = item.value - else: - value = item.raw_value + value = item.value model.set_param_hint(MINIMIZER_PARAMETER_PREFIX + str(name), value=value, min=item.min, max=item.max) From 1b83cd3b427ee4d7e74fdf83ce1072c770cffbcc Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:18:40 +0100 Subject: [PATCH 35/41] Updated todos --- src/easyscience/Constraints.py | 7 +------ src/easyscience/Objects/Inferface.py | 9 ++------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/easyscience/Constraints.py b/src/easyscience/Constraints.py index 4dcad12a..9628db9e 100644 --- a/src/easyscience/Constraints.py +++ b/src/easyscience/Constraints.py @@ -194,7 +194,7 @@ def __init__(self, dependent_obj: V, operator: str, value: Number): super(NumericConstraint, self).__init__(dependent_obj, operator=operator, value=value) def _parse_operator(self, obj: V, *args, **kwargs) -> Number: - ## TODO clean when full move to new_variable + ## TODO Probably needs to be updated when DescriptorArray is implemented value = obj.value_no_call_back @@ -254,7 +254,6 @@ def __init__(self, dependent_obj: V, operator: str, value: str): super(SelfConstraint, self).__init__(dependent_obj, operator=operator, value=value) def _parse_operator(self, obj: V, *args, **kwargs) -> Number: - ## TODO clean when full move to new_variable value = obj.value_no_call_back self.aeval.symtable['value1'] = value @@ -313,7 +312,6 @@ def __init__(self, dependent_obj: V, operator: str, independent_obj: V): self.external = True def _parse_operator(self, obj: V, *args, **kwargs) -> Number: - ## TODO clean when full move to new_variable value = obj.value_no_call_back self.aeval.symtable['value1'] = value @@ -407,7 +405,6 @@ def _parse_operator(self, independent_objs: List[V], *args, **kwargs) -> Number: in_str = '' value = None for idx, obj in enumerate(independent_objs): - ## TODO clean when full move to new_variable self.aeval.symtable['p' + str(self.independent_obj_ids[idx])] = obj.value_no_call_back in_str += ' p' + str(self.independent_obj_ids[idx]) @@ -472,12 +469,10 @@ def _parse_operator(self, obj: V, *args, **kwargs) -> Number: value_str = f'r_value = f{id(self.function)}(' if isinstance(obj, list): for o in obj: - ## TODO clean when full move to new_variable value_str += f'{o.value_no_call_back},' value_str = value_str[:-1] else: - ## TODO clean when full move to new_variable value_str += f'{obj.value_no_call_back}' value_str += ')' diff --git a/src/easyscience/Objects/Inferface.py b/src/easyscience/Objects/Inferface.py index e1d324b7..cb37b27e 100644 --- a/src/easyscience/Objects/Inferface.py +++ b/src/easyscience/Objects/Inferface.py @@ -152,7 +152,6 @@ def generate_bindings(self, model, *args, ifun=None, **kwargs): :return: binding property :rtype: property """ - import easyscience.Objects.variable.parameter class_links = self.__interface_obj.create(model) props = model._get_linkable_attributes() @@ -164,12 +163,8 @@ def generate_bindings(self, model, *args, ifun=None, **kwargs): idx = props_names.index(item_key) prop = props[idx] - ## TODO clean when full move to new_variable - if isinstance(prop, easyscience.Objects.variable.parameter.Parameter): - # Should be fetched this way to ensure we don't get value from callback - prop_value = prop.value_no_call_back - else: - prop_value = prop.raw_value + # Should be fetched this way to ensure we don't get value from callback + prop_value = prop.value_no_call_back prop._callback = item.make_prop(item_key) prop._callback.fset(prop_value) From 596be0b847e98cd3e6630c731ddddb3111773069 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:21:28 +0100 Subject: [PATCH 36/41] update test to new parameter --- tests/unit_tests/Fitting/minimizers/test_minimizer_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py index d89935a7..281b97e6 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_base.py @@ -110,9 +110,9 @@ def test_prepare_parameters(self, minimizer: MinimizerBase): 'b': MagicMock(), 'c': MagicMock() } - minimizer._cached_pars['a'].raw_value = 3 - minimizer._cached_pars['b'].raw_value = 4 - minimizer._cached_pars['c'].raw_value = 5 + minimizer._cached_pars['a'].value = 3 + minimizer._cached_pars['b'].value = 4 + minimizer._cached_pars['c'].value = 5 # Then parameters = minimizer._prepare_parameters(parameters) From 2f9f1f73f631ad4e9eb272829e03d929b9cb1b4e Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:23:29 +0100 Subject: [PATCH 37/41] update tests --- src/easyscience/Objects/Groups.py | 2 +- .../Fitting/minimizers/test_minimizer_bumps.py | 10 +++++----- .../Fitting/minimizers/test_minimizer_dfo.py | 14 +++++++------- tests/unit_tests/Objects/test_BaseObj.py | 4 ++-- tests/unit_tests/utils/io_tests/test_dict.py | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index dfe5a177..b75a1d9c 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -242,7 +242,7 @@ def sort(self, mapping: Callable[[Union[B, V]], Any], reverse: bool = False) -> """ Sort the collection according to the given mapping. - :param mapping: mapping function to sort the collection. i.e. lambda parameter: parameter.raw_value + :param mapping: mapping function to sort the collection. i.e. lambda parameter: parameter.value :type mapping: Callable :param reverse: Reverse the sorting. :type reverse: bool diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py index 42050a86..7feaf8e0 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_bumps.py @@ -57,7 +57,7 @@ def test_fit(self, minimizer: Bumps, monkeypatch) -> None: minimizer._gen_fit_results = MagicMock(return_value='gen_fit_results') cached_par = MagicMock() - cached_par.raw_value = 1 + cached_par.value = 1 cached_pars = {'mock_parm_1': cached_par} minimizer._cached_pars = cached_pars @@ -140,9 +140,9 @@ def test_gen_fit_results(self, minimizer: Bumps, monkeypatch): minimizer._cached_model = mock_cached_model mock_cached_par_1 = MagicMock() - mock_cached_par_1.raw_value = 'par_raw_value_1' + mock_cached_par_1.value = 'par_value_1' mock_cached_par_2 = MagicMock() - mock_cached_par_2.raw_value = 'par_raw_value_2' + mock_cached_par_2.value = 'par_value_2' minimizer._cached_pars = {'par_1': mock_cached_par_1, 'par_2': mock_cached_par_2} minimizer._p_0 = 'p_0' @@ -157,10 +157,10 @@ def test_gen_fit_results(self, minimizer: Bumps, monkeypatch): assert domain_fit_results.success == True assert domain_fit_results.y_obs == 'y' assert domain_fit_results.x == 'x' - assert domain_fit_results.p == {'ppar_1': 'par_raw_value_1', 'ppar_2': 'par_raw_value_2'} + assert domain_fit_results.p == {'ppar_1': 'par_value_1', 'ppar_2': 'par_value_2'} assert domain_fit_results.p0 == 'p_0' assert domain_fit_results.y_calc == 'evaluate' assert domain_fit_results.y_err == 'dy' assert str(domain_fit_results.minimizer_engine) == "" assert domain_fit_results.fit_args is None - minimizer.evaluate.assert_called_once_with('x', minimizer_parameters={'ppar_1': 'par_raw_value_1', 'ppar_2': 'par_raw_value_2'}) + minimizer.evaluate.assert_called_once_with('x', minimizer_parameters={'ppar_1': 'par_value_1', 'ppar_2': 'par_value_2'}) diff --git a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py index 30006198..8c39b8a5 100644 --- a/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py +++ b/tests/unit_tests/Fitting/minimizers/test_minimizer_dfo.py @@ -53,7 +53,7 @@ def test_fit(self, minimizer: DFO) -> None: minimizer._gen_fit_results = MagicMock(return_value='gen_fit_results') cached_par = MagicMock() - cached_par.raw_value = 1 + cached_par.value = 1 cached_pars = {'mock_parm_1': cached_par} minimizer._cached_pars = cached_pars @@ -104,10 +104,10 @@ def test_make_model(self, minimizer: DFO) -> None: mock_parm_1 = MagicMock() mock_parm_1.unique_name = 'mock_parm_1' - mock_parm_1.raw_value = 1000.0 + mock_parm_1.value = 1000.0 mock_parm_2 = MagicMock() mock_parm_2.unique_name = 'mock_parm_2' - mock_parm_2.raw_value = 2000.0 + mock_parm_2.value = 2000.0 # Then model = minimizer._make_model(parameters=[mock_parm_1, mock_parm_2]) @@ -160,9 +160,9 @@ def test_gen_fit_results(self, minimizer: DFO, monkeypatch): minimizer._cached_model = mock_cached_model mock_cached_par_1 = MagicMock() - mock_cached_par_1.raw_value = 'par_raw_value_1' + mock_cached_par_1.value = 'par_value_1' mock_cached_par_2 = MagicMock() - mock_cached_par_2.raw_value = 'par_raw_value_2' + mock_cached_par_2.value = 'par_value_2' minimizer._cached_pars = {'par_1': mock_cached_par_1, 'par_2': mock_cached_par_2} minimizer._p_0 = 'p_0' @@ -177,13 +177,13 @@ def test_gen_fit_results(self, minimizer: DFO, monkeypatch): assert domain_fit_results.success == True assert domain_fit_results.y_obs == 'y' assert domain_fit_results.x == 'x' - assert domain_fit_results.p == {'ppar_1': 'par_raw_value_1', 'ppar_2': 'par_raw_value_2'} + assert domain_fit_results.p == {'ppar_1': 'par_value_1', 'ppar_2': 'par_value_2'} assert domain_fit_results.p0 == 'p_0' assert domain_fit_results.y_calc == 'evaluate' assert domain_fit_results.y_err == 'weights' assert str(domain_fit_results.minimizer_engine) == "" assert domain_fit_results.fit_args is None - minimizer.evaluate.assert_called_once_with('x', minimizer_parameters={'ppar_1': 'par_raw_value_1', 'ppar_2': 'par_raw_value_2'}) + minimizer.evaluate.assert_called_once_with('x', minimizer_parameters={'ppar_1': 'par_value_1', 'ppar_2': 'par_value_2'}) def test_dfo_fit(self, minimizer: DFO, monkeypatch): # When diff --git a/tests/unit_tests/Objects/test_BaseObj.py b/tests/unit_tests/Objects/test_BaseObj.py index 11260e3b..d95d9b0a 100644 --- a/tests/unit_tests/Objects/test_BaseObj.py +++ b/tests/unit_tests/Objects/test_BaseObj.py @@ -410,11 +410,11 @@ def from_pars(cls, a: float): a = A.from_pars(a_start) graph = a._global_object.map - assert a.a.raw_value == a_start + assert a.a.value == a_start assert len(graph.get_edges(a)) == 1 setattr(a, "a", a_end) - assert a.a.raw_value == a_end + assert a.a.value == a_end assert len(graph.get_edges(a)) == 1 diff --git a/tests/unit_tests/utils/io_tests/test_dict.py b/tests/unit_tests/utils/io_tests/test_dict.py index 5c68b14d..884f86b6 100644 --- a/tests/unit_tests/utils/io_tests/test_dict.py +++ b/tests/unit_tests/utils/io_tests/test_dict.py @@ -350,7 +350,7 @@ def test_group_encode2(): # # def test_objs(reference_obj, test_obj, in_dict): # if 'value' in in_dict.keys(): -# in_dict['raw_value'] = in_dict.pop('value') +# in_dict['value'] = in_dict.pop('value') # if 'units' in in_dict.keys(): # del in_dict['units'] # for k in in_dict.keys(): From 43657603400e3974d853e37bd9e8b9387699b9c3 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:29:22 +0100 Subject: [PATCH 38/41] make ruff happy --- src/easyscience/Objects/Groups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/easyscience/Objects/Groups.py b/src/easyscience/Objects/Groups.py index b75a1d9c..90d4f0c6 100644 --- a/src/easyscience/Objects/Groups.py +++ b/src/easyscience/Objects/Groups.py @@ -15,12 +15,12 @@ from typing import List from typing import Optional from typing import Tuple -from typing import Union from typing import TypeVar +from typing import Union from easyscience.global_object.undo_redo import NotarizedDict -from easyscience.Objects.variable.descriptor_base import DescriptorBase from easyscience.Objects.ObjectClasses import BasedBase +from easyscience.Objects.variable.descriptor_base import DescriptorBase if TYPE_CHECKING: from easyscience.Objects.Inferface import iF From b9213ad10b49dd181667a98f216229d7d3e7d2a0 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Fri, 17 Jan 2025 09:55:23 +0100 Subject: [PATCH 39/41] Reintroduced tests that I should not have deleted --- .../variable/test_descriptor_from_legacy.py | 215 +++++++++ .../variable/test_parameter_from_legacy.py | 424 ++++++++++++++++++ 2 files changed, 639 insertions(+) create mode 100644 tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py create mode 100644 tests/unit_tests/Objects/variable/test_parameter_from_legacy.py diff --git a/tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py b/tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py new file mode 100644 index 00000000..038697ae --- /dev/null +++ b/tests/unit_tests/Objects/variable/test_descriptor_from_legacy.py @@ -0,0 +1,215 @@ +# SPDX-FileCopyrightText: 2023 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +# © 2021-2023 Contributors to the EasyScience project " + d = DescriptorNumber("test", 1, unit="cm") + assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 cm>" + + +def test_descriptor_number_as_dict(): + d = DescriptorNumber("test", 1) + result = d.as_dict() + expected = { + "@module": DescriptorNumber.__module__, + "@class": DescriptorNumber.__name__, + "@version": easyscience.__version__, + "name": "test", + "value": 1, + "unit": "dimensionless", + "description": "", + "url": "", + "display_name": "test", + "callback": None, + } + for key in expected.keys(): + if key == "callback": + continue + assert result[key] == expected[key] + + +@pytest.mark.parametrize( + "reference, constructor", + ( + [ + { + "@module": DescriptorBool.__module__, + "@class": DescriptorBool.__name__, + "@version": easyscience.__version__, + "name": "test", + "value": False, + "description": "", + "url": "", + "display_name": "test", + }, + DescriptorBool, + ], + [ + { + "@module": DescriptorNumber.__module__, + "@class": DescriptorNumber.__name__, + "@version": easyscience.__version__, + "name": "test", + "value": 1, + "unit": "dimensionless", + "variance": 0.0, + "description": "", + "url": "", + "display_name": "test", + }, + DescriptorNumber, + ], + [ + { + "@module": DescriptorStr.__module__, + "@class": DescriptorStr.__name__, + "@version": easyscience.__version__, + "name": "test", + "value": "string", + "description": "", + "url": "", + "display_name": "test", + }, + DescriptorStr, + ], + ), + ids=["DescriptorBool", "DescriptorNumber", "DescriptorStr"], +) +def test_item_from_dict(reference, constructor): + d = constructor.from_dict(reference) + for key, item in reference.items(): + if key.startswith("@"): + continue + obtained = getattr(d, key) + assert obtained == item + + +@pytest.mark.parametrize("value", ("This is ", "a fun ", "test")) +def test_parameter_display_name(value): + p = DescriptorNumber("test", 1, display_name=value) + assert p.display_name == value + + +def test_item_boolean_value(): + item = DescriptorBool("test", True) + assert item.value is True + item.value = False + assert item.value is False + + item = DescriptorBool("test", False) + assert item.value is False + item.value = True + assert item.value is True diff --git a/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py b/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py new file mode 100644 index 00000000..f4dcd2ea --- /dev/null +++ b/tests/unit_tests/Objects/variable/test_parameter_from_legacy.py @@ -0,0 +1,424 @@ +# SPDX-FileCopyrightText: 2023 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +# © 2021-2023 Contributors to the EasyScience project " + d = Parameter("test", 1, unit="cm") + assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 cm, bounds=[-inf:inf]>" + d = Parameter("test", 1, variance=0.1) + assert repr(d) == f"<{d.__class__.__name__} 'test': 1.0000 ± 0.3162, bounds=[-inf:inf]>" + + d = Parameter("test", 1, fixed=True) + assert ( + repr(d) + == f"<{d.__class__.__name__} 'test': 1.0000 (fixed), bounds=[-inf:inf]>" + ) + d = Parameter("test", 1, unit="cm", variance=0.1, fixed=True) + assert ( + repr(d) + == f"<{d.__class__.__name__} 'test': 1.0000 ± 0.3162 cm (fixed), bounds=[-inf:inf]>" + ) + + +def test_parameter_as_dict(): + d = Parameter("test", 1) + result = d.as_dict() + expected = { + "@module": Parameter.__module__, + "@class": Parameter.__name__, + "@version": easyscience.__version__, + "name": "test", + "value": 1.0, + "variance": 0.0, + "min": -np.inf, + "max": np.inf, + "fixed": False, + "unit": "dimensionless", + } + for key in expected.keys(): + assert result[key] == expected[key] + + # Check that additional arguments work + d = Parameter("test", 1, unit="km", url="https://www.boo.com") + result = d.as_dict() + expected = { + "@module": Parameter.__module__, + "@class": Parameter.__name__, + "@version": easyscience.__version__, + "name": "test", + "unit": "km", + "value": 1.0, + "variance": 0.0, + "min": -np.inf, + "max": np.inf, + "fixed": False, + "url": "https://www.boo.com", + } + for key in expected.keys(): + assert result[key] == expected[key] + + +def test_item_from_dict(): + reference = { + "@module": Parameter.__module__, + "@class": Parameter.__name__, + "@version": easyscience.__version__, + "name": "test", + "unit": "km", + "value": 1.0, + "variance": 0.0, + "min": -np.inf, + "max": np.inf, + "fixed": False, + "url": "https://www.boo.com", + } + constructor = Parameter + d = constructor.from_dict(reference) + for key, item in reference.items(): + if key == "callback" or key.startswith("@"): + continue + obtained = getattr(d, key) + assert obtained == item + + +@pytest.mark.parametrize( + "construct", + ( + { + "@module": Parameter.__module__, + "@class": Parameter.__name__, + "@version": easyscience.__version__, + "name": "test", + "unit": "km", + "value": 1.0, + "variance": 0.0, + "min": -np.inf, + "max": np.inf, + "fixed": False, + "url": "https://www.boo.com", + }, + ), + ids=["Parameter"], +) +def test_item_from_Decoder(construct): + + from easyscience.Utils.io.dict import DictSerializer + + d = DictSerializer().decode(construct) + assert d.__class__.__name__ == construct["@class"] + for key, item in construct.items(): + if key == "callback" or key.startswith("@"): + continue + obtained = getattr(d, key) + assert obtained == item + + +@pytest.mark.parametrize("value", (-np.inf, 0, 1.0, 2147483648, np.inf)) +def test_parameter_min(value): + d = Parameter("test", -0.1) + if d.value < value: + with pytest.raises(ValueError): + d.min = value + else: + d.min = value + assert d.min == value + + +@pytest.mark.parametrize("value", [-np.inf, 0, 1.1, 2147483648, np.inf]) +def test_parameter_max(value): + d = Parameter("test", 2147483649) + if d.value > value: + with pytest.raises(ValueError): + d.max = value + else: + d.max = value + assert d.max == value + + +@pytest.mark.parametrize("value", [True, False, 5]) +def test_parameter_fixed(value): + d = Parameter("test", -np.inf) + if isinstance(value, bool): + d.fixed = value + assert d.fixed == value + else: + with pytest.raises(ValueError): + d.fixed = value + + +@pytest.mark.parametrize("value", (-np.inf, -0.1, 0, 1.0, 2147483648, np.inf)) +def test_parameter_error(value): + d = Parameter("test", 1) + if value >= 0: + d.error = value + assert d.error == value + else: + with pytest.raises(ValueError): + d.error = value + + +def _generate_advanced_inputs(): + temp = _generate_inputs() + # These will be the optional parameters + advanced = {"variance": 1.0, "min": -0.1, "max": 2147483648, "fixed": False} + advanced_result = { + "variance": {"name": "variance", "value": advanced["variance"]}, + "min": {"name": "min", "value": advanced["min"]}, + "max": {"name": "max", "value": advanced["max"]}, + "fixed": {"name": "fixed", "value": advanced["fixed"]}, + } + + def create_entry(base, key, value, ref, ref_key=None): + this_temp = deepcopy(base) + for item in base: + test, res = item + new_opt = deepcopy(test[1]) + new_res = deepcopy(res) + if ref_key is None: + ref_key = key + new_res[ref_key] = ref + new_opt[key] = value + this_temp.append(([test[0], new_opt], new_res)) + return this_temp + + for add_opt in advanced.keys(): + if isinstance(advanced[add_opt], list): + for idx, item in enumerate(advanced[add_opt]): + temp = create_entry( + temp, + add_opt, + item, + advanced_result[add_opt]["value"][idx], + ref_key=advanced_result[add_opt]["name"], + ) + else: + temp = create_entry( + temp, + add_opt, + advanced[add_opt], + advanced_result[add_opt]["value"], + ref_key=advanced_result[add_opt]["name"], + ) + return temp + + +@pytest.mark.parametrize("element, expected", _generate_advanced_inputs()) +def test_parameter_advanced_creation(element, expected): + if len(element[0]) > 0: + value = element[0][1] + else: + value = element[1]["value"] + if "min" in element[1].keys(): + if element[1]["min"] > value: + with pytest.raises(ValueError): + d = Parameter(*element[0], **element[1]) + elif "max" in element[1].keys(): + if element[1]["max"] < value: + with pytest.raises(ValueError): + d = Parameter(*element[0], **element[1]) + else: + d = Parameter(*element[0], **element[1]) + for field in expected.keys(): + ref = expected[field] + obtained = getattr(d, field) + assert obtained == ref + + +@pytest.mark.parametrize("value", ("This is ", "a fun ", "test")) +def test_parameter_display_name(value): + p = Parameter("test", 1, display_name=value) + assert p.display_name == value + + +@pytest.mark.parametrize("value", (True, False)) +def test_parameter_bounds(value): + for fixed in (True, False): + p = Parameter("test", 1, enabled=value, fixed=fixed) + assert p.min == -np.inf + assert p.max == np.inf + assert p.fixed == fixed + assert p.bounds == (-np.inf, np.inf) + + p.bounds = (0, 2) + assert p.min == 0 + assert p.max == 2 + assert p.bounds == (0, 2) + assert p.enabled is True + assert p.fixed is False \ No newline at end of file From f4bc0edfb4152a6197e7a2ba8c62d6d213936c4b Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 20 Jan 2025 12:11:43 +0100 Subject: [PATCH 40/41] Remove unused code --- .../Objects/variable/descriptor_number.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/easyscience/Objects/variable/descriptor_number.py b/src/easyscience/Objects/variable/descriptor_number.py index 16e7521d..91f71549 100644 --- a/src/easyscience/Objects/variable/descriptor_number.py +++ b/src/easyscience/Objects/variable/descriptor_number.py @@ -198,20 +198,6 @@ def error(self, value: float) -> None: else: self._scalar.variance = None - # def convert_unit(self, unit_str: str): - # """ - # Convert the value from one unit system to another. - - # :param unit_str: New unit in string form - # """ - # if not isinstance(unit_str, str): - # raise TypeError(f'{unit_str=} must be a string representing a valid scipp unit') - # try: - # new_unit = sc.Unit(unit_str) - # except UnitError as message: - # raise UnitError(message) from None - # self._scalar = self._scalar.to(unit=new_unit) - def convert_unit(self, unit_str: str) -> None: """ Convert the value from one unit system to another. From 0f962dec6bf960de98fa7c638073cbaff43e6e81 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 20 Jan 2025 13:30:49 +0100 Subject: [PATCH 41/41] added tests of error (sqrt of variance) --- .../Objects/variable/test_descriptor_number.py | 14 ++++++++++++++ .../unit_tests/global_object/test_undo_redo.py | 18 ++++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/unit_tests/Objects/variable/test_descriptor_number.py b/tests/unit_tests/Objects/variable/test_descriptor_number.py index df47a9f6..62b359a4 100644 --- a/tests/unit_tests/Objects/variable/test_descriptor_number.py +++ b/tests/unit_tests/Objects/variable/test_descriptor_number.py @@ -157,6 +157,20 @@ def test_set_variance(self, descriptor: DescriptorNumber): # Expect assert descriptor._scalar.variance == 0.2 + assert descriptor.error == 0.4472135954999579 + + def test_error(self, descriptor: DescriptorNumber): + # When Then Expect + assert descriptor.error == 0.31622776601683794 + + def test_set_error(self, descriptor: DescriptorNumber): + # When Then + descriptor.error = 0.31622776601683794 + + # Expect + assert descriptor.error == 0.31622776601683794 + assert descriptor.variance == 0.1 + def test_value(self, descriptor: DescriptorNumber): # When Then Expect diff --git a/tests/unit_tests/global_object/test_undo_redo.py b/tests/unit_tests/global_object/test_undo_redo.py index f660ade1..1bcc8841 100644 --- a/tests/unit_tests/global_object/test_undo_redo.py +++ b/tests/unit_tests/global_object/test_undo_redo.py @@ -84,8 +84,7 @@ def test_DescriptorNumberUndoRedo(test): value = test[1] e = doUndoRedo(obj, attr, value) - if e: - raise e + assert not e def test_DescriptorBoolUndoRedo(): obj = DescriptorBool('DescriptorBool',False) @@ -93,16 +92,14 @@ def test_DescriptorBoolUndoRedo(): value = True e = doUndoRedo(obj, attr, value) - if e: - raise e + assert not e obj = DescriptorBool('DescriptorBool',True) attr = 'value' value = False e = doUndoRedo(obj, attr, value) - if e: - raise e + assert not e def test_DescriptorStrUndoRedo(): obj = DescriptorStr('DescriptorStr','Foo') @@ -110,8 +107,7 @@ def test_DescriptorStrUndoRedo(): value = 'Bar' e = doUndoRedo(obj, attr, value) - if e: - raise e + assert not e @pytest.mark.parametrize( "test", @@ -136,8 +132,7 @@ def test_ParameterUndoRedo(test): value = test[1] e = doUndoRedo(obj, attr, value) - if e: - raise e + assert not e @pytest.mark.parametrize("value", (True, False)) def test_Parameter_Bounds_UndoRedo(value): @@ -174,8 +169,7 @@ def test_BaseObjUndoRedo(): # Test setting value for b_obj in objs.values(): e = doUndoRedo(obj, b_obj.name, b_obj.value + 1, "value") - if e: - raise e + assert not e def test_BaseCollectionUndoRedo():