Esempio n. 1
0
 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)
Esempio n. 2
0
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
Esempio n. 3
0
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)
Esempio n. 4
0
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)
Esempio n. 5
0
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
Esempio n. 7
0
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
Esempio n. 9
0
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
Esempio n. 10
0
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
Esempio n. 14
0
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
Esempio n. 15
0
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
Esempio n. 16
0
    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)
Esempio n. 18
0
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
Esempio n. 21
0
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])
Esempio n. 22
0
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()
Esempio n. 23
0
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)
Esempio n. 25
0
    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)
Esempio n. 28
0
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])
Esempio n. 29
0
# :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)
Esempio n. 30
0
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