def test_send_possible_commands_allocate(): 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=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 assert len(backend.received_commands) == 1 assert backend.received_commands[0].gate == Allocate assert backend.received_commands[0].qubits[0][0].id == 3 assert backend.received_commands[0].tags == [LogicalQubitIDTag(0)] assert mapper._currently_allocated_ids == {10, 0}
def test_run_infinite_loop_detection(): 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) qb2 = WeakQubitRef(engine=None, idx=2) qb3 = WeakQubitRef(engine=None, idx=3) qb4 = WeakQubitRef(engine=None, idx=4) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0],)) cmd1 = Command(engine=None, gate=Allocate, qubits=([qb1],)) cmd2 = Command(engine=None, gate=Allocate, qubits=([qb2],)) cmd3 = Command(engine=None, gate=Allocate, qubits=([qb3],)) cmd4 = Command(engine=None, gate=Allocate, qubits=([qb4],)) cmd5 = Command(None, X, qubits=([qb0],), controls=[qb1]) qb2 = WeakQubitRef(engine=None, idx=-1) cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb2],)) with pytest.raises(RuntimeError): mapper.receive([cmd0, cmd1, cmd2, cmd3, cmd4, cmd5, cmd_flush])
def test_run_and_receive(): mapper = lm.LinearMapper(num_qubits=3, cyclic=False) 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) qb2 = WeakQubitRef(engine=None, idx=2) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0],)) cmd1 = Command(engine=None, gate=Allocate, qubits=([qb1],)) cmd2 = Command(engine=None, gate=Allocate, qubits=([qb2],)) cmd3 = Command(None, X, qubits=([qb0],), controls=[qb1]) cmd4 = Command(None, X, qubits=([qb1],), controls=[qb2]) cmd5 = Command(engine=None, gate=Deallocate, qubits=([qb1],)) mapper.receive([cmd0, cmd1, cmd2, cmd3, cmd4, cmd5]) assert mapper._stored_commands == [cmd0, cmd1, cmd2, cmd3, cmd4, cmd5] qb3 = WeakQubitRef(engine=None, idx=-1) cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb3],)) mapper.receive([cmd_flush]) assert mapper._stored_commands == [] assert len(backend.received_commands) == 7 assert mapper._currently_allocated_ids == set([0, 2]) assert (mapper.current_mapping == {0: 2, 2: 0} or mapper.current_mapping == {0: 0, 2: 2}) cmd6 = Command(None, X, qubits=([qb0],), controls=[qb2]) mapper.storage = 1 mapper.receive([cmd6]) assert mapper._currently_allocated_ids == set([0, 2]) assert mapper._stored_commands == [] assert len(mapper.current_mapping) == 2 assert 0 in mapper.current_mapping assert 2 in mapper.current_mapping assert len(backend.received_commands) == 11 for cmd in backend.received_commands: print(cmd) assert (backend.received_commands[-1] == Command(None, X, qubits=([WeakQubitRef(engine=None, idx=mapper.current_mapping[qb0.id])],), controls=[WeakQubitRef(engine=None, idx=mapper.current_mapping[qb2.id])])) assert mapper.num_mappings == 1
def _run(self): """ Run the circuit. Send the circuit via the AQT API using the provided user token / ask for the user token. """ # finally: measurements # NOTE AQT DOESN'T SEEM TO HAVE MEASUREMENT INSTRUCTIONS (no # intermediate measurements are allowed, so implicit at the end) # return if no operations. if self._circuit == []: return n_qubit = max(self._allocated_qubits) + 1 info = {} # Hack: AQT instructions specifically need "GATE" string representation # instead of 'GATE' info['circuit'] = str(self._circuit).replace("'", '"') info['nq'] = n_qubit info['shots'] = self._num_runs info['backend'] = {'name': self.device} if self._num_runs > 200: raise Exception("Number of shots limited to 200") try: if self._retrieve_execution is None: res = send( info, device=self.device, token=self._token, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) else: res = retrieve( device=self.device, token=self._token, jobid=self._retrieve_execution, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) self._num_runs = len(res) counts = _format_counts(res, n_qubit) # Determine random outcome random_outcome = random.random() p_sum = 0.0 measured = "" for state in counts: probability = counts[state] * 1.0 / self._num_runs p_sum += probability star = "" if p_sum >= random_outcome and measured == "": measured = state star = "*" self._probabilities[state] = probability if self._verbose and probability > 0: print(str(state) + " with p = " + str(probability) + star) # register measurement result from AQT for qubit_id in self._measured_ids: location = self._logical_to_physical(qubit_id) result = int(measured[location]) self.main_engine.set_measurement_result( WeakQubitRef(self, qubit_id), result) self._reset() except TypeError as err: raise Exception("Failed to run the circuit. Aborting.") from err
def test_invalid_arguments(): qb0 = WeakQubitRef(engine=None, idx=0) qb1 = WeakQubitRef(engine=None, idx=1) cmd = Command(None, StatePreparation([0, 1j]), qubits=([qb0], [qb1])) with pytest.raises(ValueError): stateprep2cnot._decompose_state_preparation(cmd)
def test_qubitop2singlequbit_invalid(): qb0 = WeakQubitRef(None, idx=0) qb1 = WeakQubitRef(None, idx=1) with pytest.raises(ValueError): qubitop2onequbit._decompose_qubitop( Command(None, QubitOperator(), ([qb0], [qb1])))
def _handle(self, cmd): """ Handle all commands, i.e., call the member functions of the C++- simulator object corresponding to measurement, allocation/ deallocation, and (controlled) single-qubit gate. Args: cmd (Command): Command to handle. Raises: Exception: If a non-single-qubit gate needs to be processed (which should never happen due to is_available). """ if cmd.gate == Measure: assert (get_control_count(cmd) == 0) ids = [qb.id for qr in cmd.qubits for qb in qr] out = self._simulator.measure_qubits(ids) i = 0 for qr in cmd.qubits: for qb in qr: # Check if a mapper assigned a different logical id logical_id_tag = None for tag in cmd.tags: if isinstance(tag, LogicalQubitIDTag): logical_id_tag = tag if logical_id_tag is not None: qb = WeakQubitRef(qb.engine, logical_id_tag.logical_qubit_id) self.main_engine.set_measurement_result(qb, out[i]) i += 1 elif cmd.gate == Allocate: ID = cmd.qubits[0][0].id self._simulator.allocate_qubit(ID) elif cmd.gate == Deallocate: ID = cmd.qubits[0][0].id self._simulator.deallocate_qubit(ID) elif isinstance(cmd.gate, BasicMathGate): # improve performance by using C++ code for some commomn gates from projectq.libs.math import (AddConstant, AddConstantModN, MultiplyByConstantModN) qubitids = [] for qr in cmd.qubits: qubitids.append([]) for qb in qr: qubitids[-1].append(qb.id) if FALLBACK_TO_PYSIM: math_fun = cmd.gate.get_math_function(cmd.qubits) self._simulator.emulate_math( math_fun, qubitids, [qb.id for qb in cmd.control_qubits]) else: # individual code for different standard gates to make it faster! if isinstance(cmd.gate, AddConstant): self._simulator.emulate_math_addConstant( cmd.gate.a, qubitids, [qb.id for qb in cmd.control_qubits]) elif isinstance(cmd.gate, AddConstantModN): self._simulator.emulate_math_addConstantModN( cmd.gate.a, cmd.gate.N, qubitids, [qb.id for qb in cmd.control_qubits]) elif isinstance(cmd.gate, MultiplyByConstantModN): self._simulator.emulate_math_multiplyByConstantModN( cmd.gate.a, cmd.gate.N, qubitids, [qb.id for qb in cmd.control_qubits]) else: math_fun = cmd.gate.get_math_function(cmd.qubits) self._simulator.emulate_math( math_fun, qubitids, [qb.id for qb in cmd.control_qubits]) elif isinstance(cmd.gate, TimeEvolution): op = [(list(term), coeff) for (term, coeff) in cmd.gate.hamiltonian.terms.items()] t = cmd.gate.time qubitids = [qb.id for qb in cmd.qubits[0]] ctrlids = [qb.id for qb in cmd.control_qubits] self._simulator.emulate_time_evolution(op, t, qubitids, ctrlids) elif len(cmd.gate.matrix) <= 2**5: matrix = cmd.gate.matrix ids = [qb.id for qr in cmd.qubits for qb in qr] if not 2**len(ids) == len(cmd.gate.matrix): raise Exception("Simulator: Error applying {} gate: " "{}-qubit gate applied to {} qubits.".format( str(cmd.gate), int(math.log(len(cmd.gate.matrix), 2)), len(ids))) self._simulator.apply_controlled_gate( matrix.tolist(), ids, [qb.id for qb in cmd.control_qubits]) if not self._gate_fusion: self._simulator.run() else: raise Exception("This simulator only supports controlled k-qubit" " gates with k < 6!\nPlease add an auto-replacer" " engine to your list of compiler engines.")
def _handle(self, cmd): """ Handle all commands. Args: cmd (Command): Command to handle. Raises: RuntimeError: If a measurement is performed before flush gate. """ if isinstance(cmd.gate, AllocateQubitGate): self._qubit_map[cmd.qubits[0][0].id] = self._num_qubits self._num_qubits += 1 self._unitary = np.kron(np.identity(2), self._unitary) self._state.extend([0] * len(self._state)) elif isinstance(cmd.gate, DeallocateQubitGate): pos = self._qubit_map[cmd.qubits[0][0].id] self._qubit_map = { key: value - 1 if value > pos else value for key, value in self._qubit_map.items() } self._num_qubits -= 1 self._is_valid = False elif isinstance(cmd.gate, MeasureGate): self._is_valid = False if not self._is_flushed: raise RuntimeError( 'Please make sure all previous gates are flushed before measurement so the state gets updated' ) if get_control_count(cmd) != 0: raise ValueError( 'Cannot have control qubits with a measurement gate!') all_qubits = [qb for qr in cmd.qubits for qb in qr] measurements = self.measure_qubits([qb.id for qb in all_qubits]) for qb, res in zip(all_qubits, measurements): # Check if a mapper assigned a different logical id for tag in cmd.tags: if isinstance(tag, LogicalQubitIDTag): qb = WeakQubitRef(qb.engine, tag.logical_qubit_id) break self.main_engine.set_measurement_result(qb, res) elif isinstance(cmd.gate, FlushGate): self._flush() else: if not self._is_valid: self._flush() warnings.warn( "Processing of other gates after a qubit deallocation or measurement will reset the unitary," "previous unitary can be accessed in history") self._history.append(self._unitary) self._unitary = np.identity(2**self._num_qubits, dtype=complex) self._state = np.array([1] + ([0] * (2**self._num_qubits - 1)), dtype=complex) self._is_valid = True self._is_flushed = False mask_list = _qidmask( [self._qubit_map[qb.id] for qr in cmd.qubits for qb in qr], [self._qubit_map[qb.id] for qb in cmd.control_qubits], self._num_qubits, ) for mask in mask_list: cache = np.identity(2**self._num_qubits, dtype=complex) cache[np.ix_(mask, mask)] = cmd.gate.matrix self._unitary = cache @ self._unitary
def _run(self): # pylint: disable=too-many-locals """ Run the circuit. Send the circuit via a non documented IBM API (using JSON written circuits) using the provided user data / ask for the user token. """ # finally: add measurements (no intermediate measurements are allowed) for measured_id in self._measured_ids: qb_loc = self.main_engine.mapper.current_mapping[measured_id] self.qasm += "\nmeasure q[{0}] -> c[{0}];".format(qb_loc) self._json.append({ 'qubits': [qb_loc], 'name': 'measure', 'memory': [qb_loc] }) # return if no operations / measurements have been performed. if self.qasm == "": return max_qubit_id = max(self._allocated_qubits) + 1 info = {} info['json'] = self._json info['nq'] = max_qubit_id info['shots'] = self._num_runs info['maxCredits'] = 10 info['backend'] = {'name': self.device} try: if self._retrieve_execution is None: res = send( info, device=self.device, token=self._token, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) else: res = retrieve( device=self.device, token=self._token, jobid=self._retrieve_execution, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) counts = res['data']['counts'] # Determine random outcome random_outcome = random.random() p_sum = 0.0 measured = "" for state in counts: probability = counts[state] * 1.0 / self._num_runs state = "{0:b}".format(int(state, 0)) state = state.zfill(max_qubit_id) # states in ibmq are right-ordered, so need to reverse state string state = state[::-1] p_sum += probability star = "" if p_sum >= random_outcome and measured == "": measured = state star = "*" self._probabilities[state] = probability if self._verbose and probability > 0: print(str(state) + " with p = " + str(probability) + star) # register measurement result from IBM for qubit_id in self._measured_ids: location = self._logical_to_physical(qubit_id) result = int(measured[location]) self.main_engine.set_measurement_result( WeakQubitRef(self, qubit_id), result) self._reset() except TypeError as err: raise Exception("Failed to run the circuit. Aborting.") from err
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 test_run_and_receive(num_optimization_steps, different_backend_ids): if different_backend_ids: map_to_backend_ids = {0: 21, 1: 32, 2: 3, 3: 0} else: map_to_backend_ids = None def choose_last_permutation(swaps): choose_last_permutation.counter -= 1 return choose_last_permutation.counter choose_last_permutation.counter = 100 mapper = two_d.GridMapper( num_rows=2, num_columns=2, mapped_ids_to_backend_ids=map_to_backend_ids, optimization_function=choose_last_permutation, num_optimization_steps=num_optimization_steps, ) 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) qb2 = WeakQubitRef(engine=None, idx=2) qb3 = WeakQubitRef(engine=None, idx=3) cmd0 = Command(engine=None, gate=Allocate, qubits=([qb0],)) cmd1 = Command(engine=None, gate=Allocate, qubits=([qb1],)) cmd2 = Command(engine=None, gate=Allocate, qubits=([qb2],)) cmd3 = Command(engine=None, gate=Allocate, qubits=([qb3],)) cmd4 = Command(None, X, qubits=([qb0],), controls=[qb1]) cmd5 = Command(None, X, qubits=([qb1],), controls=[qb3]) cmd6 = Command(None, X, qubits=([qb3],), controls=[qb2]) cmd7 = Command(None, X, qubits=([qb0],), controls=[qb2]) cmd8 = Command(engine=None, gate=Deallocate, qubits=([qb1],)) all_cmd = [cmd0, cmd1, cmd2, cmd3, cmd4, cmd5, cmd6, cmd7, cmd8] mapper.receive(all_cmd) assert mapper._stored_commands == all_cmd qb4 = WeakQubitRef(engine=None, idx=-1) cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb4],)) mapper.receive([cmd_flush]) assert mapper._stored_commands == [] assert len(backend.received_commands) == 10 assert mapper._currently_allocated_ids == {0, 2, 3} if different_backend_ids: assert ( mapper.current_mapping == {0: 21, 2: 3, 3: 0} or mapper.current_mapping == {0: 32, 2: 0, 3: 21} or mapper.current_mapping == {0: 3, 2: 21, 3: 32} or mapper.current_mapping == {0: 0, 2: 32, 3: 3} ) else: assert ( mapper.current_mapping == {0: 0, 2: 2, 3: 3} or mapper.current_mapping == {0: 1, 2: 3, 3: 0} or mapper.current_mapping == {0: 2, 2: 0, 3: 1} or mapper.current_mapping == {0: 3, 2: 1, 3: 2} ) cmd9 = Command(None, X, qubits=([qb0],), controls=[qb3]) mapper.storage = 1 mapper.receive([cmd9]) assert mapper._currently_allocated_ids == {0, 2, 3} assert mapper._stored_commands == [] assert len(mapper.current_mapping) == 3 assert 0 in mapper.current_mapping assert 2 in mapper.current_mapping assert 3 in mapper.current_mapping assert mapper.num_mappings == 1
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
def _run(self): """ Run the circuit. Send the circuit via the AWS Boto3 SDK. Use the provided Access Key and Secret key or ask for them if not provided """ # NB: the AWS Braket API does not require explicit measurement commands at the end of a circuit; after running # any circuit, all qubits are implicitly measured. Also, AWS Braket currently does not support intermediate # measurements. # If the clear flag is set, nothing to do here... if self._clear: return # In Braket the results for the jobs are stored in S3. You can recover the results from previous jobs using # the TaskArn (self._retrieve_execution). if self._retrieve_execution is not None: res = retrieve( credentials=self._credentials, task_arn=self._retrieve_execution, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) else: # Return if no operations have been added. if not self._circuit: return n_qubit = len(self._allocated_qubits) info = {} info['circuit'] = self._circuithead + self._circuit.rstrip( ', ') + self._circuittail info['nq'] = n_qubit info['shots'] = self._num_runs info['backend'] = {'name': self.device} res = send( info, device=self.device, credentials=self._credentials, s3_folder=self._s3_folder, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) counts = res # Determine random outcome random_outcome = random.random() p_sum = 0.0 measured = "" for state in counts: probability = counts[state] p_sum += probability star = "" if p_sum >= random_outcome and measured == "": measured = state star = "*" self._probabilities[state] = probability if self._verbose and probability > 0: print(state + " with p = " + str(probability) + star) # register measurement result for qubit_id in self._measured_ids: result = int(measured[self._logical_to_physical(qubit_id)]) self.main_engine.set_measurement_result( WeakQubitRef(self.main_engine, qubit_id), result) self._reset()
def workspace(cmd): touched = set(q.id for reg in cmd.all_qubits for q in reg) avail = set(q.id for q in cmd.engine.main_engine.active_qubits) untouched = avail - touched result = list(WeakQubitRef(cmd.engine, qid) for qid in untouched) return sorted(result, key=lambda q: q.id)
def _run(self): # pylint: disable=too-many-locals """Run the circuit this object has built during engine execution.""" # Nothing to do with an empty circuit. if len(self._circuit) == 0: return if self._retrieve_execution is None: qubit_mapping = self.main_engine.mapper.current_mapping measured_ids = self._measured_ids[:] info = { 'circuit': self._circuit, 'nq': len(qubit_mapping.keys()), 'shots': self._num_runs, 'meas_mapped': [qubit_mapping[qubit_id] for qubit_id in measured_ids], 'meas_qubit_ids': measured_ids, } res = http_client.send( info, device=self.device, token=self._token, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) if res is None: raise RuntimeError('Failed to submit job to the server!') else: res = http_client.retrieve( device=self.device, token=self._token, jobid=self._retrieve_execution, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, ) if res is None: raise RuntimeError( "Failed to retrieve job with id: '{}'!".format( self._retrieve_execution)) self._measured_ids = measured_ids = res['meas_qubit_ids'] # Determine random outcome from probable states. random_outcome = random.random() p_sum = 0.0 measured = "" star = "" num_measured = len(measured_ids) probable_outcomes = res['output_probs'] states = probable_outcomes.keys() self._probabilities = {} for idx, state_int in enumerate(states): state = _rearrange_result(int(state_int), num_measured) probability = probable_outcomes[state_int] p_sum += probability if p_sum >= random_outcome and measured == "" or (idx == len(states) - 1): measured = state star = "*" self._probabilities[state] = probability if self._verbose and probability > 0: # pragma: no cover print(state + " with p = " + str(probability) + star) # Register measurement results for idx, qubit_id in enumerate(measured_ids): result = int(measured[idx]) qubit_ref = WeakQubitRef(self.main_engine, qubit_id) self.main_engine.set_measurement_result(qubit_ref, result)
def test_return_new_mapping(different_backend_ids): if different_backend_ids: map_to_backend_ids = { 0: 21, 1: 32, 2: 1, 3: 4, 4: 5, 5: 6, 6: 10, 7: 7, 8: 0, 9: 56, 10: 55, 11: 9, } else: map_to_backend_ids = None mapper = two_d.GridMapper(num_rows=4, num_columns=3, mapped_ids_to_backend_ids=map_to_backend_ids) eng = projectq.MainEngine(DummyEngine(), [mapper]) linear_chain_ids = [33, 22, 11, 2, 3, 0, 6, 7, 9, 12, 4, 88] mapper._stored_commands = [] for i in range(12): qb = WeakQubitRef(engine=None, idx=linear_chain_ids[i]) cmd = Command(None, Allocate, ([qb],)) mapper._stored_commands.append(cmd) for i in range(11): qb0 = WeakQubitRef(engine=None, idx=linear_chain_ids[i]) qb1 = WeakQubitRef(engine=None, idx=linear_chain_ids[i + 1]) cmd = Command(None, X, qubits=([qb0],), controls=[qb1]) mapper._stored_commands.append(cmd) new_mapping = mapper._return_new_mapping() possible_solution_1 = { 33: 0, 22: 1, 11: 2, 2: 5, 3: 4, 0: 3, 6: 6, 7: 7, 9: 8, 12: 11, 4: 10, 88: 9, } possible_solution_2 = { 88: 0, 4: 1, 12: 2, 9: 5, 7: 4, 6: 3, 0: 6, 3: 7, 2: 8, 11: 11, 22: 10, 33: 9, } assert new_mapping == possible_solution_1 or new_mapping == possible_solution_2 eng.flush() if different_backend_ids: transformed_sol1 = {} for logical_id, mapped_id in possible_solution_1.items(): transformed_sol1[logical_id] = map_to_backend_ids[mapped_id] transformed_sol2 = {} for logical_id, mapped_id in possible_solution_2.items(): transformed_sol2[logical_id] = map_to_backend_ids[mapped_id] assert mapper.current_mapping == transformed_sol1 or mapper.current_mapping == transformed_sol2 else: assert mapper.current_mapping == possible_solution_1 or mapper.current_mapping == possible_solution_2
def _run(self): """ Creates 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 = dict() else: self._send_possible_commands() if len(self._stored_commands) == 0: return new_mapping = self.return_new_mapping(self.num_qubits, self.cyclic, self._currently_allocated_ids, self._stored_commands, self.current_mapping) swaps = self._odd_even_transposition_sort_swaps( old_mapping=self.current_mapping, new_mapping=new_mapping) 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_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=mapped_id) cmd = Command(engine=self, gate=Allocate, qubits=([qb], )) self.send([cmd]) # Send swap operations to arrive at new_mapping: for qubit_id0, qubit_id1 in swaps: q0 = WeakQubitRef(engine=self, idx=qubit_id0) q1 = WeakQubitRef(engine=self, idx=qubit_id1) cmd = Command(engine=self, gate=Swap, qubits=([q0], [q1])) 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_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=mapped_id) cmd = Command(engine=self, gate=Deallocate, qubits=([qb], )) self.send([cmd]) # Change to new map: 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 remap_qureg(self, qureg, id_map): return Qureg(WeakQubitRef(q.engine, id_map[q.id]) for q in qureg)