Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/easyscience/Objects/new_variable/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def __repr__(self) -> str:
if self.fixed:
super_str += ' (fixed)'
s.append(super_str)
s.append('bounds=[%s:%s]' % (repr(self.min), repr(self.max)))
s.append('bounds=[%s:%s]' % (repr(float(self.min)), repr(float(self.max))))
return '%s>' % ', '.join(s)

# Seems redundant
Expand Down
61 changes: 33 additions & 28 deletions tests/unit_tests/Objects/new_variable/test_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ def test_min(self, parameter: Parameter):

def test_set_min(self, parameter: Parameter):
# When Then
self.mock_callback.fget.return_value = 1.0 # Ensure fget returns a scalar value

parameter.min = 0.1

# Expect
Expand Down Expand Up @@ -189,6 +191,7 @@ def test_bounds(self, parameter: Parameter):

def test_set_bounds(self, parameter: Parameter):
# When
self.mock_callback.fget.return_value = 1.0 # Ensure fget returns a scalar value
parameter._enabled = False
parameter._fixed = True

Expand Down Expand Up @@ -298,6 +301,7 @@ def test_set_full_value(self, parameter: Parameter):

def test_copy(self, parameter: Parameter):
# When Then
self.mock_callback.fget.return_value = 1.0 # Ensure fget returns a scalar value
parameter_copy = parameter.__copy__()

# Expect
Expand All @@ -317,6 +321,7 @@ def test_copy(self, parameter: Parameter):

def test_as_data_dict(self, clear, parameter: Parameter):
# When Then
self.mock_callback.fget.return_value = 1.0 # Ensure fget returns a scalar value
parameter_dict = parameter.as_data_dict()

# Expect
Expand All @@ -337,7 +342,7 @@ def test_as_data_dict(self, clear, parameter: Parameter):

@pytest.mark.parametrize("test, expected, expected_reverse", [
(Parameter("test", 2, "m", 0.01, -10, 20), Parameter("name + test", 3, "m", 0.02, -10, 30), Parameter("test + name", 3, "m", 0.02, -10, 30)),
(Parameter("test", 2, "m", 0.01), Parameter("name + test", 3, "m", 0.02, min=-np.Inf, max=np.Inf),Parameter("test + name", 3, "m", 0.02, min=-np.Inf, max=np.Inf)),
(Parameter("test", 2, "m", 0.01), Parameter("name + test", 3, "m", 0.02, min=-np.inf, max=np.inf),Parameter("test + name", 3, "m", 0.02, min=-np.inf, max=np.inf)),
(Parameter("test", 2, "cm", 0.01, -10, 10), Parameter("name + test", 1.02, "m", 0.010001, -0.1, 10.1), Parameter("test + name", 102, "cm", 100.01, -10, 1010))],
ids=["regular", "no_bounds", "unit_conversion"])
def test_addition_with_parameter(self, parameter : Parameter, test : Parameter, expected : Parameter, expected_reverse : Parameter):
Expand Down Expand Up @@ -427,7 +432,7 @@ def test_addition_exception(self, parameter : Parameter, test):

@pytest.mark.parametrize("test, expected, expected_reverse", [
(Parameter("test", 2, "m", 0.01, -20, 20), Parameter("name - test", -1, "m", 0.02, -20, 30), Parameter("test - name", 1, "m", 0.02, -30, 20)),
(Parameter("test", 2, "m", 0.01), Parameter("name - test", -1, "m", 0.02, min=-np.Inf, max=np.Inf),Parameter("test - name", 1, "m", 0.02, min=-np.Inf, max=np.Inf)),
(Parameter("test", 2, "m", 0.01), Parameter("name - test", -1, "m", 0.02, min=-np.inf, max=np.inf),Parameter("test - name", 1, "m", 0.02, min=-np.inf, max=np.inf)),
(Parameter("test", 2, "cm", 0.01, -10, 10), Parameter("name - test", 0.98, "m", 0.010001, -0.1, 10.1), Parameter("test - name", -98, "cm", 100.01, -1010, 10))],
ids=["regular", "no_bounds", "unit_conversion"])
def test_subtraction_with_parameter(self, parameter : Parameter, test : Parameter, expected : Parameter, expected_reverse : Parameter):
Expand Down Expand Up @@ -457,8 +462,8 @@ def test_subtraction_with_parameter(self, parameter : Parameter, test : Paramete

def test_subtraction_with_parameter_nan_cases(self):
# When
parameter = Parameter(name="name", value=1, variance=0.01, min=-np.Inf, max=np.Inf)
test = Parameter(name="test", value=2, variance=0.01, min=-np.Inf, max=np.Inf)
parameter = Parameter(name="name", value=1, variance=0.01, min=-np.inf, max=np.inf)
test = Parameter(name="test", value=2, variance=0.01, min=-np.inf, max=np.inf)

# Then
result = parameter - test
Expand All @@ -469,15 +474,15 @@ def test_subtraction_with_parameter_nan_cases(self):
assert result.value == -1.0
assert result.unit == "dimensionless"
assert result.variance == 0.02
assert result.min == -np.Inf
assert result.max == np.Inf
assert result.min == -np.inf
assert result.max == np.inf

assert result_reverse.name == result_reverse.unique_name
assert result_reverse.value == 1.0
assert result_reverse.unit == "dimensionless"
assert result_reverse.variance == 0.02
assert result_reverse.min == -np.Inf
assert result_reverse.max == np.Inf
assert result_reverse.min == -np.inf
assert result_reverse.max == np.inf

def test_subtraction_with_scalar(self):
# When
Expand Down Expand Up @@ -541,7 +546,7 @@ def test_subtraction_exception(self, parameter : Parameter, test):

@pytest.mark.parametrize("test, expected, expected_reverse", [
(Parameter("test", 2, "m", 0.01, -10, 20), Parameter("name * test", 2, "m^2", 0.05, -100, 200), Parameter("test * name", 2, "m^2", 0.05, -100, 200)),
(Parameter("test", 2, "m", 0.01), Parameter("name * test", 2, "m^2", 0.05, min=-np.Inf, max=np.Inf), Parameter("test * name", 2, "m^2", 0.05, min=-np.Inf, max=np.Inf)),
(Parameter("test", 2, "m", 0.01), Parameter("name * test", 2, "m^2", 0.05, min=-np.inf, max=np.inf), Parameter("test * name", 2, "m^2", 0.05, min=-np.inf, max=np.inf)),
(Parameter("test", 2, "dm", 0.01, -10, 20), Parameter("name * test", 0.2, "m^2", 0.0005, -10, 20), Parameter("test * name", 0.2, "m^2", 0.0005, -10, 20))],
ids=["regular", "no_bounds", "base_unit_conversion"])
def test_multiplication_with_parameter(self, parameter : Parameter, test : Parameter, expected : Parameter, expected_reverse : Parameter):
Expand All @@ -568,12 +573,12 @@ def test_multiplication_with_parameter(self, parameter : Parameter, test : Param
assert result_reverse.max == expected_reverse.max

@pytest.mark.parametrize("test, expected, expected_reverse", [
(Parameter("test", 0, "", 0.01, -10, 0), Parameter("name * test", 0.0, "dimensionless", 0.01, -np.Inf, 0), Parameter("test * name", 0, "dimensionless", 0.01, -np.Inf, 0)),
(Parameter("test", 0, "", 0.01, 0, 10), Parameter("name * test", 0.0, "dimensionless", 0.01, 0, np.Inf), Parameter("test * name", 0, "dimensionless", 0.01, 0, np.Inf))],
(Parameter("test", 0, "", 0.01, -10, 0), Parameter("name * test", 0.0, "dimensionless", 0.01, -np.inf, 0), Parameter("test * name", 0, "dimensionless", 0.01, -np.inf, 0)),
(Parameter("test", 0, "", 0.01, 0, 10), Parameter("name * test", 0.0, "dimensionless", 0.01, 0, np.inf), Parameter("test * name", 0, "dimensionless", 0.01, 0, np.inf))],
ids=["zero_min", "zero_max"])
def test_multiplication_with_parameter_nan_cases(self, test, expected, expected_reverse):
# When
parameter = Parameter(name="name", value=1, variance=0.01, min=1, max=np.Inf)
parameter = Parameter(name="name", value=1, variance=0.01, min=1, max=np.inf)

# Then
result = parameter * test
Expand Down Expand Up @@ -656,9 +661,9 @@ def test_multiplication_with_scalar(self, parameter : Parameter, test, expected,
assert result_reverse.max == expected_reverse.max

@pytest.mark.parametrize("test, expected, expected_reverse", [
(Parameter("test", 2, "s", 0.01, -10, 20), Parameter("name / test", 0.5, "m/s", 0.003125, -np.Inf, np.Inf), Parameter("test / name", 2, "s/m", 0.05, -np.Inf, np.Inf)),
(Parameter("test", 2, "s", 0.01, 0, 20), Parameter("name / test", 0.5, "m/s", 0.003125, 0.0, np.Inf), Parameter("test / name", 2, "s/m", 0.05, 0.0, np.Inf)),
(Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", -0.5, "m/s", 0.003125, -np.Inf, 0.0), Parameter("test / name", -2, "s/m", 0.05, -np.Inf, 0.0))],
(Parameter("test", 2, "s", 0.01, -10, 20), Parameter("name / test", 0.5, "m/s", 0.003125, -np.inf, np.inf), Parameter("test / name", 2, "s/m", 0.05, -np.inf, np.inf)),
(Parameter("test", 2, "s", 0.01, 0, 20), Parameter("name / test", 0.5, "m/s", 0.003125, 0.0, np.inf), Parameter("test / name", 2, "s/m", 0.05, 0.0, np.inf)),
(Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", -0.5, "m/s", 0.003125, -np.inf, 0.0), Parameter("test / name", -2, "s/m", 0.05, -np.inf, 0.0))],
ids=["crossing_zero", "only_positive", "only_negative"])
def test_division_with_parameter(self, parameter : Parameter, test, expected, expected_reverse):
# When
Expand Down Expand Up @@ -686,8 +691,8 @@ def test_division_with_parameter(self, parameter : Parameter, test, expected, ex
assert result_reverse.max == expected_reverse.max

@pytest.mark.parametrize("first, second, expected", [
(Parameter("name", 1, "m", 0.01, -10, 20), Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", -0.5, "m/s", 0.003125, -np.Inf, np.Inf)),
(Parameter("name", -10, "m", 0.01, -20, -10), Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", 5.0, "m/s", 0.065, 1, np.Inf)),
(Parameter("name", 1, "m", 0.01, -10, 20), Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", -0.5, "m/s", 0.003125, -np.inf, np.inf)),
(Parameter("name", -10, "m", 0.01, -20, -10), Parameter("test", -2, "s", 0.01, -10, 0), Parameter("name / test", 5.0, "m/s", 0.065, 1, np.inf)),
(Parameter("name", 10, "m", 0.01, 10, 20), Parameter("test", -20, "s", 0.01, -20, -10), Parameter("name / test", -0.5, "m/s", 3.125e-5, -2, -0.5))],
ids=["first_crossing_zero_second_negative_0", "both_negative_second_negative_0", "finite_limits"])
def test_division_with_parameter_remaining_cases(self, first, second, expected):
Expand All @@ -703,8 +708,8 @@ def test_division_with_parameter_remaining_cases(self, first, second, expected):
assert result.max == expected.max

@pytest.mark.parametrize("test, expected, expected_reverse", [
(DescriptorNumber(name="test", value=2, variance=0.1, unit="s"), Parameter("name / test", 0.5, "m/s", 0.00875, 0, 5), Parameter("test / name", 2, "s/m", 0.14, 0.2, np.Inf)),
(2, Parameter("name / 2", 0.5, "m", 0.0025, 0, 5), Parameter("2 / name", 2, "m**-1", 0.04, 0.2, np.Inf))],
(DescriptorNumber(name="test", value=2, variance=0.1, unit="s"), Parameter("name / test", 0.5, "m/s", 0.00875, 0, 5), Parameter("test / name", 2, "s/m", 0.14, 0.2, np.inf)),
(2, Parameter("name / 2", 0.5, "m", 0.0025, 0, 5), Parameter("2 / name", 2, "m**-1", 0.04, 0.2, np.inf))],
ids=["descriptor_number", "number"])
def test_division_with_descriptor_number_and_number(self, parameter : Parameter, test, expected, expected_reverse):
# When
Expand Down Expand Up @@ -750,10 +755,10 @@ def test_zero_value_divided_by_parameter(self, parameter : Parameter, test, expe
assert result.variance == expected.variance

@pytest.mark.parametrize("first, second, expected", [
(DescriptorNumber("name", 1, "m", 0.01), Parameter("test", 2, "s", 0.1, -10, 10), Parameter("name / test", 0.5, "m/s", 0.00875, -np.Inf, np.Inf)),
(DescriptorNumber("name", -1, "m", 0.01), Parameter("test", 2, "s", 0.1, 0, 10), Parameter("name / test", -0.5, "m/s", 0.00875, -np.Inf, -0.1)),
(DescriptorNumber("name", 1, "m", 0.01), Parameter("test", -2, "s", 0.1, -10, 0), Parameter("name / test", -0.5, "m/s", 0.00875, -np.Inf, -0.1)),
(DescriptorNumber("name", -1, "m", 0.01), Parameter("test", -2, "s", 0.1, -10, 0), Parameter("name / test", 0.5, "m/s", 0.00875, 0.1, np.Inf)),
(DescriptorNumber("name", 1, "m", 0.01), Parameter("test", 2, "s", 0.1, -10, 10), Parameter("name / test", 0.5, "m/s", 0.00875, -np.inf, np.inf)),
(DescriptorNumber("name", -1, "m", 0.01), Parameter("test", 2, "s", 0.1, 0, 10), Parameter("name / test", -0.5, "m/s", 0.00875, -np.inf, -0.1)),
(DescriptorNumber("name", 1, "m", 0.01), Parameter("test", -2, "s", 0.1, -10, 0), Parameter("name / test", -0.5, "m/s", 0.00875, -np.inf, -0.1)),
(DescriptorNumber("name", -1, "m", 0.01), Parameter("test", -2, "s", 0.1, -10, 0), Parameter("name / test", 0.5, "m/s", 0.00875, 0.1, np.inf)),
(DescriptorNumber("name", 1, "m", 0.01), Parameter("test", 2, "s", 0.1, 1, 10), Parameter("name / test", 0.5, "m/s", 0.00875, 0.1, 1))],
ids=["crossing_zero", "positive_0_with_negative", "negative_0_with_positive", "negative_0_with_negative", "finite_limits"])
def test_division_with_descriptor_number_missing_cases(self, first, second, expected):
Expand Down Expand Up @@ -789,8 +794,8 @@ def test_divide_by_zero_value_parameter(self):
@pytest.mark.parametrize("test, expected", [
(3, Parameter("name ** 3", 125, "m^3", 281.25, -125, 1000)),
(2, Parameter("name ** 2", 25, "m^2", 5.0, 0, 100)),
(-1, Parameter("name ** -1", 0.2, "1/m", 8e-5, -np.Inf, np.Inf)),
(-2, Parameter("name ** -2", 0.04, "1/m^2", 1.28e-5, 0, np.Inf)),
(-1, Parameter("name ** -1", 0.2, "1/m", 8e-5, -np.inf, np.inf)),
(-2, Parameter("name ** -2", 0.04, "1/m^2", 1.28e-5, 0, np.inf)),
(0, DescriptorNumber("name ** 0", 1, "dimensionless", 0)),
(DescriptorNumber("test", 2), Parameter("name ** test", 25, "m^2", 5.0, 0, 100))],
ids=["power_3", "power_2", "power_-1", "power_-2", "power_0", "power_descriptor_number"])
Expand All @@ -812,13 +817,13 @@ def test_power_of_parameter(self, test, expected):
assert result.max == expected.max

@pytest.mark.parametrize("test, exponent, expected", [
(Parameter("name", 5, "m", 0.05, 0, 10), -1, Parameter("name ** -1", 0.2, "1/m", 8e-5, 0.1, np.Inf)),
(Parameter("name", -5, "m", 0.05, -5, 0), -1, Parameter("name ** -1", -0.2, "1/m", 8e-5, -np.Inf, -0.2)),
(Parameter("name", 5, "m", 0.05, 0, 10), -1, Parameter("name ** -1", 0.2, "1/m", 8e-5, 0.1, np.inf)),
(Parameter("name", -5, "m", 0.05, -5, 0), -1, Parameter("name ** -1", -0.2, "1/m", 8e-5, -np.inf, -0.2)),
(Parameter("name", 5, "m", 0.05, 5, 10), -1, Parameter("name ** -1", 0.2, "1/m", 8e-5, 0.1, 0.2)),
(Parameter("name", -5, "m", 0.05, -10, -5), -1, Parameter("name ** -1", -0.2, "1/m", 8e-5, -0.2, -0.1)),
(Parameter("name", -5, "m", 0.05, -10, -5), -2, Parameter("name ** -2", 0.04, "1/m^2", 1.28e-5, 0.01, 0.04)),
(Parameter("name", 5, "", 0.1, 1, 10), 0.3, Parameter("name ** 0.3", 1.6206565966927624, "", 0.0009455500095853564, 1, 1.9952623149688795)),
(Parameter("name", 5, "", 0.1), 0.5, Parameter("name ** 0.5", 2.23606797749979, "", 0.005, 0, np.Inf))],
(Parameter("name", 5, "", 0.1), 0.5, Parameter("name ** 0.5", 2.23606797749979, "", 0.005, 0, np.inf))],
ids=["0_positive", "negative_0", "both_positive", "both_negative_invert", "both_negative_invert_square", "fractional", "fractional_negative_limit"])
def test_power_of_diffent_parameters(self, test, exponent, expected):
# When Then
Expand Down
Loading