def createPlot(exactGroundStateEnergy=-1.14, numberOfIterations=1000, bondLength=0.735, initialParameters=None, numberOfParameters=16, shotsPerPoint=1000, registerSize=12, map_type='jordan_wigner'): if initialParameters is None: initialParameters = np.random.rand(numberOfParameters) global qubitOp global qr_size global shots global values global plottingTime plottingTime = True shots = shotsPerPoint qr_size = registerSize optimizer = COBYLA(maxiter=numberOfIterations) iterations = [] values = [] for i in range(numberOfIterations): iterations.append(i + 1) #Build molecule with PySCF driver = PySCFDriver(atom="H .0 .0 .0; H .0 .0 " + str(bondLength), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') molecule = driver.run() repulsion_energy = molecule.nuclear_repulsion_energy num_spin_orbitals = molecule.num_orbitals * 2 num_particles = molecule.num_alpha + molecule.num_beta #Map fermionic operator to qubit operator and start optimization ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubitOp = ferOp.mapping(map_type=map_type, threshold=0.00000001) sol_opt = optimizer.optimize(numberOfParameters, energy_opt, gradient_function=None, variable_bounds=None, initial_point=initialParameters) #Adjust values to obtain Energy Error for i in range(len(values)): values[i] = values[i] + repulsion_energy - exactGroundStateEnergy #Saving and Plotting Data filename = 'Energy Error - Iterations' with open(filename, 'wb') as f: pickle.dump([iterations, values], f) plt.plot(iterations, values) plt.ylabel('Energy Error') plt.xlabel('Iterations') plt.show()
def main(): # Parse all command line arguments args = parser.parse_args() shots = args.shots seed = args.seed basis = args.bell logfile = args.logfile verbose = args.verbose # Define the logger logger = logging.getLogger('task2') if logfile: logger.addHandler(logging.FileHandler(logfile)) if verbose: logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) np.random.seed(seed=seed) # Get the noise model device_backend = FakeVigo() noise_model = NoiseModel.from_backend(device_backend) coupling_map = device_backend.configuration().coupling_map basis_gates = noise_model.basis_gates backend = Aer.get_backend('qasm_simulator') statevector_backend = Aer.get_backend('statevector_simulator') circuit = build_circuit(measure=basis) circuit_for_counts = build_circuit(measure='computational') unmeasured_circuit = build_circuit(measure=None) # qiskit's COBYLA can't take args to pass to the objective function, so we freeze them with functools.partial optimizer = COBYLA(maxiter=1000, tol=1e-8, disp=True) for nshots in shots: logger.debug('====================================================================================') logger.debug(f'\nShots per iteration: {nshots}') logger.debug(circuit) partial_objective_function = partial(objective_function, circuit=circuit, shots=nshots, backend=backend, bell_basis=(basis == 'bell'), noise_model=noise_model, coupling_map=coupling_map, basis_gates=basis_gates) ret = optimizer.optimize(num_vars=2, objective_function=partial_objective_function, initial_point=np.random.rand(len(circuit.parameters))*4*np.pi - 2*np.pi) params = ret[0] logger.debug(f'\nParameters:\n{params}') logger.debug(f'\nStatevector:\n{execute_circuit(unmeasured_circuit, params, statevector_backend).result().get_statevector()}') logger.debug(f'\nSimulated results:\n{execute_circuit(circuit_for_counts, params, backend, nshots, noise_model, coupling_map, basis_gates).result().get_counts()}') logger.debug('====================================================================================\n') sys.exit(ExitStatus.success)
def createPlot1(bondLengthMin=0.5, bondLengthMax=1.5, numberOfPoints=10, initialParameters=None, numberOfParameters=16, shotsPerPoint=1000, registerSize=12, map_type='jordan_wigner'): if initialParameters is None: initialParameters = np.random.rand(numberOfParameters) global qubitOp global qr_size global shots shots = shotsPerPoint qr_size = registerSize optimizer = COBYLA(maxiter=20) bondLengths = [] values = [] delta = (bondLengthMax - bondLengthMin) / numberOfPoints for i in range(numberOfPoints): bondLengths.append(bondLengthMin + i * delta) for bondLength in bondLengths: driver = PySCFDriver(atom="H .0 .0 .0; H .0 .0 " + str(bondLength), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') molecule = driver.run() repulsion_energy = molecule.nuclear_repulsion_energy num_spin_orbitals = molecule.num_orbitals * 2 num_particles = molecule.num_alpha + molecule.num_beta ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubitOp = ferOp.mapping(map_type=map_type, threshold=0.00000001) sol_opt = optimizer.optimize(numberOfParameters, energy_opt, gradient_function=None, variable_bounds=None, initial_point=initialParameters) values.append(sol_opt[1] + repulsion_energy) filename = 'Energy - BondLengths' with open(filename, 'wb') as f: pickle.dump([bondLengths, values], f) plt.plot(bondLengths, values) plt.ylabel('Ground State Energy') plt.xlabel('Bond Length') plt.show()
def optimize(self): # Define objective function def objfunc(params): return -self.expectation(beta=params[0:self.p], gamma=params[self.p:2 * self.p]) # Optimize parameters optimizer = COBYLA(maxiter=1000, tol=0.0001) params = self.beta_val + self.gamma_val ret = optimizer.optimize(num_vars=2 * self.p, objective_function=objfunc, initial_point=params) self.beta_val = ret[0][0:self.p] self.gamma_val = ret[0][self.p:2 * self.p] self.error = ret[1] return
def optimize_params(self, n_iterations, tolerance): ''' This function will take care of optimizing the parameters for the circuit. Algorithm being used: COBYLA Parameters: n_iterations : Maximum number of iterations for the optimizer tolerance : Consecutive loss tolerance value for convergence check Returns: List of optimized parameters, final loss value, iterations performed ''' cobyla_optimizer = COBYLA(maxiter=n_iterations, disp=True, tol=tolerance) return cobyla_optimizer.optimize(num_vars=2, variable_bounds=[(0, np.pi), (0, np.pi)], objective_function=self.entropy_loss, initial_point=np.random.uniform( 0, 2 * np.pi, 2))
# Execute the quantum circuit to obtain the probability distribution associated with the current parameters result = execute(qc, backend, shots=NUM_SHOTS).result() # Obtain the counts for each measured state, and convert those counts into a probability vector output_distr = get_probability_distribution(result.get_counts(qc)) # Calculate the cost as the distance between the output distribution and the target distribution cost = sum([np.abs(output_distr[i] - target_distr[i]) for i in range(2)]) return cost # Initialize the COBYLA optimizer; number of shots will increase accuracy optimizer = COBYLA(maxiter=500, tol=0.0001) # Create the initial parameters (noting that our single qubit variational form has 3 parameters) params = np.random.rand(3) ret = optimizer.optimize(num_vars=3, objective_function=objective_function, initial_point=params) # Obtain the output distribution using the final parameters qc = get_var_form(ret[0]) counts = execute(qc, backend, shots=NUM_SHOTS).result().get_counts(qc) output_distr = get_probability_distribution(counts) print("Target Distribution:", target_distr) print("Obtained Distribution:", output_distr) print("Output Error (Manhattan Distance):", ret[1]) print("Parameters Found:", ret[0]) ################################################################################################ ################ https: // qiskit.org/textbook/ch-applications/vqe-molecules.html ############## ################################################################################################
class HeuristicOpt(Optimizer): def __init__(self, ansatz: Ansatz, budget=120): super().__init__(ansatz) self.__similarity_thresh = 1e-2 self.sweep_count = 1 self.maxiter = 200 self.budget = budget self.sweep_divisions = 4 self.optimizer = COBYLA(maxiter=self.maxiter, tol=1e-3) self.objective_function = None self.sweepspace_dimension = None self.num_vars = None def optimal_single_parameter(self, param_index): objective_function = self.ansatz.cost_function initial_point = self.get_parameter_list() # Yields the optimal parameterization when ONLY considering param_index (not globally optimal) initial_point[param_index] = 0 self.set_parameter_list(initial_point) exp_0 = objective_function() initial_point[param_index] = np.pi self.set_parameter_list(initial_point) exp_pi = objective_function() # If the two evaluations are very close to each other, there is no need to waste another 2 function calls if np.abs(exp_0 - exp_pi) < self.__similarity_thresh: self.set_parameter_list(initial_point) return initial_point[param_index] = np.pi / 2 self.set_parameter_list(initial_point) exp_pi_div_2 = objective_function() initial_point[param_index] = -np.pi / 2 self.set_parameter_list(initial_point) exp_minus_pi_div_2 = objective_function() # If all evaluations where the same, just return. if exp_0 == exp_pi == exp_pi_div_2 == exp_minus_pi_div_2: self.set_parameter_list() return B = np.arctan2(exp_0 - exp_pi, exp_pi_div_2 - exp_minus_pi_div_2) # Set the parameter optimally theta_opt = -(np.pi / 2) - B + 2 * np.pi initial_point[param_index] = theta_opt self.set_parameter_list(initial_point) return def get_parameter_index(self, best_node): targ_q = best_node.cell.get_qubit() targ_d = best_node.cell.get_depth() # Get the index in the parameter array of the node to optimize index = 0 for ii in range(self.max_depth): for i in range(self.num_qubits): if (i == targ_q) and (ii == targ_d): return index - 1 else: index += self.ansatz[i, ii].get_param_count() raise AssertionError("[ASSERTION FAILURE] Unreachable code reached.") @staticmethod def gram_schmidt_columns(X): Q, R = np.linalg.qr(X) return Q def meta_cost_function(self, hyperparams): sweeps = [] for i in range(self.sweepspace_dimension): sweeps.append(np.array(self.gram_schmidt_columns(self.X[:,[i]]))) vec = 0 for i, hyperparam in enumerate(hyperparams): vec += sweeps[i] * hyperparam params = [vec[i] for i in range(self.num_vars)] self.set_parameter_list(params) return self.objective_function() def sweep(self): initial_point = self.get_parameter_list() num_vars = len(initial_point) self.sweepspace_dimension = int(num_vars / self.sweep_divisions) self.num_vars = num_vars best = self.objective_function() sweep_params = [np.random.rand()] * self.sweepspace_dimension best_params = sweep_params for sweep in range(self.sweep_count): self.X = np.random.normal(size=(num_vars, self.sweepspace_dimension)) ret = self.optimizer.optimize(num_vars=self.sweepspace_dimension, objective_function=self.meta_cost_function, initial_point=np.asarray(sweep_params)) sweep_params = ret[0] if ret[1] < best: best = ret[1] best_params = sweep_params self.meta_cost_function(best_params) def optimize(self, random=False): # params = self.get_parameter_list() budget = self.budget if random: # permutation = list(np.random.permutation([i for i in range(len(self.get_parameter_list()))])) permutation = [i for i in range(len(self.get_parameter_list()))] permutation *= int(budget / len(permutation)) for i in permutation: self.optimal_single_parameter(i) else: target_n = self.num_qubits - 1 target_d = 0 # target_n = np.random.randint(0, self.num_qubits) # target_d = np.random.randint(0, self.max_depth) # @TODO Run sweep opt on the parameters on the deepest layer # Pick a random node to add to the priority queue first. self.objective_function = self.ansatz.cost_function self.sweep() first_cell = self.ansatz[target_n, target_d] pq = _PriorityQueue(first_cell) # Iterate over all nodes in the ansatz, and add each to the PQ, if it is not the node that was already selected for i in range(self.num_qubits): for ii in range(self.max_depth): if (i != target_n) or (ii != target_d): new_item = _Node(self.ansatz[i, ii]) pq.insert(new_item) for i in range(budget): best_node = pq.pop() next_cell = best_node.cell.get_next_cell() if next_cell is not None: next_cell_n = next_cell.get_qubit() next_cell_d = next_cell.get_depth() f = lambda xn, xd: (next_cell_n == xn) and (next_cell_d == xd) pq.increment_priority(f) bd = best_node.cell.get_depth() bn = best_node.cell.get_qubit() if bn + 1 < self.num_qubits: f = lambda xn, xd: (xn == bn + 1) and (xd == bd) pq.increment_priority(f) if bd + 1 < self.max_depth: f = lambda xn, xd: (xn == bn) and (xd == bd + 1) pq.increment_priority(f) if bd - 1 >= 0: f = lambda xn, xd: (xn == bn) and (xd == bd - 1) pq.increment_priority(f) param_index = self.get_parameter_index(best_node) self.optimal_single_parameter(param_index) pq.insert(best_node) if np.random.rand() < 0.005: self.sweep() return
def createPlot3(minShots=50, stepSize=50, maxShots=1000, exactGroundStateEnergy=-1.14, totalNumberOfShots=10000, bondLength=0.735, initialParameters=None, numberOfParameters=16, registerSize=12, map_type='jordan_wigner'): if initialParameters is None: initialParameters = np.random.rand(numberOfParameters) global qubitOp global qr_size global shots global values qr_size = registerSize #Build Molecule and stuff driver = PySCFDriver(atom="H .0 .0 .0; H .0 .0 " + str(bondLength), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') molecule = driver.run() repulsion_energy = molecule.nuclear_repulsion_energy num_spin_orbitals = molecule.num_orbitals * 2 num_particles = molecule.num_alpha + molecule.num_beta ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubitOp = ferOp.mapping(map_type=map_type, threshold=0.00000001) #create Shots Array shotValues = [] numberOfDataPoints = math.floor((maxShots - minShots) / stepSize) for i in range(numberOfDataPoints): shotValues.append(minShots + i * stepSize) values = [] print(shotValues) for shotsPerPoint in shotValues: shots = shotsPerPoint if shotsPerPoint <= totalNumberOfShots: optimizer = COBYLA(maxiter=math.floor(totalNumberOfShots / shotsPerPoint)) sol_opt = optimizer.optimize(numberOfParameters, energy_opt, gradient_function=None, variable_bounds=None, initial_point=initialParameters) values.append(sol_opt[1] + repulsion_energy) else: raise Exception('Error: Total number of shots too small.') #Calculate Energy Error for i in range(len(values)): values[i] = values[i] + repulsion_energy - exactGroundStateEnergy #Saving and Plotting Data filename = 'Energy Error - Number of Shots' with open(filename, 'wb') as f: pickle.dump([shotValues, values], f) plt.plot(shotValues, values) plt.ylabel('Energy Error') plt.xlabel('Number of Shots') plt.show()
class VQE: def __init__(self, num_qubits, cost_function, variational_circuit: VariationalCircuit): self._num_qubits = num_qubits self._cost_function = cost_function self._variational_circuit = variational_circuit self._num_shots = 10000 self.min_cost = 9e9 self._max_itr = int(2000 * (num_qubits / 4)) self._tol = float('1e-' + str(num_qubits)) print(self._max_itr, "tol", self._tol) self._num_itr = 0 self._backend = Aer.get_backend("qasm_simulator") self._optimizer = COBYLA(rhobeg=1.5, maxiter=self._max_itr, tol=self._tol) self._SCALE_FACTOR = 1 # Factor by which the expectation value is scaled by # This method is similar to the objective_function method in the piazza example. def objective_function(self, params): qc = self._variational_circuit.generateCircuit(params) result = execute(qc, self._backend, shots=self._num_shots).result().get_counts() cost = self._cost_function(result) if cost < self.min_cost: self.min_cost = cost self._num_itr += 1 if self._num_itr == self._max_itr: print("max itr reached") return cost def execute(self): # Bool to check whether the variational circuit is a custom one isCustom = self._variational_circuit.isCustom() num_params = self._num_qubits num_params *= 9 if isCustom else 6 # Scaling the number of parameters according the the circuit print("Processing...") # Using the optimizer to optimize the parameters for the quantum circuit. self._optimizer.optimize(num_vars=num_params, objective_function=self.objective_function, initial_point=np.zeros(num_params)) print("Processing Complete") # Returning the min cost. return -self.min_cost / self._SCALE_FACTOR