Skip to content

Conversation

mtreinish
Copy link
Member

@mtreinish mtreinish commented Dec 4, 2023

Summary

This commit updates the standard library gate classes' to define an eq methods to optimize the runtime performance. Their parent class Instruction's eq is optimized for generality to compare equality between any gate instruction object. But this generality has a runtime cost which is unnecessary for the standard library gates which it equality is much simpler. This commit adds the faster path equality check for the standard gate library classes which will improve the performance of anything doing a lot gate equality.

Details and comments

TODO:

  • Add UGate and other missing gates

@mtreinish mtreinish added performance Changelog: None Do not include in changelog labels Dec 4, 2023
@mtreinish mtreinish added this to the 1.0.0 milestone Dec 4, 2023
@mtreinish mtreinish requested a review from a team as a code owner December 4, 2023 23:03
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Cryoris
  • @Qiskit/terra-core
  • @ajavadia

This commit updates the standard library gate classes' to define an
__eq__ methods to optimize the runtime performance. Their parent class
`Instruction`'s __eq__ is optimized for generality to compare equality
between any gate instruction object. But this generality has a runtime
cost which is unnecessary for the standard library gates which it
equality is much simpler. This commit adds the faster path equality
check for the standard gate library classes which will improve the
performance of anything doing a lot gate equality.
@mtreinish mtreinish force-pushed the standard-lib-eq-update branch from 256b747 to a993f95 Compare December 4, 2023 23:04
@coveralls
Copy link

coveralls commented Dec 5, 2023

Pull Request Test Coverage Report for Build 7618275801

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.02%) to 89.491%

Totals Coverage Status
Change from base Build 7614776689: -0.02%
Covered Lines: 59591
Relevant Lines: 66589

💛 - Coveralls

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Thanks for this - I think this'll help performance of quite a lot of little bits and bobs across the code base.

@@ -959,6 +980,9 @@ def inverse(self):
"""Invert this gate. The C4X is its own inverse."""
return C4XGate(ctrl_state=self.ctrl_state)

def __eq__(self, other):
return isinstance(other, C4XGate) and self.ctrl_state == other.ctrl_state


class MCXGate(ControlledGate):
Copy link
Member

Choose a reason for hiding this comment

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

This might well want to be a follow-up, but if we could get a decent way to improving the MCXGate equality checks, it'd help (part of) #10641.

@@ -112,3 +112,6 @@ def _define(self):
def inverse(self):
"""Return inverse ECR gate (itself)."""
return ECRGate() # self-inverse

def __eq__(self, other):
return isinstance(other, ECRGate)
Copy link
Contributor

Choose a reason for hiding this comment

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

If someone were to extend ECRGate via inheritance, this definition of __eq__ would be wrong for that subclass. Is it worth instead defining this once for SingletonGate as:

def __eq__(self, other):
    return isinstance(other, type(self))

Copy link
Member

Choose a reason for hiding this comment

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

That wouldn't actually be correct for singleton gates, where the singleton instance is a separate part of the inheritance tree to the non-singleton versions.

I'm not super convinced that __eq__ should attempt to drive additional logic within subclasses; we can't really know what additional information a subclass might add on, nor what should really cause them to be considered equal. It largely seems to me that subclasses should almost always be responsible for overriding __eq__.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I forgot about that nuance with the class hierarchy. It seems to me like __eq__ should behave the same for all SingletonGates. If there is more state to compare, then I'd think it's no longer a SingletonGate from a traiting perspective.

@@ -222,3 +225,6 @@ def _define(self):
def inverse(self):
"""Return inverted CH gate (itself)."""
return CHGate(ctrl_state=self.ctrl_state) # self-inverse

def __eq__(self, other):
return isinstance(other, CHGate) and self.ctrl_state == other.ctrl_state
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment. Perhaps a shared implementation is possible for all SingletonControlledGate instances?

jakelishman
jakelishman previously approved these changes Dec 5, 2023
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

This looks fine to me, but I'm not tagging for merge so Kevin can respond as well.

Comment on lines +658 to +666
def _compare_parameters(self, other):
for x, y in zip(self.params, other.params):
try:
if not math.isclose(x, y, rel_tol=0, abs_tol=1e-10):
return False
except TypeError:
if x != y:
return False
return True
Copy link
Member

Choose a reason for hiding this comment

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

I guess specifically this is stdlib-gate parameters, but that's fine.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah it's why I made it private, I can add a comment or docstring for it. Or were you propoposing I rename it _compare_stdlib_gate_parameters()?

Copy link
Contributor

@kevinhartman kevinhartman left a comment

Choose a reason for hiding this comment

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

Looks good to me.

I see now why it'd be difficult to define __eq__ for some base in the class hierarchy.

@kevinhartman kevinhartman added this pull request to the merge queue Jan 23, 2024
Merged via the queue into Qiskit:main with commit 6c3f047 Jan 23, 2024
@mtreinish mtreinish deleted the standard-lib-eq-update branch January 23, 2024 21:23
jakelishman added a commit to jakelishman/qiskit-terra that referenced this pull request Oct 15, 2024
Most standard-library gates had a specialised `__eq__` added in
Qiskitgh-11370, but a small number were left over.  This adds specialisation
to almost all the stragglers, which were mostly old soft-deperecated
gates.  Despite not appearing in most circuits, these gates have an
outsized effect on equality comparisons if they ever _do_ appear,
because they involve construction of entire `definition` fields.
github-merge-queue bot pushed a commit that referenced this pull request Oct 15, 2024
…13327)

Most standard-library gates had a specialised `__eq__` added in
gh-11370, but a small number were left over.  This adds specialisation
to almost all the stragglers, which were mostly old soft-deperecated
gates.  Despite not appearing in most circuits, these gates have an
outsized effect on equality comparisons if they ever _do_ appear,
because they involve construction of entire `definition` fields.
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 changelog performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants