def __init__(self, env, qnode, params, alpha=0.1, gamma=0.9, epsilon_max=1.0, epsilon_min=0.2, epsilon_halflife=10, memory_size=100000, memory_sampling=10, update_freq=100): self.env = env self.learner = qnode self.params = np.array(params, requires_grad=True) self.actions = [0, 1, 2, 3] self.alpha = alpha self.gamma = gamma self.opt = qml.NesterovMomentumOptimizer(self.alpha) self.epsilon_max = epsilon_max self.epsilon_min = epsilon_min self.epsilon_halflife = epsilon_halflife self.visits = {} self.training = True self.memory_size = memory_size self.memory_sampling = memory_sampling self.memory_table = [] self.update_freq = 100 self.counter = 0
def QAOA(n_layers=1, verbose=False): """ Quantum Approximation Optimization Algorithm: Uses a Nesterov Momentum optimizer to variate the hyperparameters theta (one per gate Rx, Rz and layer) to minimize the cost 1 - F. """ init_params = 2*np.pi * \ np.random.rand( 2, n_layers, n_wires) # Possible improvement: limit params. to [0, 2pi) (due to periodicity) def cost(params): thetaEven_s = params[0] thetaOdd_s = params[1] return (1 - np.sqrt(circuit(thetaEven_s, thetaOdd_s, n_layers=n_layers))) # itialize optimizer: Nesterov with momentum chosen after trying various opt = qml.NesterovMomentumOptimizer(stepsize=0.02, momentum=0.9) losses = [] # optimize parameters in cost params = init_params for i in range(steps): params = opt.step(cost, params) loss = cost(params) losses.append(loss) if verbose == True: if (i + 1) % 5 == 0: print("Objective after step {:5d}: {: .7f}".format( i + 1, loss)) print(f"Layer architecture {0} finished".format(n_layers)) return losses
def find_max_independent_set(graph, params): """Find the maximum independent set of an input graph given some optimized QAOA parameters. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. You should create a device, set up the QAOA ansatz circuit and measure the probabilities of that circuit using the given optimized parameters. Your next step will be to analyze the probabilities and determine the maximum independent set of the graph. Return the maximum independent set as an ordered list of nodes. Args: graph (nx.Graph): A NetworkX graph params (np.ndarray): Optimized QAOA parameters of shape (2, 10) Returns: list[int]: the maximum independent set, specified as a list of nodes in ascending order """ cost_h, mixer_h = qaoa.max_independent_set(graph) def qaoa_layer(gamma, alpha): qaoa.cost_layer(gamma, cost_h) qaoa.mixer_layer(alpha, mixer_h) def circuit(params, **kwargs): for w in range(NODES): qml.Hadamard(wires=w) qml.layer(qaoa_layer, N_LAYERS, params[0], params[1]) dev = qml.device('default.qubit', wires=NODES) cost_function = qml.ExpvalCost(circuit, cost_h, dev) optimizer = qml.NesterovMomentumOptimizer() for i in range(3): params = optimizer.step(cost_function, params) @qml.qnode(dev) def probability_circuit(gamma, alpha): circuit([gamma, alpha]) return qml.probs(wires=[x for x in range(NODES)]) probs = probability_circuit(params[0], params[1]) most_freq_bit_string = np.argmax(probs) result = [] for i in reversed(range(NODES)): if most_freq_bit_string & (1 << i) != 0: result.append(NODES - 1 - i) return result
def train(weights, wires, X, Y, steps, batch_size, k): #opt = qml.GradientDescentOptimizer(0.1) opt = qml.NesterovMomentumOptimizer(0.1) for _ in range(steps): batch_idx = np.random.randint(0, len(X), (batch_size,)) X_batch = X[batch_idx] Y_batch = Y[batch_idx] weights, prev_cost = opt.step_and_cost(lambda weights: cost(weights, wires, X_batch, Y_batch, k), weights) print(prev_cost) #print(weights) opt_cost = cost(weights, wires, X, Y, k) print(opt_cost) return weights
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 = ... # Instantiate the QNode dev = qml.device('default.qubit', wires=WIRES) circuit = qml.QNode(variational_circuit, dev) gd_cost = [] opt = qml.NesterovMomentumOptimizer(stepsize=0.07) for _ in range(60): params = opt.step(circuit, params) gd_cost.append(circuit(params)) # Minimize the circuit optimal_value = circuit(params) # QHACK # # Return the value of the minimized QNode return optimal_value
def train_circuit(num_vertices, H): """Trains a quantum circuit to learn the ground state of the UDMIS Hamiltonian. Args: - num_vertices (int): The number of vertices/wires in the graph - H (qml.Hamiltonian): The result of qml.Hamiltonian(coeffs, obs) Returns: - E / num_vertices (float): The ground state energy density. """ dev = qml.device("default.qubit", wires=num_vertices) @qml.qnode(dev) def cost(params): """The energy expectation value of a Hamiltonian""" variational_circuit(params, num_vertices) return qml.expval(H) # QHACK # # define your trainable parameters and optimizer here # change the number of training iterations, `epochs`, if you want to # just be aware of the 80s time limit! epochs = 500 # number of training iterations num_layers = 2 # number of layers is variational quantum circuit params = np.ones([num_layers, num_vertices, 3]) # initial guess opt = qml.NesterovMomentumOptimizer() # optimiser # QHACK # for i in range(epochs): params, E = opt.step_and_cost(cost, params) return E / float(num_vertices)
def classify_ising_data(ising_configs, labels): """Learn the phases of the classical Ising model. Args: - ising_configs (np.ndarray): 250 rows of binary (0 and 1) Ising model configurations - labels (np.ndarray): 250 rows of labels (1 or -1) Returns: - predictions (list(int)): Your final model predictions Feel free to add any other functions than `cost` and `circuit` within the "# QHACK #" markers that you might need. """ # QHACK # num_wires = ising_configs.shape[1] dev = qml.device("default.qubit", wires=num_wires) # Define a variational circuit below with your needed arguments and return something meaningful @qml.qnode(dev) def circuit(params, ising_config): """ Variational quantum circuit """ # data encoding qml.BasisState(ising_config, wires=range(num_wires)) # variational quantum circuit for i in range(len(params)): for j in range(num_wires): qml.Rot(*params[i][j], wires=j) for j in range(num_wires - 1): qml.CNOT(wires=(j, j + 1)) qml.CNOT(wires=(num_wires - 1, 0)) # Return NN parity of entire spin chain # parity = [qml.PauliZ(i) for i in range(num_wires)] # parity = qml.operation.Tensor(*parity) # return qml.expval(parity) # return [qml.expval(qml.PauliZ(i) @ qml.PauliZ(i+1)) for i in range(num_wires-1)] return [qml.expval(qml.PauliZ(i)) for i in range(num_wires)] def variational_classifier(params, bias, ising_config): """ Decodes the quantum circuit output into a classification """ magnetisation = np.sum(circuit(params, ising_config)) return magnetisation + bias # Define a cost function below with your needed arguments def cost(params, bias, X, Y): # QHACK # # Insert an expression for your model predictions here predictions = [variational_classifier(params, bias, x) for x in X] # QHACK # return square_loss(Y, predictions) # DO NOT MODIFY this line # optimize your circuit here opt = qml.NesterovMomentumOptimizer() batch_size = 5 # number of ising_configs to train on in each iter num_layers = 3 # number of mixing layers in variational quantum circuit params = np.ones([num_layers, num_wires, 3]) # initial guess bias = np.array(0.0) # initial guess for i in range(100): # iteratively optimise batch_index = np.random.randint(0, len(labels), (batch_size, )) X = ising_configs[batch_index] Y = labels[batch_index] params, bias, _, _ = opt.step(cost, params, bias, X, Y) # make predictions w/ optimised circuit predictions = [] for ising_config in ising_configs: predict = variational_classifier(params, bias, ising_config) predictions.append(int(np.sign(predict))) # QHACK # return predictions