From 0e2113b64d616ec303bfff22a38af05a74583bbe Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Sat, 22 Apr 2017 01:54:47 -0700 Subject: [PATCH 1/3] Fix ControlledGate failing when a register is empty (#49) --- projectq/ops/_basics.py | 10 ++++------ projectq/ops/_metagates.py | 21 +++++++++++---------- projectq/ops/_metagates_test.py | 9 +++++++++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/projectq/ops/_basics.py b/projectq/ops/_basics.py index 497e00449..5d8b1bb43 100755 --- a/projectq/ops/_basics.py +++ b/projectq/ops/_basics.py @@ -170,12 +170,10 @@ def generate_command(self, qubits): """ qubits = self.make_tuple_of_qureg(qubits) - for i in range(len(qubits)): - for j in range(len(qubits[i]) - 1): - assert(qubits[i][j].engine == qubits[i][j + 1].engine) - if i < len(qubits) - 1: - assert(qubits[i][-1].engine == qubits[i + 1][0].engine) - return Command(qubits[0][0].engine, self, qubits) + engines = [q.engine for reg in qubits for q in reg] + eng = engines[0] + assert all(e is eng for e in engines) + return Command(eng, self, qubits) def __or__(self, qubits): """ Operator| overload which enables the syntax Gate | qubits. diff --git a/projectq/ops/_metagates.py b/projectq/ops/_metagates.py index 975483a92..7886fc919 100755 --- a/projectq/ops/_metagates.py +++ b/projectq/ops/_metagates.py @@ -187,22 +187,23 @@ def __or__(self, qubits): the gate. """ qubits = BasicGate.make_tuple_of_qureg(qubits) - n = self._n + ctrl = [] gate_quregs = [] - added_ctrl_qubits = 0 - for qureg in qubits: - if added_ctrl_qubits < n: - ctrl = ctrl + qureg - added_ctrl_qubits += len(qureg) + adding_to_controls = True + for reg in qubits: + if adding_to_controls: + ctrl += reg + adding_to_controls = len(ctrl) < self._n else: - gate_quregs.append(qureg) - # Test that there were enough control qubits and that that + gate_quregs.append(reg) + # Test that there were enough control quregs and that that # the last control qubit was the last qubit in a qureg. - if added_ctrl_qubits != n: + if len(ctrl) != self._n: raise ControlQubitError("Wrong number of control qubits. " "First qureg(s) need to contain exactly " - "the required number of control qubits.") + "the required number of control quregs.") + import projectq.meta with projectq.meta.Control(gate_quregs[0][0].engine, ctrl): self._gate | tuple(gate_quregs) diff --git a/projectq/ops/_metagates_test.py b/projectq/ops/_metagates_test.py index 8fa94fa1f..e78866a45 100755 --- a/projectq/ops/_metagates_test.py +++ b/projectq/ops/_metagates_test.py @@ -123,6 +123,15 @@ def test_controlled_gate_get_inverse(): assert one_control.get_inverse() == expected +def test_controlled_gate_empty_controls(): + rec = DummyEngine(save_commands=True) + eng = MainEngine(backend=rec, engine_list=[]) + + a = eng.allocate_qureg(1) + _metagates.ControlledGate(Y, 0) | ((), a) + assert rec.received_commands[-1] == Command(eng, Y, [a]) + + def test_controlled_gate_or(): saving_backend = DummyEngine(save_commands=True) main_engine = MainEngine(backend=saving_backend, From d8b1d4278c02ca7ea73cdfe42284fdd4860485b0 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Sat, 22 Apr 2017 06:00:30 -0700 Subject: [PATCH 2/3] Mark SwapGate as a BasicMathGate (#66) --- projectq/ops/_gates.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/projectq/ops/_gates.py b/projectq/ops/_gates.py index de6c25c96..08f0863bb 100755 --- a/projectq/ops/_gates.py +++ b/projectq/ops/_gates.py @@ -36,7 +36,8 @@ SelfInverseGate, BasicRotationGate, ClassicalInstructionGate, - FastForwardingGate) + FastForwardingGate, + BasicMathGate) class HGate(SelfInverseGate): @@ -113,9 +114,10 @@ def __str__(self): Tdag = Tdagger = get_inverse(T) -class SwapGate(SelfInverseGate): +class SwapGate(SelfInverseGate, BasicMathGate): """ Swap gate class (swaps 2 qubits) """ def __init__(self): + BasicMathGate.__init__(self, lambda x, y: (y, x)) SelfInverseGate.__init__(self) self.interchangeable_qubit_indices = [[0, 1]] From 2ae36086ea39cbe6677eeda6080c8b42822c46a2 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Sat, 22 Apr 2017 06:33:24 -0700 Subject: [PATCH 3/3] Add 'controls' and 'tags' parameters to Command.__init__ (#57) --- projectq/backends/_ibm/_ibm_test.py | 3 +- projectq/ops/_command.py | 56 ++++++++++++++++------------- projectq/ops/_metagates_test.py | 4 +-- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/projectq/backends/_ibm/_ibm_test.py b/projectq/backends/_ibm/_ibm_test.py index ea3e06721..4dfaf4f3a 100755 --- a/projectq/backends/_ibm/_ibm_test.py +++ b/projectq/backends/_ibm/_ibm_test.py @@ -57,8 +57,7 @@ def test_ibm_backend_is_available_control_not(num_ctrl_qubits, is_available): qubit1 = eng.allocate_qubit() qureg = eng.allocate_qureg(num_ctrl_qubits) ibm_backend = _ibm.IBMBackend() - cmd = Command(eng, NOT, (qubit1,)) - cmd.add_control_qubits(qureg) + cmd = Command(eng, NOT, (qubit1,), controls=qureg) assert ibm_backend.is_available(cmd) == is_available diff --git a/projectq/ops/_command.py b/projectq/ops/_command.py index 65d495692..50bf42e77 100755 --- a/projectq/ops/_command.py +++ b/projectq/ops/_command.py @@ -81,7 +81,7 @@ class Command(object): all_qubits: A tuple of control_qubits + qubits """ - def __init__(self, engine, gate, qubits): + def __init__(self, engine, gate, qubits, controls=(), tags=()): """ Initialize a Command object. @@ -93,18 +93,25 @@ def __init__(self, engine, gate, qubits): (see WeakQubitRef). Args: - engine: engine which created the qubit (mostly the MainEngine) - gate: Gate to be executed - qubits: Tuple of quantum registers (to which the gate is applied) + engine (projectq.cengines.BasicEngine): + engine which created the qubit (mostly the MainEngine) + gate (projectq.ops.Gate): + Gate to be executed + qubits (tuple[Qureg]): + Tuple of quantum registers (to which the gate is applied) + controls (Qureg|list[Qubit]): + Qubits that condition the command. + tags (list[object]): + Tags associated with the command. """ - qubits = tuple([[WeakQubitRef(qubit.engine, qubit.id) - for qubit in qreg] - for qreg in qubits]) + qubits = tuple([WeakQubitRef(qubit.engine, qubit.id) + for qubit in qreg] + for qreg in qubits) self.gate = gate - self.tags = [] + self.tags = list(tags) self.qubits = qubits # property - self._control_qubits = [] # access via self.control_qubits property + self.control_qubits = controls # property self.engine = engine # property @property @@ -117,10 +124,11 @@ def qubits(self, qubits): def __deepcopy__(self, memo): """ Deepcopy implementation. Engine should stay a reference.""" - cpy = Command(self.engine, deepcopy(self.gate), self.qubits) - cpy.tags = deepcopy(self.tags) - cpy.add_control_qubits(self.control_qubits) - return cpy + return Command(self.engine, + deepcopy(self.gate), + self.qubits, + list(self.control_qubits), + deepcopy(self.tags)) def get_inverse(self): """ @@ -133,11 +141,11 @@ def get_inverse(self): NotInvertible: If the gate does not provide an inverse (see BasicGate.get_inverse) """ - cmd = Command(self._engine, projectq.ops.get_inverse(self.gate), - self.qubits) - cmd.tags = deepcopy(self.tags) - cmd.add_control_qubits(self.control_qubits) - return cmd + return Command(self._engine, + projectq.ops.get_inverse(self.gate), + self.qubits, + list(self.control_qubits), + deepcopy(self.tags)) def get_merged(self, other): """ @@ -152,12 +160,12 @@ def get_merged(self, other): or can't be merged for other reasons. """ if (self.tags == other.tags and self.all_qubits == other.all_qubits and - self.engine == other.engine): - merged_command = Command(self.engine, self.gate, self.qubits) - merged_command.gate = merged_command.gate.get_merged(other.gate) - merged_command.add_control_qubits(self.control_qubits) - merged_command.tags = deepcopy(self.tags) - return merged_command + self.engine == other.engine): + return Command(self.engine, + self.gate.get_merged(other.gate), + self.qubits, + self.control_qubits, + deepcopy(self.tags)) raise projectq.ops.NotMergeable("Commands not mergeable.") def _order_qubits(self, qubits): diff --git a/projectq/ops/_metagates_test.py b/projectq/ops/_metagates_test.py index e78866a45..d5c4e5ac9 100755 --- a/projectq/ops/_metagates_test.py +++ b/projectq/ops/_metagates_test.py @@ -141,8 +141,8 @@ def test_controlled_gate_or(): qubit1 = Qubit(main_engine, 1) qubit2 = Qubit(main_engine, 2) qubit3 = Qubit(main_engine, 3) - expected_cmd = Command(main_engine, gate, ([qubit3],)) - expected_cmd.add_control_qubits([qubit0, qubit1, qubit2]) + expected_cmd = Command(main_engine, gate, ([qubit3],), + controls=[qubit0, qubit1, qubit2]) received_commands = [] # Option 1: _metagates.ControlledGate(gate, 3) | ([qubit1], [qubit0],