easyscience 2.0.0__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {easyscience-2.0.0 → easyscience-2.1.0}/.gitignore +3 -0
  2. {easyscience-2.0.0 → easyscience-2.1.0}/PKG-INFO +10 -1
  3. {easyscience-2.0.0 → easyscience-2.1.0}/README.md +4 -0
  4. {easyscience-2.0.0 → easyscience-2.1.0}/pyproject.toml +8 -1
  5. easyscience-2.1.0/src/easyscience/__version__.py +1 -0
  6. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/__init__.py +4 -0
  7. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/based_base.py +19 -4
  8. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/collection_base.py +1 -2
  9. easyscience-2.1.0/src/easyscience/base_classes/model_base.py +119 -0
  10. easyscience-2.1.0/src/easyscience/base_classes/new_base.py +145 -0
  11. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/obj_base.py +7 -15
  12. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/fitter.py +1 -0
  13. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/__init__.py +4 -1
  14. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_base.py +1 -1
  15. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_bumps.py +2 -2
  16. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_dfo.py +3 -4
  17. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_lmfit.py +3 -3
  18. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/utils.py +1 -0
  19. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/__init__.py +1 -0
  20. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/hugger.py +18 -22
  21. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/property.py +66 -90
  22. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/logger.py +1 -3
  23. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/map.py +4 -4
  24. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/undo_redo.py +7 -1
  25. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_base.py +74 -1
  26. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_component.py +2 -3
  27. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_dict.py +3 -3
  28. easyscience-2.1.0/src/easyscience/job/__init__.py +8 -0
  29. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/analysis.py +6 -13
  30. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/experiment.py +2 -3
  31. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/job.py +9 -8
  32. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/theoreticalmodel.py +3 -3
  33. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/dict.py +9 -14
  34. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/models/__init__.py +3 -1
  35. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/models/polynomial.py +1 -2
  36. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/__init__.py +1 -1
  37. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/classUtils.py +1 -1
  38. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/decorators.py +4 -6
  39. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/__init__.py +2 -0
  40. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_any_type.py +5 -6
  41. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_array.py +106 -102
  42. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_bool.py +1 -0
  43. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_number.py +22 -7
  44. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/parameter.py +202 -55
  45. easyscience-2.1.0/src/easyscience/variable/parameter_dependency_resolver.py +147 -0
  46. easyscience-2.0.0/src/easyscience/__version__.py +0 -1
  47. easyscience-2.0.0/src/easyscience/job/__init__.py +0 -0
  48. {easyscience-2.0.0 → easyscience-2.1.0}/LICENSE +0 -0
  49. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/__init__.py +0 -0
  50. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/__init__.py +0 -0
  51. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/available_minimizers.py +0 -0
  52. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/calculators/__init__.py +0 -0
  53. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/calculators/interface_factory.py +0 -0
  54. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/factory.py +0 -0
  55. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/multi_fitter.py +0 -0
  56. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/global_object.py +0 -0
  57. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/__init__.py +0 -0
  58. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/__init__.py +0 -0
  59. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/json.py +0 -0
  60. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/legacy_core.py +0 -0
  61. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/xml.py +0 -0
  62. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/classTools.py +0 -0
  63. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/string.py +0 -0
  64. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_base.py +1 -1
  65. {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_str.py +0 -0
@@ -23,6 +23,9 @@ dist
23
23
  poetry.lock
24
24
  *.egg-info
25
25
 
26
+ # Pixi
27
+ .pixi/
28
+
26
29
  # PyInstaller
27
30
  build
28
31
  *.spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: easyscience
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Generic logic for easyScience libraries
5
5
  Project-URL: homepage, https://github.com/EasyScience/EasyScience
6
6
  Project-URL: documentation, https://easyscience.github.io/EasyScience/
@@ -52,10 +52,15 @@ Requires-Dist: lmfit
52
52
  Requires-Dist: numpy
53
53
  Requires-Dist: scipp
54
54
  Requires-Dist: uncertainties
55
+ Provides-Extra: build
56
+ Requires-Dist: build; extra == 'build'
57
+ Requires-Dist: hatchling<=1.21.0; extra == 'build'
58
+ Requires-Dist: setuptools-git-versioning; extra == 'build'
55
59
  Provides-Extra: dev
56
60
  Requires-Dist: build; extra == 'dev'
57
61
  Requires-Dist: codecov; extra == 'dev'
58
62
  Requires-Dist: flake8; extra == 'dev'
63
+ Requires-Dist: jupyterlab; extra == 'dev'
59
64
  Requires-Dist: matplotlib; extra == 'dev'
60
65
  Requires-Dist: pytest; extra == 'dev'
61
66
  Requires-Dist: pytest-cov; extra == 'dev'
@@ -91,6 +96,10 @@ Or direct from the repository:
91
96
 
92
97
  ```pip install https://github.com/easyScience/EasyScience```
93
98
 
99
+ ### Development
100
+
101
+ For development setup and workflow instructions, please see [CONTRIBUTING.md](CONTRIBUTING.md).
102
+
94
103
  ## Test
95
104
 
96
105
  After installation, launch the test suite:
@@ -19,6 +19,10 @@ Or direct from the repository:
19
19
 
20
20
  ```pip install https://github.com/easyScience/EasyScience```
21
21
 
22
+ ### Development
23
+
24
+ For development setup and workflow instructions, please see [CONTRIBUTING.md](CONTRIBUTING.md).
25
+
22
26
  ## Test
23
27
 
24
28
  After installation, launch the test suite:
@@ -38,6 +38,11 @@ dependencies = [
38
38
  ]
39
39
 
40
40
  [project.optional-dependencies]
41
+ build = [
42
+ "hatchling <= 1.21.0",
43
+ "setuptools-git-versioning",
44
+ "build"
45
+ ]
41
46
  dev = [
42
47
  "build",
43
48
  "codecov",
@@ -46,7 +51,8 @@ dev = [
46
51
  "pytest",
47
52
  "pytest-cov",
48
53
  "ruff",
49
- "tox-gh-actions"
54
+ "tox-gh-actions",
55
+ "jupyterlab"
50
56
  ]
51
57
  docs = [
52
58
  "doc8",
@@ -136,6 +142,7 @@ python =
136
142
  PLATFORM =
137
143
  ubuntu-latest: linux
138
144
  macos-latest: macos
145
+ macos-15-intel: macos-intel
139
146
  windows-latest: windows
140
147
  [testenv]
141
148
  passenv =
@@ -0,0 +1 @@
1
+ __version__ = '2.1.0'
@@ -1,9 +1,13 @@
1
1
  from .based_base import BasedBase
2
2
  from .collection_base import CollectionBase
3
+ from .model_base import ModelBase
4
+ from .new_base import NewBase
3
5
  from .obj_base import ObjBase
4
6
 
5
7
  __all__ = [
6
8
  BasedBase,
7
9
  CollectionBase,
8
10
  ObjBase,
11
+ ModelBase,
12
+ NewBase,
9
13
  ]
@@ -3,8 +3,10 @@ from __future__ import annotations
3
3
  # SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
4
4
  # SPDX-License-Identifier: BSD-3-Clause
5
5
  # © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
6
- from inspect import getfullargspec
6
+ from inspect import signature
7
7
  from typing import TYPE_CHECKING
8
+ from typing import Any
9
+ from typing import Dict
8
10
  from typing import Iterable
9
11
  from typing import List
10
12
  from typing import Optional
@@ -38,9 +40,9 @@ class BasedBase(SerializerComponent):
38
40
  @property
39
41
  def _arg_spec(self) -> Set[str]:
40
42
  base_cls = getattr(self, '__old_class__', self.__class__)
41
- spec = getfullargspec(base_cls.__init__)
42
- names = set(spec.args[1:])
43
- return names
43
+ sign = signature(base_cls.__init__)
44
+ names = [param.name for param in sign.parameters.values() if param.kind == param.POSITIONAL_OR_KEYWORD]
45
+ return set(names[1:])
44
46
 
45
47
  def __reduce__(self):
46
48
  """
@@ -195,4 +197,17 @@ class BasedBase(SerializerComponent):
195
197
  new_obj = self.__class__.from_dict(temp)
196
198
  return new_obj
197
199
 
200
+ def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]:
201
+ """
202
+ Convert an object into a full dictionary using `SerializerDict`.
203
+ This is a shortcut for ```obj.encode(encoder=SerializerDict)```
198
204
 
205
+ :param skip: List of field names as strings to skip when forming the dictionary
206
+ :return: encoded object containing all information to reform an EasyScience object.
207
+ """
208
+ # extend skip to include unique_name by default
209
+ if skip is None:
210
+ skip = []
211
+ if 'unique_name' not in skip:
212
+ skip.append('unique_name')
213
+ return super().as_dict(skip=skip)
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
23
23
  from ..fitting.calculators import InterfaceFactoryTemplate
24
24
 
25
25
 
26
-
27
26
  class CollectionBase(BasedBase, MutableSequence):
28
27
  """
29
28
  This is the base class for which all higher level classes are built off of.
@@ -232,7 +231,7 @@ class CollectionBase(BasedBase, MutableSequence):
232
231
  return tuple(self._kwargs.values())
233
232
 
234
233
  def __repr__(self) -> str:
235
- return f"{self.__class__.__name__} `{getattr(self, 'name')}` of length {len(self)}"
234
+ return f'{self.__class__.__name__} `{getattr(self, "name")}` of length {len(self)}'
236
235
 
237
236
  def sort(self, mapping: Callable[[Union[BasedBase, DescriptorBase]], Any], reverse: bool = False) -> None:
238
237
  """
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ # SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
4
+ # SPDX-License-Identifier: BSD-3-Clause
5
+ # © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
6
+ from typing import TYPE_CHECKING
7
+
8
+ from easyscience.variable.descriptor_number import DescriptorNumber
9
+
10
+ if TYPE_CHECKING:
11
+ from typing import Any
12
+ from typing import Dict
13
+ from typing import List
14
+ from typing import Optional
15
+
16
+ from ..io import SerializerBase
17
+ from ..variable import Parameter
18
+ from ..variable.descriptor_base import DescriptorBase
19
+ from .new_base import NewBase
20
+
21
+
22
+ class ModelBase(NewBase):
23
+ """
24
+ This is the base class for all model classes in EasyScience.
25
+ It provides methods to get parameters for fitting and analysis as well as proper serialization/deserialization for
26
+ DescriptorNumber/Parameter attributes.
27
+
28
+ It assumes that Parameters/DescriptorNumbers are assigned as properties with the getters returning the parameter
29
+ but the setter only setting the value of the parameter.
30
+ e.g.
31
+ ```python
32
+ @property
33
+ def my_param(self) -> Parameter:
34
+ return self._my_param
35
+
36
+ @my_param.setter
37
+ def my_param(self, new_value: float) -> None:
38
+ self._my_param.value = new_value
39
+ ```
40
+ """
41
+
42
+ def __init__(self, unique_name: Optional[str] = None, display_name: Optional[str] = None):
43
+ super().__init__(unique_name=unique_name, display_name=display_name)
44
+
45
+ def get_all_variables(self) -> List[DescriptorBase]:
46
+ """
47
+ Get all `Descriptor` and `Parameter` objects as a list.
48
+
49
+ :return: List of `Descriptor` and `Parameter` objects.
50
+ """
51
+ vars = []
52
+ for attr_name in dir(self):
53
+ attr = getattr(self, attr_name)
54
+ if isinstance(attr, DescriptorBase):
55
+ vars.append(attr)
56
+ elif hasattr(attr, 'get_all_variables'):
57
+ vars += attr.get_all_variables()
58
+ return vars
59
+
60
+ def get_all_parameters(self) -> List[Parameter]:
61
+ """
62
+ Get all `Parameter` objects as a list.
63
+
64
+ :return: List of `Parameter` objects.
65
+ """
66
+ return [param for param in self.get_all_variables() if isinstance(param, Parameter)]
67
+
68
+ def get_fittable_parameters(self) -> List[Parameter]:
69
+ """
70
+ Get all parameters which can be fitted as a list.
71
+
72
+ :return: List of `Parameter` objects.
73
+ """
74
+ return [param for param in self.get_all_parameters() if param.independent]
75
+
76
+ def get_free_parameters(self) -> List[Parameter]:
77
+ """
78
+ Get all parameters which are currently free to be fitted as a list.
79
+
80
+ :return: List of `Parameter` objects.
81
+ """
82
+ return [param for param in self.get_fittable_parameters() if not param.fixed]
83
+
84
+ def get_fit_parameters(self) -> List[Parameter]:
85
+ """
86
+ This is an alias for `get_free_parameters`.
87
+ To be removed when fully moved to new base classes and minimizer can be changed.
88
+ """
89
+ return self.get_free_parameters()
90
+
91
+ @classmethod
92
+ def from_dict(cls, obj_dict: Dict[str, Any]) -> ModelBase:
93
+ """
94
+ Re-create an EasyScience object with DescriptorNumber attributes from a full encoded dictionary.
95
+
96
+ :param obj_dict: dictionary containing the serialized contents (from `SerializerDict`) of an EasyScience object
97
+ :return: Reformed EasyScience object
98
+ """
99
+ if not SerializerBase._is_serialized_easyscience_object(obj_dict):
100
+ raise ValueError('Input must be a dictionary representing an EasyScience object.')
101
+ if obj_dict['@class'] == cls.__name__:
102
+ kwargs = SerializerBase.deserialize_dict(obj_dict)
103
+ parameter_placeholder = {}
104
+ for key, value in kwargs.items():
105
+ if isinstance(value, DescriptorNumber):
106
+ parameter_placeholder[key] = value
107
+ kwargs[key] = value.value
108
+ cls_instance = cls(**kwargs)
109
+ for key, value in parameter_placeholder.items():
110
+ try:
111
+ temp_param = getattr(cls_instance, key)
112
+ setattr(cls_instance, '_' + key, value)
113
+ cls_instance._global_object.map.prune(temp_param.unique_name)
114
+ except Exception as e:
115
+ raise SyntaxError(f"""Could not set parameter {key} during `from_dict` with full deserialized variable. \n'
116
+ This should be fixed in the class definition. Error: {e}""") from e
117
+ return cls_instance
118
+ else:
119
+ raise ValueError(f'Class name in dictionary does not match the expected class: {cls.__name__}.')
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ # SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
4
+ # SPDX-License-Identifier: BSD-3-Clause
5
+ # © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
6
+ from inspect import signature
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from typing import Any
11
+ from typing import Dict
12
+ from typing import Iterable
13
+ from typing import List
14
+ from typing import Optional
15
+ from typing import Set
16
+
17
+ from easyscience import global_object
18
+
19
+ from ..global_object.undo_redo import property_stack
20
+ from ..io.serializer_base import SerializerBase
21
+
22
+
23
+ class NewBase:
24
+ """
25
+ This is the new base class for easyscience objects.
26
+ It provides serialization capabilities as well as unique naming and display naming.
27
+ """
28
+
29
+ def __init__(self, unique_name: Optional[str] = None, display_name: Optional[str] = None):
30
+ self._global_object = global_object
31
+ if unique_name is None:
32
+ unique_name = self._global_object.generate_unique_name(self.__class__.__name__)
33
+ self._default_unique_name = True
34
+ else:
35
+ self._default_unique_name = False
36
+ if not isinstance(unique_name, str):
37
+ raise TypeError('Unique name has to be a string.')
38
+ self._unique_name = unique_name
39
+ self._global_object.map.add_vertex(self, obj_type='created')
40
+ if display_name is not None and not isinstance(display_name, str):
41
+ raise TypeError('Display name must be a string or None')
42
+ self._display_name = display_name
43
+
44
+ @property
45
+ def _arg_spec(self) -> Set[str]:
46
+ """
47
+ This method is used by the serializer to determine which arguments are needed
48
+ by the constructor to deserialize the object.
49
+ """
50
+ sign = signature(self.__class__.__init__)
51
+ names = [param.name for param in sign.parameters.values() if param.kind == param.POSITIONAL_OR_KEYWORD]
52
+ return set(names[1:])
53
+
54
+ @property
55
+ def unique_name(self) -> str:
56
+ """Get the unique name of the object."""
57
+ return self._unique_name
58
+
59
+ @unique_name.setter
60
+ def unique_name(self, new_unique_name: str):
61
+ """Set a new unique name for the object. The old name is still kept in the map.
62
+
63
+ :param new_unique_name: New unique name for the object"""
64
+ if not isinstance(new_unique_name, str):
65
+ raise TypeError('Unique name has to be a string.')
66
+ self._unique_name = new_unique_name
67
+ self._global_object.map.add_vertex(self)
68
+ self._default_unique_name = False
69
+
70
+ @property
71
+ def display_name(self) -> str:
72
+ """
73
+ Get a pretty display name.
74
+
75
+ :return: The pretty display name.
76
+ """
77
+ display_name = self._display_name
78
+ if display_name is None:
79
+ display_name = self.unique_name
80
+ return display_name
81
+
82
+ @display_name.setter
83
+ @property_stack
84
+ def display_name(self, name: str | None) -> None:
85
+ """
86
+ Set the pretty display name.
87
+
88
+ :param name: Pretty display name of the object.
89
+ """
90
+ if name is not None and not isinstance(name, str):
91
+ raise TypeError('Display name must be a string or None')
92
+ self._display_name = name
93
+
94
+ def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]:
95
+ """
96
+ Convert an EasyScience object into a full dictionary using `SerializerBase`s generic `convert_to_dict` method.
97
+
98
+ :param skip: List of field names as strings to skip when forming the dictionary
99
+ :return: encoded object containing all information to reform an EasyScience object.
100
+ """
101
+ serializer = SerializerBase()
102
+ if skip is None:
103
+ skip = []
104
+ if self._default_unique_name and 'unique_name' not in skip:
105
+ skip.append('unique_name')
106
+ if self._display_name is None:
107
+ skip.append('display_name')
108
+ return serializer._convert_to_dict(self, skip=skip, full_encode=False)
109
+
110
+ @classmethod
111
+ def from_dict(cls, obj_dict: Dict[str, Any]) -> NewBase:
112
+ """
113
+ Re-create an EasyScience object from a full encoded dictionary.
114
+
115
+ :param obj_dict: dictionary containing the serialized contents (from `SerializerDict`) of an EasyScience object
116
+ :return: Reformed EasyScience object
117
+ """
118
+ if not SerializerBase._is_serialized_easyscience_object(obj_dict):
119
+ raise ValueError('Input must be a dictionary representing an EasyScience object.')
120
+ if obj_dict['@class'] == cls.__name__:
121
+ kwargs = SerializerBase.deserialize_dict(obj_dict)
122
+ return cls(**kwargs)
123
+ else:
124
+ raise ValueError(f'Class name in dictionary does not match the expected class: {cls.__name__}.')
125
+
126
+ def __dir__(self) -> Iterable[str]:
127
+ """
128
+ This creates auto-completion and helps out in iPython notebooks.
129
+
130
+ :return: list of function and parameter names for auto-completion
131
+ """
132
+ new_class_objs = list(k for k in dir(self.__class__) if not k.startswith('_'))
133
+ return sorted(new_class_objs)
134
+
135
+ def __copy__(self) -> NewBase:
136
+ """Return a copy of the object."""
137
+ temp = self.to_dict(skip=['unique_name'])
138
+ new_obj = self.__class__.from_dict(temp)
139
+ return new_obj
140
+
141
+ def __deepcopy__(self, memo):
142
+ return self.__copy__()
143
+
144
+ def __repr__(self) -> str:
145
+ return f'{self.__class__.__name__} `{self.unique_name}`'
@@ -15,7 +15,6 @@ if TYPE_CHECKING:
15
15
  from ..io import SerializerComponent
16
16
 
17
17
 
18
-
19
18
  class ObjBase(BasedBase):
20
19
  """
21
20
  This is the base class for which all higher level classes are built off of.
@@ -69,17 +68,12 @@ class ObjBase(BasedBase):
69
68
  Dynamically add a component to the class. This is an internal method, though can be called remotely.
70
69
  The recommended alternative is to use typing, i.e.
71
70
 
72
- class Foo(Bar):
73
- def __init__(self, foo: Parameter, bar: Parameter):
74
- super(Foo, self).__init__(bar=bar)
75
- self._add_component("foo", foo)
71
+ .. code-block:: python
76
72
 
77
- Goes to:
78
- class Foo(Bar):
79
- foo: ClassVar[Parameter]
80
- def __init__(self, foo: Parameter, bar: Parameter):
81
- super(Foo, self).__init__(bar=bar)
82
- self.foo = foo
73
+ class Foo(Bar):
74
+ def __init__(self, foo: Parameter, bar: Parameter):
75
+ super(Foo, self).__init__(bar=bar)
76
+ self._add_component("foo", foo)
83
77
 
84
78
  :param key: Name of component to be added
85
79
  :param component: Component to be added
@@ -127,7 +121,7 @@ class ObjBase(BasedBase):
127
121
  self.generate_bindings()
128
122
 
129
123
  def __repr__(self) -> str:
130
- return f"{self.__class__.__name__} `{getattr(self, 'name')}`"
124
+ return f'{self.__class__.__name__} `{getattr(self, "name")}`'
131
125
 
132
126
  @staticmethod
133
127
  def __getter(key: str) -> Callable[[SerializerComponent], SerializerComponent]:
@@ -139,9 +133,7 @@ class ObjBase(BasedBase):
139
133
  @staticmethod
140
134
  def __setter(key: str) -> Callable[[SerializerComponent], None]:
141
135
  def setter(obj: SerializerComponent, value: float) -> None:
142
- if issubclass(obj._kwargs[key].__class__, (DescriptorBase)) and not issubclass(
143
- value.__class__, (DescriptorBase)
144
- ):
136
+ if issubclass(obj._kwargs[key].__class__, (DescriptorBase)) and not issubclass(value.__class__, (DescriptorBase)):
145
137
  obj._kwargs[key].value = value
146
138
  else:
147
139
  obj._kwargs[key] = value
@@ -69,6 +69,7 @@ class Fitter:
69
69
  def switch_minimizer(self, minimizer_enum: Union[AvailableMinimizers, str]) -> None:
70
70
  """
71
71
  Switch minimizer and initialize.
72
+
72
73
  :param minimizer_enum: The enum of the minimizer to create and instantiate.
73
74
  """
74
75
  if isinstance(minimizer_enum, str):
@@ -3,7 +3,10 @@
3
3
  # © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/easyscience
4
4
 
5
5
  from .minimizer_base import MinimizerBase
6
+ from .minimizer_bumps import Bumps
7
+ from .minimizer_dfo import DFO
8
+ from .minimizer_lmfit import LMFit
6
9
  from .utils import FitError
7
10
  from .utils import FitResults
8
11
 
9
- __all__ = [MinimizerBase, FitError, FitResults]
12
+ __all__ = [MinimizerBase, Bumps, DFO, LMFit, FitError, FitResults]
@@ -212,7 +212,7 @@ class MinimizerBase(metaclass=ABCMeta):
212
212
  for name, value in kwargs.items():
213
213
  par_name = name[1:]
214
214
  if par_name in self._cached_pars.keys():
215
- # This will take into account constraints
215
+ # This will take into account constraints
216
216
  if self._cached_pars[par_name].value != value:
217
217
  self._cached_pars[par_name].value = value
218
218
 
@@ -105,13 +105,13 @@ class Bumps(MinimizerBase):
105
105
 
106
106
  if y.shape != x.shape:
107
107
  raise ValueError('x and y must have the same shape.')
108
-
108
+
109
109
  if weights.shape != x.shape:
110
110
  raise ValueError('Weights must have the same shape as x and y.')
111
111
 
112
112
  if not np.isfinite(weights).all():
113
113
  raise ValueError('Weights cannot be NaN or infinite.')
114
-
114
+
115
115
  if (weights <= 0).any():
116
116
  raise ValueError('Weights must be strictly positive and non-zero.')
117
117
 
@@ -90,13 +90,13 @@ class DFO(MinimizerBase):
90
90
 
91
91
  if y.shape != x.shape:
92
92
  raise ValueError('x and y must have the same shape.')
93
-
93
+
94
94
  if weights.shape != x.shape:
95
95
  raise ValueError('Weights must have the same shape as x and y.')
96
-
96
+
97
97
  if not np.isfinite(weights).all():
98
98
  raise ValueError('Weights cannot be NaN or infinite.')
99
-
99
+
100
100
  if (weights <= 0).any():
101
101
  raise ValueError('Weights must be strictly positive and non-zero.')
102
102
 
@@ -152,7 +152,6 @@ class DFO(MinimizerBase):
152
152
 
153
153
  def _outer(obj: DFO):
154
154
  def _make_func(x, y, weights):
155
-
156
155
  dfo_pars = {}
157
156
  if not parameters:
158
157
  for name, par in obj._cached_pars.items():
@@ -116,13 +116,13 @@ class LMFit(MinimizerBase): # noqa: S101
116
116
 
117
117
  if y.shape != x.shape:
118
118
  raise ValueError('x and y must have the same shape.')
119
-
119
+
120
120
  if weights.shape != x.shape:
121
121
  raise ValueError('Weights must have the same shape as x and y.')
122
-
122
+
123
123
  if not np.isfinite(weights).all():
124
124
  raise ValueError('Weights cannot be NaN or infinite.')
125
-
125
+
126
126
  if (weights <= 0).any():
127
127
  raise ValueError('Weights must be strictly positive and non-zero.')
128
128
 
@@ -51,6 +51,7 @@ class FitResults:
51
51
  def reduced_chi(self):
52
52
  return self.chi2 / (len(self.x) - self.n_pars)
53
53
 
54
+
54
55
  class FitError(Exception):
55
56
  def __init__(self, e: Exception = None):
56
57
  self.e = e
@@ -2,3 +2,4 @@ from .global_object import GlobalObject # noqa: F401
2
2
  from .hugger.hugger import ScriptManager # noqa: F401
3
3
  from .logger import Logger # noqa: F401
4
4
  from .map import Map # noqa: F401
5
+ from .undo_redo import UndoStack # noqa: F401