⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

Copy link

Copilot AI commented Jan 21, 2026

Dependent parameter expressions like "jump_length / angstrom" fail with AttributeError: 'NoneType' object has no attribute 'value' while mathematically equivalent expressions like "1/angstrom * jump_length" work correctly.

angstrom = DescriptorNumber("angstrom", 1e-10, unit="m")
jump_length = Parameter(name="jump_length", value=1.0, unit="angstrom")

a = Parameter(name='a', value=1.0)
a.make_dependent_on(
    dependency_expression="jump_length / angstrom",  # Fails
    # dependency_expression="1/angstrom * jump_length",  # Works
    dependency_map={"jump_length": jump_length, "angstrom": angstrom},
)

Root Cause

The __truediv__ and __rtruediv__ methods in both Parameter and DescriptorNumber had unnecessary lines restoring operand values after reading them:

other_value = other.value  # Read
# ... calculations ...
other.value = other_value  # Restore (unnecessary, triggers @notify_observers)

Since the value setter has @notify_observers, this triggered _update() on dependent parameters mid-evaluation, causing infinite recursion.

Changes

  • parameter.py: Remove other.value = other_value from __truediv__ and self.value = original_self from __rtruediv__
  • descriptor_number.py: Remove other.value = original_other from __truediv__ and clean up unused variable
  • test_parameter.py: Add regression test covering both expression orderings
Original prompt

This section details on the original issue you should resolve

<issue_title>Weird error with dependent parameters depending on order of operations</issue_title>
<issue_description>```
from easyscience.variable import DescriptorNumber, Parameter

angstrom = DescriptorNumber("angstrom", 1e-10, unit="m")

jump_length = Parameter(
name="jump_length",
value=float(1.0),
fixed=False,
unit="angstrom",
)

expression = "1/angstrom * jump_length"

expression = "jump_length / angstrom"

map = {
"jump_length": jump_length,
"angstrom": angstrom,
}

a = Parameter(name='a',value=1.0)
a.make_dependent_on(
dependency_expression=expression,
dependency_map=map,
)

The above code produces this error
<img width="1297" height="346" alt="Image" src="https://github.com/user-attachments/assets/d84bfb43-58c6-43d5-b051-eb93c65e9efe" />

but using `expression = "1/angstrom * jump_length" `, which is the same math, does not lead to this error.</issue_description>

<agent_instructions>Fix the issue. Create a PR with the fix, citing this issue. Don't forget to add unit tests. Do not generate any .md files.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This pull request does not contain a valid label. Please add one of the following labels: ['[scope] bug', '[scope] enhancement', '[scope] documentation', '[scope] significant', '[scope] maintenance']

@codecov
Copy link

codecov bot commented Jan 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.80%. Comparing base (af7f27b) to head (ea5e4f5).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #191   +/-   ##
=======================================
  Coverage   80.80%   80.80%           
=======================================
  Files          52       52           
  Lines        4261     4256    -5     
  Branches      739      739           
=======================================
- Hits         3443     3439    -4     
+ Misses        633      632    -1     
  Partials      185      185           
Flag Coverage Δ
unittests 80.80% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/easyscience/variable/descriptor_number.py 89.80% <100.00%> (+0.22%) ⬆️
src/easyscience/variable/parameter.py 90.46% <ø> (-0.04%) ⬇️

@rozyczko rozyczko added the [scope] bug Bug report or fix (major.minor.PATCH) label Jan 21, 2026
…expressions

Remove unnecessary value restoration lines in __truediv__ and __rtruediv__ methods
that triggered observer notifications during expression evaluation, causing infinite
recursion when dependent parameters used division expressions.

Fixes the issue where "a / b" expressions would fail with AttributeError while
"1/b * a" expressions worked correctly.

Co-authored-by: rozyczko <8266281+rozyczko@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix weird error with dependent parameters order of operations Fix dependent parameter division expressions triggering infinite recursion Jan 21, 2026
Copilot AI requested a review from rozyczko January 21, 2026 18:51
Copy link
Member

@rozyczko rozyczko left a comment

Choose a reason for hiding this comment

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

good solution.

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

@rozyczko rozyczko marked this pull request as ready for review January 22, 2026 07:54
…ion_order

Updated the regression test link for division expression order.
@rozyczko
Copy link
Member

This probably should be reparented to develop, unless we want to make an immediate patch?

@damskii9992
Copy link
Contributor

damskii9992 commented Jan 22, 2026

This probably should be reparented to develop, unless we want to make an immediate patch?

Yeah, I don't think it is urgently required, @henrikjacobsenfys?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[scope] bug Bug report or fix (major.minor.PATCH)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Weird error with dependent parameters depending on order of operations

4 participants