Beispiel #1
0
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)
Beispiel #2
0
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)}"
        )
Beispiel #3
0
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)
Beispiel #4
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)
Beispiel #6
0
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)