Skip to content

Conversation

mtreinish
Copy link
Member

Summary

Normally creating a custom gate class that overloads the name of a Qiskit defined operation is not valid and not allowed. The names have meaning and are often used as identifiers and this overloading the name will prevent Qiskit from correctly identifying an operation. However as was discovered in #14103 there are some paths available around serialization and I/O where Qiskit does this itself. For example, qasm (both 2 and 3) is a lossy serialization format and qasm2 doesn't have a representation of a UnitaryGate. So when the qasm2 exporter encounteres a UnitaryGate it is serialized as a custom gate definition with the name "unitary" in the output qasm2 and the definition is a decomposition of the unitary from the UnitaryGate. When that qasm2 program is subsequently deserialized by qiskit parser the custom gate named "unitary" is added as a _DefinedGate subclass which includes an __array__ implementation which computes the unitary from the definition using the quantum info Operator class. This makes the custom gate parsed from qasm2 look like a UnitaryGate despite not actually one so this is typically fine for most use cases. However, since #13759 trying to add that not UnitaryGate object named "unitary" would cause the Python -> Rust translation to panic (which happens as part of qasm2 desierailzation). because the conversion was expecting a gate named unitary to be a UnitaryGate as is prescribed by the data model.

This commit fixes this by gracefully handling the lack of a matrix parameter as it not actually being a UnitaryGate and instead the object gets treated as a PyGate in rust which is the expected behavior.

Details and comments

Related to #14103

Normally creating a custom gate class that overloads the name of a
Qiskit defined operation is not valid and not allowed. The names have
meaning and are often used as identifiers and this overloading the name
will prevent Qiskit from correctly identifying an operation. However as
was discovered in Qiskit#14103 there are some paths available around
serialization and I/O where Qiskit does this itself. For example, qasm
(both 2 and 3) is a lossy serialization format and qasm2 doesn't have a
representation of a UnitaryGate. So when the qasm2 exporter encounteres
a `UnitaryGate` it is serialized as a custom gate definition with the
name "unitary" in the output qasm2 and the definition is a decomposition
of the unitary from the `UnitaryGate`. When that qasm2 program is
subsequently deserialized by qiskit parser the custom gate named "unitary"
is added as a `_DefinedGate` subclass which includes an `__array__`
implementation which computes the unitary from the definition using
the quantum info Operator class. This makes the custom gate parsed from
qasm2 look like a `UnitaryGate` despite not actually one so this is
typically fine for most use cases. However, since Qiskit#13759 trying to add
that not `UnitaryGate` object named "unitary" would cause the Python ->
Rust translation to panic (which happens as part of qasm2
desierailzation). because the conversion was expecting a gate named
`unitary` to be a `UnitaryGate` as is prescribed by the data model.

This commit fixes this by gracefully handling the lack of a matrix
parameter as it not actually being a `UnitaryGate` and instead the
object gets treated as a `PyGate` in rust which is the expected
behavior.

Related to Qiskit#14103
@mtreinish mtreinish added Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository labels Mar 26, 2025
@mtreinish mtreinish added this to the 2.0.0 milestone Mar 26, 2025
@mtreinish mtreinish requested a review from a team as a code owner March 26, 2025 19:22
@qiskit-bot
Copy link
Collaborator

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

  • @Qiskit/terra-core

@mtreinish mtreinish added the mod: qasm2 Relating to OpenQASM 2 import or export label Mar 26, 2025
@coveralls
Copy link

Pull Request Test Coverage Report for Build 14091753516

Details

  • 1 of 1 (100.0%) changed or added relevant line in 1 file are covered.
  • 6 unchanged lines in 2 files lost coverage.
  • Overall coverage increased (+0.007%) to 88.094%

Files with Coverage Reduction New Missed Lines %
crates/accelerate/src/unitary_synthesis.rs 1 94.79%
crates/qasm2/src/lex.rs 5 92.23%
Totals Coverage Status
Change from base Build 14090658887: 0.007%
Covered Lines: 72762
Relevant Lines: 82596

💛 - Coveralls

@mtreinish mtreinish added the stable backport potential The bug might be minimal and/or import enough to be port to stable label Mar 27, 2025
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

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

This is a sensible fix! That said, are we hoping to fix said parsing to PyGate in the future to use the UnitaryGate implementation introduced by #13759?

@ElePT
Copy link
Contributor

ElePT commented Mar 27, 2025

@Mergifyio backport stable/1.4

Copy link
Contributor

mergify bot commented Mar 27, 2025

backport stable/1.4

✅ Backports have been created

@mtreinish
Copy link
Member Author

That said, are we hoping to fix said parsing to PyGate in the future to use the UnitaryGate implementation introduced by #13759?

No I don't think so, qasm2 doesn't have a representation of a UnitaryGate (qasm3 does IIRC) and we should only be using the UnitaryGate in rust if it comes from a UnitaryGate object in Python. So in this case it should always be treated as a PyGate. The reason for the bug is we can't do an isinstance() check in the rust code because loading UnitaryGate python class would cause an import cycle.

@mtreinish
Copy link
Member Author

@Mergifyio dequeue

Copy link
Contributor

mergify bot commented Mar 27, 2025

dequeue

☑️ The pull request is not queued

@mtreinish
Copy link
Member Author

@ElePT this PR is only for 2.0, the 1.4 fix is a separate PR: #14108 as we didn't have a Rust representation of UnitaryGate in 1.4.

@mtreinish mtreinish added this pull request to the merge queue Mar 27, 2025
Merged via the queue into Qiskit:main with commit ddb8025 Mar 27, 2025
22 checks passed
mergify bot pushed a commit that referenced this pull request Mar 27, 2025
Normally creating a custom gate class that overloads the name of a
Qiskit defined operation is not valid and not allowed. The names have
meaning and are often used as identifiers and this overloading the name
will prevent Qiskit from correctly identifying an operation. However as
was discovered in #14103 there are some paths available around
serialization and I/O where Qiskit does this itself. For example, qasm
(both 2 and 3) is a lossy serialization format and qasm2 doesn't have a
representation of a UnitaryGate. So when the qasm2 exporter encounteres
a `UnitaryGate` it is serialized as a custom gate definition with the
name "unitary" in the output qasm2 and the definition is a decomposition
of the unitary from the `UnitaryGate`. When that qasm2 program is
subsequently deserialized by qiskit parser the custom gate named "unitary"
is added as a `_DefinedGate` subclass which includes an `__array__`
implementation which computes the unitary from the definition using
the quantum info Operator class. This makes the custom gate parsed from
qasm2 look like a `UnitaryGate` despite not actually one so this is
typically fine for most use cases. However, since #13759 trying to add
that not `UnitaryGate` object named "unitary" would cause the Python ->
Rust translation to panic (which happens as part of qasm2
desierailzation). because the conversion was expecting a gate named
`unitary` to be a `UnitaryGate` as is prescribed by the data model.

This commit fixes this by gracefully handling the lack of a matrix
parameter as it not actually being a `UnitaryGate` and instead the
object gets treated as a `PyGate` in rust which is the expected
behavior.

Related to #14103

(cherry picked from commit ddb8025)

# Conflicts:
#	crates/circuit/src/circuit_instruction.rs
#	test/python/transpiler/test_split_2q_unitaries.py
mergify bot pushed a commit that referenced this pull request Mar 27, 2025
Normally creating a custom gate class that overloads the name of a
Qiskit defined operation is not valid and not allowed. The names have
meaning and are often used as identifiers and this overloading the name
will prevent Qiskit from correctly identifying an operation. However as
was discovered in #14103 there are some paths available around
serialization and I/O where Qiskit does this itself. For example, qasm
(both 2 and 3) is a lossy serialization format and qasm2 doesn't have a
representation of a UnitaryGate. So when the qasm2 exporter encounteres
a `UnitaryGate` it is serialized as a custom gate definition with the
name "unitary" in the output qasm2 and the definition is a decomposition
of the unitary from the `UnitaryGate`. When that qasm2 program is
subsequently deserialized by qiskit parser the custom gate named "unitary"
is added as a `_DefinedGate` subclass which includes an `__array__`
implementation which computes the unitary from the definition using
the quantum info Operator class. This makes the custom gate parsed from
qasm2 look like a `UnitaryGate` despite not actually one so this is
typically fine for most use cases. However, since #13759 trying to add
that not `UnitaryGate` object named "unitary" would cause the Python ->
Rust translation to panic (which happens as part of qasm2
desierailzation). because the conversion was expecting a gate named
`unitary` to be a `UnitaryGate` as is prescribed by the data model.

This commit fixes this by gracefully handling the lack of a matrix
parameter as it not actually being a `UnitaryGate` and instead the
object gets treated as a `PyGate` in rust which is the expected
behavior.

Related to #14103

(cherry picked from commit ddb8025)
github-merge-queue bot pushed a commit that referenced this pull request Mar 27, 2025
Normally creating a custom gate class that overloads the name of a
Qiskit defined operation is not valid and not allowed. The names have
meaning and are often used as identifiers and this overloading the name
will prevent Qiskit from correctly identifying an operation. However as
was discovered in #14103 there are some paths available around
serialization and I/O where Qiskit does this itself. For example, qasm
(both 2 and 3) is a lossy serialization format and qasm2 doesn't have a
representation of a UnitaryGate. So when the qasm2 exporter encounteres
a `UnitaryGate` it is serialized as a custom gate definition with the
name "unitary" in the output qasm2 and the definition is a decomposition
of the unitary from the `UnitaryGate`. When that qasm2 program is
subsequently deserialized by qiskit parser the custom gate named "unitary"
is added as a `_DefinedGate` subclass which includes an `__array__`
implementation which computes the unitary from the definition using
the quantum info Operator class. This makes the custom gate parsed from
qasm2 look like a `UnitaryGate` despite not actually one so this is
typically fine for most use cases. However, since #13759 trying to add
that not `UnitaryGate` object named "unitary" would cause the Python ->
Rust translation to panic (which happens as part of qasm2
desierailzation). because the conversion was expecting a gate named
`unitary` to be a `UnitaryGate` as is prescribed by the data model.

This commit fixes this by gracefully handling the lack of a matrix
parameter as it not actually being a `UnitaryGate` and instead the
object gets treated as a `PyGate` in rust which is the expected
behavior.

Related to #14103

(cherry picked from commit ddb8025)

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
@mtreinish mtreinish deleted the qasm2-doesnt-like-linear-algebra branch March 27, 2025 17:54
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 mod: qasm2 Relating to OpenQASM 2 import or export Rust This PR or issue is related to Rust code in the repository stable backport potential The bug might be minimal and/or import enough to be port to stable
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants