コード例 #1
0
def test_get_string():
    with patch('pyquil.api.QVMConnection') as cxn:
        cxn.run_and_measure.return_value = [[1] * 10]
        qaoa = QAOA(cxn, [0])
        prog = Program()
        prog.inst(X(0))
        qaoa.get_parameterized_program = lambda: lambda angles: prog
        samples = 10
        bitstring, freq = qaoa.get_string(betas=None,
                                          gammas=None,
                                          samples=samples)
        assert len(freq) <= samples
        assert bitstring[0] == 1

    with patch('pyquil.api.QVMConnection') as cxn:
        cxn.run_and_measure.return_value = [[0, 1] * 10]
        qaoa = QAOA(qvm=cxn, qubits=[0, 1], embedding={0: 1, 1: 0})
        prog = Program()
        prog.inst(X(0))
        qaoa.get_parameterized_program = lambda: lambda angles: prog
        samples = 10
        bitstring, freq = qaoa.get_string(betas=None,
                                          gammas=None,
                                          samples=samples)
        assert len(freq) <= samples
        assert bitstring[0:2] == (1, 0)
コード例 #2
0
ファイル: test_qaoa.py プロジェクト: stevenheidel/grove
def test_get_string():
    qvm = qvm_module.SyncConnection()
    qaoa = QAOA(qvm, n_qubits=1)
    prog = Program()
    prog.inst(X(0))
    qaoa.get_parameterized_program = lambda: lambda angles: prog
    samples = 10
    bitstring, freq = qaoa.get_string(betas=None, gammas=None, samples=samples)
    assert len(freq) <= samples
    assert bitstring[0] == 1
コード例 #3
0
def solve_tsp(weights,
              connection=None,
              steps=1,
              ftol=1.0e-4,
              xtol=1.0e-4,
              samples=1000):
    """
    Method for solving travelling salesman problem.
    :param weights: Weight matrix. weights[i, j] = cost to traverse edge from city i to j. If
        weights[i, j] = 0, then no edge exists between cities i and j.
    :param connection: (Optional) connection to the QVM. Default is None.
    :param steps: (Optional. Default=1) Trotterization order for the QAOA algorithm.
    :param ftol: (Optional. Default=1.0e-4) ftol parameter for the Nelder-Mead optimizer
    :param xtol: (Optional. Default=1.0e-4) xtol parameter for the Nelder-Mead optimizer
    :param samples: (Optional. Default=1000) number of bit string samples to use.
    """
    if connection is None:
        connection = api.QVMConnection()
    number_of_cities = weights.shape[0]
    list_of_qubits = list(range(number_of_cities**2))
    number_of_qubits = len(list_of_qubits)
    cost_operators = create_cost_hamiltonian(weights)
    driver_operators = create_mixer_operators(number_of_cities)
    initial_state_program = create_initial_state_program(number_of_cities)

    minimizer_kwargs = {
        'method': 'Nelder-Mead',
        'options': {
            'ftol': ftol,
            'xtol': xtol,
            'disp': False
        }
    }

    vqe_option = {'disp': print_fun, 'return_all': True, 'samples': None}

    qaoa_inst = QAOA(connection,
                     list_of_qubits,
                     steps=steps,
                     cost_ham=cost_operators,
                     ref_ham=driver_operators,
                     driver_ref=initial_state_program,
                     store_basis=True,
                     minimizer=scipy.optimize.minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     vqe_options=vqe_option)

    betas, gammas = qaoa_inst.get_angles()
    most_frequent_string, _ = qaoa_inst.get_string(betas,
                                                   gammas,
                                                   samples=samples)
    solution = binary_state_to_points_order(most_frequent_string)
    return solution
コード例 #4
0
def test_get_string():
    with patch('pyquil.api.QuantumComputer') as qc:
        qc.run.return_value = [[1] * 10]
        qaoa = QAOA(qc, [0])
        prog = Program()
        prog.inst(X(0))
        qaoa.get_parameterized_program = lambda: lambda angles: prog
        samples = 10
        bitstring, freq = qaoa.get_string(betas=None,
                                          gammas=None,
                                          samples=samples)
        assert len(freq) <= samples
        assert bitstring[0] == 1
コード例 #5
0
ファイル: tsp_qaoa.py プロジェクト: mstechly/grove
def solve_tsp(nodes_array, connection=None, steps=3, ftol=1.0e-4, xtol=1.0e-4):
    """
    Method for solving travelling salesman problem.

    :param nodes_array: An array of points, which represent coordinates of the cities.
    :param connection: (Optional) connection to the QVM. Default is None.
    :param steps: (Optional. Default=1) Trotterization order for the QAOA algorithm.
    :param ftol: (Optional. Default=1.0e-4) ftol parameter for the Nelder-Mead optimizer
    :param xtol: (Optional. Default=1.0e-4) xtol parameter for the Nelder-Mead optimizer
    """
    if connection is None:
        connection = api.QVMConnection()
    list_of_qubits = list(range(len(nodes_array)**2))
    number_of_qubits = len(list_of_qubits)
    cost_operators = create_cost_hamiltonian(nodes_array)
    driver_operators = create_mixer_operators(len(nodes_array))
    initial_state_program = create_initial_state_program(len(nodes_array))

    minimizer_kwargs = {
        'method': 'Nelder-Mead',
        'options': {
            'ftol': ftol,
            'xtol': xtol,
            'disp': False
        }
    }

    vqe_option = {'disp': print_fun, 'return_all': True, 'samples': None}

    qaoa_inst = QAOA(connection,
                     number_of_qubits,
                     steps=steps,
                     cost_ham=cost_operators,
                     ref_hamiltonian=driver_operators,
                     driver_ref=initial_state_program,
                     store_basis=True,
                     minimizer=scipy.optimize.minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     vqe_options=vqe_option)

    betas, gammas = qaoa_inst.get_angles()
    most_frequent_string, _ = qaoa_inst.get_string(betas, gammas, samples=1000)
    solution = binary_state_to_points_order(most_frequent_string)
    return solution
コード例 #6
0
class ForestTSPSolver(object):
    """docstring for TSPSolver"""
    def __init__(self, nodes_array, steps=3, ftol=1.0e-4, xtol=1.0e-4, all_ones_coefficient=-2):
        self.nodes_array = nodes_array
        self.qvm = api.QVMConnection()
        self.steps = steps
        self.ftol = ftol
        self.xtol = xtol
        self.betas = None
        self.gammas = None
        self.qaoa_inst = None
        self.most_freq_string = None
        self.number_of_qubits = self.get_number_of_qubits()
        self.all_ones_coefficient = all_ones_coefficient

        cost_operators = self.create_cost_operators()
        driver_operators = self.create_driver_operators()

        minimizer_kwargs = {'method': 'Nelder-Mead',
                                'options': {'ftol': self.ftol, 'xtol': self.xtol,
                                            'disp': False}}

        vqe_option = {'disp': print_fun, 'return_all': True,
                      'samples': None}
        self.qaoa_inst = QAOA(self.qvm, list(range(self.number_of_qubits)), steps=self.steps, cost_ham=cost_operators,
                         ref_hamiltonian=driver_operators, store_basis=True,
                         minimizer=scipy.optimize.minimize,
                         minimizer_kwargs=minimizer_kwargs,
                         vqe_options=vqe_option)
        
    def solve_tsp(self):
        self.find_angles()
        return self.get_solution()

    def find_angles(self):
        self.betas, self.gammas = self.qaoa_inst.get_angles()
        return self.betas, self.gammas

    def get_results(self):
        most_freq_string, sampling_results = self.qaoa_inst.get_string(self.betas, self.gammas, samples=10000)
        self.most_freq_string = most_freq_string
        return sampling_results.most_common()

    def get_solution(self):
        if self.most_freq_string is None:
            self.most_freq_string, sampling_results = self.qaoa_inst.get_string(self.betas, self.gammas, samples=10000)
        quantum_order = TSP_utilities.binary_state_to_points_order_full(self.most_freq_string)
        return quantum_order

    def create_cost_operators(self):
        cost_operators = []
        cost_operators += self.create_weights_cost_operators()

        # 0 - 1: 3
        # 0 - 2: 4
        # 1 - 2: 5
        # cost_operators += self.create_single_weight_operator(0, 1, 3)
        # cost_operators += self.create_single_weight_operator(0, 2, 4)
        # cost_operators += self.create_single_weight_operator(1, 2, 5)
        # cost_operators += self.create_single_z_operator(0, 1, 0, 3)
        # cost_operators += self.create_single_z_operator(0, 1, 0, -3)

        cost_operators += self.create_penalty_operators_for_bilocation()
        cost_operators += self.create_penalty_operators_for_repetition()

        return cost_operators

    def create_single_weight_operator(self, i, j, weight):
        # Positive weights makes given connection more probable.
        cost_operators = []
        for t in [0, 1]:
            cost_operators.append(create_single_z_operator(i, j, t, weight))
            # qubit_1 = t * number_of_nodes + i
            # qubit_2 = (t + 1) * number_of_nodes + j
            # # cost_operators.append(PauliTerm("I", 0, weight) - PauliTerm("Z", qubit_1, weight) * PauliTerm("Z", qubit_2))
            # cost_operators.append(PauliTerm("Z", qubit_1, weight) * PauliTerm("Z", qubit_2) - PauliTerm("I", 0, weight))
            # cost_operators.append(PauliTerm("Z", qubit_2, weight) * PauliTerm("Z", qubit_1) - PauliTerm("I", 0, weight))
        return cost_operators

    def create_single_z_operator(self, i, j, t, weight):
        number_of_nodes = 3
        qubit_1 = t * number_of_nodes + i
        qubit_2 = (t + 1) * number_of_nodes + j
        cost_operators = []
        cost_operators.append(PauliTerm("Z", qubit_1, weight) * PauliTerm("Z", qubit_2) - PauliTerm("I", 0, weight))
        cost_operators.append(PauliTerm("Z", qubit_2, weight) * PauliTerm("Z", qubit_1) - PauliTerm("I", 0, weight))
        return cost_operators

    def create_penalty_operators_for_bilocation(self):
        # Additional cost for visiting more than one node in given time t
        cost_operators = []
        number_of_nodes = len(self.nodes_array)
        for t in range(number_of_nodes):
            range_of_qubits = list(range(t * number_of_nodes, (t + 1) * number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(range_of_qubits)
        return cost_operators

    def create_penalty_operators_for_repetition(self):
        # Additional cost for visiting given node more than one time
        cost_operators = []
        number_of_nodes = len(self.nodes_array)
        for i in range(number_of_nodes):
            range_of_qubits = list(range(i, number_of_nodes**2, number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(range_of_qubits)
        return cost_operators

    def create_penalty_operators_for_qubit_range(self, range_of_qubits):
        cost_operators = []
        tsp_matrix = TSP_utilities.get_tsp_matrix(self.nodes_array)
        weight = -10 * np.max(tsp_matrix)
        # weight = -0.5
        for i in range_of_qubits:
            if i == range_of_qubits[0]:
                z_term = PauliTerm("Z", i, weight)
                all_ones_term = PauliTerm("I", 0, 0.5 * weight) - PauliTerm("Z", i, 0.5 * weight)
            else:
                z_term = z_term * PauliTerm("Z", i)
                all_ones_term = all_ones_term * (PauliTerm("I", 0, 0.5) - PauliTerm("Z", i, 0.5))

        z_term = PauliSum([z_term])
        cost_operators.append(PauliTerm("I", 0, weight) - z_term + self.all_ones_coefficient * all_ones_term)

        return cost_operators

    def create_weights_cost_operators(self):
        cost_operators = []
        number_of_nodes = len(self.nodes_array)
        tsp_matrix = TSP_utilities.get_tsp_matrix(self.nodes_array)
        
        for i in range(number_of_nodes):
            for j in range(i, number_of_nodes):
                for t in range(number_of_nodes - 1):
                    weight = -tsp_matrix[i][j] / 2
                    if tsp_matrix[i][j] != 0:
                        qubit_1 = t * number_of_nodes + i
                        qubit_2 = (t + 1) * number_of_nodes + j
                        cost_operators.append(PauliTerm("I", 0, weight) - PauliTerm("Z", qubit_1, weight) * PauliTerm("Z", qubit_2))

        return cost_operators

    def create_driver_operators(self):
        driver_operators = []
        
        for i in range(self.number_of_qubits):
            driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

        return driver_operators

    def get_number_of_qubits(self):
        return len(self.nodes_array)**2
コード例 #7
0
                     init_betas=initial_betas,
                     init_gammas=initial_gammas,
                     minimizer_kwargs=minimizer_kwargs)

    # calculating angles using VQE (simulation)
    betas, gammas = QAOA_inst.get_angles()
    print(" p =", i)
    print("Values of betas:", betas)
    print("Values of gammas:", gammas, '\n')

    # setting next angles
    initial_betas = next_angles(betas)
    initial_gammas = next_angles(gammas)

    print("Values of betas ansatz:", initial_betas)
    print("Values of gammas ansatz:", initial_gammas, '\n')

# resulting bitstrings and the most common one
print("\nAnd the most common measurement is... ")
most_common_bitstring, results_counter = QAOA_inst.get_string(
    betas, gammas, samples=n_samples)

print(most_common_bitstring)
print(tuple(qubits))

# graphing distribution
results.counter_histogram(results_counter, title=" p = " + str(p))

# timing final
end = time.time()
print("Time ", end - start)
コード例 #8
0
def interp_pyquil(G,
                  p,
                  initial_betas=0.8,
                  initial_gammas=0.35,
                  minimizer_kwargs={
                      'method': 'BFGS',
                      'options': {
                          'ftol': 1.0e-2,
                          'xtol': 1.0e-2,
                          'disp': True
                      }
                  },
                  n_samples=1024,
                  plot_hist=False):
    # timing start
    start = time.time()

    # setting up connection with the QVM
    qvm_connection = api.QVMConnection()

    # labelling qubits according to graph
    qubits = [int(n) for n in list(G.nodes)]

    # turning unweighted graphs into weighted graph with weight wij = 1
    for (u, v) in G.edges():
        if G.edges[u, v] == {}:
            G.edges[u, v]['weight'] = 1

    # constructing cost and mixer hamiltonian elements in list
    # N.B. This algorithm only works if all the terms in the cost Hamiltonian commute with each other.
    Hc = hamiltonians.cost(G)
    Hb = hamiltonians.mixer(G)

    # saving data from all layers
    results = {}

    # Entering Loop
    if type(initial_betas) == float:
        p_start = 1
    else:
        p_start = len(initial_betas)

    for i in range(p_start, p + 1):
        QAOA_inst = QAOA(qvm_connection,
                         qubits,
                         steps=i,
                         cost_ham=Hc,
                         ref_ham=Hb,
                         init_betas=initial_betas,
                         init_gammas=initial_gammas,
                         minimizer_kwargs=minimizer_kwargs,
                         vqe_options={
                             'disp': print,
                             'return_all': True
                         })

        # calculating angles using VQE (simulation)
        print(" p =", i)
        betas, gammas = QAOA_inst.get_angles()
        print("Values of betas:", betas)
        print("Values of gammas:", gammas, '\n')

        # resulting bitstrings and the most common one
        print("\nAnd the most common measurement is... ")
        most_common_bitstring, results_counter = QAOA_inst.get_string(
            betas, gammas, samples=n_samples)

        print(most_common_bitstring)
        print(tuple(qubits))

        # graphing distribution
        if plot_hist:
            counter_histogram(results_counter, title=" p = " + str(p))

        # timing final
        end = time.time()
        print("Time ", end - start)

        # saving data from this layer
        results_i = {}
        results_i['p'] = i
        results_i['time'] = end - start
        results_i['graph'] = nx.to_dict_of_dicts(G)
        results_i['n_nodes'] = len(G.nodes)
        results_i['n_edges'] = len(G.edges)
        results_i['most_common_bistring'] = most_common_bitstring
        results_i['qubit_order'] = tuple(qubits)
        results_i['counter'] = dict(results_counter)
        results_i['n_Fp_evals'] = len(QAOA_inst.result['expectation_vals'])
        results_i['n_samples'] = n_samples
        results_i['Fp'] = -QAOA_inst.result['fun']
        results_i['Fp_sampled'] = sum([
            objective_value(G, k, qubits) * v
            for k, v in results_counter.items()
        ]) / n_samples
        results_i['cutvalue_best'] = cutvalue_best(G, results_counter, qubits)
        results_i['cutvalue_most_sampled'] = cutvalue_most_sampled(
            G, results_counter, qubits)
        results_i['minimizer_method'] = minimizer_kwargs['method']

        # time symmetry
        if all(gammas > 0) and all(betas > 0):
            results_i['gammas'] = gammas.copy()
            results_i['betas'] = betas.copy()
        else:
            results_i['gammas'] = -gammas.copy()
            results_i['betas'] = -betas.copy()

        results[i] = results_i

        # setting next angles
        initial_betas = next_angles(betas)
        initial_gammas = next_angles(gammas)

        print("Values of betas ansatz:", initial_betas)
        print("Values of gammas ansatz:", initial_gammas, '\n')

    return results
コード例 #9
0
def ising(
    h: List[int],
    J: Dict[Tuple[int, int], int],
    num_steps: int = 0,
    verbose: bool = True,
    rand_seed: int = None,
    connection: QuantumComputer = None,
    samples: int = None,
    initial_beta: List[float] = None,
    initial_gamma: List[float] = None,
    minimizer_kwargs: Dict[str, Any] = None,
    vqe_option: Dict[str, Union[bool, int]] = None
) -> Tuple[List[int], Union[int, float], Program]:
    """
    Ising set up method

    :param h: External magnetic term of the Ising problem.
    :param J: Interaction term of the Ising problem.
    :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the
                  QAOA algorithm.
    :param verbose: (Optional.Default=True) Verbosity of the code.
    :param rand_seed: (Optional. Default=None) random seed when beta and
                      gamma angles are not provided.
    :param connection: (Optional) connection to the QVM. Default is None.
    :param samples: (Optional. Default=None) VQE option. Number of samples
                    (circuit preparation and measurement) to use in operator
                    averaging.
    :param initial_beta: (Optional. Default=None) Initial guess for beta
                         parameters.
    :param initial_gamma: (Optional. Default=None) Initial guess for gamma
                          parameters.
    :param minimizer_kwargs: (Optional. Default=None). Minimizer optional
                             arguments.  If None set to
                             {'method': 'Nelder-Mead',
                             'options': {'fatol': 1.0e-2, 'xatol': 1.0e-2,
                                        'disp': False}
    :param vqe_option: (Optional. Default=None). VQE optional
                             arguments.  If None set to
                       vqe_option = {'disp': print_fun, 'return_all': True,
                       'samples': samples}
    :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result.
    """
    if num_steps == 0:
        num_steps = 2 * len(h)

    n_nodes = len(h)

    cost_operators = []
    driver_operators = []
    for i, j in J.keys():
        cost_operators.append(
            PauliSum([PauliTerm("Z", i, J[(i, j)]) * PauliTerm("Z", j)]))

    for i in range(n_nodes):
        cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])]))
        driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

    if connection is None:
        qubits = list(sum(J.keys(), ()))
        connection = get_qc(f"{len(qubits)}q-qvm")

    if minimizer_kwargs is None:
        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'fatol': 1.0e-2,
                'xatol': 1.0e-2,
                'disp': False
            }
        }
    if vqe_option is None:
        vqe_option = {'disp': print, 'return_all': True, 'samples': samples}

    if not verbose:
        vqe_option['disp'] = None

    qaoa_inst = QAOA(connection,
                     list(range(n_nodes)),
                     steps=num_steps,
                     init_betas=initial_beta,
                     init_gammas=initial_gamma,
                     cost_ham=cost_operators,
                     ref_ham=driver_operators,
                     minimizer=minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     rand_seed=rand_seed,
                     vqe_options=vqe_option,
                     store_basis=True)

    betas, gammas = qaoa_inst.get_angles()
    most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas)
    most_freq_string_ising = [ising_trans(it) for it in most_freq_string]
    energy_ising = energy_value(h, J, most_freq_string_ising)
    param_prog = qaoa_inst.get_parameterized_program()
    circuit = param_prog(np.hstack((betas, gammas)))

    return most_freq_string_ising, energy_ising, circuit
コード例 #10
0
def ising(h,
          J,
          num_steps=0,
          verbose=True,
          rand_seed=None,
          connection=None,
          samples=None,
          initial_beta=None,
          initial_gamma=None,
          minimizer_kwargs=None,
          vqe_option=None):
    """
    Ising set up method

    :param h: External magnectic term of the Ising problem. List.
    :param J: Interaction term of the Ising problem. Dictionary.
    :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the
                  QAOA algorithm.
    :param verbose: (Optional.Default=True) Verbosity of the code.
    :param rand_seed: (Optional. Default=None) random seed when beta and
                      gamma angles are not provided.
    :param connection: (Optional) connection to the QVM. Default is None.
    :param samples: (Optional. Default=None) VQE option. Number of samples
                    (circuit preparation and measurement) to use in operator
                    averaging.
    :param initial_beta: (Optional. Default=None) Initial guess for beta
                         parameters.
    :param initial_gamma: (Optional. Default=None) Initial guess for gamma
                          parameters.
    :param minimizer_kwargs: (Optional. Default=None). Minimizer optional
                             arguments.  If None set to
                             {'method': 'Nelder-Mead',
                             'options': {'ftol': 1.0e-2, 'xtol': 1.0e-2,
                                        'disp': False}
    :param vqe_option: (Optional. Default=None). VQE optional
                             arguments.  If None set to
                       vqe_option = {'disp': print_fun, 'return_all': True,
                       'samples': samples}
    :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result.
    :rtype: List, Integer or float, 'pyquil.quil.Program'.

    """
    if num_steps == 0:
        num_steps = 2 * len(h)

    n_nodes = len(h)

    cost_operators = []
    driver_operators = []
    for i, j in J.keys():
        cost_operators.append(
            PauliSum([PauliTerm("Z", i, J[(i, j)]) * PauliTerm("Z", j)]))

    for i in range(n_nodes):
        cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])]))

    for i in range(n_nodes):
        driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

    if connection is None:
        connection = CXN

    if minimizer_kwargs is None:
        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'ftol': 1.0e-2,
                'xtol': 1.0e-2,
                'disp': False
            }
        }
    if vqe_option is None:
        vqe_option = {
            'disp': print_fun,
            'return_all': True,
            'samples': samples
        }

    if not verbose:
        vqe_option['disp'] = None

    qaoa_inst = QAOA(connection,
                     range(n_nodes),
                     steps=num_steps,
                     cost_ham=cost_operators,
                     ref_hamiltonian=driver_operators,
                     store_basis=True,
                     rand_seed=rand_seed,
                     init_betas=initial_beta,
                     init_gammas=initial_gamma,
                     minimizer=minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     vqe_options=vqe_option)

    betas, gammas = qaoa_inst.get_angles()
    most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas)
    most_freq_string_ising = [ising_trans(it) for it in most_freq_string]
    energy_ising = energy_value(h, J, most_freq_string_ising)
    param_prog = qaoa_inst.get_parameterized_program()
    circuit = param_prog(np.hstack((betas, gammas)))

    return most_freq_string_ising, energy_ising, circuit
コード例 #11
0
ファイル: experiment.py プロジェクト: soosub/bachelor-thesis
def run_experiment(G,
                   p,
                   optimizer,
                   qvm_connection,
                   n_shots=8192,
                   print_result=False):
    '''Runs (pyQuil) QAOA experiment with the given parameters
    Inputs

    Returns a dictionary with the following keys (some are yet to be implemented)
    - energy
    - time (in seconds)
    - iterations
    - max-cut objective
    - solution
    - solution objective

    '''
    n = len(G)  # number of nodes / qubits
    qubits = [int(n) for n in list(G.nodes)]  # list of qubits

    # constructing cost and mixer hamiltonian elements in list
    Hc = hamiltonians.cost(G)
    Hb = hamiltonians.mixer(G)

    # setting up the QAOA circuit
    initial_beta = 0
    initial_gamma = 0

    QAOA_inst = QAOA(qvm_connection,
                     qubits,
                     steps=p,
                     cost_ham=Hc,
                     ref_ham=Hb,
                     init_betas=initial_beta,
                     init_gammas=initial_gamma,
                     minimizer_kwargs=optimizer,
                     vqe_options={'samples': n_shots})

    # calculating angles using VQE (simulation)
    angles = QAOA_inst.get_angles()
    betas, gammas = angles

    # resulting bitstrings and the most common one
    most_common_bitstring, results_counter = QAOA_inst.get_string(
        betas, gammas, samples=n_shots)

    # Results
    energy = None
    time = None
    iterations = None
    objective = None
    solution = most_common_bitstring
    solution_objective = None
    distribution = results_counter
    angles = angles

    if print_result:
        print('energy:', energy)
        print('time:', time, 's')
        print('max-cut objective:', objective)
        print('solution:', solution)
        print('solution objective:', solution_objective)
        print('anlges:', angles)

    return {
        'energy': energy,
        'time': time,
        'iterations': iterations,
        'max-cut objective': objective,
        'solution': solution,
        'solution objective': solution_objective,
        'distribution': distribution,
        'angles': angles
    }
コード例 #12
0
class ForestTSPSolver(object):
    """
    Class for solving Travelling Salesman Problem (with starting point) using Forest - quantum computing library.
    It uses QAOA method with operators as described in the following paper:
    https://arxiv.org/pdf/1709.03489.pdf by Stuart Hadfield et al.
    
    """
    def __init__(self, distance_matrix, steps=2, ftol=1.0e-3, xtol=1.0e-3, initial_state="all", starting_node=0):

        self.distance_matrix = distance_matrix
        self.starting_node = starting_node
        # Since we fixed the starting city, the effective number of nodes is smaller by 1
        self.reduced_number_of_nodes = len(self.distance_matrix) - 1
        self.qvm = api.QVMConnection()
        self.steps = steps
        self.ftol = ftol
        self.xtol = xtol
        self.betas = None
        self.gammas = None
        self.qaoa_inst = None
        self.number_of_qubits = self.get_number_of_qubits()
        self.solution = None
        self.distribution = None

        cost_operators = self.create_phase_separator()
        driver_operators = self.create_mixer()
        initial_state_program = self.create_initial_state_program(initial_state)

        minimizer_kwargs = {'method': 'Nelder-Mead',
                                'options': {'ftol': self.ftol, 'xtol': self.xtol,
                                            'disp': False}}

        vqe_option = {'disp': print_fun, 'return_all': True,
                      'samples': None}

        self.qaoa_inst = QAOA(self.qvm, self.number_of_qubits, steps=self.steps, cost_ham=cost_operators,
                         ref_hamiltonian=driver_operators, driver_ref=initial_state_program, store_basis=True,
                         minimizer=scipy.optimize.minimize,
                         minimizer_kwargs=minimizer_kwargs,
                         vqe_options=vqe_option)
        
    def solve_tsp(self):
        """
        Calculates the optimal angles (betas and gammas) for the QAOA algorithm 
        and returns a list containing the order of nodes.
        """
        self.find_angles()
        self.calculate_solution()
        return self.solution, self.distribution

    def find_angles(self):
        """
        Runs the QAOA algorithm for finding the optimal angles.
        """
        self.betas, self.gammas = self.qaoa_inst.get_angles()
        return self.betas, self.gammas

    def calculate_solution(self):
        """
        Samples the QVM for the results of the algorithm 
        and returns a list containing the order of nodes.
        """
        most_frequent_string, sampling_results = self.qaoa_inst.get_string(self.betas, self.gammas, samples=10000)
        reduced_solution = TSP_utilities.binary_state_to_points_order(most_frequent_string)
        full_solution = self.get_solution_for_full_array(reduced_solution)
        self.solution = full_solution
        
        all_solutions = sampling_results.keys()
        distribution = {}
        for sol in all_solutions:
            reduced_sol = TSP_utilities.binary_state_to_points_order(sol)
            full_sol = self.get_solution_for_full_array(reduced_sol)
            distribution[tuple(full_sol)] = sampling_results[sol]
        self.distribution = distribution

    def get_solution_for_full_array(self, reduced_solution):
        """
        Transforms the solution from its reduced version to the full initial version.
        """
        full_solution = reduced_solution
        for i in range(len(full_solution)):
            if full_solution[i] >= self.starting_node:
                full_solution[i] += 1
        full_solution.insert(0, self.starting_node)
        return full_solution

    def create_phase_separator(self):
        """
        Creates phase-separation operators, which depend on the objective function.
        """
        cost_operators = []
        reduced_distance_matrix = np.delete(self.distance_matrix, self.starting_node, axis=0)
        reduced_distance_matrix = np.delete(reduced_distance_matrix, self.starting_node, axis=1)
        for t in range(self.reduced_number_of_nodes - 1):
            for city_1 in range(self.reduced_number_of_nodes):
                for city_2 in range(self.reduced_number_of_nodes):
                    if city_1 != city_2:
                        distance = reduced_distance_matrix[city_1, city_2]
                        qubit_1 = t * (self.reduced_number_of_nodes) + city_1
                        qubit_2 = (t + 1) * (self.reduced_number_of_nodes) + city_2
                        cost_operators.append(PauliTerm("Z", qubit_1, distance) * PauliTerm("Z", qubit_2))

        costs_to_starting_node = np.delete(self.distance_matrix[:, self.starting_node], self.starting_node)

        for city in range(self.reduced_number_of_nodes):
            distance_from_0 = -costs_to_starting_node[city]
            qubit = city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        for city in range(self.reduced_number_of_nodes):
            distance_from_0 = -costs_to_starting_node[city]
            qubit = self.number_of_qubits - (self.reduced_number_of_nodes) + city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))
 

        phase_separator = [PauliSum(cost_operators)]
        return phase_separator

    def create_mixer(self):
        """
        Creates mixing operators, which depend on the structure of the problem.
        Indexing comes directly from 4.1.2 from the  https://arxiv.org/pdf/1709.03489.pdf article, 
        equations 54 - 58.
        """
        mixer_operators = []

        for t in range(self.reduced_number_of_nodes - 1):
            for city_1 in range(self.reduced_number_of_nodes):
                for city_2 in range(self.reduced_number_of_nodes):
                    i = t
                    u = city_1
                    v = city_2
                    first_part = 1
                    first_part *= self.s_plus(u, i)
                    first_part *= self.s_plus(v, i+1)
                    first_part *= self.s_minus(u, i+1)
                    first_part *= self.s_minus(v, i)

                    second_part = 1
                    second_part *= self.s_minus(u, i)
                    second_part *= self.s_minus(v, i+1)
                    second_part *= self.s_plus(u, i+1)
                    second_part *= self.s_plus(v, i)
                    mixer_operators.append(first_part + second_part)
        return mixer_operators

    def create_initial_state_program(self, initial_state):
        """
        Creates a pyquil program representing the initial state for the QAOA.
        As an argument it takes either a list with order of the cities, or 
        a string "all". In the second case the initial state is superposition
        of all possible states for this problem.
        """
        initial_state_program = pq.Program()
        if type(initial_state) is list:
            for i in range(self.reduced_number_of_nodes):
                initial_state_program.inst(X(i * (self.reduced_number_of_nodes) + initial_state[i]))

        elif initial_state == "all":
            vector_of_states = np.zeros(2**self.number_of_qubits)
            list_of_possible_states = []
            initial_order = range(0, self.reduced_number_of_nodes)
            all_permutations = [list(x) for x in itertools.permutations(initial_order)]
            for permutation in all_permutations:
                coding_of_permutation = 0
                for i in range(len(permutation)):
                    coding_of_permutation += 2**(i * (self.reduced_number_of_nodes) + permutation[i])
                vector_of_states[coding_of_permutation] = 1
            initial_state_program = create_arbitrary_state(vector_of_states)

        return initial_state_program

    def get_number_of_qubits(self):
        return (self.reduced_number_of_nodes)**2

    def s_plus(self, city, time):
        qubit = time * (self.reduced_number_of_nodes) + city
        return PauliTerm("X", qubit) + PauliTerm("Y", qubit, 1j)

    def s_minus(self, city, time):
        qubit = time * (self.reduced_number_of_nodes) + city
        return PauliTerm("X", qubit) - PauliTerm("Y", qubit, 1j)
コード例 #13
0
class ForestTSPSolver(object):
    """docstring for TSPSolver"""
    def __init__(self,
                 full_nodes_array,
                 steps=3,
                 ftol=1.0e-4,
                 xtol=1.0e-4,
                 initial_state="all",
                 starting_node=0):
        reduced_nodes_array, costs_to_starting_node = self.reduce_nodes_array(
            full_nodes_array, starting_node)

        self.nodes_array = reduced_nodes_array
        self.starting_node = starting_node
        self.full_nodes_array = full_nodes_array
        self.costs_to_starting_node = costs_to_starting_node
        self.qvm = api.QVMConnection()
        self.steps = steps
        self.ftol = ftol
        self.xtol = xtol
        self.betas = None
        self.gammas = None
        self.qaoa_inst = None
        self.most_freq_string = None
        self.number_of_qubits = self.get_number_of_qubits()
        self.initial_state = initial_state

        cost_operators = self.create_phase_separator()
        driver_operators = self.create_mixer()
        initial_state_program = self.create_initial_state_program()

        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'ftol': self.ftol,
                'xtol': self.xtol,
                'disp': False
            }
        }

        vqe_option = {'disp': print_fun, 'return_all': True, 'samples': None}

        self.qaoa_inst = QAOA(self.qvm,
                              list(range(self.number_of_qubits)),
                              steps=self.steps,
                              cost_ham=cost_operators,
                              ref_hamiltonian=driver_operators,
                              driver_ref=initial_state_program,
                              store_basis=True,
                              minimizer=scipy.optimize.minimize,
                              minimizer_kwargs=minimizer_kwargs,
                              vqe_options=vqe_option)

    def solve_tsp(self):
        self.find_angles()
        return self.get_solution()

    def find_angles(self):
        self.betas, self.gammas = self.qaoa_inst.get_angles()
        return self.betas, self.gammas

    def get_results(self):
        most_freq_string, sampling_results = self.qaoa_inst.get_string(
            self.betas, self.gammas, samples=10000)
        self.most_freq_string = most_freq_string
        return sampling_results.most_common()

    def get_solution(self):
        if self.most_freq_string is None:
            self.most_freq_string, sampling_results = self.qaoa_inst.get_string(
                self.betas, self.gammas, samples=10000)
        reduced_solution = TSP_utilities.binary_state_to_points_order_full(
            self.most_freq_string)
        full_solution = self.get_solution_for_full_array(reduced_solution)
        return full_solution

    def reduce_nodes_array(self, full_nodes_array, starting_node):
        reduced_nodes_array = np.delete(full_nodes_array, starting_node, 0)
        tsp_matrix = TSP_utilities.get_tsp_matrix(full_nodes_array)
        costs_to_starting_node = np.delete(tsp_matrix[:, starting_node],
                                           starting_node)
        return reduced_nodes_array, costs_to_starting_node

    def get_solution_for_full_array(self, reduced_solution):
        full_solution = reduced_solution
        for i in range(len(full_solution)):
            if full_solution[i] >= self.starting_node:
                full_solution[i] += 1
        full_solution.insert(0, self.starting_node)
        return full_solution

    def create_phase_separator(self):
        cost_operators = []
        for t in range(len(self.nodes_array) - 1):
            for city_1 in range(len(self.nodes_array)):
                for city_2 in range(len(self.nodes_array)):
                    if city_1 != city_2:
                        tsp_matrix = TSP_utilities.get_tsp_matrix(
                            self.nodes_array)
                        distance = tsp_matrix[city_1, city_2]
                        qubit_1 = t * len(self.nodes_array) + city_1
                        qubit_2 = (t + 1) * len(self.nodes_array) + city_2
                        cost_operators.append(
                            PauliTerm("Z", qubit_1, distance) *
                            PauliTerm("Z", qubit_2))

        for city in range(len(self.costs_to_starting_node)):
            distance_from_0 = -self.costs_to_starting_node[city]
            qubit = city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        phase_separator = [PauliSum(cost_operators)]
        return phase_separator

    def create_mixer(self):
        """
        Indexing here comes directly from 4.1.2 from paper 1709.03489, equations 54 - 58.
        """
        mixer_operators = []

        n = len(self.nodes_array)
        for t in range(n - 1):
            for city_1 in range(n):
                for city_2 in range(n):
                    i = t
                    u = city_1
                    v = city_2
                    first_part = 1
                    first_part *= self.s_plus(u, i)
                    first_part *= self.s_plus(v, i + 1)
                    first_part *= self.s_minus(u, i + 1)
                    first_part *= self.s_minus(v, i)

                    second_part = 1
                    second_part *= self.s_minus(u, i)
                    second_part *= self.s_minus(v, i + 1)
                    second_part *= self.s_plus(u, i + 1)
                    second_part *= self.s_plus(v, i)
                    mixer_operators.append(first_part + second_part)
        return mixer_operators

    def create_initial_state_program(self):
        """
        As an initial state I use state, where in t=i we visit i-th city.
        """
        initial_state = pq.Program()
        number_of_nodes = len(self.nodes_array)
        if type(self.initial_state) is list:
            for i in range(number_of_nodes):
                initial_state.inst(
                    X(i * number_of_nodes + self.initial_state[i]))

        elif self.initial_state == "all":
            vector_of_states = np.zeros(2**self.number_of_qubits)
            list_of_possible_states = []
            initial_order = range(0, number_of_nodes)
            all_permutations = [
                list(x) for x in itertools.permutations(initial_order)
            ]
            for permutation in all_permutations:
                coding_of_permutation = 0
                for i in range(len(permutation)):
                    coding_of_permutation += 2**(i * number_of_nodes +
                                                 permutation[i])
                vector_of_states[coding_of_permutation] = 1
            initial_state = create_arbitrary_state(vector_of_states)

        return initial_state

    def get_number_of_qubits(self):
        return len(self.nodes_array)**2

    def s_plus(self, city, time):
        qubit = time * len(self.nodes_array) + city
        return PauliTerm("X", qubit) + PauliTerm("Y", qubit, 1j)

    def s_minus(self, city, time):
        qubit = time * len(self.nodes_array) + city
        return PauliTerm("X", qubit) - PauliTerm("Y", qubit, 1j)
コード例 #14
0
class ForestTSPSolver(object):
    def __init__(self,
                 distance_matrix,
                 steps=1,
                 ftol=1.0e-2,
                 xtol=1.0e-2,
                 use_constraints=False,
                 add_weight_constraints=True):

        self.distance_matrix = distance_matrix
        self.number_of_qubits = self.get_number_of_qubits()
        self.qvm = get_qc(str(self.number_of_qubits) + "q-qvm")
        self.steps = steps
        self.ftol = ftol
        self.xtol = xtol
        self.betas = None
        self.gammas = None
        self.qaoa_inst = None
        self.solution = None
        self.naive_distribution = None
        self.most_frequent_string = None
        self.sampling_results = None
        self.use_constraints = use_constraints
        self.add_weight_constraints = add_weight_constraints

        self.sensible_distribution = None

        cost_operators = self.create_cost_operators()
        driver_operators = self.create_driver_operators()

        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'ftol': self.ftol,
                'xtol': self.xtol,
                'disp': False
            }
        }

        # vqe_option = {'disp': print_fun, 'return_all': True,
        #               'samples': None}

        qubits = list(range(self.number_of_qubits))

        self.qaoa_inst = QAOA(
            self.qvm,
            qubits,
            steps=self.steps,
            cost_ham=cost_operators,
            ref_ham=driver_operators,
            store_basis=True,
            minimizer=scipy.optimize.minimize,
            minimizer_kwargs=minimizer_kwargs,
            # vqe_options=vqe_option
        )

    def solve_tsp(self):
        """
        Calculates the optimal angles (betas and gammas) for the QAOA algorithm
        and returns a list containing the order of nodes.
        """
        self.find_angles()
        self.calculate_solution()
        return self.solution, self.naive_distribution

    def find_angles(self):
        """
        Runs the QAOA algorithm for finding the optimal angles.
        """
        self.betas, self.gammas = self.qaoa_inst.get_angles()
        print("betas: ", self.betas)
        print("gammas: ", self.gammas)
        return self.betas, self.gammas

    def calculate_solution(self):
        """
        Samples the QVM for the results of the algorithm
        and returns a list containing the order of nodes.
        """
        most_frequent_string, sampling_results = self.qaoa_inst.get_string(
            self.betas, self.gammas, samples=10000)
        self.most_frequent_string = most_frequent_string
        self.sampling_results = sampling_results
        self.solution = binary_state_to_points_order(most_frequent_string)

        print()  # uncomment to show raw sampling results
        print("Raw sampling results: ")
        print(sampling_results)

        all_solutions = sampling_results.keys()
        naive_distribution = {}
        for sol in all_solutions:
            points_order_solution = error_binary_state_to_points_order(sol)
            if tuple(points_order_solution) in naive_distribution.keys(
            ):  # only true during error conditions of qubits
                naive_distribution[tuple(
                    points_order_solution)] += sampling_results[sol]
            else:
                naive_distribution[tuple(
                    points_order_solution)] = sampling_results[sol]

        # TODO: make use of sensible_distribution as well as naive
        self.naive_distribution = naive_distribution

    def create_cost_operators(self):
        cost_operators = []
        if self.add_weight_constraints:
            cost_operators += self.create_weights_cost_operators()
        if self.use_constraints:
            cost_operators += self.create_penalty_operators_for_bilocation()
            cost_operators += self.create_penalty_operators_for_repetition()

        return cost_operators

    def create_penalty_operators_for_bilocation(self):
        # Additional cost for visiting more than one node in given time t
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)
        for t in range(number_of_nodes):
            range_of_qubits = list(
                range(t * number_of_nodes, (t + 1) * number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(
                range_of_qubits)

        # print()
        # print("Cost operators for bilocation: ")
        # print(cost_operators)  # uncomment to see cost operator

        return cost_operators

    def create_penalty_operators_for_repetition(self):
        # Additional cost for visiting given node more than one time
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)
        for i in range(number_of_nodes):
            range_of_qubits = list(
                range(i, number_of_nodes**2, number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(
                range_of_qubits)

        # print()  # uncomment to see cost operator
        # print("Cost operators for repetition: ")
        # print(cost_operators)

        return cost_operators

    def create_penalty_operators_for_qubit_range(self, range_of_qubits):
        cost_operators = []
        weight = -100 * np.max(self.distance_matrix)
        for i in range_of_qubits:
            if i == range_of_qubits[0]:
                z_term = PauliTerm("Z", i, weight)
                all_ones_term = PauliTerm("I", 0, 0.5 * weight) - PauliTerm(
                    "Z", i, 0.5 * weight)
            else:
                z_term = z_term * PauliTerm("Z", i)
                all_ones_term = all_ones_term * (PauliTerm("I", 0, 0.5) -
                                                 PauliTerm("Z", i, 0.5))

        z_term = PauliSum([z_term])
        cost_operators.append(
            PauliTerm("I", 0, weight) - z_term - all_ones_term)

        return cost_operators

    def create_weights_cost_operators(self):
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)

        for i in range(number_of_nodes):
            for j in range(i, number_of_nodes):
                for t in range(number_of_nodes - 1):
                    weight = -self.distance_matrix[i][j] / 2
                    if self.distance_matrix[i][j] != 0:
                        qubit_1 = t * number_of_nodes + i
                        qubit_2 = (t + 1) * number_of_nodes + j
                        cost_operators.append(
                            PauliTerm("I", 0, weight) -
                            PauliTerm("Z", qubit_1, weight) *
                            PauliTerm("Z", qubit_2))

        return cost_operators

    def create_driver_operators(self):
        driver_operators = []

        for i in range(self.number_of_qubits):
            driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

        return driver_operators

    def get_number_of_qubits(self):
        return len(self.distance_matrix)**2
コード例 #15
0
class ForestTSPSolverImproved(object):
    def __init__(self,
                 distance_matrix,
                 steps=1,
                 ftol=1.0e-2,
                 xtol=1.0e-2,
                 use_constraints=False):

        self.costs_to_first_city = distance_matrix[:0]
        distance_matrix = np.delete(distance_matrix, 0, 0)
        self.distance_matrix = np.delete(distance_matrix, 0, 1)
        self.qvm = api.QVMConnection()
        self.steps = steps
        self.ftol = ftol
        self.xtol = xtol
        self.betas = None
        self.gammas = None
        self.qaoa_inst = None
        self.number_of_qubits = self.get_number_of_qubits()
        self.solution = None
        self.naive_distribution = None
        self.most_frequent_string = None
        self.sampling_results = None
        self.use_constraints = use_constraints

        cost_operators = self.create_cost_operators()
        driver_operators = self.create_driver_operators()

        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'ftol': self.ftol,
                'xtol': self.xtol,
                'disp': False
            }
        }

        vqe_option = {'disp': print_fun, 'return_all': True, 'samples': None}

        qubits = list(range(self.number_of_qubits))

        self.qaoa_inst = QAOA(self.qvm,
                              qubits,
                              steps=self.steps,
                              cost_ham=cost_operators,
                              ref_ham=driver_operators,
                              store_basis=True,
                              minimizer=scipy.optimize.minimize,
                              minimizer_kwargs=minimizer_kwargs,
                              vqe_options=vqe_option)

    def solve_tsp(self):
        """
        Calculates the optimal angles (betas and gammas) for the QAOA algorithm 
        and returns a list containing the order of nodes.
        """
        self.find_angles()
        self.calculate_solution()
        return self.solution, self.naive_distribution

    def find_angles(self):
        """
        Runs the QAOA algorithm for finding the optimal angles.
        """
        self.betas, self.gammas = self.qaoa_inst.get_angles()
        return self.betas, self.gammas

    def calculate_solution(self):
        """
        Samples the QVM for the results of the algorithm 
        and returns a list containing the order of nodes.
        """
        most_frequent_string, sampling_results = self.qaoa_inst.get_string(
            self.betas, self.gammas, samples=10000)
        self.most_frequent_string = most_frequent_string
        self.sampling_results = sampling_results
        self.solution = utilities.binary_state_to_points_order_with_fixed_start(
            most_frequent_string)

        all_solutions = sampling_results.keys()
        naive_distribution = {}
        for sol in all_solutions:
            points_order_solution = utilities.binary_state_to_points_order_with_fixed_start(
                sol)
            if tuple(points_order_solution) in naive_distribution.keys():
                naive_distribution[tuple(
                    points_order_solution)] += sampling_results[sol]
            else:
                naive_distribution[tuple(
                    points_order_solution)] = sampling_results[sol]

        self.naive_distribution = naive_distribution

    def create_cost_operators(self):
        cost_operators = []
        cost_operators += self.create_weights_cost_operators()
        if self.use_constraints:
            cost_operators += self.create_penalty_operators_for_bilocation()
            cost_operators += self.create_penalty_operators_for_repetition()

        return cost_operators

    def create_penalty_operators_for_bilocation(self):
        # Additional cost for visiting more than one node in given time t
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)
        for t in range(number_of_nodes):
            range_of_qubits = list(
                range(t * number_of_nodes, (t + 1) * number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(
                range_of_qubits)
        return cost_operators

    def create_penalty_operators_for_repetition(self):
        # Additional cost for visiting given node more than one time
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)
        for i in range(number_of_nodes):
            range_of_qubits = list(
                range(i, number_of_nodes**2, number_of_nodes))
            cost_operators += self.create_penalty_operators_for_qubit_range(
                range_of_qubits)
        return cost_operators

    def create_penalty_operators_for_qubit_range(self, range_of_qubits):
        cost_operators = []
        weight = -100 * np.max(self.distance_matrix)
        for i in range_of_qubits:
            if i == range_of_qubits[0]:
                z_term = PauliTerm("Z", i, weight)
                all_ones_term = PauliTerm("I", 0, 0.5 * weight) - PauliTerm(
                    "Z", i, 0.5 * weight)
            else:
                z_term = z_term * PauliTerm("Z", i)
                all_ones_term = all_ones_term * (PauliTerm("I", 0, 0.5) -
                                                 PauliTerm("Z", i, 0.5))

        z_term = PauliSum([z_term])
        cost_operators.append(
            PauliTerm("I", 0, weight) - z_term - all_ones_term)

        return cost_operators

    def create_weights_cost_operators(self):
        cost_operators = []
        number_of_nodes = len(self.distance_matrix)

        for i in range(number_of_nodes):
            for j in range(i, number_of_nodes):
                for t in range(number_of_nodes - 1):
                    weight = -self.distance_matrix[i][j] / 2
                    if self.distance_matrix[i][j] != 0:
                        qubit_1 = t * number_of_nodes + i
                        qubit_2 = (t + 1) * number_of_nodes + j
                        cost_operators.append(
                            PauliTerm("I", 0, weight) -
                            PauliTerm("Z", qubit_1, weight) *
                            PauliTerm("Z", qubit_2))

        for city in range(len(self.costs_to_first_city)):
            distance_from_0 = -self.costs_to_first_city[city]
            qubit = city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        return cost_operators

    def create_driver_operators(self):
        driver_operators = []

        for i in range(self.number_of_qubits):
            driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

        return driver_operators

    def get_number_of_qubits(self):
        return len(self.distance_matrix)**2
コード例 #16
0
class OptimizationEngine(object):
    """
    The optimization engine for the VQF algorithm.

    This class takes a problem encoded as clauses, further encodes it into hamiltonian
    and solves it using QAOA.

    Args:
        clauses (list): List of clauses (sympy expressions) representing the problem.
        m (int): Number to be factored. Needed only for the purpose of tagging result files.
        steps (int, optional): Number of steps in the QAOA algorithm. Default: 1
        grid_size (int, optional): The resolution of the grid for grid search. Default: None
        tol (float, optional): Parameter of BFGS optimization method. Gradient norm must be less than tol before successful termination. Default:1e-5
        gate_noise (float, optional): Specifies gate noise for qvm. Default: None.
        verbose (bool): Boolean flag, if True, information about the execution will be printed to the console. Default: False
        visualize (bool): Flag indicating if visualizations should be created. Default: False

    Attributes:
        clauses (list): See Args.
        grid_size (int): See Args.
        mapping (dict): Maps variables into qubit indices.
        qaoa_inst (object): Instance of QAOA class from Grove.
        samples (int): If noise model is active, specifies how many samples we should take for any given quantum program.
        ax (object): Matplotlib `axis` object, used for plotting optimization trajectory.

    """
    def __init__(self,
                 clauses,
                 m=None,
                 steps=1,
                 grid_size=None,
                 tol=1e-5,
                 gate_noise=None,
                 verbose=False,
                 visualize=False):
        self.clauses = clauses
        self.m = m
        self.verbose = verbose
        self.visualize = visualize
        self.step_by_step_results = None
        self.optimization_history = None
        self.gate_noise = gate_noise
        if grid_size is None:
            self.grid_size = len(clauses) + len(qubits)
        else:
            self.grid_size = grid_size

        cost_operators, mapping = self.create_operators_from_clauses()
        self.mapping = mapping
        driver_operators = self.create_driver_operators()
        # minimizer_kwargs = {'method': 'BFGS',
        #                         'options': {'gtol': tol, 'disp': False}}

        # bounds = [(0, np.pi)]*steps + [(0, 2*np.pi)]*steps
        # minimizer_kwargs = {'method': 'L-BFGS-B',
        #                         'options': {'gtol': tol, 'disp': False},
        #                         'bounds': bounds}
        minimizer_kwargs = {
            'method': 'Nelder-Mead',
            'options': {
                'ftol': tol,
                'tol': tol,
                'disp': False
            }
        }

        if self.verbose:
            print_fun = print
        else:
            print_fun = pass_fun

        qubits = list(range(len(mapping)))

        if gate_noise:
            self.samples = int(1e3)
            pauli_channel = [gate_noise] * 3
        else:
            self.samples = None
            pauli_channel = None
        connection = ForestConnection()
        qvm = QVM(connection=connection, gate_noise=pauli_channel)
        topology = nx.complete_graph(len(qubits))
        device = NxDevice(topology=topology)
        qc = QuantumComputer(name="my_qvm",
                             qam=qvm,
                             device=device,
                             compiler=QVMCompiler(
                                 device=device,
                                 endpoint=connection.compiler_endpoint))

        vqe_option = {
            'disp': print_fun,
            'return_all': True,
            'samples': self.samples
        }

        self.qaoa_inst = QAOA(qc,
                              qubits,
                              steps=steps,
                              init_betas=None,
                              init_gammas=None,
                              cost_ham=cost_operators,
                              ref_ham=driver_operators,
                              minimizer=scipy.optimize.minimize,
                              minimizer_kwargs=minimizer_kwargs,
                              rand_seed=None,
                              vqe_options=vqe_option,
                              store_basis=True)

        self.ax = None

    def create_operators_from_clauses(self):
        """
        Creates cost hamiltonian from clauses.
        For details see section IIC from the article.
        """
        operators = []
        mapping = {}
        variable_counter = 0
        for clause in self.clauses:
            if clause == 0:
                continue
            variables = list(clause.free_symbols)
            for variable in variables:
                if str(variable) not in mapping.keys():
                    mapping[str(variable)] = variable_counter
                    variable_counter += 1
            pauli_terms = []
            quadratic_pauli_terms = []
            if type(clause) == Add:
                clause_terms = clause.args
            elif type(clause) == Mul:
                clause_terms = [clause]
            for single_term in clause_terms:
                if len(single_term.free_symbols) == 0:
                    if self.verbose:
                        print("Constant term", single_term)
                    pauli_terms.append(PauliTerm("I", 0, int(single_term)))
                elif len(single_term.free_symbols) == 1:
                    if self.verbose:
                        print("Single term", single_term)
                    multiplier = 1
                    if type(single_term) == Mul:
                        multiplier = int(single_term.args[0])
                    symbol = list(single_term.free_symbols)[0]
                    symbol_id = mapping[str(symbol)]
                    pauli_terms.append(
                        PauliTerm("I", symbol_id, 1 / 2 * multiplier))
                    pauli_terms.append(
                        PauliTerm("Z", symbol_id, -1 / 2 * multiplier))
                elif len(single_term.free_symbols) == 2 and type(
                        single_term) == Mul:
                    if self.verbose:
                        print("Double term", single_term)
                    multiplier = 1
                    if isinstance(single_term.args[0], Number):
                        multiplier = int(single_term.args[0])
                    symbol_1 = list(single_term.free_symbols)[0]
                    symbol_2 = list(single_term.free_symbols)[1]
                    symbol_id_1 = mapping[str(symbol_1)]
                    symbol_id_2 = mapping[str(symbol_2)]
                    pauli_term_1 = PauliTerm(
                        "I", symbol_id_1, 1 / 2 * multiplier) - PauliTerm(
                            "Z", symbol_id_1, 1 / 2 * multiplier)
                    pauli_term_2 = PauliTerm("I", symbol_id_2,
                                             1 / 2) - PauliTerm(
                                                 "Z", symbol_id_2, 1 / 2)
                    quadratic_pauli_terms.append(pauli_term_1 * pauli_term_2)
                else:
                    Exception(
                        "Terms of orders higher than quadratic are not handled."
                    )

            clause_operator = PauliSum(pauli_terms)
            for quadratic_term in quadratic_pauli_terms:
                clause_operator += quadratic_term

            squared_clause_operator = clause_operator**2
            if self.verbose:
                print("C:", clause_operator)
                print("C**2:", squared_clause_operator)
            operators.append(squared_clause_operator)

        return operators, mapping

    def create_driver_operators(self):
        """
        Creates driver hamiltonian.
        """

        driver_operators = []

        for key, value in self.mapping.items():
            driver_operators.append(PauliSum([PauliTerm("X", value, -1.0)]))

        return driver_operators

    def perform_qaoa(self):
        """
        Finds optimal angles for QAOA.

        Returns:
            sampling_results (Counter): Counter, where each element represents a bitstring that has been obtained.
            mapping (dict): See class description.

        """
        # betas, gammas = self.simple_grid_search_angles(save_data=True)
        # betas, gammas = self.step_by_step_grid_search_angles(starting_angles=self.step_by_step_results)
        betas, gammas = self.find_initial_angles_with_interp(
            starting_angles=self.step_by_step_results)
        self.step_by_step_results = [betas, gammas]
        self.qaoa_inst.betas = betas
        self.qaoa_inst.gammas = gammas
        betas, gammas, bfgs_evaluations = self.get_angles()
        self.qaoa_inst.betas = betas
        self.qaoa_inst.gammas = gammas
        try:
            _, sampling_results = self.qaoa_inst.get_string(betas,
                                                            gammas,
                                                            samples=10000)
        except:
            pdb.set_trace()
        return sampling_results, self.mapping, bfgs_evaluations

    def get_angles(self):
        """
        Finds optimal angles with the quantum variational eigensolver method.
        
        It's direct copy of the function `get_angles` from Grove. I decided to copy it here
        to access to the optimization trajectory (`angles_history`).
        Returns:
            best_betas, best_gammas (np.arrays): best values of the betas and gammas found. 

        """
        stacked_params = np.hstack(
            (self.qaoa_inst.betas, self.qaoa_inst.gammas))
        vqe = VQE(self.qaoa_inst.minimizer,
                  minimizer_args=self.qaoa_inst.minimizer_args,
                  minimizer_kwargs=self.qaoa_inst.minimizer_kwargs)
        cost_ham = reduce(lambda x, y: x + y, self.qaoa_inst.cost_ham)
        # maximizing the cost function!
        param_prog = self.qaoa_inst.get_parameterized_program()
        result, bfgs_evaluations = vqe.vqe_run(param_prog,
                                               cost_ham,
                                               stacked_params,
                                               qc=self.qaoa_inst.qc,
                                               **self.qaoa_inst.vqe_options)
        best_betas = result.x[:self.qaoa_inst.steps]
        best_gammas = result.x[self.qaoa_inst.steps:]

        optimization_trajectory = result.iteration_params
        energy_history = result.expectation_vals

        if self.ax is not None and self.visualize and self.qaoa_inst.steps == 1:
            plot_optimization_trajectory(self.ax, optimization_trajectory)

        if len(optimization_trajectory) != 0 and len(energy_history) != 0:
            self.optimization_history = np.hstack([
                np.array(optimization_trajectory),
                np.array([energy_history]).T
            ])
        else:
            self.optimization_history = np.array([])

        return best_betas, best_gammas, bfgs_evaluations

    def simple_grid_search_angles(self, save_data=False):
        """
        Finds optimal angles for QAOA by performing grid search on all the angles.
        This is not recommended for higher values of steps parameter, 
        since it results in grid_size**(2*steps) evaluations.

        Returns:
            best_betas, best_gammas (np.arrays): best values of the betas and gammas found. 

        """
        best_betas = None
        best_gammas = None
        best_energy = np.inf

        # For some reasons np.meshgrid returns columns in order, where values in second
        # grow slower than in the first one. This a fix to it.
        if self.qaoa_inst.steps == 1:
            column_order = [0]
        else:
            column_order = [1, 0] + list(range(2, self.qaoa_inst.steps))

        new_indices = np.argsort(column_order)
        beta_ranges = [np.linspace(0, np.pi, self.grid_size)
                       ] * self.qaoa_inst.steps
        all_betas = np.vstack(np.meshgrid(*beta_ranges)).reshape(
            self.qaoa_inst.steps, -1).T
        all_betas = all_betas[:, column_order]

        gamma_ranges = [np.linspace(0, 2 * np.pi, self.grid_size)
                        ] * self.qaoa_inst.steps
        all_gammas = np.vstack(np.meshgrid(*gamma_ranges)).reshape(
            self.qaoa_inst.steps, -1).T
        all_gammas = all_gammas[:, column_order]

        vqe = VQE(self.qaoa_inst.minimizer,
                  minimizer_args=self.qaoa_inst.minimizer_args,
                  minimizer_kwargs=self.qaoa_inst.minimizer_kwargs)
        cost_hamiltonian = reduce(lambda x, y: x + y, self.qaoa_inst.cost_ham)
        all_energies = []
        data_to_save = []
        if save_data:
            file_name = "_".join(
                [str(self.m), "grid",
                 str(self.grid_size),
                 str(time.time())]) + ".csv"
        for betas in all_betas:
            for gammas in all_gammas:
                stacked_params = np.hstack((betas, gammas))
                program = self.qaoa_inst.get_parameterized_program()
                energy = vqe.expectation(program(stacked_params),
                                         cost_hamiltonian, self.samples,
                                         self.qaoa_inst.qc)
                all_energies.append(energy)
                if self.verbose:
                    print(betas, gammas, energy, end="\r")
                if save_data:
                    data_to_save.append(np.hstack([betas, gammas, energy]))
                if energy < best_energy:
                    best_energy = energy
                    best_betas = betas
                    best_gammas = gammas
                    if self.verbose:
                        print("Lowest energy:", best_energy)
                        print("Angles:", best_betas, best_gammas)
            if save_data:
                np.savetxt(file_name, np.array(data_to_save), delimiter=",")

        if self.visualize:
            if self.qaoa_inst.steps == 1:
                self.ax = plot_energy_landscape(all_betas,
                                                all_gammas,
                                                np.array(all_energies),
                                                log_legend=True)
            else:
                plot_variance_landscape(all_betas, all_gammas,
                                        np.array(all_energies))

        return best_betas, best_gammas

    def step_by_step_grid_search_angles(self, starting_angles=None):
        """
        Finds optimal angles for QAOA by performing "step-by-step" grid search.
        It finds optimal angles by performing grid search on the QAOA instance with steps=1.
        Then it fixes these angles and performs grid search on the second pair of angles.
        This method requires steps*grid_size**2 evaluations and hence is more suitable
        for higger values of steps.

        Returns:
            best_betas, best_gammas (np.arrays): best values of the betas and gammas found. 

        """
        max_step = self.qaoa_inst.steps
        if starting_angles is None:
            all_steps = range(1, max_step + 1)
            best_betas = np.array([])
            best_gammas = np.array([])
        elif len(starting_angles[0]) == max_step - 1:
            all_steps = [max_step]
            best_betas = starting_angles[0]
            best_gammas = starting_angles[1]
        elif len(starting_angles[0]) == max_step:
            best_betas = starting_angles[0]
            best_gammas = starting_angles[1]
            return best_betas, best_gammas
        else:
            all_steps = range(1, max_step + 1)
            best_betas = np.array([])
            best_gammas = np.array([])

        self.qaoa_inst.betas = best_betas
        self.qaoa_inst.gammas = best_gammas

        for current_step in all_steps:
            if self.verbose:
                print("step:", current_step, "\n")
            beta, gamma = self.one_step_grid_search(current_step)
            best_betas = np.append(best_betas, beta)
            best_gammas = np.append(best_gammas, gamma)
            self.qaoa_inst.betas = best_betas
            self.qaoa_inst.gammas = best_gammas

        return best_betas, best_gammas

    def one_step_grid_search(self, current_step):
        """
        Grid search on n-th pair of QAOA angles, where n=current_step.

        Args:
            current_step (int): specify on which layer do we perform search.

        Returns:
            best_beta, best_gamma (floats): best values of the beta and gamma found. 
        """
        self.qaoa_inst.steps = current_step
        best_beta = None
        best_gamma = None
        best_energy = np.inf

        fixed_betas = self.qaoa_inst.betas
        fixed_gammas = self.qaoa_inst.gammas
        beta_range = np.linspace(0, np.pi, self.grid_size)
        gamma_range = np.linspace(0, 2 * np.pi, self.grid_size)

        vqe = VQE(self.qaoa_inst.minimizer,
                  minimizer_args=self.qaoa_inst.minimizer_args,
                  minimizer_kwargs=self.qaoa_inst.minimizer_kwargs)
        cost_hamiltonian = reduce(lambda x, y: x + y, self.qaoa_inst.cost_ham)
        for beta in beta_range:
            for gamma in gamma_range:
                betas = np.append(fixed_betas, beta)
                gammas = np.append(fixed_gammas, gamma)
                stacked_params = np.hstack((betas, gammas))
                program = self.qaoa_inst.get_parameterized_program()
                energy = vqe.expectation(program(stacked_params),
                                         cost_hamiltonian, self.samples,
                                         self.qaoa_inst.qc)
                print(beta, gamma, end="\r")
                if energy < best_energy:
                    best_energy = energy
                    best_beta = beta
                    best_gamma = gamma

        return best_beta, best_gamma

    def find_initial_angles_with_interp(self, starting_angles):
        """
        Implements INTERP strategy proposed in https://arxiv.org/pdf/1812.01041.pdf, appendix B.1
        """
        if self.qaoa_inst.steps == 1:
            betas, gammas = self.simple_grid_search_angles(save_data=False)
            return betas, gammas

        p = self.qaoa_inst.steps - 1
        if len(starting_angles[0]) == p:
            previous_betas = np.hstack([0, starting_angles[0], 0])
            previous_gammas = np.hstack([0, starting_angles[1], 0])
            new_betas = []
            new_gammas = []
            for i in range(1, p + 2):
                beta_i = previous_betas[i]
                beta_i_minus_1 = previous_betas[i - 1]
                new_beta_i = (i - 1) / p * beta_i_minus_1 + (p - i +
                                                             1) / p * beta_i
                new_betas.append(new_beta_i)

                gamma_i = previous_gammas[i]
                gamma_i_minus_1 = previous_gammas[i - 1]
                new_gamma_i = (i - 1) / p * gamma_i_minus_1 + (p - i +
                                                               1) / p * gamma_i
                new_gammas.append(new_gamma_i)

            return np.array(new_betas), np.array(new_gammas)

        elif len(starting_angles[0]) == p + 1:
            best_betas = starting_angles[0]
            best_gammas = starting_angles[1]
            return best_betas, best_gammas
        else:
            Exception(
                "INTERP strategy won't work without proper starting angles.")
コード例 #17
0
ファイル: ising_qaoa.py プロジェクト: ProteinQure/grove
def ising_qaoa(h, J, num_steps=0, embedding=None, driver_operators=None, verbose=True,
               rand_seed=None, connection=None, samples=None, initial_state=None,
               initial_beta=None, initial_gamma=None, minimizer_kwargs=None,
               vqe_option=None):
    """
    Ising set up method for QAOA. Supports 2-local as well as k-local interaction terms.

    :param h: (dict) External magnectic term of the Ising problem.
    :param J: (dict) Interaction terms of the Ising problem (may be k-local).
    :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the
                  QAOA algorithm.
    :param embedding: (dict) (Optional. Default: Identity dict) Mapping of logical to physical
                qubits in the QPU hardware graph. Logical qubits must be the
                dict keys.
    :param driver_operators: (Optional. Default: X on all qubits.) The mixer/driver
                Hamiltonian used in QAOA. Can be used to enforce hard constraints
                and ensure that solution stays in feasible subspace.
                Must be PauliSum objects.
    :param verbose: (Optional.Default=True) Verbosity of the code.
    :param rand_seed: (Optional. Default=None) random seed when beta and
                      gamma angles are not provided.
    :param connection: (Optional) connection to the QVM. Default is None.
    :param samples: (Optional. Default=None) VQE option. Number of samples
                    (circuit preparation and measurement) to use in operator
                    averaging. Required when using QPU backend.
    :param initial_state: (Optional. Default=Superposition of all bitstrings) A quantum circuit
                    to initialize the initial state. Must be a pyquil Program.
    :param initial_beta: (Optional. Default=None) Initial guess for beta
                         parameters.
    :param initial_gamma: (Optional. Default=None) Initial guess for gamma
                          parameters.
    :param minimizer_kwargs: (Optional. Default=None). Minimizer optional
                             arguments.  If None set to
                             {'method': 'Nelder-Mead', 'options': {'ftol': 1.0e-2,
                             'xtol': 1.0e-2, disp': False}
    :param vqe_option: (Optional. Default=None). VQE optional arguments. If None set to
                       vqe_option = {'disp': print_fun, 'return_all': True,
                       'samples': samples}
    :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result.
    :rtype: List, Integer or float, 'pyquil.quil.Program'.

    """

    n_nodes = len(set([ index for tuple_ in list(J.keys()) for index in tuple_]
                        + list(h.keys())))

    if num_steps == 0:
        num_steps = 2 * len(n_nodes)

    if embedding is None:
        embedding = {i:i for i in range(n_nodes)}

    cost_operators = []
    driver_operators = []
    for key in J.keys():
        # first PauliTerm is multiplied with coefficient obtained from J
        pauli_product = PauliTerm("Z", embedding[key[0]], J[key])

        for i in range(1,len(key)):
            # multiply with additional Z PauliTerms depending
            # on the locality of the interaction terms
            pauli_product *= PauliTerm("Z", embedding[key[i]])

        cost_operators.append(PauliSum([pauli_product]))

    for i in h.keys():
        cost_operators.append(PauliSum([PauliTerm("Z", embedding[i], h[i])]))

    if driver_operators is None:
        driver_operators = []
        # default to X mixer
        for i in embedding.values():
            driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

    if connection is None:
        connection = CXN

    if minimizer_kwargs is None:
        minimizer_kwargs = {'method': 'Nelder-Mead',
                            'options': {'ftol': 1.0e-2, 'xtol': 1.0e-2,
                                        'disp': False}}
    if vqe_option is None:
        vqe_option = {'disp': print_fun, 'return_all': True,
                      'samples': samples}

    if not verbose:
        vqe_option['disp'] = None

    qaoa_inst = QAOA(connection, qubits=list(sorted(embedding.values())), steps=num_steps, cost_ham=cost_operators,
                     ref_ham=driver_operators, store_basis=True,
                     rand_seed=rand_seed,
                     embedding=embedding,
                     init_betas=initial_beta,
                     init_gammas=initial_gamma,
                     minimizer=minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     vqe_options=vqe_option)

    betas, gammas = qaoa_inst.get_angles()
    most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas)
    most_freq_string_ising = [ising_trans(it) for it in most_freq_string]
    energy_ising = energy_value(h, J, most_freq_string_ising)
    param_prog = qaoa_inst.get_parameterized_program()
    circuit = param_prog(np.hstack((betas, gammas)))

    return most_freq_string_ising, energy_ising, circuit