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

Implement __array__ for qasm2._DefinedGate (backport #12119) #12523

Merged
merged 1 commit into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions qiskit/qasm2/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import math
from typing import Iterable, Callable

import numpy as np

from qiskit.circuit import (
Barrier,
CircuitInstruction,
Expand All @@ -30,6 +32,7 @@
Reset,
library as lib,
)
from qiskit.quantum_info import Operator
from qiskit._accelerate.qasm2 import (
OpCode,
UnaryOpCode,
Expand Down Expand Up @@ -315,6 +318,11 @@ def _define(self):
raise ValueError(f"received invalid bytecode to build gate: {op}")
self._definition = qc

def __array__(self, dtype=None, copy=None):
if copy is False:
raise ValueError("unable to avoid copy while creating an array as requested")
return np.asarray(Operator(self.definition), dtype=dtype)

# It's fiddly to implement pickling for PyO3 types (the bytecode stream), so instead if we need
# to pickle ourselves, we just eagerly create the definition and pickle that.

Expand Down
9 changes: 9 additions & 0 deletions releasenotes/notes/qasm2-to-matrix-c707fe1e61b3987f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Custom gates (those stemming from a ``gate`` statement) in imported OpenQASM 2 programs will now
have an :meth:`.Gate.to_matrix` implementation. Previously they would have no matrix definition,
meaning that roundtrips through OpenQASM 2 could needlessly lose the ability to derive the gate
matrix. Note, though, that the matrix is calculated by recursively finding the matrices of the
inner gate definitions, as :class:`.Operator` does, which might be less performant than before
the round-trip.
26 changes: 26 additions & 0 deletions test/python/qasm2/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import tempfile

import ddt
import numpy as np

import qiskit.qasm2
from qiskit import qpy
Expand All @@ -34,6 +35,7 @@
Qubit,
library as lib,
)
from qiskit.quantum_info import Operator
from test import QiskitTestCase # pylint: disable=wrong-import-order

from . import gate_builder
Expand Down Expand Up @@ -906,6 +908,30 @@ def test_conditioned_broadcast_against_empty_register(self):
)
self.assertEqual(parsed, qc)

def test_has_to_matrix(self):
program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg qr[1];
gate my_gate(a) q {
rz(a) q;
rx(pi / 2) q;
rz(-a) q;
}
my_gate(1.0) qr[0];
"""
parsed = qiskit.qasm2.loads(program)
expected = (
lib.RZGate(-1.0).to_matrix()
@ lib.RXGate(math.pi / 2).to_matrix()
@ lib.RZGate(1.0).to_matrix()
)
defined_gate = parsed.data[0].operation
self.assertEqual(defined_gate.name, "my_gate")
np.testing.assert_allclose(defined_gate.to_matrix(), expected, atol=1e-14, rtol=0)
# Also test that the standard `Operator` method on the whole circuit still works.
np.testing.assert_allclose(Operator(parsed), expected, atol=1e-14, rtol=0)


class TestReset(QiskitTestCase):
def test_single(self):
Expand Down
Loading