Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3a3738f
Merge pull request #76 from EasyScience/develop
andped10 Sep 18, 2024
59c4a90
Merge pull request #83 from EasyScience/develop
AndrewSazonov Oct 29, 2024
23f4ced
Merge pull request #86 from EasyScience/develop
AndrewSazonov Nov 12, 2024
a6d9379
Merge pull request #87 from EasyScience/develop
andped10 Nov 13, 2024
5fdd558
Merge pull request #90 from EasyScience/develop
AndrewSazonov Nov 21, 2024
7141fc2
Numpy2 (#93)
henrikjacobsenfys Dec 19, 2024
2ea1bcc
Remove old parameter (#94)
henrikjacobsenfys Jan 20, 2025
d087e07
Remove virtual (#98)
henrikjacobsenfys Jan 20, 2025
5e63988
make descriptor_array
henrikjacobsenfys Jan 22, 2025
69cd6bd
update from_scipp method
henrikjacobsenfys Jan 22, 2025
4b51382
Update value (not finished)
henrikjacobsenfys Jan 22, 2025
755fc91
change _scalar to _array
henrikjacobsenfys Jan 22, 2025
3073d78
outcomment error for now
henrikjacobsenfys Jan 22, 2025
4921185
outcommented add, radd etc.
henrikjacobsenfys Jan 22, 2025
865c2da
identify TODOs
henrikjacobsenfys Jan 22, 2025
527ae1d
Update __init__
henrikjacobsenfys Jan 22, 2025
8619a1e
update variance
henrikjacobsenfys Jan 22, 2025
710a555
update error
henrikjacobsenfys Jan 22, 2025
bd6d12d
add repr
henrikjacobsenfys Jan 22, 2025
7287ebd
add __add__
henrikjacobsenfys Jan 22, 2025
222bd4e
update __init__ of variable
henrikjacobsenfys Jan 27, 2025
34d08bb
Add addition of DescriptorNumber
henrikjacobsenfys Jan 27, 2025
baf35fc
add neg and abs
henrikjacobsenfys Jan 27, 2025
19cdf78
update __radd__ to handle DescriptorNumber
henrikjacobsenfys Jan 27, 2025
2ecbb0a
fix __add__
henrikjacobsenfys Jan 27, 2025
94fad5f
fixed add and radd
henrikjacobsenfys Jan 27, 2025
ee215dc
autogenerated tests
henrikjacobsenfys Jan 27, 2025
9320b9a
start over with tests
henrikjacobsenfys Jan 27, 2025
ea62449
adapted some tests from descriptor_number
henrikjacobsenfys Jan 27, 2025
964e8cf
more tests
henrikjacobsenfys Jan 27, 2025
c9436d0
update __repr__ and more tests
henrikjacobsenfys Jan 27, 2025
1ca4049
update tests more
henrikjacobsenfys Jan 27, 2025
1edf993
minor spelling fixup
elindgren Feb 19, 2025
9f17ef0
work on tests for array addition
elindgren Feb 19, 2025
54f4524
manually perform broadcasting without considering correlations, and r…
elindgren Feb 20, 2025
7a19de5
investigate issue where a DescriptorArray is converted to a set of nu…
elindgren Feb 21, 2025
a6f02b1
manually implement numpy __add__ ufunc compatibility
elindgren Feb 25, 2025
2d9978e
add wrapper function for handling type conversions for basic operations
elindgren Feb 25, 2025
bc93a90
drop support for operations with Numpy arrays
elindgren Feb 25, 2025
75483ef
let scipp handle array multiplication for uncertainty propagation
elindgren Feb 25, 2025
e5a1574
add division; revorked unit handling to make division work
elindgren Feb 26, 2025
cfeaaa9
add pow
elindgren Feb 27, 2025
0bdbb3f
apply linter changes
elindgren Feb 27, 2025
997d5f9
add tests for abs and neg
elindgren Feb 27, 2025
c84fd9e
allow for multiplication and division with dimensionless lists and nu…
elindgren Feb 27, 2025
0e007a1
fix scipp error
elindgren Feb 28, 2025
dbc72e1
add sum function
elindgren Feb 28, 2025
063e632
start working on trace and slicing
elindgren Feb 28, 2025
e71f21f
add trace operation
elindgren Mar 3, 2025
4cef1d5
add exception test for trace
elindgren Mar 3, 2025
972779c
try implementing set item and realize we need views
elindgren Mar 3, 2025
a526530
do not allow __setitem__
elindgren Mar 3, 2025
bed71a9
add some docstrings and cleanup
elindgren Mar 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/documentation-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: 3.10
- name: Install Pandoc, repo and dependencies
run: |
sudo apt install pandoc
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.13']
os: [ubuntu-latest, macos-latest, windows-latest]

runs-on: ${{ matrix.os }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11']
python-version: ['3.10', '3.11', '3.12', '3.13']
if: "!contains(github.event.head_commit.message, '[ci skip]')"

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:

- uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: 3.10

- name: Install dependencies and build
run: |
Expand Down
12 changes: 6 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ classifiers = [
"Topic :: Scientific/Engineering",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Development Status :: 3 - Alpha"
]
requires-python = ">=3.9,<3.13"
requires-python = ">=3.10"
dependencies = [
"asteval",
"bumps",
"DFO-LS",
"lmfit",
"numpy==1.26", # Should be updated to numpy 2.0
"numpy",
"uncertainties",
"xarray",
"pint==0.23", # Only to ensure that unit is reported as dimensionless rather than empty string
"pint", # Only to ensure that unit is reported as dimensionless rather than empty string
"scipp"
]

Expand Down Expand Up @@ -128,13 +128,13 @@ force-single-line = true
legacy_tox_ini = """
[tox]
isolated_build = True
envlist = py{3.9,3.10,3.11,3.12}
envlist = py{3.10,3.11,3.12,3.13}
[gh-actions]
python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312
3.13: py313
[gh-actions:env]
PLATFORM =
ubuntu-latest: linux
Expand Down
44 changes: 7 additions & 37 deletions src/easyscience/Constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,9 @@ 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
import easyscience.Objects.new_variable.parameter
## TODO Probably needs to be updated when DescriptorArray is implemented

if isinstance(obj, easyscience.Objects.new_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)
Expand Down Expand Up @@ -258,13 +254,7 @@ 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
import easyscience.Objects.new_variable.parameter

if isinstance(obj, easyscience.Objects.new_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)
Expand Down Expand Up @@ -322,13 +312,7 @@ 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
import easyscience.Objects.new_variable.parameter

if isinstance(obj, easyscience.Objects.new_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:
Expand Down Expand Up @@ -417,16 +401,11 @@ def __init__(
self.external = True

def _parse_operator(self, independent_objs: List[V], *args, **kwargs) -> Number:
import easyscience.Objects.new_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):
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):
Expand Down Expand Up @@ -485,25 +464,16 @@ def __init__(
self.external = True

def _parse_operator(self, obj: V, *args, **kwargs) -> Number:
import easyscience.Objects.new_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):
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.new_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:
Expand Down
18 changes: 9 additions & 9 deletions src/easyscience/Objects/Groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
from typing import List
from typing import Optional
from typing import Tuple
from typing import TypeVar
from typing import Union

from easyscience.global_object.undo_redo import NotarizedDict
from easyscience.Objects.new_variable.descriptor_base import DescriptorBase
from easyscience.Objects.ObjectClasses import BasedBase
from easyscience.Objects.ObjectClasses import Descriptor
from easyscience.Objects.variable.descriptor_base import DescriptorBase

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):
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down
9 changes: 2 additions & 7 deletions src/easyscience/Objects/Inferface.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ def generate_bindings(self, model, *args, ifun=None, **kwargs):
:return: binding property
:rtype: property
"""
import easyscience.Objects.new_variable.parameter

class_links = self.__interface_obj.create(model)
props = model._get_linkable_attributes()
Expand All @@ -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.new_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)
Expand Down
36 changes: 14 additions & 22 deletions src/easyscience/Objects/ObjectClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,18 @@
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

from .core import ComponentSerializer
from .new_variable import Parameter as NewParameter
from .new_variable.descriptor_base import DescriptorBase
from .Variable import Descriptor
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
from easyscience.Objects.Variable import V
V = TypeVar('V', bound=DescriptorBase)


class BasedBase(ComponentSerializer):
Expand Down Expand Up @@ -160,8 +157,7 @@ def constraints(self) -> List[C]:
constraints.append(con[key])
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.

Expand All @@ -171,11 +167,10 @@ 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

## TODO clean when full move to new_variable
def _get_linkable_attributes(self) -> List[V]:
"""
Get all objects which can be linked against as a list.
Expand All @@ -186,12 +181,11 @@ 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]]:
def get_fit_parameters(self) -> List[Parameter]:
"""
Get all objects which can be fitted (and are not fixed) as a list.

Expand All @@ -201,7 +195,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
Expand Down Expand Up @@ -235,7 +229,6 @@ class BaseObj(BasedBase):
cheat with `BaseObj(*[Descriptor(...), Parameter(...), ...])`.
"""

## TODO clean when full move to new_variable
def __init__(
self,
name: str,
Expand All @@ -253,15 +246,15 @@ 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()
self._kwargs = kwargs
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])
Expand Down Expand Up @@ -310,7 +303,6 @@ def __init__(self, foo: Parameter, bar: Parameter):
test_class=BaseObj,
)

## TODO clean when full move to new_variable
def __setattr__(self, key: str, value: BV) -> None:
# Assume that the annotation is a ClassVar
old_obj = None
Expand All @@ -323,12 +315,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)
Expand All @@ -352,8 +344,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:
Expand Down
Loading
Loading