def test_main_engine_atexit_no_error(): # Clear previous exceptions of other tests sys.last_type = None del sys.last_type backend = DummyEngine(save_commands=True) eng = _main.MainEngine(backend=backend, engine_list=[]) qb = eng.allocate_qubit() # noqa: F841 eng._delfun(weakref.ref(eng)) assert len(backend.received_commands) == 3 assert backend.received_commands[0].gate == AllocateQubitGate() assert backend.received_commands[1].gate == DeallocateQubitGate() assert backend.received_commands[2].gate == FlushGate()
def test_main_engine_flush(): backend = DummyEngine(save_commands=True) eng = _main.MainEngine(backend=backend, engine_list=[DummyEngine()]) qubit = eng.allocate_qubit() H | qubit eng.flush() assert len(backend.received_commands) == 3 assert backend.received_commands[0].gate == AllocateQubitGate() assert backend.received_commands[1].gate == H assert backend.received_commands[2].gate == FlushGate() eng.flush(deallocate_qubits=True) assert len(backend.received_commands) == 5 assert backend.received_commands[3].gate == DeallocateQubitGate() #keep the qubit alive until at least here assert len(str(qubit)) != 0
def _process_cmd(self, cmd): current_mapping = self.current_mapping if current_mapping is None: current_mapping = {} if isinstance(cmd.gate, AllocateQubitGate): qubit_id = cmd.qubits[0][0].id if qubit_id in current_mapping: raise RuntimeError( "Qubit with id {} has already been allocated!".format( qubit_id)) if self._qubit_idx >= self.max_qubits: raise RuntimeError( "Cannot allocate more than {} qubits!".format( self.max_qubits)) new_id = self._qubit_idx self._qubit_idx += 1 current_mapping[qubit_id] = new_id qb = WeakQubitRef(engine=self, idx=new_id) new_cmd = Command( engine=self, gate=AllocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(qubit_id)], ) self.current_mapping = current_mapping self.send([new_cmd]) elif isinstance(cmd.gate, DeallocateQubitGate): qubit_id = cmd.qubits[0][0].id if qubit_id not in current_mapping: raise RuntimeError( "Cannot deallocate a qubit that is not already allocated!") qb = WeakQubitRef(engine=self, idx=current_mapping[qubit_id]) new_cmd = Command( engine=self, gate=DeallocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(qubit_id)], ) current_mapping.pop(qubit_id) self.current_mapping = current_mapping self.send([new_cmd]) else: self._send_cmd_with_mapped_ids(cmd)
def test_cannot_deallocate_same_qubit(): mapper = BoundedQubitMapper(1) engine = MainEngine( Simulator(), engine_list=[mapper], verbose=True, ) qureg = engine.allocate_qubit() qubit_id = qureg[0].id engine.deallocate_qubit(qureg[0]) with pytest.raises(RuntimeError) as excinfo: deallocate_cmd = Command( engine=engine, gate=DeallocateQubitGate(), qubits=([WeakQubitRef(engine=engine, idx=qubit_id)], ), tags=[LogicalQubitIDTag(qubit_id)], ) engine.send([deallocate_cmd]) assert str(excinfo.value ) == "Cannot deallocate a qubit that is not already allocated!"
def test_cannot_deallocate_unknown_qubit(): engine = MainEngine( Simulator(), engine_list=[BoundedQubitMapper(1)], verbose=True, ) qureg = engine.allocate_qubit() with pytest.raises(RuntimeError) as excinfo: deallocate_cmd = Command( engine=engine, gate=DeallocateQubitGate(), qubits=([WeakQubitRef(engine=engine, idx=1)], ), tags=[LogicalQubitIDTag(1)], ) engine.send([deallocate_cmd]) assert str(excinfo.value ) == "Cannot deallocate a qubit that is not already allocated!" # but we can still deallocate an already allocated one engine.deallocate_qubit(qureg[0]) del qureg del engine
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 _send_possible_commands(self): """ Sends the stored commands possible without changing the mapping. Note: self.current_mapping must exist already """ active_ids = deepcopy(self._currently_allocated_ids) for logical_id in self.current_mapping: active_ids.add(logical_id) new_stored_commands = [] for i in range(len(self._stored_commands)): cmd = self._stored_commands[i] if len(active_ids) == 0: new_stored_commands += self._stored_commands[i:] break if isinstance(cmd.gate, AllocateQubitGate): if cmd.qubits[0][0].id in self.current_mapping: self._currently_allocated_ids.add(cmd.qubits[0][0].id) qb = WeakQubitRef( engine=self, idx=self.current_mapping[cmd.qubits[0][0].id]) new_cmd = Command( engine=self, gate=AllocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(cmd.qubits[0][0].id)]) self.send([new_cmd]) else: new_stored_commands.append(cmd) elif isinstance(cmd.gate, DeallocateQubitGate): if cmd.qubits[0][0].id in active_ids: qb = WeakQubitRef( engine=self, idx=self.current_mapping[cmd.qubits[0][0].id]) new_cmd = Command( engine=self, gate=DeallocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(cmd.qubits[0][0].id)]) self._currently_allocated_ids.remove(cmd.qubits[0][0].id) active_ids.remove(cmd.qubits[0][0].id) self._current_mapping.pop(cmd.qubits[0][0].id) self.send([new_cmd]) else: new_stored_commands.append(cmd) else: send_gate = True mapped_ids = set() for qureg in cmd.all_qubits: for qubit in qureg: if qubit.id not in active_ids: send_gate = False break mapped_ids.add(self.current_mapping[qubit.id]) # Check that mapped ids are nearest neighbour if len(mapped_ids) == 2: mapped_ids = list(mapped_ids) diff = abs(mapped_ids[0] - mapped_ids[1]) if self.cyclic: if diff != 1 and diff != self.num_qubits - 1: send_gate = False else: if diff != 1: send_gate = False if send_gate: self._send_cmd_with_mapped_ids(cmd) else: for qureg in cmd.all_qubits: for qubit in qureg: active_ids.discard(qubit.id) new_stored_commands.append(cmd) self._stored_commands = new_stored_commands
def _run(self): # pylint: disable=too-many-locals.too-many-branches,too-many-statements """ Create a new mapping and executes possible gates. It first allocates all 0, ..., self.num_qubits-1 mapped qubit ids, if they are not already used because we might need them all for the swaps. Then it creates a new map, swaps all the qubits to the new map, executes all possible gates, and finally deallocates mapped qubit ids which don't store any information. """ num_of_stored_commands_before = len(self._stored_commands) if not self.current_mapping: self.current_mapping = {} else: self._send_possible_commands() if len(self._stored_commands) == 0: return new_row_major_mapping = self._return_new_mapping() # Find permutation of matchings with lowest cost swaps = None lowest_cost = None matchings_numbers = list(range(self.num_rows)) if self.num_optimization_steps <= math.factorial(self.num_rows): permutations = itertools.permutations(matchings_numbers, self.num_rows) else: permutations = [] for _ in range(self.num_optimization_steps): permutations.append( self._rng.sample(matchings_numbers, self.num_rows)) for permutation in permutations: trial_swaps = self.return_swaps( old_mapping=self._current_row_major_mapping, new_mapping=new_row_major_mapping, permutation=permutation, ) if swaps is None: swaps = trial_swaps lowest_cost = self.optimization_function(trial_swaps) elif lowest_cost > self.optimization_function(trial_swaps): swaps = trial_swaps lowest_cost = self.optimization_function(trial_swaps) if swaps: # first mapping requires no swaps # Allocate all mapped qubit ids (which are not already allocated, # i.e., contained in self._currently_allocated_ids) mapped_ids_used = set() for logical_id in self._currently_allocated_ids: mapped_ids_used.add( self._current_row_major_mapping[logical_id]) not_allocated_ids = set(range( self.num_qubits)).difference(mapped_ids_used) for mapped_id in not_allocated_ids: qb = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[mapped_id]) cmd = Command(engine=self, gate=AllocateQubitGate(), qubits=([qb], )) self.send([cmd]) # Send swap operations to arrive at new_mapping: for qubit_id0, qubit_id1 in swaps: qb0 = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[qubit_id0]) qb1 = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[qubit_id1]) cmd = Command(engine=self, gate=Swap, qubits=([qb0], [qb1])) self.send([cmd]) # Register statistics: self.num_mappings += 1 depth = return_swap_depth(swaps) if depth not in self.depth_of_swaps: self.depth_of_swaps[depth] = 1 else: self.depth_of_swaps[depth] += 1 if len(swaps) not in self.num_of_swaps_per_mapping: self.num_of_swaps_per_mapping[len(swaps)] = 1 else: self.num_of_swaps_per_mapping[len(swaps)] += 1 # Deallocate all previously mapped ids which we only needed for the # swaps: mapped_ids_used = set() for logical_id in self._currently_allocated_ids: mapped_ids_used.add(new_row_major_mapping[logical_id]) not_needed_anymore = set(range( self.num_qubits)).difference(mapped_ids_used) for mapped_id in not_needed_anymore: qb = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[mapped_id]) cmd = Command(engine=self, gate=DeallocateQubitGate(), qubits=([qb], )) self.send([cmd]) # Change to new map: self._current_row_major_mapping = new_row_major_mapping new_mapping = {} for logical_id, mapped_id in new_row_major_mapping.items(): new_mapping[logical_id] = self._mapped_ids_to_backend_ids[ mapped_id] self.current_mapping = new_mapping # Send possible gates: self._send_possible_commands() # Check that mapper actually made progress if len(self._stored_commands) == num_of_stored_commands_before: raise RuntimeError( "Mapper is potentially in an infinite loop. It is likely that the algorithm requires too" "many qubits. Increase the number of qubits for this mapper.")
def _send_possible_commands(self): # pylint: disable=too-many-branches """ Send the stored commands possible without changing the mapping. Note: self._current_row_major_mapping (hence also self.current_mapping) must exist already """ active_ids = deepcopy(self._currently_allocated_ids) for logical_id in self._current_row_major_mapping: # So that loop doesn't stop before AllocateGate applied active_ids.add(logical_id) new_stored_commands = [] for i, cmd in enumerate(self._stored_commands): if len(active_ids) == 0: new_stored_commands += self._stored_commands[i:] break if isinstance(cmd.gate, AllocateQubitGate): if cmd.qubits[0][0].id in self._current_row_major_mapping: self._currently_allocated_ids.add(cmd.qubits[0][0].id) mapped_id = self._current_row_major_mapping[cmd.qubits[0] [0].id] qb = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[mapped_id]) new_cmd = Command( engine=self, gate=AllocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(cmd.qubits[0][0].id)], ) self.send([new_cmd]) else: new_stored_commands.append(cmd) elif isinstance(cmd.gate, DeallocateQubitGate): if cmd.qubits[0][0].id in active_ids: mapped_id = self._current_row_major_mapping[cmd.qubits[0] [0].id] qb = WeakQubitRef( engine=self, idx=self._mapped_ids_to_backend_ids[mapped_id]) new_cmd = Command( engine=self, gate=DeallocateQubitGate(), qubits=([qb], ), tags=[LogicalQubitIDTag(cmd.qubits[0][0].id)], ) self._currently_allocated_ids.remove(cmd.qubits[0][0].id) active_ids.remove(cmd.qubits[0][0].id) self._current_row_major_mapping.pop(cmd.qubits[0][0].id) self._current_mapping.pop(cmd.qubits[0][0].id) self.send([new_cmd]) else: new_stored_commands.append(cmd) else: send_gate = True mapped_ids = set() for qureg in cmd.all_qubits: for qubit in qureg: if qubit.id not in active_ids: send_gate = False break mapped_ids.add( self._current_row_major_mapping[qubit.id]) # Check that mapped ids are nearest neighbour on 2D grid if len(mapped_ids) == 2: qb0, qb1 = sorted(mapped_ids) send_gate = False if qb1 - qb0 == self.num_columns: send_gate = True elif qb1 - qb0 == 1 and qb1 % self.num_columns != 0: send_gate = True if send_gate: # Note: This sends the cmd correctly with the backend ids as it looks up the mapping in # self.current_mapping and not our internal mapping self._current_row_major_mapping self._send_cmd_with_mapped_ids(cmd) else: for qureg in cmd.all_qubits: for qubit in qureg: active_ids.discard(qubit.id) new_stored_commands.append(cmd) self._stored_commands = new_stored_commands