def test_send_possible_commands_deallocate(): mapper = lm.LinearMapper(num_qubits=4, cyclic=False) backend = DummyEngine(save_commands=True) backend.is_last_engine = True mapper.next_engine = backend qb0 = WeakQubitRef(engine=None, idx=0) cmd0 = Command(engine=None, gate=Deallocate, qubits=([qb0],), controls=[], tags=[]) mapper._stored_commands = [cmd0] mapper.current_mapping = {} mapper._currently_allocated_ids = {10} # not yet allocated: mapper._send_possible_commands() assert len(backend.received_commands) == 0 assert mapper._stored_commands == [cmd0] # allocated: mapper.current_mapping = {0: 3} mapper._currently_allocated_ids.add(0) mapper._send_possible_commands() assert len(backend.received_commands) == 1 assert backend.received_commands[0].gate == Deallocate assert backend.received_commands[0].qubits[0][0].id == 3 assert backend.received_commands[0].tags == [LogicalQubitIDTag(0)] assert len(mapper._stored_commands) == 0 assert mapper.current_mapping == {} assert mapper._currently_allocated_ids == {10}
def test_send_possible_commands_allocate(different_backend_ids): if different_backend_ids: map_to_backend_ids = {0: 21, 1: 32, 2: 3, 3: 4, 4: 5, 5: 6} else: map_to_backend_ids = None mapper = two_d.GridMapper(num_rows=3, num_columns=2, mapped_ids_to_backend_ids=map_to_backend_ids) backend = DummyEngine(save_commands=True) backend.is_last_engine = True mapper.next_engine = backend qb0 = WeakQubitRef(engine=None, idx=0) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0],), controls=[], tags=[]) mapper._stored_commands = [cmd0] mapper._currently_allocated_ids = {10} # not in mapping: mapper.current_mapping = {} assert len(backend.received_commands) == 0 mapper._send_possible_commands() assert len(backend.received_commands) == 0 assert mapper._stored_commands == [cmd0] # in mapping: mapper.current_mapping = {0: 3} mapper._send_possible_commands() assert len(mapper._stored_commands) == 0 # Only self._run() sends Allocate gates mapped0 = WeakQubitRef(engine=None, idx=3) received_cmd = Command( engine=mapper, gate=Allocate, qubits=([mapped0],), controls=[], tags=[LogicalQubitIDTag(0)], ) assert backend.received_commands[0] == received_cmd assert mapper._currently_allocated_ids == {10, 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_basic_mapper_engine_send_cmd_with_mapped_ids(): mapper = _basicmapper.BasicMapperEngine() mapper.current_mapping = {0: 3, 1: 2, 2: 1, 3: 0} backend = DummyEngine(save_commands=True) backend.is_last_engine = True mapper.next_engine = backend # generate a few commands qb0 = WeakQubitRef(engine=None, idx=0) qb1 = WeakQubitRef(engine=None, idx=1) qb2 = WeakQubitRef(engine=None, idx=2) qb3 = WeakQubitRef(engine=None, idx=3) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0], ), controls=[], tags=[]) cmd1 = Command(engine=None, gate=Deallocate, qubits=([qb1], ), controls=[], tags=[]) cmd2 = Command(engine=None, gate=Measure, qubits=([qb2], ), controls=[], tags=["SomeTag"]) cmd3 = Command( engine=None, gate=BasicGate(), qubits=([qb0, qb1], [qb2]), controls=[qb3], tags=[], ) cmd4 = Command(None, FlushGate(), ([WeakQubitRef(None, -1)], )) mapper._send_cmd_with_mapped_ids(cmd0) mapper._send_cmd_with_mapped_ids(cmd1) mapper._send_cmd_with_mapped_ids(cmd2) mapper._send_cmd_with_mapped_ids(cmd3) mapper._send_cmd_with_mapped_ids(cmd4) rcmd0 = backend.received_commands[0] rcmd1 = backend.received_commands[1] rcmd2 = backend.received_commands[2] rcmd3 = backend.received_commands[3] rcmd4 = backend.received_commands[4] assert rcmd0.gate == Allocate assert rcmd0.qubits == ([qb3], ) assert rcmd1.gate == Deallocate assert rcmd1.qubits == ([qb2], ) assert rcmd2.gate == Measure assert rcmd2.qubits == ([qb1], ) assert rcmd2.tags == ["SomeTag", LogicalQubitIDTag(2)] assert rcmd3.gate == BasicGate() assert rcmd3.qubits == ([qb3, qb2], [qb1]) assert rcmd3.control_qubits == [qb0] assert len(rcmd4.qubits) == 1 assert len(rcmd4.qubits[0]) == 1 assert rcmd4.qubits[0][0].id == -1
def test_store_measure_gate_with_mapper(self, function_mock): mock_tag = 'mock_my_tag' function_mock.return_value = 4 mapper = MagicMock(current_mapping={mock_tag: 0}) self.qi_backend.main_engine = MagicMock(mapper=mapper) self.__store_function(self.qi_backend, 0, Allocate) command = [MagicMock(gate=Measure, qubits=[[MagicMock(id=0)]], tags=[LogicalQubitIDTag(mock_tag)])] self.qi_backend.receive(command) self.__store_function(self.qi_backend, 0, H) self.assertEqual(self.qi_backend.measured_ids, [mock_tag]) self.assertEqual(self.qi_backend.qasm, "\nmeasure q[0]\nh q[0]")
def test_store_measure_gate_with_mapper(self, function_mock): mock_tag = 'mock_my_tag' api = MockApiClient() function_mock.return_value = 4 backend = QIBackend(quantum_inspire_api=api) command = MagicMock(gate=Measure, qubits=[[MagicMock(id=0)]], control_qubits=[MagicMock(id=2), MagicMock(id=3)], tags=[LogicalQubitIDTag(mock_tag)]) backend.main_engine = MagicMock(mapper="mapper") backend._store(command) self.assertEqual(backend._measured_ids, [mock_tag])
def test_manualmapper_mapping(): backend = DummyEngine(save_commands=True) def mapping(qubit_id): return (qubit_id + 1) & 1 eng = MainEngine(backend=backend, engine_list=[ManualMapper(mapping)]) qb0 = eng.allocate_qubit() qb1 = eng.allocate_qubit() H | qb0 H | qb1 All(Measure) | (qb0 + qb1) eng.flush() num_measurements = 0 for cmd in backend.received_commands: if cmd.gate == Measure: tag = LogicalQubitIDTag(mapping(cmd.qubits[0][0].id)) assert tag in cmd.tags wrong_tag = LogicalQubitIDTag(cmd.qubits[0][0].id) assert wrong_tag not in cmd.tags num_measurements += 1 assert num_measurements == 2
def test_logical_id_tags_allocate_and_deallocate(): mapper = two_d.GridMapper(num_rows=2, num_columns=2) backend = DummyEngine(save_commands=True) backend.is_last_engine = True mapper.next_engine = backend qb0 = WeakQubitRef(engine=None, idx=0) qb1 = WeakQubitRef(engine=None, idx=1) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0],)) cmd1 = Command(engine=None, gate=Allocate, qubits=([qb1],)) cmd2 = Command(None, X, qubits=([qb0],), controls=[qb1]) cmd3 = Command(engine=None, gate=Deallocate, qubits=([qb0],)) cmd4 = Command(engine=None, gate=Deallocate, qubits=([qb1],)) mapper.current_mapping = {0: 0, 1: 3} qb_flush = WeakQubitRef(engine=None, idx=-1) cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb_flush],)) mapper.receive([cmd0, cmd1, cmd2, cmd_flush]) assert backend.received_commands[0].gate == Allocate assert backend.received_commands[0].qubits[0][0].id == 0 assert backend.received_commands[0].tags == [LogicalQubitIDTag(0)] assert backend.received_commands[1].gate == Allocate assert backend.received_commands[1].qubits[0][0].id == 3 assert backend.received_commands[1].tags == [LogicalQubitIDTag(1)] for cmd in backend.received_commands[2:]: if cmd.gate == Allocate: assert cmd.tags == [] elif cmd.gate == Deallocate: assert cmd.tags == [] mapped_id_for_0 = mapper.current_mapping[0] mapped_id_for_1 = mapper.current_mapping[1] mapper.receive([cmd3, cmd4, cmd_flush]) assert backend.received_commands[-3].gate == Deallocate assert backend.received_commands[-3].qubits[0][0].id == mapped_id_for_0 assert backend.received_commands[-3].tags == [LogicalQubitIDTag(0)] assert backend.received_commands[-2].gate == Deallocate assert backend.received_commands[-2].qubits[0][0].id == mapped_id_for_1 assert backend.received_commands[-2].tags == [LogicalQubitIDTag(1)]
def test_resource_counter_measurement(): eng = MainEngine(ResourceCounter(), []) qb1 = WeakQubitRef(engine=eng, idx=1) qb2 = WeakQubitRef(engine=eng, idx=2) cmd0 = Command(engine=eng, gate=Allocate, qubits=([qb1],)) cmd1 = Command(engine=eng, gate=Measure, qubits=([qb1],), controls=[], tags=[LogicalQubitIDTag(2)]) with pytest.raises(NotYetMeasuredError): int(qb1) with pytest.raises(NotYetMeasuredError): int(qb2) eng.send([cmd0, cmd1]) eng.flush() with pytest.raises(NotYetMeasuredError): int(qb1) assert int(qb2) == 0
def test_ibm_errors(): backend = _ibm.IBMBackend(verbose=True, num_runs=1000) mapper = BasicMapperEngine() mapper.current_mapping = {0: 0} eng = MainEngine(backend=backend, engine_list=[mapper]) qb0 = WeakQubitRef(engine=None, idx=0) # No LogicalQubitIDTag with pytest.raises(RuntimeError): eng.backend._store(Command(engine=eng, gate=Measure, qubits=([qb0],))) eng = MainEngine(backend=backend, engine_list=[]) # No mapper with pytest.raises(RuntimeError): eng.backend._store(Command(engine=eng, gate=Measure, qubits=([qb0],), tags=(LogicalQubitIDTag(1),)))
def test_command_printer_measure_mapped_qubit(): eng = MainEngine(_printer.CommandPrinter(accept_input=False), []) qb1 = WeakQubitRef(engine=eng, idx=1) qb2 = WeakQubitRef(engine=eng, idx=2) cmd0 = Command(engine=eng, gate=Allocate, qubits=([qb1], )) cmd1 = Command(engine=eng, gate=Measure, qubits=([qb1], ), controls=[], tags=[LogicalQubitIDTag(2)]) with pytest.raises(NotYetMeasuredError): int(qb1) with pytest.raises(NotYetMeasuredError): int(qb2) eng.send([cmd0, cmd1]) eng.flush() with pytest.raises(NotYetMeasuredError): int(qb1) assert int(qb2) == 0
def test_cannot_reallocate_same_qubit(): engine = MainEngine( Simulator(), engine_list=[BoundedQubitMapper(1)], verbose=True, ) qureg = engine.allocate_qubit() qubit = qureg[0] qubit_id = qubit.id with pytest.raises(RuntimeError) as excinfo: allocate_cmd = Command( engine=engine, gate=AllocateQubitGate(), qubits=([WeakQubitRef(engine=engine, idx=qubit_id)], ), tags=[LogicalQubitIDTag(qubit_id)], ) engine.send([allocate_cmd]) assert str(excinfo.value) == "Qubit with id 0 has already been allocated!"
def test_simulator_measure_mapped_qubit(sim): eng = MainEngine(sim, []) qb1 = WeakQubitRef(engine=eng, idx=1) qb2 = WeakQubitRef(engine=eng, idx=2) cmd0 = Command(engine=eng, gate=Allocate, qubits=([qb1], )) cmd1 = Command(engine=eng, gate=X, qubits=([qb1], )) cmd2 = Command(engine=eng, gate=Measure, qubits=([qb1], ), controls=[], tags=[LogicalQubitIDTag(2)]) with pytest.raises(NotYetMeasuredError): int(qb1) with pytest.raises(NotYetMeasuredError): int(qb2) eng.send([cmd0, cmd1, cmd2]) eng.flush() with pytest.raises(NotYetMeasuredError): int(qb1) assert int(qb2) == 1
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 _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 add_logical_id(command, old_tags=deepcopy(cmd.tags)): command.tags = (old_tags + [LogicalQubitIDTag(cmd.qubits[0][0].id)]) return command
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