def get_optimized_circuit(init_state):
    qaoa = QAOA(qvm,
                qubits=range(n_system),
                steps=p,
                ref_ham=Hm,
                cost_ham=Hc,
                driver_ref=init_state,
                store_basis=True,
                minimizer=fmin_bfgs,
                minimizer_kwargs={'maxiter': 50})
    beta, gamma = qaoa.get_angles()
    return qaoa.get_parameterized_program()(np.hstack((beta, gamma)))
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
示例#3
0
def test_get_angles():
    p = 2
    n_qubits = 2
    fakeQVM = Mock()
    with patch('grove.pyqaoa.qaoa.VQE', spec=VQE) as mockVQEClass:
        inst = mockVQEClass.return_value
        result = Mock()
        result.x = [1.2, 2.1, 3.4, 4.3]
        inst.vqe_run.return_value = result
        MCinst = QAOA(fakeQVM, n_qubits, steps=p,
                      cost_ham=[PauliSum([PauliTerm("X", 0)])])
        betas, gammas = MCinst.get_angles()
        assert betas == [1.2, 2.1]
        assert gammas == [3.4, 4.3]
示例#4
0
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
示例#5
0
    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)

    # 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)
示例#6
0
    def make_unclamped_QAOA(self):
        """
		Internal helper function for building QAOA circuit to get RBM expectation
		using Rigetti Quantum simulator

		Returns
		---------------------------------------------------
		nus:		(list) optimal parameters for cost hamiltonians in each layer of QAOA
		gammas:		(list) optimal parameters for mixer hamiltonians in each layer of QAOA
		para_prog:  (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas
		---------------------------------------------------

		"""

        visible_indices = [i for i in range(0, self.n_visible)]
        hidden_indices = [i + self.n_visible for i in range(0, self.n_hidden)]

        full_cost_operator = []
        full_mixer_operator = []
        for i in visible_indices:
            for j in hidden_indices:
                full_cost_operator.append(
                    PauliSum([
                        PauliTerm("Z", i,
                                  -1.0 * self.WEIGHTS[i][j - self.n_visible]) *
                        PauliTerm("Z", j, 1.0)
                    ]))

        # UNCOMMENT THIS TO ADD BIAS IN *untested* in this version of code*
        # for i in hidden_indices:
        # 	full_cost_operator.append(PauliSum([PauliTerm("Z", i, -1.0 * self.BIAS[i - self.n_visible])]))

        for i in hidden_indices + visible_indices:
            full_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)]))

        n_system = len(visible_indices) + len(hidden_indices)

        state_prep = pq.Program()
        for i in visible_indices + hidden_indices:
            tmp = pq.Program()
            tmp.inst(RX(self.state_prep_angle, i + n_system),
                     CNOT(i + n_system, i))
            state_prep += tmp

        full_QAOA = QAOA(self.qvm,
                         n_qubits=n_system,
                         steps=self.n_qaoa_steps,
                         ref_hamiltonian=full_mixer_operator,
                         cost_ham=full_cost_operator,
                         driver_ref=state_prep,
                         store_basis=True,
                         minimizer=fmin_bfgs,
                         minimizer_kwargs={'maxiter': 50},
                         vqe_args={'samples': self.n_quantum_measurements},
                         rand_seed=1234)

        nus, gammas = full_QAOA.get_angles()

        if self.verbose:
            print 'Found following for nus and gammas from QAOA'
            print nus
            print gammas
            print '-' * 80

        program = full_QAOA.get_parameterized_program()
        return nus, gammas, program, 0  #full_QAOA.result['fun']
示例#7
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
示例#8
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
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
示例#10
0
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
    }
    def make_clamped_QAOA(self, data_point, iter):
        """
        Internal helper function for building QAOA circuit to get RBM expectation
        using Rigetti Quantum simulator
        Returns
        ---------------------------------------------------
        nus:        (list) optimal parameters for cost hamiltonians in each layer of QAOA
        gammas:        (list) optimal parameters for mixer hamiltonians in each layer of QAOA
        para_prog:  (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas
        ---------------------------------------------------
        """

        # Indices
        visible_indices = [i for i in range(self.visible_units)]
        hidden_indices = [
            i + self.visible_units for i in range(self.hidden_units)
        ]
        total_indices = [i for i in range(self.total_units)]

        # Partial Mixer and Partial Cost Hamiltonian
        partial_mixer_operator = []
        for i in hidden_indices:
            partial_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)]))

        partial_cost_operator = []
        for i in visible_indices:
            for j in hidden_indices:
                partial_cost_operator.append(
                    PauliSum([
                        PauliTerm(
                            "Z", i,
                            -1.0 * self.WEIGHTS[i][j - self.visible_units]) *
                        PauliTerm("Z", j, 1.0)
                    ]))

        if self.BIAS is not None:
            for i in hidden_indices:
                partial_cost_operator.append(
                    PauliSum([
                        PauliTerm("Z", i,
                                  -1.0 * self.BIAS[i - self.visible_units])
                    ]))

        state_prep = pq.Program()
        # state_prep = arbitrary_state.create_arbitrary_state(data_point,visible_indices)

        # Prepare Visible units as computational basis state corresponding to data point.
        for i, j in enumerate(data_point):
            #print(i,j)
            if j == 1:
                state_prep += X(i)

        # Prepare Hidden units in a thermal state of the partial mixer hamiltonian.
        for i in hidden_indices:
            tmp = pq.Program()
            tmp.inst(RX(self.state_prep_angle, i + self.total_units),
                     CNOT(i + self.total_units, i))
            state_prep += tmp

        # QAOA on parital mixer and partial cost hamiltonian evolution
        partial_QAOA = QAOA(qvm=self.qvm,
                            qubits=total_indices,
                            steps=self.qaoa_steps,
                            ref_ham=partial_mixer_operator,
                            cost_ham=partial_cost_operator,
                            driver_ref=state_prep,
                            store_basis=True,
                            minimizer=fmin_bfgs,
                            minimizer_kwargs={'maxiter': 100 // iter},
                            vqe_options={'samples': self.quant_meas_num},
                            rand_seed=1234)

        nus, gammas = partial_QAOA.get_angles()

        program = partial_QAOA.get_parameterized_program()
        return nus, gammas, program, 1
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)
示例#13
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
示例#14
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
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
示例#16
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)
示例#17
0
            PauliSum(
                [PauliTerm("Z", i, 1 / 4 * w[i, j]) * PauliTerm("Z", j, 1.0)]))
        maxcut_model.append(PauliSum([PauliTerm("I", i, -1 / 4)]))

p = 1
Hm = [PauliSum([PauliTerm("X", i, 1.0)]) for i in range(n_instances)]
qaoa = QAOA(qvm,
            qubits=range(n_instances),
            steps=p,
            ref_ham=Hm,
            cost_ham=maxcut_model,
            store_basis=True,
            minimizer=fmin_bfgs,
            minimizer_kwargs={'maxiter': 50})

nu, gamma = qaoa.get_angles()
program = qaoa.get_parameterized_program()(np.hstack((nu, gamma)))
measures = qvm.run_and_measure(program, range(n_instances), trials=100)
measures = np.array(measures)

# Extract common soln
count = np.unique(measures, return_counts=True, axis=0)
weights = count[0][np.argmax(count[1])]

print(weights)

end = time.time()

print('Time elapsed for solving maxcut using QAOA', end - start, 's')
# Solve it using annealing
    def make_unclamped_QAOA(self):
        """
        Internal helper function for building QAOA circuit to get RBM expectation
        using Rigetti Quantum simulator
        Returns
        ---------------------------------------------------
        nus:        (list) optimal parameters for cost hamiltonians in each layer of QAOA
        gammas:        (list) optimal parameters for mixer hamiltonians in each layer of QAOA
        para_prog:  (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas
        ---------------------------------------------------
        """

        # Indices
        visible_indices = [i for i in range(self.visible_units)]
        hidden_indices = [
            i + self.visible_units for i in range(self.hidden_units)
        ]
        total_indices = [i for i in range(self.total_units)]

        # Full Mixer and Cost Hamiltonian Operator
        full_mixer_operator = []
        for i in total_indices:
            full_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)]))

        full_cost_operator = []
        for i in visible_indices:
            for j in hidden_indices:
                full_cost_operator.append(
                    PauliSum([
                        PauliTerm(
                            "Z", i,
                            -1.0 * self.WEIGHTS[i][j - self.visible_units]) *
                        PauliTerm("Z", j, 1.0)
                    ]))

        if self.BIAS is not None:
            for i in hidden_indices:
                print(i, self.visible_units, i - self.visible_units,
                      self.BIAS[i - self.visible_units])
                full_cost_operator.append(
                    PauliSum([
                        PauliTerm("Z", i,
                                  -1.0 * self.BIAS[i - self.visible_units])
                    ]))

        # Prepare all the units in a thermal state of the full mixer hamiltonian.
        state_prep = pq.Program()

        for i in total_indices:
            tmp = pq.Program()
            tmp.inst(RX(self.state_prep_angle, i + self.total_units),
                     CNOT(i + self.total_units, i))
            state_prep += tmp

        # QAOA on full mixer and full cost hamiltonian evolution
        full_QAOA = QAOA(self.qvm,
                         qubits=total_indices,
                         steps=self.qaoa_steps,
                         ref_ham=full_mixer_operator,
                         cost_ham=full_cost_operator,
                         driver_ref=state_prep,
                         store_basis=True,
                         minimizer=fmin_bfgs,
                         minimizer_kwargs={'maxiter': 100},
                         vqe_options={'samples': self.quant_meas_num},
                         rand_seed=1234)

        nus, gammas = full_QAOA.get_angles()

        program = full_QAOA.get_parameterized_program()
        return nus, gammas, program, 0
示例#19
0
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