From 696101c6778cee02642c3492464943d0aeddb601 Mon Sep 17 00:00:00 2001 From: Thomas Haener Date: Mon, 14 Jan 2019 14:48:01 +0100 Subject: [PATCH 1/2] Don't accept controlled single-qubit gates in restricted gate set. --- projectq/setups/restrictedgateset.py | 35 +++++++++++++++++------ projectq/setups/restrictedgateset_test.py | 28 +++++++++--------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/projectq/setups/restrictedgateset.py b/projectq/setups/restrictedgateset.py index f02268fd7..8cf8cb22d 100644 --- a/projectq/setups/restrictedgateset.py +++ b/projectq/setups/restrictedgateset.py @@ -119,26 +119,31 @@ def get_engine_list(one_qubit_gates="any", rule_set = DecompositionRuleSet(modules=[projectq.libs.math, projectq.setups.decompositions]) - allowed_gate_classes = [] + allowed_gate_classes = [] # n-qubit gates allowed_gate_instances = [] + allowed_gate_classes1 = [] # 1-qubit gates + allowed_gate_instances1 = [] + allowed_gate_classes2 = [] # 2-qubit gates + allowed_gate_instances2 = [] + if one_qubit_gates != "any": for gate in one_qubit_gates: if inspect.isclass(gate): - allowed_gate_classes.append(gate) + allowed_gate_classes1.append(gate) else: - allowed_gate_instances.append((gate, 0)) + allowed_gate_instances1.append(gate) if two_qubit_gates != "any": for gate in two_qubit_gates: if inspect.isclass(gate): # Controlled gate classes don't yet exists and would require # separate treatment assert not isinstance(gate, ControlledGate) - allowed_gate_classes.append(gate) + allowed_gate_classes2.append(gate) else: if isinstance(gate, ControlledGate): - allowed_gate_instances.append((gate._gate, gate._n)) + allowed_gate_instances2.append((gate._gate, gate._n)) else: - allowed_gate_instances.append((gate, 0)) + allowed_gate_instances2.append((gate, 0)) for gate in other_gates: if inspect.isclass(gate): # Controlled gate classes don't yet exists and would require @@ -152,6 +157,10 @@ def get_engine_list(one_qubit_gates="any", allowed_gate_instances.append((gate, 0)) allowed_gate_classes = tuple(allowed_gate_classes) allowed_gate_instances = tuple(allowed_gate_instances) + allowed_gate_classes1 = tuple(allowed_gate_classes1) + allowed_gate_instances1 = tuple(allowed_gate_instances1) + allowed_gate_classes2 = tuple(allowed_gate_classes2) + allowed_gate_instances2 = tuple(allowed_gate_instances2) def low_level_gates(eng, cmd): all_qubits = [q for qr in cmd.all_qubits for q in qr] @@ -166,8 +175,18 @@ def low_level_gates(eng, cmd): return True elif (cmd.gate, len(cmd.control_qubits)) in allowed_gate_instances: return True - else: - return False + elif (isinstance(cmd.gate, allowed_gate_classes1) + and len(all_qubits) == 1): + return True + elif (isinstance(cmd.gate, allowed_gate_classes2) + and len(all_qubits) == 2): + return True + elif cmd.gate in allowed_gate_instances1 and len(all_qubits) == 1: + return True + elif ((cmd.gate, len(cmd.control_qubits)) in allowed_gate_instances2 + and len(all_qubits) == 2): + return True + return False return [AutoReplacer(rule_set), TagRemover(), diff --git a/projectq/setups/restrictedgateset_test.py b/projectq/setups/restrictedgateset_test.py index ebe767c1e..30ec74ce8 100644 --- a/projectq/setups/restrictedgateset_test.py +++ b/projectq/setups/restrictedgateset_test.py @@ -22,6 +22,7 @@ MultiplyByConstantModN) from projectq.ops import (BasicGate, CNOT, H, Measure, QFT, QubitOperator, Rx, Rz, Swap, TimeEvolution, Toffoli, X) +from projectq.meta import Control import projectq.setups.restrictedgateset as restrictedgateset @@ -48,17 +49,18 @@ def test_restriction(): two_qubit_gates=(CNOT, AddConstant, Swap), other_gates=(Toffoli, AddConstantModN, MultiplyByConstantModN(2, 8))) backend = DummyEngine(save_commands=True) - eng = projectq.MainEngine(backend, engine_list) + eng = projectq.MainEngine(backend, engine_list, verbose=True) qubit1 = eng.allocate_qubit() qubit2 = eng.allocate_qubit() qubit3 = eng.allocate_qubit() eng.flush() CNOT | (qubit1, qubit2) H | qubit1 - Rz(0.2) | qubit1 + with Control(eng, qubit2): + Rz(0.2) | qubit1 Measure | qubit1 - AddConstant(1) | qubit1 + qubit2 - AddConstantModN(1, 9) | qubit1 + qubit2 + qubit3 + AddConstant(1) | (qubit1 + qubit2) + AddConstantModN(1, 9) | (qubit1 + qubit2 + qubit3) Toffoli | (qubit1 + qubit2, qubit3) Swap | (qubit1, qubit2) MultiplyByConstantModN(2, 8) | qubit1 + qubit2 + qubit3 @@ -70,15 +72,15 @@ def test_restriction(): assert backend.received_commands[4].gate == X assert len(backend.received_commands[4].control_qubits) == 1 assert backend.received_commands[5].gate == H - assert backend.received_commands[6].gate == Rz(0.2) - assert backend.received_commands[7].gate == Measure - assert backend.received_commands[8].gate == AddConstant(1) - assert backend.received_commands[9].gate == AddConstantModN(1, 9) - assert backend.received_commands[10].gate == X - assert len(backend.received_commands[10].control_qubits) == 2 - assert backend.received_commands[11].gate == Swap - assert backend.received_commands[12].gate == MultiplyByConstantModN(2, 8) - for cmd in backend.received_commands[13:]: + assert backend.received_commands[6].gate == Rz(0.1) + assert backend.received_commands[10].gate == Measure + assert backend.received_commands[11].gate == AddConstant(1) + assert backend.received_commands[12].gate == AddConstantModN(1, 9) + assert backend.received_commands[13].gate == X + assert len(backend.received_commands[13].control_qubits) == 2 + assert backend.received_commands[14].gate == Swap + assert backend.received_commands[15].gate == MultiplyByConstantModN(2, 8) + for cmd in backend.received_commands[16:]: assert cmd.gate != QFT assert not isinstance(cmd.gate, Rx) assert not isinstance(cmd.gate, MultiplyByConstantModN) From 936fe66a25849a044b5ad02648a8b6e236459bf5 Mon Sep 17 00:00:00 2001 From: "Damian S. Steiger" Date: Sun, 27 Jan 2019 22:24:38 -0400 Subject: [PATCH 2/2] check that no functions are supplied (e.g. CRz shortcut function) --- projectq/setups/restrictedgateset.py | 20 ++++++++++++++------ projectq/setups/restrictedgateset_test.py | 10 ++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/projectq/setups/restrictedgateset.py b/projectq/setups/restrictedgateset.py index 8cf8cb22d..7b3540cf0 100644 --- a/projectq/setups/restrictedgateset.py +++ b/projectq/setups/restrictedgateset.py @@ -29,8 +29,8 @@ from projectq.cengines import (AutoReplacer, DecompositionRuleSet, InstructionFilter, LocalOptimizer, TagRemover) -from projectq.ops import (BasicMathGate, ClassicalInstructionGate, CNOT, - ControlledGate, get_inverse, QFT, Swap) +from projectq.ops import (BasicGate, BasicMathGate, ClassicalInstructionGate, + CNOT, ControlledGate, get_inverse, QFT, Swap) def high_level_gates(eng, cmd): @@ -102,7 +102,9 @@ def get_engine_list(one_qubit_gates="any", all gates which are equal to it. If the gate is a class, it allows all instances of this class. Raises: - TypeError: If input is for the gates is not "any" or a tuple. + TypeError: If input is for the gates is not "any" or a tuple. Also if + element within tuple is not a class or instance of BasicGate + (e.g. CRz which is a shortcut function) Returns: A list of suitable compiler engines. @@ -130,8 +132,10 @@ def get_engine_list(one_qubit_gates="any", for gate in one_qubit_gates: if inspect.isclass(gate): allowed_gate_classes1.append(gate) - else: + elif isinstance(gate, BasicGate): allowed_gate_instances1.append(gate) + else: + raise TypeError("unsupported one_qubit_gates argument") if two_qubit_gates != "any": for gate in two_qubit_gates: if inspect.isclass(gate): @@ -139,22 +143,26 @@ def get_engine_list(one_qubit_gates="any", # separate treatment assert not isinstance(gate, ControlledGate) allowed_gate_classes2.append(gate) - else: + elif isinstance(gate, BasicGate): if isinstance(gate, ControlledGate): allowed_gate_instances2.append((gate._gate, gate._n)) else: allowed_gate_instances2.append((gate, 0)) + else: + raise TypeError("unsupported two_qubit_gates argument") for gate in other_gates: if inspect.isclass(gate): # Controlled gate classes don't yet exists and would require # separate treatment assert not isinstance(gate, ControlledGate) allowed_gate_classes.append(gate) - else: + elif isinstance(gate, BasicGate): if isinstance(gate, ControlledGate): allowed_gate_instances.append((gate._gate, gate._n)) else: allowed_gate_instances.append((gate, 0)) + else: + raise TypeError("unsupported other_gates argument") allowed_gate_classes = tuple(allowed_gate_classes) allowed_gate_instances = tuple(allowed_gate_instances) allowed_gate_classes1 = tuple(allowed_gate_classes1) diff --git a/projectq/setups/restrictedgateset_test.py b/projectq/setups/restrictedgateset_test.py index 30ec74ce8..544746f85 100644 --- a/projectq/setups/restrictedgateset_test.py +++ b/projectq/setups/restrictedgateset_test.py @@ -20,8 +20,8 @@ from projectq.cengines import DummyEngine from projectq.libs.math import (AddConstant, AddConstantModN, MultiplyByConstantModN) -from projectq.ops import (BasicGate, CNOT, H, Measure, QFT, QubitOperator, Rx, - Rz, Swap, TimeEvolution, Toffoli, X) +from projectq.ops import (BasicGate, CNOT, CRz, H, Measure, QFT, QubitOperator, + Rx, Rz, Swap, TimeEvolution, Toffoli, X) from projectq.meta import Control import projectq.setups.restrictedgateset as restrictedgateset @@ -94,3 +94,9 @@ def test_wrong_init(): engine_list = restrictedgateset.get_engine_list(one_qubit_gates="Any") with pytest.raises(TypeError): engine_list = restrictedgateset.get_engine_list(other_gates="any") + with pytest.raises(TypeError): + engine_list = restrictedgateset.get_engine_list(one_qubit_gates=(CRz,)) + with pytest.raises(TypeError): + engine_list = restrictedgateset.get_engine_list(two_qubit_gates=(CRz,)) + with pytest.raises(TypeError): + engine_list = restrictedgateset.get_engine_list(other_gates=(CRz,))