-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fix handling of custom gates named unitary in Split2qUnitaries #14108
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 handling of custom gates named unitary in Split2qUnitaries #14108
Conversation
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, if you then qpy serialize the circuit that contains a custom `_DefinedGate` instance that's named "unitary", QPY doesn't understand what `_DefinedGate` is and assumes that it can not fully recreate it because it's outside the standard Qiskit data model. In these cases qpy serializes the fields from the standard data model defined in Qiskit which it knows about: name, params, definition, etc and encodes that as a custom gate. When deserializing that custom gate it populates the standard fields, but anything defined outside of that is lost through the serialization because it's defined outside of Qiskit. This means the custom `__array__` implementation is missing from the qpy round-tripped, previously qasm2 round-tripped, `UnitaryGate` instance in a circuit. This was then causing the transpiler to panic (which is a hard crash) in the Split2QUnitaries pass because when it saw a gate named "unitary" it rightly assumed it was a `UnitaryGate` and there is always a matrix available (because a `UnitaryGate` is a gate defined solely by it's matrix). This is an example of why overloading gate names is typically not allowed and is considered invalid usage of Qiskit. However, in this case since the resulting gate came from valid use of Qiskit's API trying to ensure the circuit doesn't hard crash the transpiler is correct here because it's a result in internal inconsistencies in Qiskit. Treating the twice round-tripped UnitaryGate as the original unitary gate is not possible because of the loss of information via serialization, but qiskit should at least not have an internal panic!() because of this usage. Arguably we could fix this in QPY by adding either a matrix field to the format specification for custom gates, or adding handling for `_DefinedGate` to qpy. But either will require a new format version which is not eligble for backport to 1.4.x. So this commit fixes this issue by updating the transpiler pass in question to just treat the matrix of a gate named unitary as fallible, and we just ignore the custom gate named unitary that's missing a matrix. This already been fixed on main and stable/2.0 as a side effect of PR Qiskit#13765 which made the typing around UnitaryGate in rust more explicit. So the custom deserialized gate from QPY which doesn't have a matrix defined is never treated as a unitary gate and even through the qasm2 round trip followed by a qpy round trip we don't encounter any issues. Fixes Qiskit#14103
One or more of the following people are relevant to this code:
|
This isn't 100% accurate, the qasm deserialization trying to add the EDIT: actually we just shouldn't fail there and treat it as a custom gate, that's the simpler fix anyway. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Though I wonder if there are any other places within the transpiler in which this may have happened before #13765... Luckily we won't have to worry about this in future versions.
I checked and the only place we do this is in |
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 theUnitaryGate
. 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 aUnitaryGate
despite not actually one so this is typically fine for most use cases. However, if you then qpy serialize the circuit that contains a custom_DefinedGate
instance that's named "unitary", QPY doesn't understand what_DefinedGate
is and assumes that it can not fully recreate it because it's outside the standard Qiskit data model. In these cases qpy serializes the fields from the standard data model defined in Qiskit which it knows about: name, params, definition, etc and encodes that as a custom gate. When deserializing that custom gate it populates the standard fields, but anything defined outside of that is lost through the serialization because it's defined outside of Qiskit. This means the custom__array__
implementation is missing from the qpy round-tripped, previously qasm2 round-tripped,UnitaryGate
instance in a circuit. This was then causing the transpiler to panic (which is a hard crash) in the Split2QUnitaries pass because when it saw a gate named "unitary" it rightly assumed it was aUnitaryGate
and there is always a matrix available (because aUnitaryGate
is a gate defined solely by it's matrix). This is an example of why overloading gate names is typically not allowed and is considered invalid usage of Qiskit. However, in this case since the resulting gate came from valid use of Qiskit's API trying to ensure the circuit doesn't hard crash the transpiler is correct here because it's a result in internal inconsistencies in Qiskit. Treating the twice round-tripped UnitaryGate as the original unitary gate is not possible because of the loss of information via serialization, but qiskit should at least not have an internal panic!() because of this usage.Arguably we could fix this in QPY by adding either a matrix field to the format specification for custom gates, or adding handling for
_DefinedGate
to qpy. But either will require a new format version which is not eligble for backport to 1.4.x. So this commit fixes this issue by updating the transpiler pass in question to just treat the matrix of a gate named unitary as fallible, and we just ignore the custom gate named unitary that's missing a matrix.This already been fixed on main and stable/2.0 as a side effect of PR #13765 which made the typing around UnitaryGate in rust more explicit. So the custom deserialized gate from QPY which doesn't have a matrix defined is never treated as a unitary gate and even through the qasm2 round trip followed by a qpy round trip we don't encounter any issues.
Details and comments
Fixes #14103