def ADAMOPT(): for x_idx, shotnum in enumerate(SHOTRANGE): circuit = initialize(shotnum) opt = qml.AdamOptimizer(0.01) thetaadam = init_params for y_idx in range(STEPS): thetaadam = opt.step(circuit, thetaadam) ADAM_COST[x_idx, y_idx] = circuit(thetaadam) np.save("./datafiles/adamshotcosttoy.npy", ADAM_COST)
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 #np.random.seed(0) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) #params = np.random.uniform(0, np.pi, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) # QHACK # dev = qml.device('default.qubit', wires=num_qubits) # Minimize the circuit #opt = qml.GradientDescentOptimizer(stepsize=0.4) opt = qml.AdamOptimizer(stepsize=0.2, beta1=0.9, beta2=0.99, eps=1e-08) #opt = qml.MomentumOptimizer(stepsize=0.01, momentum=0.99) steps = 600 cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) for i in range(steps): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) conv = np.abs(energy - prev_energy) #if i % 4 == 0: #print("Iteration = {:}, E = {:.8f} Ha".format(i, energy)) if conv <= 1e-06: break # Return the ground state energy return energy
def train(stepsize, steps, X, Y, var): opt = qml.AdamOptimizer(stepsize) params = [] loss = [] step = [] for i in range(steps): var = opt.step(lambda v: cost(v, Xdata=X, Y=Y), var) params.append(var) loss.append(cost(var, Xdata=X, Y=Y)) step.append(i + 1) return print(step, params, loss)
def train_generator(gen_weights): opt = qml.AdamOptimizer(0.1) costs = np.array([]) for _ in tqdm(range(1)): # Number of epochs for i in tqdm(range(len(data))): global dat dat = data[i][0] gen_weights, cost = opt.step_and_cost(lambda gen_weights: gen_cost(gen_weights), gen_weights) np.append(costs, cost) np.save(f'gen-weights/gen_weights_{_}_{i}', gen_weights) np.save(f'costs/cost_{_}_{i}', costs)
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # # Initialize the quantum device n = len(H.wires) dev = qml.device('default.qubit', wires = n) # Randomly choose initial parameters (how many do you need?) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(n-1,)) # Set up a cost function opt = qml.AdamOptimizer(stepsize=0.2, beta1=0.9, beta2=0.99, eps=1e-08) #opt = qml.MomentumOptimizer(stepsize=0.01, momentum=0.99) steps = 600 cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) for i in range(steps): params, prev_energy= opt.step_and_cost(cost_fn, params) energy = cost_fn(params) conv = np.abs(energy - prev_energy) #if i % 4 == 0: #print("Iteration = {:}, E = {:.8f} Ha".format(i, energy)) if conv <= 1e-06: break # QHACK # # Return the ground state energy return energy
def optimize_circuit(params): """Minimize the variational circuit and return its minimum value. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. You should create a device and convert the variational_circuit function into an executable QNode. Next, you should minimize the variational circuit using gradient-based optimization to update the input params. Return the optimized value of the QNode as a single floating-point number. Args: params (np.ndarray): Input parameters to be optimized, of dimension 30 Returns: float: the value of the optimized QNode """ optimal_value = 0.0 # QHACK # # Initialize the device # dev = ... dev = qml.device('default.qubit', wires=WIRES) # Instantiate the QNode # circuit = qml.QNode(variational_circuit, dev) circuit = qml.QNode(variational_circuit, dev) # Minimize the circuit def cost(params): return circuit(params) opt = qml.AdamOptimizer(stepsize=0.01) steps = 90 training_params = params for i in range(steps): training_params = opt.step(cost, training_params) optimal_value = cost(training_params) # QHACK # # Return the value of the minimized QNode return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # np.random.seed(0) # print(params) # Create a quantum device, dev = qml.device('default.qubit', wires=num_qubits) # set up a cost function cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) # and optimizer, # opt = qml.GradientDescentOptimizer(stepsize=0.4) opt = qml.AdamOptimizer(stepsize=0.4) # and run the VQE. (We recommend ~500 iterations to ensure convergence for this problem, or you can design your # own convergence criteria) max_iterations = 500 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) # if n % 20 == 0: # print('Iteration = {:}, Energy = {:.8f} Ha'.format(n, energy)) # QHACK # # Return the ground state energy return energy
def optimize_circuit(params): """Minimize the variational circuit and return its minimum value. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. You should create a device and convert the variational_circuit function into an executable QNode. Next, you should minimize the variational circuit using gradient-based optimization to update the input params. Return the optimized value of the QNode as a single floating-point number. Args: params (np.ndarray): Input parameters to be optimized, of dimension 30 Returns: float: the value of the optimized QNode """ optimal_value = 0.0 # QHACK # # Initialize the device dev = qml.device("default.qubit", wires=2) # Instantiate the QNode circuit = qml.QNode(variational_circuit, dev) # Minimize the circuit # eta = 0.01 opt = qml.AdamOptimizer() # print(circuit.shape) # print(qml.ExpvalCost(variational_circuit, params, dev)) # print(cost_fn) # print(40400) # for i in range(20): # print(circuit(params)) for i in range(200): theta_new = opt.step(circuit, params) params = theta_new # print(circuit(theta_new)) optimal_value = circuit(theta_new) # print('inloop') # print(variational_circuit(params)) # QHACK # # Return the value of the minimized QNode return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2 ** num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) dev = qml.device('default.qubit', wires=num_qubits) cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) opt = qml.AdamOptimizer(stepsize=0.1) np.random.seed(0) max_iterations = 1000 conv_tolerance = 0.00001 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) conv = np.abs(energy - prev_energy) if conv <= conv_tolerance: break # QHACK # # Return the ground state energy return energy
def run_vqe(): N = 15 # The number of pieces of quantum data that are used for each step max_time = 0.1 # The maximum value of time that can be used for quantum data def cost_function(params): # Separates the parameter list weight_params = params[0:6] bias_params = params[6:10] # Randomly samples times at which the QGRNN runs times_sampled = [np.random.uniform() * max_time for i in range(0, N)] # Cycles through each of the sampled times and calculates the cost total_cost = 0 for i in times_sampled: result = qnode(weight_params, bias_params, time=i) total_cost += -1 * result return total_cost / N # Defines the new device qgrnn_dev = qml.device("default.qubit", wires=2 * qubit_number + 1) # Defines the new QNode qnode = qml.QNode(qgrnn, qgrnn_dev) iterations = 0 optimizer = qml.AdamOptimizer(stepsize=0.5) steps = 10 qgrnn_params = list( [np.random.randint(-20, 20) / 50 for i in range(0, 10)]) init = copy.copy(qgrnn_params) # Executes the optimization method for i in range(0, steps): qgrnn_params = optimizer.step(cost_function, qgrnn_params) if iterations % 5 == 0: # print( # "Fidelity at Step " + str(iterations) + ": " + str((-1 * total_cost / N)._value) # ) print(" Mean Parameters at Step " + str(iterations) + ": " + str(np.mean(qgrnn_params))) print("---------------------------------------------") return qgrnn_params, init
def train(self, n_epochs, batch_size, learning_rate=0.01, starting_weights=None, compute_cost=True): """Train the embedding with given hyperparameters. Args: n_epochs (int): Number of times the optimizer is exposed to the whole training dataset. batch_size (int): Size of batches to use when iterating over training dataset. learning_rate (float): Learning rate (stepsize) of the optimizer. starting_weights (np.array, optional): If supplied the optimizer will start from given weights. Otherwise, random weights will be generated using the BaseEmbedding.random_starting_weights method. Returns: Tuple containing final weights, weights after each epoch and cost after each epoch. """ self.opt = qml.AdamOptimizer(stepsize=learning_rate) return self._train(n_epochs, batch_size, starting_weights, compute_cost)
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # num_qubits = len(H.wires) num_param_sets = (num_qubits) - 1 energy = 0 dev = qml.device('default.qubit', wires=H.wires) cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) opt = qml.AdamOptimizer(stepsize=0.1) params = np.random.normal(0, np.pi, (num_param_sets, )) conv_tol = 1e-06 max_iterations = 200 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) conv = np.abs(energy - prev_energy) if conv <= conv_tol: break # QHACK # # Return the ground state energy return energy
def run_vqe(H): """This function runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2 ** num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. dev = qml.device("default.qubit",wires=num_qubits) qnodes= qml.QNode(variational_ansatz,dev) cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) opt = qml.AdamOptimizer(stepsize=0.1) max_iterations = 200 conv_tol = 1e-06 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) if n%10 ==0 : print(" Energy for iteration "+str(n)+" : "+str(energy)) # Return the ground state energy return energy
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) dev = qml.device("default.qubit", wires=2) circuit = qml.QNode(variational_ansatz, dev) # eta=0.01 opt = qml.AdamOptimizer(stepsize=0.03) # for i in range(500): cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) for i in range(550): theta_new = opt.step(cost_fn, params) params = theta_new energy = cost_fn(theta_new) # QHACK # # Return the ground state energy return energy
def optimize_steps(circuit, init_params, iterations=100, opt=None): """Generic optimization of a parameterized circuit using initial parameters. Args: circuit: a Pennylane circuit that accepts `params` as its argument. The output of this circuit should an observable that we wish to _maximize_. init_params (np.ndarray): The set of parameters to start optimization iterations: Number of optimization iterations to perform. opt: Pennylane optimizer. Returns: cost_tape (np.ndarray): Shape (iterations,) tape of cost evaluations param_tape (np.ndarray): Shape (iterations, len(init_params)) tape of parameter values during optimization """ if opt is None: opt = qml.AdamOptimizer(stepsize=0.01) # Convert to minimization problem cost = lambda x: -1 * circuit(x) cost_tape = np.zeros(iterations) param_tape = np.zeros((iterations, len(init_params))) # Optimize params = np.copy(init_params) for step in range(iterations): params = opt.step(cost, params) cost_eval = cost(params) cost_tape[step] = cost_eval param_tape[step, :] = params return cost_tape, param_tape
return result ############################################################################## # Evaluating our cost function with some initial parameters, we can test out # that our cost function evaluates correctly. init_params = strong_ent_layers_uniform(n_layers=num_layers, n_wires=num_wires) print(cost(init_params)) ############################################################################## # Performing the optimization, with the number of shots randomly # determined at each optimization step: opt = qml.AdamOptimizer(0.05) params = init_params cost_wrs = [] shots_wrs = [] for i in range(100): params = opt.step(cost, params) cost_wrs.append(cost(params)) shots_wrs.append(total_shots * i) print("Step {}: cost = {} shots used = {}".format(i, cost_wrs[-1], shots_wrs[-1])) ############################################################################## # Let's compare this against an optimization not using weighted random sampling. # Here, we will split the 8000 total shots evenly across all Hamiltonian terms,
def classify_data(X_train, Y_train, X_test): """Develop and train your very own variational quantum classifier. Use the provided training data to train your classifier. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. The number of qubits, choice of variational ansatz, cost function, and optimization method are all to be developed by you in this function. Args: X_train (np.ndarray): An array of floats of size (250, 3) to be used as training data. Y_train (np.ndarray): An array of size (250,) which are the categorical labels associated to the training data. The categories are labeled by -1, 0, and 1. X_test (np.ndarray): An array of floats of (50, 3) to serve as testing data. Returns: str: The predicted categories of X_test, converted from a list of ints to a comma-separated string. """ # Use this array to make a prediction for the labels of the data in X_test predictions = [] # QHACK # np.random.seed(0) num_classes = 3 margin = 0.15 feature_size = 3 # the number of the required qubits is calculated from the number of features num_qubits = int(np.ceil(np.log2(feature_size))) num_layers = 2 dev = qml.device("default.qubit", wires=num_qubits) def layer(W): for i in range(num_qubits): qml.Rot(W[i, 0], W[i, 1], W[i, 2], wires=i) for j in range(num_qubits - 1): qml.CNOT(wires=[j, j + 1]) if num_qubits >= 2: # Apply additional CNOT to entangle the last with the first qubit qml.CNOT(wires=[num_qubits - 1, 0]) def circuit(weights, feat=None): qml.templates.embeddings.AmplitudeEmbedding(feat, range(num_qubits), pad=0.0, normalize=True) for W in weights: layer(W) return qml.expval(qml.PauliZ(0)) qnodes = [] for iq in range(num_classes): qnode = qml.QNode(circuit, dev) qnodes.append(qnode) def variational_classifier(q_circuit, params, feat): weights = params[0] bias = params[1] return q_circuit(weights, feat=feat) + bias def multiclass_svm_loss(q_circuits, all_params, feature_vecs, true_labels): loss = 0 num_samples = len(true_labels) for i, feature_vec in enumerate(feature_vecs): # Compute the score given to this sample by the classifier corresponding to the # true label. So for a true label of 1, get the score computed by classifer 1, # which distinguishes between "class 1" or "not class 1". s_true = variational_classifier( q_circuits[int(true_labels[i])], (all_params[0][int(true_labels[i])], all_params[1][int( true_labels[i])]), feature_vec, ) s_true = s_true li = 0 # Get the scores computed for this sample by the other classifiers for j in range(num_classes): if j != int(true_labels[i]): s_j = variational_classifier( q_circuits[j], (all_params[0][j], all_params[1][j]), feature_vec) s_j = s_j li += max(0, s_j - s_true + margin) loss += li return loss / num_samples def classify(q_circuits, all_params, feature_vecs): predicted_labels = [] for i, feature_vec in enumerate(feature_vecs): scores = np.zeros(num_classes) for c in range(num_classes): score = variational_classifier( q_circuits[c], (all_params[0][c], all_params[1][c]), feature_vec) scores[c] = float(score) pred_class = np.argmax(scores) predicted_labels.append(pred_class) return predicted_labels all_weights = [(0.1 * np.random.rand(num_layers, num_qubits, 3)) for i in range(num_classes)] all_bias = [(0.1 * np.ones(1)) for i in range(num_classes)] training_params = (all_weights, all_bias) q_circuits = qnodes opt = qml.AdamOptimizer(stepsize=0.18) steps = 12 cost_tol = 0.008 Y_train += 1 # To change labels to 0, 1, 2 for i in range(steps): training_params, prev_cost = opt.step_and_cost( lambda v: multiclass_svm_loss(q_circuits, v, X_train, Y_train), training_params) if prev_cost <= cost_tol: break pred = classify(q_circuits, training_params, X_test) pred = [x - 1 for x in pred] # To get original label predictions = pred # QHACK # return array_to_concatenated_string(predictions)
def find_excited_states(H): """ Fill in the missing parts between the # QHACK # markers below. Implement a variational method that can find the three lowest energies of the provided Hamiltonian. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The lowest three eigenenergies of the Hamiltonian as a comma-separated string, sorted from smallest to largest. """ energies = np.zeros(3) # QHACK # num_qubits = len(H.wires) #print(H.wires) num_param_sets = (2**num_qubits) - 1 saved_params = [] dev = qml.device("default.qubit", wires=num_qubits) # circuit from vqe-100 def variational_ansatz(params, wires): n_qubits = len(wires) n_rotations = len(params) if n_rotations > 1: n_layers = n_rotations // n_qubits n_extra_rots = n_rotations - n_layers * n_qubits # Alternating layers of unitary rotations on every qubit followed by a # ring cascade of CNOTs. for layer_idx in range(n_layers): layer_params = params[layer_idx * n_qubits:layer_idx * n_qubits + n_qubits] if layer_idx == 0: qml.broadcast(qml.RY, wires, pattern="single", parameters=layer_params) else: qml.broadcast(qml.CNOT, wires, pattern="ring") qml.broadcast(qml.RY, wires, pattern="single", parameters=layer_params) # There may be "extra" parameter sets required for which it's not necessarily # to perform another full alternating cycle. Apply these to the qubits as needed. extra_params = params[-n_extra_rots:] extra_wires = wires[:n_qubits - 1 - n_extra_rots:-1] extra_wires2 = wires[:n_qubits - 2 - n_extra_rots:-1] #print("ew",extra_wires) #print("test",extra_wires2) if n_qubits > 2: qml.broadcast(qml.CNOT, extra_wires2, pattern="ring") else: qml.broadcast(qml.CNOT, [1, 0], pattern='chain') qml.broadcast(qml.RY, extra_wires, pattern="single", parameters=extra_params) else: # For 1-qubit case, just a single rotation to the qubit qml.RY(params[0], wires=wires[0]) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets)) # test #@qml.qnode(dev) #def circuit(params): # variational_ansatz(params,dev.wires) # return qml.expval(qml.PauliZ(0)) #result = circuit(params) #print(circuit.draw()) #coeffs = H.coeffs #ops = H.ops #Hmat = np.zeros((2**num_qubits,2**num_qubits)) #print(coeffs,ops) #print(ops[0].matrix) #for i in range(len(coeffs)): # Hmat += coeffs[i] * ops[i].matrix #print(Hmat) #print(np.linalg.eigs(Hmat)) #print("H",H) # find ground state cost0 = qml.ExpvalCost(variational_ansatz, H, dev) #opt = qml.GradientDescentOptimizer(0.1) opt = qml.AdamOptimizer(0.1) #opt = qml.AdagradOptimizer(0.1) #print(H.wires) min_50 = np.inf for i in range(500): if i % 25 == 0: if abs(cost0(params) - min_50) < 1e-4: break min_50 = cost0(params) #print(f"step {i}, E_0 {cost0(params)}") params = opt.step(cost0, params) energies[0] = cost0(params) saved_params.append(params) #print(energies[0],cost0(params)) # function for overlaps qml.enable_tape() @qml.qnode(dev) def get_state(params): variational_ansatz(params, dev.wires) return qml.state() overlap_state1 = get_state(params) overlap_herm1 = np.outer(overlap_state1.conj(), overlap_state1) #print("psi_0",overlap_state1) #print(overlap_herm1) # find the first excited params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets)) a = 100 # big number to enforce orthogonality overlap_Ham = qml.Hamiltonian(coeffs=[ a, ], observables=[ qml.Hermitian(overlap_herm1, dev.wires), ]) #print("a|psi_0><psi_0",overlap_Ham,overlap_Ham.ops) H1 = H + overlap_Ham #print("H1",H1) cost = qml.ExpvalCost( variational_ansatz, H1, dev) # + qml.ExpvalCost(variational_ansatz, overlap_Ham, dev) #print(cost(saved_params[0]),a+energies[0],a+cost0(saved_params[0])) min_50 = np.inf for i in range(1500): if i % 25 == 0: if abs(cost0(params) - min_50) < 1e-4: break #print(f"step {i}, E_1 {cost0(params)}, cost {cost(params)}") min_50 = cost0(params) params = opt.step(cost, params) energies[1] = cost0(params) saved_params.append(params) #print(energies[1],cost(params)) overlap_state2 = get_state(params) overlap_herm2 = np.outer(overlap_state2.conj(), overlap_state2) #print("|psi_1>",overlap_state2) #print(overlap_herm2) # find the second excited params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets)) b = 100 overlap_Ham = qml.Hamiltonian(coeffs=[a, b], observables=[ qml.Hermitian(overlap_herm1, dev.wires), qml.Hermitian(overlap_herm2, dev.wires) ]) #print("a|psi_0><psi_0|+b|psi_1><psi_1",overlap_Ham,overlap_Ham.ops) H2 = H + overlap_Ham #print("H2",H2) cost = qml.ExpvalCost( variational_ansatz, H2, dev) # + qml.ExpvalCost(variational_ansatz, overlap_Ham, dev) min_50 = np.inf for i in range(1500): if i % 25 == 0: if abs(cost0(params) - min_50) < 1e-4: break #print(f"step {i}, E_2 {cost0(params)}, cost {cost(params)}") min_50 = cost0(params) params = opt.step(cost, params) energies[2] = cost0(params) saved_params.append(params) # QHACK # return ",".join([str(E) for E in energies])
def classify_data(X_train, Y_train, X_test): """Develop and train your very own variational quantum classifier. Use the provided training data to train your classifier. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. The number of qubits, choice of variational ansatz, cost function, and optimization method are all to be developed by you in this function. Args: X_train (np.ndarray): An array of floats of size (250, 3) to be used as training data. Y_train (np.ndarray): An array of size (250,) which are the categorical labels associated to the training data. The categories are labeled by -1, 0, and 1. X_test (np.ndarray): An array of floats of (50, 3) to serve as testing data. Returns: str: The predicted categories of X_test, converted from a list of ints to a comma-separated string. """ # Use this array to make a prediction for the labels of the data in X_test predictions = [] # QHACK # dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def variational_classifier(weights, x): for w in weights: qml.Rot(*x, wires=0) qml.Rot(*w, wires=0) # return qml.expval(qml.Hermitian(y, wires=[0])) return qml.expval(qml.PauliZ(0)) def square_loss(labels, predictions): loss = 0 for l, p in zip(labels, predictions): loss = loss + (l - p)**2 loss = loss / len(labels) return loss def cost(var, X, Y): predictions = [variational_classifier(var, x) for x in X] return square_loss(Y, predictions) num_layers = 3 weights = np.random.uniform(size=(num_layers, 3)) steps = 100 batch_size = 25 opt = qml.AdamOptimizer(0.1) np.random.seed(0) for i in range(steps): batch_index = np.random.randint(0, len(X_train), (batch_size, )) X_batch = X_train[batch_index] Y_batch = Y_train[batch_index] weights = opt.step(lambda weights: cost(weights, X_batch, Y_batch), weights) predictions = [variational_classifier(weights, x) for x in X_test] for i in range(len(X_test)): if predictions[i] < -0.5: predictions[i] = -1 elif predictions[i] > 0.5: predictions[i] = 1 else: predictions[i] = 0 # QHACK # return array_to_concatenated_string(predictions)
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) dev = qml.device('default.qubit', wires=H.wires) #variational_ansatz(params,H.wires) cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) eta = 0.4 opt = qml.AdamOptimizer(stepsize=eta) #GradientDescentOptimizer(eta) max_iterations = 100 conv_tol = 1e-6 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(cost_fn, params) energy = cost_fn(params) conv = np.abs((energy - prev_energy) / energy) #if n % 20 == 0: # print('Iteration = {:}, Energy = {:.8f} Ha'.format(n, energy)) #if 1e-4<=conv<1e-2: # opt.update_stepsize(0.05) #elif conv_tol<conv<1e-4: # opt.update_stepsize(0.01) if conv <= conv_tol: break #else: # continue #print() #print('Final convergence parameter = {:.8f} Ha'.format(conv)) #print('Final value of the ground-state energy = {:.8f} Ha'.format(energy)) #print('Accuracy with respect to the FCI energy: {:.8f} Ha ({:.8f} kcal/mol)'.format( #np.abs(energy - (-1.136189454088)), np.abs(energy - (-1.136189454088))*627.503 #)) #print() #print('Final circuit parameters = \n', params) # QHACK # # Return the ground state energy return energy
else: raise NotImplementedError("Not found initial state!") # The circuit for computing the expectation value of H. cost_fn = qml.ExpvalCost(ansatz, H, dev, optimize=True) print(f"Total # of Parameters={num_params}") # Perform VQE. step_size = args.step if args.opt == "qng": opt = qml.QNGOptimizer(stepsize=step_size, diag_approx=True, lam=0.1) elif args.opt == "adagrad": opt = qml.AdagradOptimizer(stepsize=step_size) elif args.opt == "adam": opt = qml.AdamOptimizer(stepsize=step_size) else: raise NotImplementedError("Optimizer not found") # Initialize the parameters. np.random.seed(1) if args.randomize == 1: params = np.pi * (np.random.rand(num_params) - 1.0) elif args.randomize == 2: params = np.loadtxt( f"params_{args.ansatz}_{args.two_qubit}_{num_qubits}_{h}_{args.opt}_{step_size}_{initState}{1}.txt" ) print(len(params), num_params) assert len(params) == num_params else: params = np.zeros([num_params])
params0 = np.pi*(np.random.rand(num_params) - 1.0) # Optimizer parameters. rtol = 1e-5 atol = 1e-9 maxiter = 50 stepsize = 0.05 print_results = True # The different optimizers to test. opt_names = ["QNG", "Grad", "AdaGrad", "Adam"] opts = [qml.QNGOptimizer(stepsize=stepsize, diag_approx=True, lam=0.1), qml.GradientDescentOptimizer(stepsize=stepsize), qml.AdagradOptimizer(stepsize=stepsize), qml.AdamOptimizer(stepsize=stepsize)] # Perform VQE. times_opts = [] objs_opts = [] for (opt, opt_name) in zip(opts, opt_names): times = [] objs = [] print(f"OPTIMIZER {opt_name}") params = np.copy(params0) prev_obj = 0.0 for step in range(maxiter): start = time.time() params, obj = opt.step_and_cost(cost_fn, params) end = time.time()
def find_excited_states(H): """ Fill in the missing parts between the # QHACK # markers below. Implement a variational method that can find the three lowest energies of the provided Hamiltonian. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The lowest three eigenenergies of the Hamiltonian as a comma-separated string, sorted from smallest to largest. """ energies = np.zeros(3) # QHACK # # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2 ** num_qubits) - 1 #np.random.seed(0) #params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) params = np.random.uniform(low=0, high=2*np.pi, size=(6,num_qubits)) weights = [1,1,1]#np.random.uniform(0, 1, size=(3,)) print(weights) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) # QHACK # dev = qml.device('default.qubit', wires = num_qubits) @qml.qnode(dev) def testt(n_qubits): qml.BasisState(np.array([0,1] + [0 for i in range(n_qubits-2)]), wires=[i for i in range(n_qubits)]) return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)] print('testddddddddd', testt(num_qubits)) # Minimize the circuit #opt = qml.GradientDescentOptimizer(stepsize=0.4) opt = qml.AdamOptimizer(stepsize=0.1, beta1=0.9, beta2=0.99, eps=1e-08) #opt = qml.MomentumOptimizer(stepsize=0.1, momentum=0.99) steps = 600 #cost_fn_zero = qml.ExpvalCost(variational_ansatz_zero, H, dev) cost_fn_zero = qml.ExpvalCost(variational_ansatz_zero, H, dev) cost_fn_one = qml.ExpvalCost(variational_ansatz_one, H, dev) cost_fn_two = qml.ExpvalCost(variational_ansatz_two, H, dev) def cost(params): total = cost_fn_zero(params)*weights[0] + cost_fn_one(params)*weights[1] + cost_fn_two(params)*weights[2] return total def cost_fn(*qnode_args, **qnode_kwargs): """Combine results from grouped QNode executions with grouped coefficients""" total = 0 total = cost_fn_zero(*qnode_args, **qnode_kwargs)*weights[0] + cost_fn_one(*qnode_args, **qnode_kwargs)*weights[1] + cost_fn_two(*qnode_args, **qnode_kwargs)*weights[2] return total ''' method = "BFGS" options = {"disp": True, "maxiter": 50, "gtol": 1e-6} opt = minimize(cost, params, method=method, callback=callback) ''' for i in range(steps): params, prev_energy_total= opt.step_and_cost(cost_fn, params) total_energy = cost_fn(params) conv = np.abs(total_energy - prev_energy_total) if i % 4 == 0: print("Iteration = {:}, E = {:.8f} Ha".format(i, total_energy)) if conv <= 1e-06: break energies[0] = cost_fn_zero(params) energies[1] = cost_fn_one(params) energies[2] = cost_fn_two(params) # QHACK # return sorted(energies)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ###################################################################### # The next step is to optimize the weights in order to fit the ground # truth. # def cost(weights, x, y): predictions = [serial_quantum_model(weights, x_) for x_ in x] return square_loss(y, predictions) max_steps = 50 opt = qml.AdamOptimizer(0.3) batch_size = 25 cst = [cost(weights, x, target_y)] # initial cost for step in range(max_steps): # Select batch of data batch_index = np.random.randint(0, len(x), (batch_size, )) x_batch = x[batch_index] y_batch = target_y[batch_index] # Update the weights by one optimizer step weights = opt.step(lambda w: cost(w, x_batch, y_batch), weights) # Save, and possibly print, the current cost c = cost(weights, x, target_y)
return total_cost / N ###################################################################### # Next we set up for optimization. # # Defines the new device qgrnn_dev = qml.device("default.qubit", wires=2 * qubit_number + 1) # Defines the new QNode qgrnn_qnode = qml.QNode(qgrnn, qgrnn_dev) steps = 300 optimizer = qml.AdamOptimizer(stepsize=0.5) weights = rng.random(size=len(new_ising_graph.edges)) - 0.5 bias = rng.random(size=qubit_number) - 0.5 initial_weights = copy.copy(weights) initial_bias = copy.copy(bias) ###################################################################### # All that remains is executing the optimization loop. for i in range(0, steps): (weights, bias), cost = optimizer.step_and_cost(cost_function, weights, bias) # Prints the value of the cost function if i % 5 == 0:
def train_generator(gen_weights): opt = qml.AdamOptimizer(0.1) cost = lambda: gen_cost(gen_weights) for _ in range(50): gen_weights = opt.step(lambda gen_weights: gen_cost(gen_weights), gen_weights)
def classify_data(X_train, Y_train, X_test): """Develop and train your very own variational quantum classifier. Use the provided training data to train your classifier. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. The number of qubits, choice of variational ansatz, cost function, and optimization method are all to be developed by you in this function. Args: X_train (np.ndarray): An array of floats of size (250, 3) to be used as training data. Y_train (np.ndarray): An array of size (250,) which are the categorical labels associated to the training data. The categories are labeled by -1, 0, and 1. X_test (np.ndarray): An array of floats of (50, 3) to serve as testing data. Returns: str: The predicted categories of X_test, converted from a list of ints to a comma-separated string. """ # Use this array to make a prediction for the labels of the data in X_test predictions = [] # QHACK # np.random.seed(42) dev = qml.device("default.qubit", wires=3) def layer(W): qml.Rot(W[0, 0], W[0, 1], W[0, 2], wires=0) qml.Rot(W[1, 0], W[1, 1], W[1, 2], wires=1) qml.Rot(W[2, 0], W[2, 1], W[2, 2], wires=2) qml.CNOT(wires=[0, 1]) qml.CNOT(wires=[1, 2]) qml.CNOT(wires=[2, 0]) def stateprep(x): qml.templates.embeddings.AngleEmbedding(x, wires=[0, 1, 2]) @qml.qnode(dev) def circuit(weights, x): stateprep(x) for W in weights: layer(W) return qml.expval(qml.PauliZ(0)) def variational_classifier(var, x): weights = var[0] bias = var[1] return circuit(weights, x) + bias def square_loss(labels, predictions): loss = 0 for l, p in zip(labels, predictions): loss = loss + (l - p)**2 loss = loss / len(labels) return loss def cost(var, X, Y): predictions = [variational_classifier(var, x) for x in X] return square_loss(Y, predictions) def accuracy(labels, predictions): loss = 0 for l, p in zip(labels, predictions): if abs(l - p) < 1e-5: loss = loss + 1 loss = loss / len(labels) return loss num_layers = 3 num_qubits = 3 var_init = (np.random.randn(num_layers, num_qubits, 3), 0.0) opt = qml.AdamOptimizer(0.12) batch_size = 10 def pred(x): if x > 0.33: return 1 if x > -0.33: return 0 else: return -1 var = var_init for it in range(25): # Update the weights by one optimizer step batch_index = np.random.randint(0, len(X_train), (batch_size, )) X_batch = X_train[batch_index] Y_batch = Y_train[batch_index] var = opt.step(lambda v: cost(v, X_batch, Y_batch), var) # Compute accuracy predictions = [pred(variational_classifier(var, x)) for x in X_train] acc = accuracy(Y_train, predictions) #print( # "Iter: {:5d} | Cost: {:0.7f} | Accuracy: {:0.7f} ".format( # it + 1, cost(var, X_train, Y_train), acc # ) #) if acc > 0.95: break predictions = [pred(variational_classifier(var, x)) for x in X_test] # QHACK # return array_to_concatenated_string(predictions)
def find_excited_states(H): """ Fill in the missing parts between the # QHACK # markers below. Implement a variational method that can find the three lowest energies of the provided Hamiltonian. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The lowest three eigenenergies of the Hamiltonian as a comma-separated string, sorted from smallest to largest. """ energies = np.zeros(3) # QHACK # def variational_ansatz(params, wires, *, state_n): """ Args: params (np.ndarray): An array of floating-point numbers with size (n, 3), where n is the number of parameter sets required (this is determined by the problem Hamiltonian). wires (qml.Wires): The device wires this circuit will run on. """ n_qubits = len(wires) n_rotations = len(params) state = np.repeat([0], n_qubits) state[0:state_n] = 1 qml.BasisState(state, wires=wires) if n_rotations > 1: n_layers = n_rotations // n_qubits n_extra_rots = n_rotations - n_layers * n_qubits # Alternating layers of unitary rotations on every qubit followed by a # ring cascade of CNOTs. for layer_idx in range(n_layers): layer_params = params[layer_idx * n_qubits: layer_idx * n_qubits + n_qubits, :] qml.broadcast(qml.RY, wires, pattern="single", parameters=layer_params[:, 0]) qml.broadcast(qml.RZ, wires, pattern="single", parameters=layer_params[:, 1]) qml.broadcast(qml.CNOT, wires, pattern="ring") if n_extra_rots > 0: # There may be "extra" parameter sets required for which it's not necessarily # to perform another full alternating cycle. Apply these to the qubits as needed. extra_params = params[-n_extra_rots:, :] extra_wires = wires[: n_qubits - 1 - n_extra_rots: -1] qml.broadcast(qml.RY, extra_wires, pattern="single", parameters=extra_params[:, 0]) qml.broadcast(qml.RZ, extra_wires, pattern="single", parameters=extra_params[:, 1]) else: # For 1-qubit case, just a single rotation to the qubit qml.Rot(*params[0], wires=wires[0]) num_qubits = len(H.wires) dev = qml.device('default.qubit', wires=num_qubits) from functools import partial cost_fns = [qml.ExpvalCost(partial(variational_ansatz, state_n=i), H, dev) for i in [0, 1, 2]] opt = qml.AdamOptimizer(stepsize=0.4) # print('opt = qml.AdamOptimizer(stepsize=0.4)') max_iterations = 500 rel_conv_tol = 1e-6 num_param_sets = num_qubits ** 2 # np.random.seed(1234) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 2)) import time clock = time.time() weights = np.array([3., 2., 1.]) weights = weights / np.sum(weights) for n in range(max_iterations): def cost_fn(params): return [cost_fn(params) for cost_fn in cost_fns] @ weights params, prev_cost = opt.step_and_cost(cost_fn, params) cost = cost_fn(params) conv = np.abs((cost - prev_cost) / cost) # DEBUG PRINT if n % 20 == 0: energies = [cf(params) for cf in cost_fns] print(f'Iteration = {n}, cost = {cost} energies = ', energies, f'time {time.time() - clock:.0f}s') energies = np.sort(energies) alpha = 0.1 weights = np.array( [1 + alpha/(energies[2]-energies[1]) + alpha/(energies[1]-energies[0]), 1 + alpha/(energies[2]-energies[1]), 1] ) weights /= np.sum(weights) print('new weights:', weights) if (conv <= rel_conv_tol) and (n % 20 == 1): break energies = sorted(list([cf(params) for cf in cost_fns])) # from scipy.optimize import minimize # cf_for_scipy = lambda params: cost_fn(np.reshape(params, (-1, 3))) # res = minimize(cf_for_scipy, np.ravel(params), method='COBYLA', # options=dict(maxiter=1000)) # print(res) # print('energies: ', [cf(np.reshape(res['x'], (-1, 3))) for cf in cost_fns]) print(f'tot time: ', time.time() - clock) # QHACK # return ",".join([str(E) for E in energies])
# :math:`\vec{a}'=(a'_1, a'_2, a'_3)` is the vector of the state prepared # by the circuit. Optimization is carried out using the Adam optimizer. # Finally, we compare the Bloch vectors of the target and output state. # cost function def cost_fn(params): cost = 0 for k in range(3): cost += np.abs(circuit(params, A=Paulis[k]) - bloch_v[k]) return cost # set up the optimizer opt = qml.AdamOptimizer() # number of steps in the optimization routine steps = 200 # the final stage of optimization isn't always the best, so we keep track of # the best parameters along the way best_cost = cost_fn(params) best_params = np.zeros((nr_qubits, nr_layers, 3)) print("Cost after 0 steps is {:.4f}".format(cost_fn(params))) # optimization begins for n in range(steps): params = opt.step(cost_fn, params) current_cost = cost_fn(params)
def qaoa_maxcut(opt, graph, n_layers, verbose=False, shots=None, MeshGrid=False, NoiseModel=None): start = time.time() if opt == "adam": opt = qml.AdamOptimizer(0.1) elif opt == "gd": opt = qml.GradientDescentOptimizer(0.1) elif opt == "qng": opt = qml.QNGOptimizer(0.1) elif opt == "roto": opt = qml.RotosolveOptimizer() # SETUP PARAMETERS n_wires = len(graph.nodes) edges = graph.edges def U_B(beta): for wire in range(n_wires): qml.RX(2 * beta, wires=wire) def U_C(gamma): for edge in edges: wire1 = edge[0] wire2 = edge[1] qml.CNOT(wires=[wire1, wire2]) qml.RZ(gamma, wires=wire2) qml.CNOT(wires=[wire1, wire2]) if NoiseModel: if shots: print("Starting shots", shots) dev = qml.device("qiskit.aer", wires=n_wires, shots=shots, noise_model=NoiseModel) else: dev = qml.device("qiskit.aer", wires=n_wires, noise_model=NoiseModel) else: if shots: print("Starting shots", shots) dev = qml.device("default.qubit", wires=n_wires, analytic=False, shots=shots) else: dev = qml.device("default.qubit", wires=n_wires, analytic=True, shots=1) @qml.qnode(dev) def circuit(gammas, betas, edge=None, n_layers=1, n_wires=1): for wire in range(n_wires): qml.Hadamard(wires=wire) for i,j in zip(range(n_wires),range(n_layers)): U_C(gammas[i,j]) U_B(betas[i,j]) if edges is None: # measurement phase return qml.sample(comp_basis_measurement(range(n_wires))) return qml.expval(qml.Hermitian(pauli_z_2, wires=edge)) np.random.seed(42) init_params = 0.01 * np.random.rand(2, n_wires, n_layers) def obj_wrapper(params): objstart = partial(objective, params, True, False) objend = partial(objective, params, False, True) return np.vectorize(objstart), np.vectorize(objend) def objective(params, start=False, end=False, X=None, Y=None): gammas = params[0] betas = params[1] if start: gammas[0,0] = X betas[0,0] = Y elif end: gammas[-1,0] = X betas[-1,0] = Y neg_obj = 0 for edge in edges: neg_obj -= 0.5 * (1 - circuit(gammas, betas, edge=edge, n_layers=n_layers, n_wires=n_wires)) return neg_obj paramsrecord = [init_params.tolist()] print(f"Start objective fn {objective(init_params)}") params = init_params losses = [objective(params)] print(f"{str(opt).split('.')[-1]} with {len(graph.nodes)} nodes initial loss {losses[0]}") steps = NUM_STEPS for i in range(steps): params = opt.step(objective, params) if i == 0: print(f"{str(opt).split('.')[-1]} with {len(graph.nodes)} nodes took {time.time()-start:.5f}s for 1 iteration") paramsrecord.append(params.tolist()) losses.append(objective(params)) if verbose: if i % 5 == 0: print(f"Objective at step {i} is {losses[-1]}") if i % 10 == 0 and shots: print("Shots", shots, "is up to", i) if MeshGrid: grid_size = 100 X, Y = np.meshgrid(np.linspace(-np.pi,np.pi,grid_size),np.linspace(-np.pi,np.pi,grid_size)) objstart, objend = obj_wrapper(init_params) meshgridfirststartparams = objstart(X, Y) meshgridfirstlastparams = objend(X,Y) objstart, objend = obj_wrapper(params) meshgridendfirstparams = objstart(X, Y) meshgridendlastparams = objend(X,Y) return {"losses":losses, "params":paramsrecord,\ "MeshGridStartFirstParams":meshgridfirststartparams, "MeshGridStartLastParams":meshgridfirstlastparams, \ "MeshGridEndFirstParams":meshgridendfirstparams, "MeshGridEndLastParams":meshgridendlastparams} else: return shots, losses