def main(): parser = argparse.ArgumentParser( "Compare the Bridge+SWAP approach to the simple SWAP one.") parser.add_argument( "N", type=int, help= "Number of initial mapping that will be explored. Should be strictly " "over 1 (i.e. 2 or more).", ) parser.add_argument("circuit_name", type=str, help="Name of the quantum circuit to map.") parser.add_argument("hardware", type=str, help="Name of the hardware to consider.") args = parser.parse_args() N = args.N if N < 1: raise RuntimeError("N should be 2 or more.") hardware = IBMQHardwareArchitecture.load(args.hardware) circuit = add_qubits_to_quantum_circuit( read_benchmark_circuit("sabre", args.circuit_name), hardware) initial_mappings = [{ qubit: i for qubit, i in zip(circuit.qubits, permutation(range(hardware.qubit_number))) } for _ in range(N)] # using_swap_bridge_strategy_tup([circuit, hardware, initial_mappings[0]]) with ProcessPoolExecutor(max_workers=cpu_count()) as executor: best_swap_results, swap_timings = separate_lists( executor.map( using_only_swap_strategy_tup, zip( itertools.repeat(circuit, N), itertools.repeat(hardware, N), initial_mappings, ), )) print_statistics("SWAP", best_swap_results, swap_timings) best_swap_bridge_results, swap_bridge_timings = separate_lists( executor.map( using_swap_bridge_strategy_tup, zip( itertools.repeat(circuit, N), itertools.repeat(hardware, N), initial_mappings, ), )) print_statistics("SWAP+Bridge", best_swap_bridge_results, swap_bridge_timings)
def main(): N = 10 hardware = IBMQHardwareArchitecture.load("ibmq_16_melbourne") circuit = add_qubits_to_quantum_circuit( read_benchmark_circuit("sabre", "ising_model_10"), hardware ) initial_mapping = get_random_mapping(circuit) for i in range(1, N): print( f"Swap number {i}: {test_iterated_sabre(circuit, hardware, i, initial_mapping)}" )
def test_iterated_sabre( quantum_circuit: QuantumCircuit, hardware: IBMQHardwareArchitecture, iteration_number: int, initial_mapping: ty.Optional[ty.Dict[Qubit, int]] = None, ) -> int: quantum_circuit = add_qubits_to_quantum_circuit(quantum_circuit, hardware) circuit, mapping = initial_mapping_from_iterative_forward_backward( quantum_circuit, hardware, mapping_algorithm=mapping_algorithm, maximum_mapping_procedure_calls=iteration_number, initial_mapping=initial_mapping, ) return circuit.count_ops().get("swap", 0)
def initial_mapping_from_sabre( quantum_circuit: QuantumCircuit, hardware: IBMQHardwareArchitecture, mapping_algorithm: ty.Callable[ [QuantumCircuit, IBMQHardwareArchitecture, ty.Dict[Qubit, int]], ty.Tuple[QuantumCircuit, ty.Dict[Qubit, int]], ], initial_mapping: ty.Optional[ty.Dict[Qubit, int]] = None, ) -> ty.Dict[Qubit, int]: # First make sure that the quantum circuit has the same number of quantum bits as # the hardware. quantum_circuit = add_qubits_to_quantum_circuit(quantum_circuit, hardware) reversed_quantum_circuit = quantum_circuit.inverse() # Generate a random initial mapping if initial_mapping is None: initial_mapping = get_random_mapping(quantum_circuit) # Performing the forward step forward_circuit, reversed_mapping = mapping_algorithm( quantum_circuit, hardware, initial_mapping) # And the backward step _, final_mapping = mapping_algorithm(reversed_quantum_circuit, hardware, reversed_mapping) return final_mapping
def main(): parser = argparse.ArgumentParser( "Compare the annealing method to pure random.") parser.add_argument( "N", type=int, help= "Number of allowed call to the mapping procedure. Should be strictly " "over 1 (i.e. 2 or more).", ) parser.add_argument("M", type=int, help="Number of repetitions for statistics.") parser.add_argument("Nstep", type=int, help="Steps used to increase N from step " "to N.") parser.add_argument("circuit_name", type=str, help="Name of the quantum circuit to map.") parser.add_argument("hardware", type=str, help="Name of the hardware to consider.") args = parser.parse_args() N = args.N if N <= 1: raise RuntimeError("N should be 2 or more.") M = args.M Nstep = args.Nstep hardware = IBMQHardwareArchitecture.load(args.hardware) circuit = add_qubits_to_quantum_circuit( read_benchmark_circuit("sabre", args.circuit_name), hardware) results = dict() with ProcessPoolExecutor(max_workers=cpu_count()) as executor: N_values = list(range(Nstep, N + 1, Nstep)) print("Computing random...") best_random_results, random_timings = separate_lists_all_values_of_n( executor.map( random_tuple_strategy_results, itertools.repeat([N, circuit, hardware, N_values], M), )) print("Computing SABRE...") best_sabre_results, sabre_timings = separate_lists_all_values_of_n( executor.map( sabre_tuple_strategy_results, itertools.repeat([N, circuit, hardware, N_values], M), )) for i, n in enumerate(N_values): print(f"Computing for {n}:") print("\tannealing...") best_annealing_results, annealing_timings = separate_lists( executor.map( annealing_tuple_strategy_results, itertools.repeat([n, circuit, hardware], M), )) # print_statistics("Annealing", best_annealing_results, annealing_timings) print("\tSABRE-annealing...") best_sabre_annealing_results, sabre_annealing_timings = separate_lists( executor.map( sabre_annealing_tuple_strategy_results, itertools.repeat([n, circuit, hardware], M), )) # print_statistics( # "SABRE + Annealing", # best_sabre_annealing_results, # sabre_annealing_timings, # ) print("\tforward-backward...") ( best_forward_backward_results, forward_backward_timings, ) = separate_lists( executor.map( forward_backward_tuple_strategy_results, itertools.repeat([n, circuit, hardware], M), )) # print_statistics( # "Forward-backward", # best_forward_backward_results, # forward_backward_timings, # ) print("\tforward-backward annealing...") ( best_forward_backward_annealing_results, forward_backward_annealing_timings, ) = separate_lists( executor.map( forward_backward_tuple_strategy_results, itertools.repeat([n, circuit, hardware], M), )) # print_statistics( # "Forward-backward + Annealing", # best_forward_backward_annealing_results, # forward_backward_annealing_timings, # ) print("\tforward-backward neighbour...") ( best_forward_backward_neighbour_results, forward_backward_neighbour_timings, ) = separate_lists( executor.map( forward_backward_neighbour_tuple_strategy_results, itertools.repeat([n, circuit, hardware], M), )) results[n] = { "random": { "results": best_random_results[i], "times": random_timings[i], }, "annealing": { "results": best_annealing_results, "times": annealing_timings, }, "sabre": { "results": best_sabre_results[i], "times": sabre_timings[i] }, "sabre_annealing": { "results": best_sabre_annealing_results, "times": sabre_annealing_timings, }, "iterated": { "results": best_forward_backward_results, "times": forward_backward_timings, }, "iterated_annealing": { "results": best_forward_backward_annealing_results, "times": forward_backward_annealing_timings, }, "iterated_neighbour": { "results": best_forward_backward_neighbour_results, "times": forward_backward_neighbour_timings, }, } with open( f"results-{N}-{Nstep}-{M}-{args.circuit_name}-{args.hardware}.pkl", "wb") as f: pickle.dump(results, f)
def initial_mapping_from_iterative_forward_backward( quantum_circuit: QuantumCircuit, hardware: IBMQHardwareArchitecture, mapping_algorithm: ty.Callable[ [QuantumCircuit, IBMQHardwareArchitecture, ty.Dict[Qubit, int]], ty.Tuple[QuantumCircuit, ty.Dict[Qubit, int]], ], circuit_cost: ty.Callable[ [ty.Dict[Qubit, int], QuantumCircuit, IBMQHardwareArchitecture], float], initial_mapping: ty.Optional[ty.Dict[Qubit, int]] = None, maximum_mapping_procedure_calls: int = 20, ) -> ty.Tuple[ty.Dict[Qubit, int], float, int]: """Implementation of the initial_mapping method used by SABRE. :param quantum_circuit: the quantum circuit we want to find an initial mapping for :param hardware: the target hardware specifications :param mapping_algorithm: the algorithm used to map a quantum circuit to the given hardware with the given initial mapping. :param circuit_cost: a function computing the cost of a circuit. By default, the cost is the number of SWAP gates. :param initial_mapping: starting point of the algorithm. Default to a random guess. :param maximum_mapping_procedure_calls: the maximum number of calls to the mapping procedure. If the algorithm converged before, a lower number of evaluations will be performed. :return: the initial mapping, the cost of this mapping and the number of calls to the provided mapping procedure performed. """ if maximum_mapping_procedure_calls < 2: raise RuntimeError( "You should do at least 1 iteration (2 calls to the mapping procedure)!" ) # First make sure that the quantum circuit has the same number of quantum bits as # the hardware. quantum_circuit = add_qubits_to_quantum_circuit(quantum_circuit, hardware) reversed_quantum_circuit = quantum_circuit.inverse() # Generate a random initial mapping if initial_mapping is None: initial_mapping = get_random_mapping(quantum_circuit) # And improve this initial mapping according to an iterated method inspired from # SABRE. costs = list() mappings: ty.List[ty.Dict[Qubit, int]] = [initial_mapping] # We apply the forward-backward approach forward_mapping = initial_mapping for i in range(maximum_mapping_procedure_calls // 2): # Performing the forward step forward_circuit, reversed_mapping = mapping_algorithm( quantum_circuit, hardware, forward_mapping) # And the backward step _, forward_mapping = mapping_algorithm(reversed_quantum_circuit, hardware, reversed_mapping) # Adding the cost of the mapping to the list costs.append(circuit_cost(forward_mapping, quantum_circuit, hardware)) # Adding the current mapping to the list in case we will do more iterations. mappings.append(forward_mapping) # If there is a repetition or we have a cost of 0, we can stop here. if costs[-1] == 0 or _is_fixed_point(costs): break current_calls_to_mapping_procedure = 2 * (i + 1) # If we finished the allowed number of iterations, return the best result. best_mapping_index = _argmin(costs) return ( mappings[best_mapping_index], costs[best_mapping_index], current_calls_to_mapping_procedure + 1, )
def main(): parser = argparse.ArgumentParser( "Compare the annealing method to pure random.") parser.add_argument( "N", type=int, help= "Number of allowed call to the mapping procedure. Should be strictly " "over 1 (i.e. 2 or more).", ) parser.add_argument("M", type=int, help="Number of repetitions for statistics.") parser.add_argument("Nstep", type=int, help="Steps used to increase N from step to N.") parser.add_argument("circuit_name", type=str, help="Name of the quantum circuit to map.") parser.add_argument("hardware", type=str, help="Name of the hardware to consider.") args = parser.parse_args() N = args.N if N <= 1: raise RuntimeError("N should be 2 or more.") M = args.M Nstep = args.Nstep hardware = IBMQHardwareArchitecture.load(args.hardware) circuit = add_qubits_to_quantum_circuit( read_benchmark_circuit("sabre", args.circuit_name), hardware) results = dict() N_values = list(range(Nstep, N + 1, Nstep)) with ProcessPoolExecutor(max_workers=cpu_count()) as executor: for i, n in enumerate(N_values): print(f"Computing for {n}:") print("\tRandom...") best_random_results = list( executor.map( random_tuple_strategy_results, itertools.repeat([circuit, hardware, n], M), )) print("\tSABRE...") best_sabre_results = list( executor.map( sabre_tuple_strategy_results, itertools.repeat([circuit, hardware, n], M), )) print("\tAnnealing random...") best_annealing_random_results = list( executor.map( annealing_random_tuple_strategy_results, itertools.repeat([circuit, hardware, n], M), )) print("\tAnnealing hardware...") best_annealing_hardware_results = list( executor.map( annealing_hardware_aware_tuple_strategy_results, itertools.repeat([circuit, hardware, n], M), )) results[n] = { "random": { "results": deepcopy(best_random_results) }, "annealing_random": { "results": deepcopy(best_annealing_random_results) }, "sabre": { "results": deepcopy(best_sabre_results) }, "annealing_hardware": { "results": deepcopy(best_annealing_hardware_results) }, } print( f"Saving to results-{N}-{Nstep}-{M}-{args.circuit_name}-{args.hardware}.pkl" ) with open( f"results-{N}-{Nstep}-{M}-{args.circuit_name}-{args.hardware}.pkl", "wb") as f: pickle.dump(results, f)