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

Don't accept controlled single-qubit gates in restricted gate set. #301

Merged
merged 2 commits into from
Jan 30, 2019
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
53 changes: 40 additions & 13 deletions projectq/setups/restrictedgateset.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand All @@ -119,39 +121,54 @@ 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)
elif isinstance(gate, BasicGate):
allowed_gate_instances1.append(gate)
else:
allowed_gate_instances.append((gate, 0))
raise TypeError("unsupported one_qubit_gates argument")
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)
else:
allowed_gate_classes2.append(gate)
elif isinstance(gate, BasicGate):
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))
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)
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]
Expand All @@ -166,8 +183,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(),
Expand Down
38 changes: 23 additions & 15 deletions projectq/setups/restrictedgateset_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
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

Expand All @@ -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
Expand All @@ -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)
Expand All @@ -92,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,))