Skip to content

Commit

Permalink
Fix UnitaryGate.repeat() method (#12986)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Elena Peña Tapia <[email protected]>
  • Loading branch information
3 people authored Aug 26, 2024
1 parent 54ac8f1 commit 83ecf39
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 1 deletion.
5 changes: 4 additions & 1 deletion qiskit/circuit/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ def __pow__(self, exponent: float) -> "Gate":
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,
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/fix_11990-8551c7250207fc76.yaml
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 13 additions & 0 deletions test/python/circuit/library/test_linear_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,19 @@ def test_clifford_linear_function_equivalence(self, num_qubits):
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()
6 changes: 6 additions & 0 deletions test/python/circuit/library/test_permutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ def test_inverse(self):
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."""
Expand Down
13 changes: 13 additions & 0 deletions test/python/circuit/test_diagonal_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ def test_npcomplex_params_conversion(self):
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)
Expand Down
14 changes: 14 additions & 0 deletions test/python/circuit/test_gate_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
CZGate,
RYYGate,
PhaseGate,
PauliGate,
UCPauliRotGate,
CPhaseGate,
UGate,
CUGate,
Expand Down Expand Up @@ -184,6 +186,18 @@ def test_xx_minus_yy_definition(self):
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):
Expand Down
6 changes: 6 additions & 0 deletions test/python/circuit/test_hamiltonian_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ def test_adjoint(self):
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."""
Expand Down
10 changes: 10 additions & 0 deletions test/python/circuit/test_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,16 @@ def test_gates_to_uncompute(self):
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."""
Expand Down
15 changes: 15 additions & 0 deletions test/python/circuit/test_isometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ def test_isometry_inverse(self, iso):
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()
7 changes: 7 additions & 0 deletions test/python/circuit/test_uc.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ def test_inverse_ucg(self):

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)
Expand Down
5 changes: 5 additions & 0 deletions test/python/circuit/test_unitary.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ def test_adjoint(self):
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."""
Expand Down

0 comments on commit 83ecf39

Please sign in to comment.