Fix `UnitaryGate.repeat()` method (#12986)

* fix: deal with special case for `UnitaryGate`

* add test

* add release note

* typo

* generalize the fix to other gates

* fix seeds

* update unittests

* Add missing unittests

---------

Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
This commit is contained in:
Timothée Dao 2024-08-26 10:17:49 +02:00 committed by GitHub
parent 54ac8f1751
commit 83ecf39656
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 99 additions and 1 deletions

View File

@ -97,7 +97,10 @@ class Gate(Instruction):
return self.power(exponent)
def _return_repeat(self, exponent: float) -> "Gate":
return Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=self.params)
gate = Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=[])
gate.validate_parameter = self.validate_parameter
gate.params = self.params
return gate
def control(
self,

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixes an error when calling the method :meth:`.UnitaryGate.repeat`.
Refer to `#11990 <https://github.com/Qiskit/qiskit/issues/11990>`_ for more
details.

View File

@ -502,6 +502,19 @@ class TestLinearFunctions(QiskitTestCase):
self.assertEqual(Clifford(qc_to_linear_function), qc_to_clifford)
self.assertEqual(qc_to_linear_function, LinearFunction(qc_to_clifford))
@data(2, 3)
def test_repeat_method(self, num_qubits):
"""Test the repeat() method."""
rng = np.random.default_rng(127)
for num_gates, seed in zip(
[0, 5, 5 * num_qubits], rng.integers(100000, size=10, dtype=np.uint64)
):
# create a random linear circuit
linear_circuit = random_linear_circuit(num_qubits, num_gates, seed=seed)
operator = Operator(linear_circuit)
linear_function = LinearFunction(linear_circuit)
self.assertTrue(Operator(linear_function.repeat(2)), operator @ operator)
if __name__ == "__main__":
unittest.main()

View File

@ -104,6 +104,12 @@ class TestPermutationGate(QiskitTestCase):
expected_inverse_perm = PermutationGate([3, 0, 5, 1, 4, 2])
self.assertTrue(np.array_equal(inverse_perm.pattern, expected_inverse_perm.pattern))
def test_repeat(self):
"""Test the ``repeat`` method."""
pattern = [2, 4, 1, 3, 0]
perm = PermutationGate(pattern)
self.assertTrue(np.allclose(Operator(perm.repeat(2)), Operator(perm) @ Operator(perm)))
class TestPermutationGatesOnCircuit(QiskitTestCase):
"""Tests for quantum circuits containing permutations."""

View File

@ -83,6 +83,19 @@ class TestDiagonalGate(QiskitTestCase):
all(isinstance(p, complex) and not isinstance(p, np.number) for p in params)
)
def test_repeat(self):
"""Test the repeat() method."""
for phases in [
[0, 0],
np.array([0, 0.8, 1, 0]),
(2 * np.pi * np.random.rand(2**3)).tolist(),
]:
with self.subTest(phases=phases):
diag = [np.exp(1j * ph) for ph in phases]
gate = DiagonalGate(diag)
operator = Operator(gate)
self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))
def _get_diag_gate_matrix(diag):
return np.diagflat(diag)

View File

@ -54,6 +54,8 @@ from qiskit.circuit.library import (
CZGate,
RYYGate,
PhaseGate,
PauliGate,
UCPauliRotGate,
CPhaseGate,
UGate,
CUGate,
@ -184,6 +186,18 @@ class TestGateDefinitions(QiskitTestCase):
self.assertTrue(len(decomposed_circuit) > len(circuit))
self.assertTrue(Operator(circuit).equiv(Operator(decomposed_circuit), atol=1e-7))
def test_pauligate_repeat(self):
"""Test `repeat` method for `PauliGate`."""
gate = PauliGate("XYZ")
operator = Operator(gate)
self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))
def test_ucpaulirotgate_repeat(self):
"""Test `repeat` method for `UCPauliRotGate`."""
gate = UCPauliRotGate([0.3, 0.5], "X")
operator = Operator(gate)
self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))
@ddt
class TestStandardGates(QiskitTestCase):

View File

@ -62,6 +62,12 @@ class TestHamiltonianGate(QiskitTestCase):
ham.adjoint().to_matrix(), np.transpose(np.conj(ham.to_matrix()))
)
def test_repeat(self):
"""test repeat operation"""
ham = HamiltonianGate(np.array([[1, 0.5 + 4j], [0.5 - 4j, -0.2]]), np.pi * 0.143)
operator = Operator(ham)
self.assertTrue(np.allclose(Operator(ham.repeat(2)), operator @ operator))
class TestHamiltonianCircuit(QiskitTestCase):
"""Hamiltonian gate circuit tests."""

View File

@ -473,6 +473,16 @@ class TestInitialize(QiskitTestCase):
vec = Statevector(qc)
self.assertTrue(vec == Statevector(desired_vector))
def test_repeat(self):
"""Test the repeat() method."""
desired_vector = np.array([0.5, 0.5, 0.5, 0.5])
initialize = Initialize(desired_vector)
qr = QuantumRegister(2)
qc = QuantumCircuit(qr)
qc.append(initialize.repeat(2), qr)
statevector = Statevector(qc)
self.assertTrue(np.allclose(statevector, desired_vector))
class TestInstructionParam(QiskitTestCase):
"""Test conversion of numpy type parameters."""

View File

@ -133,6 +133,21 @@ class TestIsometry(QiskitTestCase):
result = Operator(qc)
np.testing.assert_array_almost_equal(result.data, np.identity(result.dim[0]))
@data(
np.eye(2, 2),
random_unitary(2, seed=297102).data,
np.eye(4, 4),
random_unitary(4, seed=123642).data,
random_unitary(8, seed=568288).data,
)
def test_isometry_repeat(self, iso):
"""Tests for the repeat of isometries from n to n qubits"""
iso_gate = Isometry(iso, 0, 0)
op = Operator(iso_gate)
op_double = Operator(iso_gate.repeat(2))
np.testing.assert_array_almost_equal(op @ op, op_double)
if __name__ == "__main__":
unittest.main()

View File

@ -100,6 +100,13 @@ class TestUCGate(QiskitTestCase):
self.assertTrue(np.allclose(unitary_desired, unitary))
def test_repeat(self):
"""test repeat operation"""
gates = [random_unitary(2, seed=seed).data for seed in [124435, 876345, 687462, 928365]]
uc = UCGate(gates, up_to_diagonal=False)
self.assertTrue(np.allclose(Operator(uc.repeat(2)), Operator(uc) @ Operator(uc)))
def _get_ucg_matrix(squs):
return block_diag(*squs)

View File

@ -67,6 +67,11 @@ class TestUnitaryGate(QiskitTestCase):
uni = UnitaryGate([[0, 1j], [-1j, 0]])
self.assertTrue(numpy.array_equal(uni.adjoint().to_matrix(), uni.to_matrix()))
def test_repeat(self):
"""test repeat operation"""
uni = UnitaryGate([[1, 0], [0, 1j]])
self.assertTrue(numpy.array_equal(Operator(uni.repeat(2)), Operator(uni) @ Operator(uni)))
class TestUnitaryCircuit(QiskitTestCase):
"""Matrix gate circuit tests."""