Skip to content

Commit 9c2970c

Browse files
authored
added constraint adjustment (#116)
1 parent 78626fe commit 9c2970c

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

RATapi/utils/convert.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Utilities for converting input files to Python `Project`s."""
22

33
import json
4+
import warnings
45
from collections.abc import Iterable
56
from os import PathLike
67
from pathlib import Path
@@ -67,7 +68,7 @@ def zip_if_several(*params) -> Union[tuple, list[tuple]]:
6768
return [params]
6869

6970
def read_param(names, constrs, values, fits):
70-
"""Read in a parameter list from the relevant keys.
71+
"""Read in a parameter list from the relevant keys, and fix constraints for non-fit parameters.
7172
7273
Parameters
7374
----------
@@ -77,10 +78,55 @@ def read_param(names, constrs, values, fits):
7778
7879
Returns
7980
-------
80-
list
81+
ClassList
8182
A list of all relevant parameters.
8283
"""
8384

85+
def fix_invalid_constraints(name: str, constrs: tuple[float, float], value: float) -> tuple[float, float]:
86+
"""Check that constraints are valid and fix them if they aren't.
87+
88+
RasCAL-1 allowed the constraints of non-fit parameters to be invalid, which means
89+
we need to fix them here so that the project is valid.
90+
91+
Parameters
92+
----------
93+
name: str
94+
The name of the parameter.
95+
constrs : tuple[float, float]
96+
The constraints of the parameter (min and max, respectively)
97+
value : float
98+
The value of the parameter.
99+
100+
Returns
101+
-------
102+
tuple[float, float]
103+
The adjusted constraints (identical to constrs if constraints were valid)
104+
105+
"""
106+
new_constrs = (min(constrs[0], value), max(constrs[1], value))
107+
if new_constrs[0] != constrs[0] or new_constrs[1] != constrs[1]:
108+
warnings.warn(
109+
f"The parameter {name} has invalid constraints,"
110+
" these have been adjusted to satisfy the current value of the parameter.",
111+
stacklevel=2,
112+
)
113+
return new_constrs
114+
115+
# adjust invalid constraints
116+
# if just one item in the classlist, these objects won't be in lists
117+
if not isinstance(fit := mat_project[fits], Iterable):
118+
if not fit:
119+
mat_project[constrs] = fix_invalid_constraints(
120+
mat_project[names], mat_project[constrs], mat_project[values]
121+
)
122+
# else they will be iterable
123+
else:
124+
for i, fit in enumerate(mat_project[fits]):
125+
if not fit:
126+
mat_project[constrs][i] = fix_invalid_constraints(
127+
mat_project[names][i], mat_project[constrs][i], mat_project[values][i]
128+
)
129+
84130
return ClassList(
85131
[
86132
Parameter(

tests/test_convert.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ def mock_load(ignored_filename, **ignored_settings):
9999
assert getattr(converted_project, class_list) == getattr(original_project, class_list)
100100

101101

102+
def test_invalid_constraints():
103+
"""Test that invalid constraints are fixed where necessary."""
104+
with pytest.warns(
105+
match="The parameter Background parameter 1 has invalid constraints,"
106+
" these have been adjusted to satisfy the current value of the parameter."
107+
):
108+
output_project = r1_to_project_class(pathlib.Path(TEST_DIR_PATH, "R1DoubleBilayerVolumeModel.mat"))
109+
110+
assert output_project.background_parameters[0].min == output_project.background_parameters[0].value
111+
112+
102113
@pytest.mark.parametrize(
103114
"project",
104115
[
10.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)