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
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]
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
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)
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']
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
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
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)
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
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
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)
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
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