def test_dagger_with_dirty_qubits(): backend = DummyEngine(save_commands=True) def allow_dirty_qubits(self, meta_tag): return meta_tag == DirtyQubitTag backend.is_meta_tag_handler = types.MethodType(allow_dirty_qubits, backend) eng = MainEngine(backend=backend, engine_list=[DummyEngine()]) qubit = eng.allocate_qubit() with _dagger.Dagger(eng): ancilla = eng.allocate_qubit(dirty=True) Rx(0.6) | ancilla CNOT | (ancilla, qubit) H | qubit Rx(-0.6) | ancilla del ancilla[0] eng.flush(deallocate_qubits=True) assert len(backend.received_commands) == 9 assert backend.received_commands[0].gate == Allocate assert backend.received_commands[1].gate == Allocate assert backend.received_commands[2].gate == Rx(0.6) assert backend.received_commands[3].gate == H assert backend.received_commands[4].gate == X assert backend.received_commands[5].gate == Rx(-0.6) assert backend.received_commands[6].gate == Deallocate assert backend.received_commands[7].gate == Deallocate assert backend.received_commands[1].tags == [DirtyQubitTag()] assert backend.received_commands[6].tags == [DirtyQubitTag()]
def test_control_engine_has_compute_tag(): eng = MainEngine(backend=DummyEngine(), engine_list=[DummyEngine()]) qubit = eng.allocate_qubit() test_cmd0 = Command(eng, H, (qubit,)) test_cmd1 = Command(eng, H, (qubit,)) test_cmd2 = Command(eng, H, (qubit,)) test_cmd0.tags = [DirtyQubitTag(), ComputeTag(), DirtyQubitTag()] test_cmd1.tags = [DirtyQubitTag(), UncomputeTag(), DirtyQubitTag()] test_cmd2.tags = [DirtyQubitTag()] assert _control._has_compute_uncompute_tag(test_cmd0) assert _control._has_compute_uncompute_tag(test_cmd1) assert not _control._has_compute_uncompute_tag(test_cmd2)
def deallocate_qubit(self, qubit): """ Deallocate a qubit (and sends the deallocation command down the pipeline). If the qubit was allocated as a dirty qubit, add DirtyQubitTag() to Deallocate command. Args: qubit (BasicQubit): Qubit to deallocate. Raises: ValueError: Qubit already deallocated. Caller likely has a bug. """ if qubit.id == -1: raise ValueError("Already deallocated.") is_dirty = qubit.id in self.dirty_qubits weak_copy = WeakQubitRef(qubit.engine, qubit.id) if not isinstance(qubit, WeakQubitRef): if qubit in self.active_qubits: self.active_qubits.remove(qubit) qubit.id = -1 from projectq.meta import DirtyQubitTag self.send([ Command(self, Deallocate, (Qureg([weak_copy]), ), tags=[DirtyQubitTag()] if is_dirty else []) ])
def allocate_qubit(self, dirty=False): """ Return a new qubit as a list containing 1 qubit object (quantum register of size 1). Allocates a new qubit by getting a (new) qubit id from the MainEngine, creating the qubit object, and then sending an AllocateQubit command down the pipeline. If dirty=True, the fresh qubit can be replaced by a pre-allocated one (in an unknown, dirty, initial state). Dirty qubits must be returned to their initial states before they are deallocated / freed. All allocated qubits are added to the MainEngine's set of active qubits as weak references. This allows proper clean-up at the end of the Python program (using atexit), deallocating all qubits which are still alive. Qubit ids of dirty qubits are registered in MainEngine's dirty_qubits set. Args: dirty (bool): If True, indicates that the allocated qubit may be dirty (i.e., in an arbitrary initial state). Returns: Qureg of length 1, where the first entry is the allocated qubit. """ new_id = self.main_engine.get_new_qubit_id() qb = Qureg([Qubit(self, new_id)]) cmd = Command(self, Allocate, (qb, )) if dirty: from projectq.meta import ( # pylint: disable=import-outside-toplevel DirtyQubitTag, ) if self.is_meta_tag_supported(DirtyQubitTag): cmd.tags += [DirtyQubitTag()] self.main_engine.dirty_qubits.add(qb[0].id) self.main_engine.active_qubits.add(qb[0]) self.send([cmd]) return qb
def deallocate_qubit(self, qubit): """ Deallocate a qubit (and sends the deallocation command down the pipeline). If the qubit was allocated as a dirty qubit, add DirtyQubitTag() to Deallocate command. Args: qubit (BasicQubit): Qubit to deallocate. Raises: ValueError: Qubit already deallocated. Caller likely has a bug. """ if qubit.id == -1: raise ValueError("Already deallocated.") from projectq.meta import ( # pylint: disable=import-outside-toplevel DirtyQubitTag, ) is_dirty = qubit.id in self.main_engine.dirty_qubits self.send([ Command( self, Deallocate, ([WeakQubitRef(engine=qubit.engine, idx=qubit.id)], ), tags=[DirtyQubitTag()] if is_dirty else [], ) ]) # Mark qubit as deallocated qubit.id = -1
def deallocate_qubit(self, qubit): """ Deallocate a qubit (and sends the deallocation command down the pipeline). If the qubit was allocated as a dirty qubit, add DirtyQubitTag() to Deallocate command. Args: qubit (BasicQubit): Qubit to deallocate. Raises: ValueError: Qubit already deallocated. Caller likely has a bug. """ if qubit.id == -1: raise ValueError("Already deallocated.") from projectq.meta import DirtyQubitTag is_dirty = qubit.id in self.main_engine.dirty_qubits self.send([Command(self, Deallocate, (Qureg([qubit]),), tags=[DirtyQubitTag()] if is_dirty else [])])
def test_basic_engine_allocate_and_deallocate_qubit_and_qureg(): eng = _basics.BasicEngine() # custom receive function which checks that main_engine does not send # any allocate or deallocate gates cmd_sent_by_main_engine = [] def receive(self, cmd_list): cmd_sent_by_main_engine.append(cmd_list) eng.receive = types.MethodType(receive, eng) # Create test engines: saving_backend = DummyEngine(save_commands=True) main_engine = MainEngine(backend=saving_backend, engine_list=[eng, DummyEngine()]) # Allocate and deallocate qubits qubit = eng.allocate_qubit() # Try to allocate dirty qubit but it should give a non dirty qubit not_dirty_qubit = eng.allocate_qubit(dirty=True) # Allocate an actual dirty qubit def allow_dirty_qubits(self, meta_tag): if meta_tag == DirtyQubitTag: return True return False saving_backend.is_meta_tag_handler = types.MethodType(allow_dirty_qubits, saving_backend) dirty_qubit = eng.allocate_qubit(dirty=True) qureg = eng.allocate_qureg(2) # Test qubit allocation assert isinstance(qubit, list) assert len(qubit) == 1 and isinstance(qubit[0], Qubit) assert qubit[0] in main_engine.active_qubits assert id(qubit[0].engine) == id(eng) # Test non dirty qubit allocation assert isinstance(not_dirty_qubit, list) assert len(not_dirty_qubit) == 1 and isinstance(not_dirty_qubit[0], Qubit) assert not_dirty_qubit[0] in main_engine.active_qubits assert id(not_dirty_qubit[0].engine) == id(eng) # Test dirty_qubit allocation assert isinstance(dirty_qubit, list) assert len(dirty_qubit) == 1 and isinstance(dirty_qubit[0], Qubit) assert dirty_qubit[0] in main_engine.active_qubits assert dirty_qubit[0].id in main_engine.dirty_qubits assert id(dirty_qubit[0].engine) == id(eng) # Test qureg allocation assert isinstance(qureg, list) assert len(qureg) == 2 for tmp_qubit in qureg: assert tmp_qubit in main_engine.active_qubits assert id(tmp_qubit.engine) == id(eng) # Test uniqueness of ids assert len(set([qubit[0].id, not_dirty_qubit[0].id, dirty_qubit[0].id, qureg[0].id, qureg[1].id])) == 5 # Test allocate gates were sent assert len(cmd_sent_by_main_engine) == 0 assert len(saving_backend.received_commands) == 5 for cmd in saving_backend.received_commands: assert cmd.gate == AllocateQubitGate() assert saving_backend.received_commands[2].tags == [DirtyQubitTag()] # Test deallocate gates were sent eng.deallocate_qubit(qubit[0]) eng.deallocate_qubit(not_dirty_qubit[0]) eng.deallocate_qubit(dirty_qubit[0]) eng.deallocate_qubit(qureg[0]) eng.deallocate_qubit(qureg[1]) assert len(cmd_sent_by_main_engine) == 0 assert len(saving_backend.received_commands) == 10 for cmd in saving_backend.received_commands[5:]: assert cmd.gate == DeallocateQubitGate() assert saving_backend.received_commands[7].tags == [DirtyQubitTag()]
def test_compute_uncompute_with_statement(): # Allocating and deallocating qubit within Compute backend = DummyEngine(save_commands=True) compare_engine0 = CompareEngine() # Allow dirty qubits dummy_cengine = DummyEngine() def allow_dirty_qubits(self, meta_tag): return meta_tag == DirtyQubitTag dummy_cengine.is_meta_tag_handler = types.MethodType( allow_dirty_qubits, dummy_cengine) eng = MainEngine(backend=backend, engine_list=[compare_engine0, dummy_cengine]) qubit = eng.allocate_qubit() with _compute.Compute(eng): Rx(0.9) | qubit ancilla = eng.allocate_qubit(dirty=True) # ancilla2 will be deallocated in Uncompute section: ancilla2 = eng.allocate_qubit() # Test that ancilla is registered in MainEngine.active_qubits: assert ancilla[0] in eng.active_qubits H | qubit Rx(0.5) | ancilla CNOT | (ancilla, qubit) Rx(0.7) | qubit Rx(-0.5) | ancilla ancilla[0].__del__() H | qubit _compute.Uncompute(eng) eng.flush(deallocate_qubits=True) assert len(backend.received_commands) == 22 # Test each Command has correct gate assert backend.received_commands[0].gate == Allocate assert backend.received_commands[1].gate == Rx(0.9) assert backend.received_commands[2].gate == Allocate assert backend.received_commands[3].gate == Allocate assert backend.received_commands[4].gate == H assert backend.received_commands[5].gate == Rx(0.5) assert backend.received_commands[6].gate == NOT assert backend.received_commands[7].gate == Rx(0.7) assert backend.received_commands[8].gate == Rx(-0.5) assert backend.received_commands[9].gate == Deallocate assert backend.received_commands[10].gate == H assert backend.received_commands[11].gate == Allocate assert backend.received_commands[12].gate == Rx(0.5) assert backend.received_commands[13].gate == Rx(-0.7) assert backend.received_commands[14].gate == NOT assert backend.received_commands[15].gate == Rx(-0.5) assert backend.received_commands[16].gate == H assert backend.received_commands[17].gate == Deallocate assert backend.received_commands[18].gate == Deallocate assert backend.received_commands[19].gate == Rx(-0.9) assert backend.received_commands[20].gate == Deallocate assert backend.received_commands[21].gate == FlushGate() # Test that each command has correct tags assert backend.received_commands[0].tags == [] assert backend.received_commands[1].tags == [_compute.ComputeTag()] assert backend.received_commands[2].tags == [ DirtyQubitTag(), _compute.ComputeTag() ] for cmd in backend.received_commands[3:9]: assert cmd.tags == [_compute.ComputeTag()] assert backend.received_commands[9].tags == [ DirtyQubitTag(), _compute.ComputeTag() ] assert backend.received_commands[10].tags == [] assert backend.received_commands[11].tags == [ DirtyQubitTag(), _compute.UncomputeTag() ] for cmd in backend.received_commands[12:18]: assert cmd.tags == [_compute.UncomputeTag()] assert backend.received_commands[18].tags == [ DirtyQubitTag(), _compute.UncomputeTag() ] assert backend.received_commands[19].tags == [_compute.UncomputeTag()] assert backend.received_commands[20].tags == [] assert backend.received_commands[21].tags == [] # Test that each command has correct qubits # Note that ancilla qubit in compute should be # different from ancilla qubit in uncompute section qubit_id = backend.received_commands[0].qubits[0][0].id ancilla_compt_id = backend.received_commands[2].qubits[0][0].id ancilla_uncompt_id = backend.received_commands[11].qubits[0][0].id ancilla2_id = backend.received_commands[3].qubits[0][0].id assert backend.received_commands[1].qubits[0][0].id == qubit_id assert backend.received_commands[4].qubits[0][0].id == qubit_id assert backend.received_commands[5].qubits[0][0].id == ancilla_compt_id assert backend.received_commands[6].qubits[0][0].id == qubit_id assert ( backend.received_commands[6].control_qubits[0].id == ancilla_compt_id) assert backend.received_commands[7].qubits[0][0].id == qubit_id assert backend.received_commands[8].qubits[0][0].id == ancilla_compt_id assert backend.received_commands[9].qubits[0][0].id == ancilla_compt_id assert backend.received_commands[10].qubits[0][0].id == qubit_id assert backend.received_commands[12].qubits[0][0].id == ancilla_uncompt_id assert backend.received_commands[13].qubits[0][0].id == qubit_id assert backend.received_commands[14].qubits[0][0].id == qubit_id assert (backend.received_commands[14].control_qubits[0].id == ancilla_uncompt_id) assert backend.received_commands[15].qubits[0][0].id == ancilla_uncompt_id assert backend.received_commands[16].qubits[0][0].id == qubit_id assert backend.received_commands[17].qubits[0][0].id == ancilla2_id assert backend.received_commands[18].qubits[0][0].id == ancilla_uncompt_id assert backend.received_commands[19].qubits[0][0].id == qubit_id assert backend.received_commands[20].qubits[0][0].id == qubit_id # Test that ancilla qubits should have seperate ids assert ancilla_uncompt_id != ancilla_compt_id # Do the same thing with CustomUncompute and compare using the # CompareEngine: backend1 = DummyEngine(save_commands=True) compare_engine1 = CompareEngine() # Allow dirty qubits dummy_cengine1 = DummyEngine() def allow_dirty_qubits(self, meta_tag): return meta_tag == DirtyQubitTag dummy_cengine1.is_meta_tag_handler = types.MethodType( allow_dirty_qubits, dummy_cengine1) eng1 = MainEngine(backend=backend1, engine_list=[compare_engine1, dummy_cengine1]) qubit = eng1.allocate_qubit() with _compute.Compute(eng1): Rx(0.9) | qubit ancilla = eng1.allocate_qubit(dirty=True) # ancilla2 will be deallocated in Uncompute section: ancilla2 = eng1.allocate_qubit() # Test that ancilla is registered in MainEngine.active_qubits: assert ancilla[0] in eng1.active_qubits H | qubit Rx(0.5) | ancilla CNOT | (ancilla, qubit) Rx(0.7) | qubit Rx(-0.5) | ancilla ancilla[0].__del__() H | qubit with _compute.CustomUncompute(eng1): ancilla = eng1.allocate_qubit(dirty=True) Rx(0.5) | ancilla Rx(-0.7) | qubit CNOT | (ancilla, qubit) Rx(-0.5) | ancilla H | qubit assert ancilla[0] in eng1.active_qubits ancilla2[0].__del__() ancilla[0].__del__() Rx(-0.9) | qubit eng1.flush(deallocate_qubits=True) assert compare_engine0 == compare_engine1
def cmd_modifier(cmd): assert (cmd.gate == Deallocate) cmd.tags += [DirtyQubitTag()] return cmd