Skip to content
Open
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
5 changes: 1 addition & 4 deletions src/easyscience/variable/descriptor_number.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,13 @@ def __rmul__(self, other: numbers.Number) -> DescriptorNumber:

def __truediv__(self, other: Union[DescriptorNumber, numbers.Number]) -> DescriptorNumber:
if isinstance(other, numbers.Number):
original_other = other
if other == 0:
raise ZeroDivisionError('Cannot divide by zero')
new_value = self.full_value / other
elif type(other) is DescriptorNumber:
original_other = other.value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually question whether restoration is needed at all. Reading .value shouldn't modify it: these are pure read operations followed by arithmetic. The only scenario where restoration would matter is if the getter has side effects that modify state, or some intermediate step in the calculation mutates the operand. This is highly unlikely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could swear this restoration was there for a reason, but I can't for the love of me figure out what that reason was . . .
But I guess we have unit tests and they don't fail after this change, so it must be fine? Right????

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DON'T QUESTION THE AI OVERLORDS

if original_other == 0:
if other.value == 0:
raise ZeroDivisionError('Cannot divide by zero')
new_value = self.full_value / other.full_value
other.value = original_other
else:
return NotImplemented
descriptor_number = DescriptorNumber.from_scipp(name=self.name, full_value=new_value)
Expand Down
2 changes: 0 additions & 2 deletions src/easyscience/variable/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,6 @@ def __truediv__(self, other: Union[DescriptorNumber, Parameter, numbers.Number])
combinations = [self.min / other.min, self.max / other.max, self.min / other.max, self.max / other.min]
else:
combinations = [self.min / other.value, self.max / other.value]
other.value = other_value
else:
return NotImplemented
min_value = min(combinations)
Expand Down Expand Up @@ -926,7 +925,6 @@ def __rtruediv__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parame
parameter = Parameter.from_scipp(name=self.name, full_value=new_full_value, min=min_value, max=max_value)
parameter._convert_unit(parameter._base_unit())
parameter.name = parameter.unique_name
self.value = original_self
return parameter

def __pow__(self, other: Union[DescriptorNumber, numbers.Number]) -> Parameter:
Expand Down
47 changes: 46 additions & 1 deletion tests/unit_tests/variable/test_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,51 @@ def test_dependent_parameter_return_is_descriptor_number(self):
assert dependent_parameter.min == 2*descriptor_number.value
assert dependent_parameter.max == 2*descriptor_number.value

def test_dependent_parameter_division_expression_order(self):
"""Test that division expressions work regardless of operand order.

This is a regression test for https://github.com/easyscience/corelib/issues/190
where 'a / b' would fail with an observer notification error while '1/b * a'
would work correctly.
"""
# When
angstrom = DescriptorNumber("angstrom", 1e-10, unit="m")
jump_length = Parameter(
name="jump_length",
value=float(1.0),
fixed=False,
unit="angstrom",
)

expression = "jump_length / angstrom"
dependency_map = {
"jump_length": jump_length,
"angstrom": angstrom,
}

# Calculate the expected result from direct division
expected_result = jump_length / angstrom
expected_value = expected_result.value

# Then - This should not raise an error
dependent_param = Parameter(name='a', value=1.0)
dependent_param.make_dependent_on(
dependency_expression=expression,
dependency_map=dependency_map,
)

# Expect - The dependent parameter should have the same value as the direct division
assert dependent_param.value == pytest.approx(expected_value)

# Also test the alternative expression that previously worked
expression_alt = "1/angstrom * jump_length"
dependent_param_alt = Parameter(name='b', value=1.0)
dependent_param_alt.make_dependent_on(
dependency_expression=expression_alt,
dependency_map=dependency_map,
)
assert dependent_param_alt.value == pytest.approx(expected_value)

def test_dependent_parameter_overwrite_dependency(self, normal_parameter: Parameter):
# When
dependent_parameter = Parameter.from_dependency(
Expand Down Expand Up @@ -1221,4 +1266,4 @@ def test_abs(self, test, expected):
assert result.unit == expected.unit
assert result.variance == expected.variance
assert result.min == expected.min
assert result.max == expected.max
assert result.max == expected.max
Loading