From b970a2179662b2fc4abfe0a2e8fbf61d8f83f6ad Mon Sep 17 00:00:00 2001 From: Luciano Date: Tue, 6 Aug 2019 11:46:28 -0400 Subject: [PATCH] instruction.repeat (#2901) * from unittest.mock import patch * . * moving from a different branch to master * clean up import * style --- qiskit/circuit/gate.py | 4 + qiskit/circuit/instruction.py | 29 +++ .../python/circuit/test_instruction_repeat.py | 227 ++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 test/python/circuit/test_instruction_repeat.py diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 1c6a374e61..02d87c037a 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -42,6 +42,10 @@ class Gate(Instruction): """ raise QiskitError("to_matrix not defined for this {}".format(type(self))) + def _return_repeat(self, exponent): + return Gate(name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, + params=self.params) + def assemble(self): """Assemble a QasmQobjInstruction""" instruction = super().assemble() diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 36b7049cf4..deb2c1f248 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -39,6 +39,7 @@ import numpy from qiskit.qasm.node import node from qiskit.exceptions import QiskitError +from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.classicalregister import ClassicalRegister from qiskit.circuit.parameter import Parameter from qiskit.qobj.models.qasm import QasmQobjInstruction @@ -311,3 +312,31 @@ class Instruction: flat_qargs = [qarg for sublist in qargs for qarg in sublist] flat_cargs = [carg for sublist in cargs for carg in sublist] yield flat_qargs, flat_cargs + + def _return_repeat(self, exponent): + return Instruction(name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, + num_clbits=self.num_clbits, params=self.params) + + def repeat(self, n): + """Creates an instruction with `gate` repeated `n` amount of times. + + Args: + n (int): Number of times to repeat the instruction + + Returns: + Instruction: Containing the definition. + + Raises: + QiskitError: If n < 1. + """ + if int(n) != n or n < 1: + raise QiskitError("Repeat can only be called with strictly positive integer.") + + n = int(n) + + instruction = self._return_repeat(n) + qargs = [] if self.num_qubits == 0 else QuantumRegister(self.num_qubits, 'q') + cargs = [] if self.num_clbits == 0 else ClassicalRegister(self.num_clbits, 'c') + + instruction.definition = [(self, qargs[:], cargs[:])] * n + return instruction diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py new file mode 100644 index 0000000000..8db218074a --- /dev/null +++ b/test/python/circuit/test_instruction_repeat.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + + +"""Test Qiskit's repeat instruction operation.""" + +import unittest +from numpy import pi + +from qiskit.transpiler import PassManager +from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.test import QiskitTestCase +from qiskit.extensions import SGate, U3Gate, UnitaryGate, CnotGate +from qiskit.circuit import Instruction, Measure, Gate +from qiskit.transpiler.passes import Unroller +from qiskit.exceptions import QiskitError + + +class TestRepeatInt1Q(QiskitTestCase): + """Test gate_q1.repeat() with integer""" + + def test_standard_1Q_two(self): + """Test standard gate.repeat(2) method. + """ + qr = QuantumRegister(1, 'qr') + expected_circ = QuantumCircuit(qr) + expected_circ.append(SGate(), [qr[0]]) + expected_circ.append(SGate(), [qr[0]]) + expected = expected_circ.to_instruction() + + result = SGate().repeat(2) + + self.assertEqual(result.name, 's*2') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Gate) + + def test_standard_1Q_one(self): + """Test standard gate.repeat(1) method. + """ + qr = QuantumRegister(1, 'qr') + expected_circ = QuantumCircuit(qr) + expected_circ.append(SGate(), [qr[0]]) + expected = expected_circ.to_instruction() + + result = SGate().repeat(1) + + self.assertEqual(result.name, 's*1') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Gate) + + +class TestRepeatInt2Q(QiskitTestCase): + """Test gate_q2.repeat() with integer""" + + def test_standard_2Q_two(self): + """Test standard 2Q gate.repeat(2) method. + """ + qr = QuantumRegister(2, 'qr') + expected_circ = QuantumCircuit(qr) + expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected = expected_circ.to_instruction() + + result = CnotGate().repeat(2) + + self.assertEqual(result.name, 'cx*2') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Gate) + + def test_standard_2Q_one(self): + """Test standard 2Q gate.repeat(1) method. + """ + qr = QuantumRegister(2, 'qr') + expected_circ = QuantumCircuit(qr) + expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected = expected_circ.to_instruction() + + result = CnotGate().repeat(1) + + self.assertEqual(result.name, 'cx*1') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Gate) + + +class TestRepeatIntMeasure(QiskitTestCase): + """Test Measure.repeat() with integer""" + + def test_measure_two(self): + """Test Measure.repeat(2) method. + """ + qr = QuantumRegister(1, 'qr') + cr = ClassicalRegister(1, 'cr') + expected_circ = QuantumCircuit(qr, cr) + expected_circ.append(Measure(), [qr[0]], [cr[0]]) + expected_circ.append(Measure(), [qr[0]], [cr[0]]) + expected = expected_circ.to_instruction() + + result = Measure().repeat(2) + + self.assertEqual(result.name, 'measure*2') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Instruction) + self.assertNotIsInstance(result, Gate) + + def test_measure_one(self): + """Test Measure.repeat(1) method. + """ + qr = QuantumRegister(1, 'qr') + cr = ClassicalRegister(1, 'cr') + expected_circ = QuantumCircuit(qr, cr) + expected_circ.append(Measure(), [qr[0]], [cr[0]]) + expected = expected_circ.to_instruction() + + result = Measure().repeat(1) + + self.assertEqual(result.name, 'measure*1') + self.assertEqual(result.definition, expected.definition) + self.assertIsInstance(result, Instruction) + self.assertNotIsInstance(result, Gate) + + +class TestRepeatUnroller(QiskitTestCase): + """Test unrolling Gate.repeat""" + + def test_unroller_two(self): + """Test unrolling gate.repeat(2). + """ + qr = QuantumRegister(1, 'qr') + + circuit = QuantumCircuit(qr) + circuit.append(SGate().repeat(2), [qr[0]]) + result = PassManager(Unroller('u3')).run(circuit) + + expected = QuantumCircuit(qr) + expected.append(U3Gate(0, 0, pi / 2), [qr[0]]) + expected.append(U3Gate(0, 0, pi / 2), [qr[0]]) + + self.assertEqual(result, expected) + + def test_unroller_one(self): + """Test unrolling gate.repeat(1). + """ + qr = QuantumRegister(1, 'qr') + + circuit = QuantumCircuit(qr) + circuit.append(SGate().repeat(1), [qr[0]]) + result = PassManager(Unroller('u3')).run(circuit) + + expected = QuantumCircuit(qr) + expected.append(U3Gate(0, 0, pi / 2), [qr[0]]) + + self.assertEqual(result, expected) + + +class TestRepeatErrors(QiskitTestCase): + """Test when Gate.repeat() should raise.""" + + def test_unitary_no_int(self): + """Test UnitaryGate.repeat(2/3) method. Raises, since n is not int. + """ + with self.assertRaises(QiskitError) as context: + _ = UnitaryGate([[0, 1j], [-1j, 0]]).repeat(2 / 3) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_starndard_no_int(self): + """Test standard Gate.repeat(2/3) method. Raises, since n is not int. + """ + with self.assertRaises(QiskitError) as context: + _ = SGate().repeat(2 / 3) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_measure_zero(self): + """Test Measure.repeat(0) method. Raises, since n<1 + """ + with self.assertRaises(QiskitError) as context: + _ = Measure().repeat(0) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_standard_1Q_zero(self): + """Test standard 2Q gate.repeat(0) method. Raises, since n<1. + """ + with self.assertRaises(QiskitError) as context: + _ = SGate().repeat(0) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_standard_1Q_minus_one(self): + """Test standard 2Q gate.repeat(-1) method. Raises, since n<1. + """ + with self.assertRaises(QiskitError) as context: + _ = SGate().repeat(-1) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_standard_2Q_minus_one(self): + """Test standard 2Q gate.repeat(-1) method. Raises, since n<1. + """ + with self.assertRaises(QiskitError) as context: + _ = CnotGate().repeat(-1) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_measure_minus_one(self): + """Test Measure.repeat(-1) method. Raises, since n<1 + """ + with self.assertRaises(QiskitError) as context: + _ = Measure().repeat(-1) + self.assertIn('strictly positive integer', str(context.exception)) + + def test_standard_2Q_zero(self): + """Test standard 2Q gate.repeat(0) method. Raises, since n<1. + """ + with self.assertRaises(QiskitError) as context: + _ = CnotGate().repeat(0) + self.assertIn('strictly positive integer', str(context.exception)) + + +if __name__ == '__main__': + unittest.main()