Skip to content

Remove equality between Param::Obj and Param::ParameterExpression#16084

Merged
jakelishman merged 1 commit into
Qiskit:mainfrom
mtreinish:fix-param-equality
Apr 24, 2026
Merged

Remove equality between Param::Obj and Param::ParameterExpression#16084
jakelishman merged 1 commit into
Qiskit:mainfrom
mtreinish:fix-param-equality

Conversation

@mtreinish

Copy link
Copy Markdown
Member

This commit fixes a mismatch in the Param::eq() method where a Python object param was potentially viewed as equal to a ParameterExpression param. When comparing a python object parameter with a ParameterExpression it would convert the ParameterExpression into a pyobject and then uses Python's == to evaluate the equality. Historically this was something that was done because the ParameterExpression object was also a Python object. However, since the function was first written the source of truth for the inner ParameterExpression has moved to Rust. We're never in a situation anymore when a ParameterExpression parameter on an instruction should be treated as a Python object.

While this legacy equality may seem harmless because the equality should typically just evaluate to false (albeit slowly through Python's equality check), there are edge cases where this is doing the incorrect comparison. Primarily the bug that #16076 was fixing was not caught initially because in tests this legacy logic was converting a ParameterExpression that is a constant int expression with no symbols to a Python int during the conversion of the expression to Python. That was then being compared against a Param::Obj() of a true Python int. The equality check would incorrectly assert the ParameterExpression was the same as the python int. This is what masked the bug in #16076 because this differs from how the rust data model treats these parameters.

AI/LLM disclosure

  • I didn't use LLM tooling, or only used it privately.
  • I used the following tool to help write this PR description:
  • I used the following tool to generate or modify code:

This commit fixes a mismatch in the Param::eq() method where a Python
object param was potentially viewed as equal to a ParameterExpression
param. When comparing a python object parameter with a
ParameterExpression it would convert the ParameterExpression into a
pyobject and then uses Python's == to evaluate the equality.
Historically this was something that was done because the
ParameterExpression object was also a Python object. However, since
the function was first written the source of truth for the inner
ParameterExpression has moved to Rust. We're never in a situation
anymore when a ParameterExpression parameter on an instruction should be
treated as a Python object.

While this legacy equality may seem harmless because the equality should
typically just evaluate to false (albeit slowly through Python's
equality check), there are edge cases where this is doing the incorrect
comparison. Primarily the bug that Qiskit#16076 was fixing was not caught
initially because in tests this legacy logic was converting a
ParameterExpression that is a constant int expression with no symbols
to a Python int during the conversion of the expression to Python.
That was then being compared against a Param::Obj() of a true Python
int. The equality check would incorrectly assert the ParameterExpression
was the same as the python int. This is what masked the bug in Qiskit#16076
because this differs from how the rust data model treats these
parameters.
@mtreinish mtreinish requested a review from a team as a code owner April 24, 2026 15:37
@mtreinish mtreinish added Changelog: None Do not include in the GitHub Release changelog. Rust This PR or issue is related to Rust code in the repository labels Apr 24, 2026
@qiskit-bot

Copy link
Copy Markdown
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@jakelishman jakelishman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tbf, the fact that ParameterExpression attempts to typecast itself on conversion to Python space was another reason the bug was masked as well - hard to pin down when type(qc.data[0].params[0]) is int but the OQ3 dumper is saying "failed to parse ParameterExpression"!

@jakelishman jakelishman added this pull request to the merge queue Apr 24, 2026
Merged via the queue into Qiskit:main with commit 091e404 Apr 24, 2026
27 checks passed
@mtreinish mtreinish deleted the fix-param-equality branch April 24, 2026 17:38
@mtreinish

Copy link
Copy Markdown
Member Author

Thinking more about that we probably need to fix that too. We'll end in a really weird state where type(qc.data[0].params[0]) is int but qc.data[0].is_parameterized() returns True. I'm sure there is all sorts of other things broken by that too.

@jakelishman

Copy link
Copy Markdown
Member

Yeah - my initial patch for the QPY debugging included that change, it just needed to be stripped from the backport.

@ShellyGarion ShellyGarion added this to the 2.5.0 milestone Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: None Do not include in the GitHub Release changelog. Rust This PR or issue is related to Rust code in the repository

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants