Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sympy error #5930

Merged
merged 9 commits into from
Oct 30, 2022
Merged

Conversation

dstrain115
Copy link
Collaborator

  • Sympy will soon error out if an expression is not parsable by sympy. It used to just ignore this.
  • We have one test that exercises this (resolver_test.py:247). Modify code to handle this and print out a warning.
  • This is breaking some internal google code and preventing sympy upgrade.

- Sympy will soon error out if an expression is not parsable by
  sympy.  It used to just ignore this.
- We have one test that exercises this (resolver_test.py:247).
  Modify code to handle this and print out a warning.
- This is breaking some internal google code and preventing sympy
  upgrade.
@dstrain115 dstrain115 requested review from a team, vtomole and cduck as code owners October 25, 2022 18:33
@dstrain115 dstrain115 requested a review from viathor October 25, 2022 18:33
@CirqBot CirqBot added the Size: XS <10 lines changed label Oct 25, 2022
v = value.subs(self.param_dict, simultaneous=True)
try:
v = value.subs(self.param_dict, simultaneous=True)
except sympy.SympifyError:
Copy link
Collaborator

Choose a reason for hiding this comment

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

What do you mean by "not parsable"? An actual syntax error? If so, then this is probably a user mistake and it's better to let the exception propagate out of here. Then the PR could catch it in resolver_test.py rather than in resolver.py. WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The actual sympy bug is here:
sympy/sympy#23887

Their definition is "sympifiable" whatever that means. I think that it is either a sympy object or a basic python type, possibly a few other cases. I think it is talking about objects foreign to the sympy library. In our case, that is the symbol 'NotImplemented'.

I am hesitant to change the existing behavior. The test currently asserts that resolving {'b': NotImplemented} would just leave sympy.Symbol('b') alone. I am loathe to change this behavior right now, since there is a lot of special handling around NotImplemented.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree we should preserve existing behavior.

I have two conerns:

  • the fact that if one of the values is unsympifiable then none of the substitutions are made,
  • the fact that we're failing to communicate a potential mistake to the user as soon as we discover it (we could be passing an unsympifiable object as a result of a simple typo in the initialization of the ParamDict).

Is NotImplemented the only offender? If so, could we just filter them out by saying something like

v = value.subs({k: w for k, w in self.param_dict.values() if w is not NotImplemented}, simultaneous=True)

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I changed this to raise a ValueError instead, explaining what the problem substitution is.

We cannot do your suggestion, because the test case is a function that defines resolved_value as NotImplemented. This is indistinguishable from something like Sympy.Symbol('b')+Sympy.Symbol('c') which also has an unimplemented resolved_value.

This does change the behavior of the function slightly, but, as you pointed out, the previous PR would also change the value as some additional variables would not be substituted.

I think this is the best solution we can do, since this preserves the behavior 'the variable could not be substituted' but makes it explicit as a ValueError rather than silently refusing to substitute it. It's also quite a buried case, because it involves the user specifying something that was unresolvable in the first place.

@CirqBot CirqBot added the size: S 10< lines changed <50 label Oct 26, 2022
Copy link
Collaborator

@viathor viathor left a comment

Choose a reason for hiding this comment

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

LGTM with one optional comment: I think it's better to just let the sympy.SympifyError exception propagate rather than turning it into a ValueError. The former allows for more fine-grained handling in the client code (and also makes it obvious to a human that this is coming out of sympy which aids debugging).

Comment on lines 249 to 250
with pytest.raises(ValueError, match='Could not resolve parameter b'):
_ = r.value_of(b)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should move this to a separate test function marked with a strict xfail, for example,

@pytest.mark.xfail(reason='this test requires sympy 1.12', strict=True)
def test_custom_value_not_implemented():
    ...

When the new sympy is released, the strict xfail will produce a CI failure
after which we can remove the xfail mark.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea done.

@dstrain115
Copy link
Collaborator Author

Changed to not rethrow sympify error.

@@ -112,6 +112,7 @@ def value_of(
Raises:
RecursionError: If the ParamResolver detects a loop in recursive
resolution.
ValueError: If the resulting value cannot be interpreted.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Update to sympy.SympifyError?

Copy link
Collaborator

@pavoljuhas pavoljuhas left a comment

Choose a reason for hiding this comment

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

LGTM with Adam's comment

@viathor viathor merged commit c45b2c5 into quantumlib:master Oct 30, 2022
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: S 10< lines changed <50 Size: XS <10 lines changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants