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.
- {easyscience-2.0.0 → easyscience-2.1.0}/.gitignore +3 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/PKG-INFO +10 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/README.md +4 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/pyproject.toml +8 -1
- easyscience-2.1.0/src/easyscience/__version__.py +1 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/__init__.py +4 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/based_base.py +19 -4
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/collection_base.py +1 -2
- easyscience-2.1.0/src/easyscience/base_classes/model_base.py +119 -0
- easyscience-2.1.0/src/easyscience/base_classes/new_base.py +145 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/base_classes/obj_base.py +7 -15
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/fitter.py +1 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/__init__.py +4 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_base.py +1 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_bumps.py +2 -2
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_dfo.py +3 -4
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_lmfit.py +3 -3
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/utils.py +1 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/__init__.py +1 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/hugger.py +18 -22
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/property.py +66 -90
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/logger.py +1 -3
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/map.py +4 -4
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/undo_redo.py +7 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_base.py +74 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_component.py +2 -3
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/serializer_dict.py +3 -3
- easyscience-2.1.0/src/easyscience/job/__init__.py +8 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/analysis.py +6 -13
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/experiment.py +2 -3
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/job.py +9 -8
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/job/theoreticalmodel.py +3 -3
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/dict.py +9 -14
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/models/__init__.py +3 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/models/polynomial.py +1 -2
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/__init__.py +1 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/classUtils.py +1 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/decorators.py +4 -6
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/__init__.py +2 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_any_type.py +5 -6
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_array.py +106 -102
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_bool.py +1 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_number.py +22 -7
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/parameter.py +202 -55
- easyscience-2.1.0/src/easyscience/variable/parameter_dependency_resolver.py +147 -0
- easyscience-2.0.0/src/easyscience/__version__.py +0 -1
- easyscience-2.0.0/src/easyscience/job/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/LICENSE +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/available_minimizers.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/calculators/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/calculators/interface_factory.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/factory.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/multi_fitter.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/global_object.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/global_object/hugger/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/io/__init__.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/json.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/legacy_core.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/legacy/xml.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/classTools.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/utils/string.py +0 -0
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_base.py +1 -1
- {easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/variable/descriptor_str.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: easyscience
|
|
3
|
-
Version: 2.
|
|
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
|
|
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
|
-
|
|
42
|
-
names =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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]
|
{easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_base.py
RENAMED
|
@@ -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
|
-
|
|
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
|
|
{easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_bumps.py
RENAMED
|
@@ -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():
|
{easyscience-2.0.0 → easyscience-2.1.0}/src/easyscience/fitting/minimizers/minimizer_lmfit.py
RENAMED
|
@@ -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
|
|