Ejemplo n.º 1
0
    def test_finite_diff_coherent_two_wires(self, tol):
        """Test that the jacobian of the probability for a coherent states is
        approximated well with finite differences"""
        cutoff = 4

        dev = qml.device("strawberryfields.fock", wires=2, cutoff_dim=cutoff)

        @qml.qnode(dev)
        def circuit(a, phi):
            qml.Displacement(a, phi, wires=0)
            qml.Displacement(a, phi, wires=1)
            return qml.probs(wires=[0, 1])

        a = 0.4
        phi = -0.12

        c = np.arange(cutoff)
        d = np.arange(cutoff)
        n0, n1 = np.meshgrid(c, d)
        n0 = n0.flatten()
        n1 = n1.flatten()

        # differentiate with respect to parameter a
        res_F = circuit.jacobian([a, phi], wrt={0}, method="F").flat
        expected_gradient = (2 * (a**(-1 + 2 * n0 + 2 * n1)) *
                             np.exp(-2 * a**2) * (-2 * a**2 + n0 + n1) /
                             (fac(n0) * fac(n1)))
        assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)

        # differentiate with respect to parameter phi
        res_F = circuit.jacobian([a, phi], wrt={1}, method="F").flat
        expected_gradient = 0
        assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)
Ejemplo n.º 2
0
def plot_surface(surface):
    X = np.arange(-np.pi, np.pi, 0.25)
    Y = np.arange(-np.pi, np.pi, 0.25)
    X, Y = np.meshgrid(X, Y)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    surf = ax.plot_surface(X, Y, surface, cmap="viridis", linewidth=0, antialiased=False)
    ax.set_zlim(0, 1)
    ax.zaxis.set_major_locator(LinearLocator(10))
    ax.zaxis.set_major_formatter(FormatStrFormatter("%.02f"))
    plt.show()
Ejemplo n.º 3
0
def make_meshgrid(X, padding=0.05, n_x=20, n_y=20):
    __sanity_checks(X)

    x_min, x_max = X[:, 0].min(), X[:, 0].max()
    y_min, y_max = X[:, 1].min(), X[:, 1].max()

    padding_x, padding_y = padding * (x_max - x_min), padding * (y_max - y_min)
    x_min -= padding_x
    x_max += padding_x
    y_min -= padding_y
    y_max += padding_y

    xx, yy = np.meshgrid(np.linspace(x_min, x_max, n_x),
                         np.linspace(y_min, y_max, n_y))
    return xx, yy
Ejemplo n.º 4
0
def generate_surface(cost_function):
    Z = []
    Z_assembler = []

    X = np.arange(-np.pi, np.pi, 0.25)
    Y = np.arange(-np.pi, np.pi, 0.25)
    X, Y = np.meshgrid(X, Y)

    for x in X[0, :]:
        for y in Y[:, 0]:
            rotations = [[x for i in range(wires)], [y for i in range(wires)]]
            Z_assembler.append(cost_function(rotations))
        Z.append(Z_assembler)
        Z_assembler = []

    Z = np.asarray(Z)
    return Z
Ejemplo n.º 5
0
def plot_decision_boundaries(classifier, ax, N_gridpoints=14):
    _xx, _yy = np.meshgrid(np.linspace(-1, 1, N_gridpoints),
                           np.linspace(-1, 1, N_gridpoints))

    _zz = np.zeros_like(_xx)
    for idx in np.ndindex(*_xx.shape):
        _zz[idx] = classifier.predict(
            np.array([_xx[idx], _yy[idx]])[np.newaxis, :])

    plot_data = {'_xx': _xx, '_yy': _yy, '_zz': _zz}
    ax.contourf(_xx,
                _yy,
                _zz,
                cmap=mpl.colors.ListedColormap(['#FF0000', '#0000FF']),
                alpha=.2,
                levels=[-1, 0, 1])
    dataset.plot(ax)

    return plot_data
Ejemplo n.º 6
0
def plot_surface(surface):
    X = np.arange(-np.pi, np.pi, 0.25)
    Y = np.arange(-np.pi, np.pi, 0.25)
    X, Y = np.meshgrid(X, Y)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    surf = ax.plot_surface(X,
                           Y,
                           surface,
                           cmap="viridis",
                           linewidth=0,
                           antialiased=False)
    ax.set_zlim(0, 1)
    ax.zaxis.set_major_locator(LinearLocator(10))
    ax.zaxis.set_major_formatter(FormatStrFormatter("%.02f"))
    #plt.show()
    plt.draw()
    plt.pause(0.001)
    input("Open Ports --> Open Preview or Browser --> push enter to continue")
Ejemplo n.º 7
0
def plot_decision_boundaries(classifier, ax, N_gridpoints=14):
    _xx, _yy = np.meshgrid(np.linspace(-1, 1, N_gridpoints),
                           np.linspace(-1, 1, N_gridpoints))

    _zz = np.zeros_like(_xx)
    for idx in np.ndindex(*_xx.shape):
        _zz[idx] = classifier.predict(
            np.array([_xx[idx], _yy[idx]])[np.newaxis, :])

    plot_data = {"_xx": _xx, "_yy": _yy, "_zz": _zz}
    ax.contourf(
        _xx,
        _yy,
        _zz,
        cmap=mpl.colors.ListedColormap(["#FF0000", "#0000FF"]),
        alpha=0.2,
        levels=[-1, 0, 1],
    )
    plot_double_cake_data(X, Y, ax)

    return plot_data
Ejemplo n.º 8
0
    def test_finite_diff_coherent_two_wires(self, tol):
        """Test that the jacobian of the probability for a coherent states is approximated well with
        finite differences"""
        cutoff = 4

        dev = qml.device("strawberryfields.gaussian", wires=2, cutoff_dim=cutoff)

        @qml.qnode_old.qnode(dev, diff_method="finite-diff")
        def circuit(a, phi):
            qml.Displacement(a, phi, wires=0)
            qml.Displacement(a, phi, wires=1)
            return qml.probs(wires=[0, 1])

        a = np.array(0.4, requires_grad=True)
        phi = np.array(-0.12, requires_grad=False)

        c = np.arange(cutoff)
        d = np.arange(cutoff)
        n0, n1 = np.meshgrid(c, d)
        n0 = n0.flatten()
        n1 = n1.flatten()

        # differentiate with respect to parameter a
        res_F = qml.jacobian(circuit)(a, phi)
        expected_gradient = (
            2
            * (a ** (-1 + 2 * n0 + 2 * n1))
            * np.exp(-2 * a**2)
            * (-2 * a**2 + n0 + n1)
            / (fac(n0) * fac(n1))
        )
        assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)

        # differentiate with respect to parameter phi
        a = np.array(0.4, requires_grad=False)
        phi = np.array(-0.12, requires_grad=True)

        res_F = qml.jacobian(circuit)(a, phi)
        expected_gradient = 0
        assert np.allclose(res_F, expected_gradient, atol=tol, rtol=0)
Ejemplo n.º 9
0
def draw(var_gd, var_ada):
    fig = plt.figure(figsize=(6, 4))
    ax = fig.gca(projection='3d')

    X = np.linspace(-3, 3, 30)
    Y = np.linspace(-3, 3, 30)
    xx, yy = np.meshgrid(X, Y)
    Z = np.array([[cost([x, y]) for x in X]
                  for y in Y]).reshape(len(Y), len(X))
    surf = ax.plot_surface(xx, yy, Z, cmap=cm.coolwarm, antialiased=False)

    path_z = [cost(var) + 1e-8 for var in var_gd]
    path_x = [v[0] for v in var_gd]
    path_y = [v[1] for v in var_gd]
    ax.plot(path_x,
            path_y,
            path_z,
            c='green',
            marker='.',
            label="graddesc",
            zorder=10)

    path_z = [cost(var) + 1e-8 for var in var_ada]
    path_x = [v[0] for v in var_ada]
    path_y = [v[1] for v in var_ada]
    ax.plot(path_x,
            path_y,
            path_z,
            c='purple',
            marker='.',
            label="adagrad",
            zorder=11)

    ax.set_xlabel("v1")
    ax.set_ylabel("v2")
    ax.zaxis.set_major_locator(MaxNLocator(nbins=5, prune='lower'))

    plt.legend()
    plt.show()
Ejemplo n.º 10
0
##############################################################################
# Cost function surface for circuit ansatz
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Now, we plot the cost function surface for later comparison with the surface generated
# by learning the circuit structure.

from matplotlib import cm
from matplotlib.ticker import MaxNLocator
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(6, 4))
ax = fig.gca(projection="3d")

X = np.linspace(-4.0, 4.0, 40)
Y = np.linspace(-4.0, 4.0, 40)
xx, yy = np.meshgrid(X, Y)
Z = np.array([[cost([x, y]) for x in X] for y in Y]).reshape(len(Y), len(X))
surf = ax.plot_surface(xx, yy, Z, cmap=cm.coolwarm, antialiased=False)

ax.set_xlabel(r"$\theta_1$")
ax.set_ylabel(r"$\theta_2$")
ax.zaxis.set_major_locator(MaxNLocator(nbins=5, prune="lower"))

plt.show()

##############################################################################
# It is apparent that, based on the circuit structure
# chosen above, the cost function does not depend on the angle parameter :math:`\theta_2`
# for the rotation gate :math:`R_y`. As we will show in the following sections, this independence is not true
# for alternative gate choices.
#

print(quantum_circ(inits, x=[0.1, 0.3]))
print(quantum_circ(inits, x=[0.2, 0.2]))


# Plot the normalized Rosenbrock function

# In[55]:


XX = np.linspace(-2, 2., 30)   #Could use linspace instead if dividing
YY = np.linspace(-2, 4., 30)   #evenly instead of stepping...

#Create the mesh grid(s) for all X/Y combos.
XX, YY = np.meshgrid(XX, YY)

#Rosenbrock function w/ two parameters using numpy Arrays and normalized
ZZ = (1.-XX)**2 + 100.*(YY-XX*XX)**2
ZZ = ZZ/np.max(ZZ)

# plot
ax = fig.gca(projection='3d')
ax.view_init(30, 35)
ax.dist = 13
surf = ax.plot_surface(XX, YY, ZZ, rstride=1, cstride=1, 
         cmap='coolwarm', edgecolor='none',antialiased=True)  #Try coolwarm vs jet
ax.set_xlabel('x', rotation=0, labelpad=10, size=22)
ax.set_ylabel('y', rotation=0, labelpad=10, size=22)
ax.set_xticks([-2, 0, 2])
ax.set_yticks([-2, 0, 2,4])
    acc_train = accuracy(Y_train, predictions_train)
    acc_val = accuracy(Y_val, predictions_val)

    print(
        "Iter: {:5d} | Cost: {:0.7f} | Acc train: {:0.7f} | Acc validation: {:0.7f} "
        "".format(it + 1, cost(var, features, Y), acc_train, acc_val))

##############################################################################
# We can plot the continuous output of the variational classifier for the
# first two dimensions of the Iris data set.

plt.figure()
cm = plt.cm.RdBu

# make data for decision regions
xx, yy = np.meshgrid(np.linspace(0.0, 1.5, 20), np.linspace(0.0, 1.5, 20))
X_grid = [np.array([x, y]) for x, y in zip(xx.flatten(), yy.flatten())]

# preprocess grid points like data inputs above
padding = 0.3 * np.ones((len(X_grid), 1))
X_grid = np.c_[np.c_[X_grid, padding],
               np.zeros((len(X_grid), 1))]  # pad each input
normalization = np.sqrt(np.sum(X_grid**2, -1))
X_grid = (X_grid.T / normalization).T  # normalize each input
features_grid = np.array([get_angles(x) for x in X_grid
                          ])  # angles for state preparation are new features
predictions_grid = [
    variational_classifier(var, angles=f) for f in features_grid
]
Z = np.reshape(predictions_grid, xx.shape)
Ejemplo n.º 13
0
def visualize_trained(var, X_train, X_val, Y_train, Y_val):
    plt.figure()
    cm = plt.cm.RdBu

    # make data for decision regions
    xx, yy = np.meshgrid(np.linspace(0.0, 1.5, 20), np.linspace(0.0, 1.5, 20))
    X_grid = [np.array([x, y]) for x, y in zip(xx.flatten(), yy.flatten())]

    # preprocess grid points like data inputs above
    padding = 0.3 * np.ones((len(X_grid), 1))
    X_grid = np.c_[np.c_[X_grid, padding],
                   np.zeros((len(X_grid), 1))]  # pad each input
    normalization = np.sqrt(np.sum(X_grid**2, -1))
    X_grid = (X_grid.T / normalization).T  # normalize each input
    features_grid = np.array(
        [get_angles(x)
         for x in X_grid])  # angles for state preparation are new features
    predictions_grid = [
        variational_classifier(var, angles=f) for f in features_grid
    ]
    Z = np.reshape(predictions_grid, xx.shape)

    # plot decision regions
    cnt = plt.contourf(xx,
                       yy,
                       Z,
                       levels=np.arange(-1, 1.1, 0.1),
                       cmap=cm,
                       alpha=.8,
                       extend='both')
    plt.contour(xx,
                yy,
                Z,
                levels=[0.0],
                colors=('black', ),
                linestyles=('--', ),
                linewidths=(0.8, ))
    plt.colorbar(cnt, ticks=[-1, 0, 1])

    # plot data
    plt.scatter(X_train[:, 0][Y_train == 1],
                X_train[:, 1][Y_train == 1],
                c='b',
                marker='o',
                edgecolors='k',
                label="class 1 train")
    plt.scatter(X_val[:, 0][Y_val == 1],
                X_val[:, 1][Y_val == 1],
                c='b',
                marker='^',
                edgecolors='k',
                label="class 1 validation")
    plt.scatter(X_train[:, 0][Y_train == -1],
                X_train[:, 1][Y_train == -1],
                c='r',
                marker='o',
                edgecolors='k',
                label="class -1 train")
    plt.scatter(X_val[:, 0][Y_val == -1],
                X_val[:, 1][Y_val == -1],
                c='r',
                marker='^',
                edgecolors='k',
                label="class -1 validation")

    plt.legend()
    plt.show()
def plot_cost_and_model(fun,
                        model,
                        parameters,
                        shift_radius=5 * np.pi / 8,
                        num_points=20):
    """
    Args:
        fun (callable): Original cost function.
        model (callable): Model cost function.
        parameters (array[float]): Parameters at which the model was built.
        shift_radius (float): Maximal shift value for each parameter.
        num_points (int): Number of points to create grid.
    """
    coords = np.linspace(-shift_radius, shift_radius, num_points)
    X, Y = np.meshgrid(coords + parameters[0], coords + parameters[1])
    # Compute the original cost function and the model on the grid.
    Z_original = np.array(
        [[fun(parameters + np.array([t1, t2])) for t2 in coords]
         for t1 in coords])
    Z_model = np.array([[model(np.array([t1, t2])) for t2 in coords]
                        for t1 in coords])
    # Prepare sampled points for plotting.
    shifts = [-np.pi / 2, 0, np.pi / 2]
    samples = chain.from_iterable([[[
        parameters[0] + s2, parameters[1] + s1,
        fun(parameters + np.array([s1, s2]))
    ] for s2 in shifts] for s1 in shifts])
    # Display landscapes incl. sampled points and deviation.
    # Transparency parameter for landscapes.
    alpha = 0.6
    fig, ax = plt.subplots(1,
                           2,
                           subplot_kw={"projection": "3d"},
                           figsize=(9, 4))
    green = "#209494"
    orange = "#ED7D31"
    surf = ax[0].plot_surface(X,
                              Y,
                              Z_original,
                              label="Original energy",
                              color=green,
                              alpha=alpha)
    surf._facecolors2d = surf._facecolor3d
    surf._edgecolors2d = surf._edgecolor3d
    surf = ax[0].plot_surface(X,
                              Y,
                              Z_model,
                              label="Model energy",
                              color=orange,
                              alpha=alpha)
    surf._facecolors2d = surf._facecolor3d
    surf._edgecolors2d = surf._edgecolor3d
    for sample in samples:
        ax[0].scatter(*sample, marker="d", color="r")
        ax[0].plot([sample[0]] * 2, [sample[1]] * 2,
                   [np.min(Z_original), sample[2]],
                   color="k")
    surf = ax[1].plot_surface(X,
                              Y,
                              Z_original - Z_model,
                              label="Deviation",
                              color=green,
                              alpha=alpha)
    surf._facecolors2d = surf._facecolor3d
    surf._edgecolors2d = surf._edgecolor3d
    ax[0].legend()
    ax[1].legend()
theta_func = np.linspace(0, 2 * np.pi, num_samples)
C1 = [circuit(np.array([theta, .5])) for theta in theta_func]
C2 = [circuit(np.array([3.3, theta])) for theta in theta_func]

# Show the sweeps.
fig, ax = plt.subplots(1, 1, figsize=(4, 3))
ax.plot(theta_func, C1, label="$E(\\theta, 0.5)$", color="r")
ax.plot(theta_func, C2, label="$E(3.3, \\theta)$", color="orange")
ax.set_xlabel("$\\theta$")
ax.set_ylabel("$E$")
ax.legend()
plt.tight_layout()

# Create a 2D grid and evaluate the energy on the grid points.
# We cut out a part of the landscape to increase clarity.
X, Y = np.meshgrid(theta_func, theta_func)
Z = np.zeros_like(X)
for i, t1 in enumerate(theta_func):
    for j, t2 in enumerate(theta_func):
        # Cut out the viewer-facing corner
        if (2 * np.pi - t2)**2 + t1**2 > 4:
            Z[i, j] = circuit([t1, t2])
        else:
            X[i, j] = np.nan
            Y[i, j] = np.nan
            Z[i, j] = np.nan

# Show the energy landscape on the grid.
fig, ax = plt.subplots(1, 1, subplot_kw={"projection": "3d"}, figsize=(4, 4))
surf = ax.plot_surface(X,
                       Y,
Ejemplo n.º 16
0
#%% Let's have a look at the cost landscape
from functools import partial


def wrap_circuit(param1, param2):
    def circuit_wrap(theta0, theta1, theta2, theta3):
        return circuit(np.array([theta0, theta1, theta2, theta3]))

    circuitwrapvec = np.vectorize(partial(circuit_wrap, param1, param2))
    return circuitwrapvec


#circuitwrapvec = wrap_circuit(theta[0],theta[1])
circuitwrapvec = wrap_circuit(thetart[0], thetart[1])
grid_size = 30
X, Y = np.meshgrid(np.linspace(-np.pi, np.pi, grid_size),
                   np.linspace(-np.pi, np.pi, grid_size))
Z = circuitwrapvec(X, Y)

#%%
# Plot this stuff
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, cmap='viridis', edgecolor='none')
ax.set_title('Cost landscape for two parameters')
ax.set_xlabel("theta_1")
ax.set_ylabel("$theta_2$")
ax.set_zlabel("$\mathcal{L}(\mathbb{\theta})")
plt.show()

# %%
import pickle as pkl
Ejemplo n.º 17
0
    params = 2 * np.pi * (np.random.rand(sum(param_size)) - 0.5)
    
    # Define the Hamiltonian operators
    operators, coeffs = Hamiltonian()
    
    # Define the QNodeCollection
    qnodes = qml.map(circuit, operators, dev, measure="expval")

    # Evaluate the QNodeCollection
    def HNode(params):
        return np.dot(coeffs, qnodes(params, size=SIZE, layers=LAYERS))

    # Define the lattice
    X = np.linspace(-np.pi, np.pi, 100)
    Y = np.linspace(-np.pi, np.pi, 100)
    X, Y = np.meshgrid(X, Y)
    
    Op, OpG = HNode, qml.grad(HNode)
    Op_mat, OpG_mat = np.zeros(shape=(100, 100)), np.zeros(shape=(sum(param_size), 100, 100))

    for i in range(100):
        for j in range(100):
            params[args.coord[0]] = X[i, j]
            params[args.coord[1]] = Y[i, j]
            
            Op_mat[i, j] = Op(params)
            OpG_mat[:, i, j] = OpG(params)[0]

    # fig = plt.figure()
    # ax = fig.gca(projection='3d')
    # surf = ax.plot_surface(X=X, Y=Y, Z=Op_mat)
Ejemplo n.º 18
0
def plot(X, y, log, name="", density=23):
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_pdf import PdfPages

    # data = [(i, step["batch_cost"]) for i, step in enumerate(log)]
    # data = list(zip(*data))
    # plt.figure(figsize=(11, 11))
    # plt.plot(*data)
    # plt.title(f"Learning curve")
    # plt.xlabel("Learning step")
    # plt.ylabel("Batch loss")
    # plt.savefig("learning_curve.pdf")
    # plt.close()
    # exit(-1)

    with PdfPages(f"{name}.pdf") as pdf:
        for i, step in enumerate(log):
            theta = step["theta"]

            plt.figure(figsize=(8, 8))

            extent = X[:, 0].min(), X[:, 0].max(), X[:, 1].min(), X[:, 1].max()
            extent = 0, np.pi, 0, np.pi

            xx = np.linspace(*extent[0:2], density)
            yy = np.linspace(*extent[2:4], density)
            xx, yy = np.meshgrid(xx, yy)
            Xfull = np.c_[xx.ravel(), yy.ravel()]

            # Xfull = np.random.rand(density**2,2)*np.pi

            # View probabilities:
            scores_full = np.array([circuit(theta, x=x) for x in Xfull])

            vmin, vmax = -1, 1

            scores = np.array([circuit(theta, x=x) for x in X])
            y_pred = sgn(scores)

            print(metrics.confusion_matrix(y, y_pred))

            accuracy = metrics.accuracy_score(y, y_pred)
            plt.title(f"Classification score, accuracy={accuracy:1.2f} ")
            plt.xlabel("feature 1")
            plt.ylabel("feature 2")

            imshow_handle = plt.contourf(xx,
                                         yy,
                                         scores_full.reshape(
                                             (density, density)),
                                         vmin=vmin,
                                         vmax=vmax,
                                         cmap='seismic')

            plt.xticks(np.linspace(0, np.pi, 5))
            plt.yticks(np.linspace(0, np.pi, 5))

            for cls_val, cls_col in {0: 'b', 1: 'r'}.items():
                # get row indexes for samples with this class
                row_ix = np.where(y == cls_val)
                # create scatter of these samples
                plt.scatter(X[row_ix, 0],
                            X[row_ix, 1],
                            cmap='seismic',
                            c=cls_col,
                            lw=1,
                            edgecolor='k')

            ax = plt.axes([0.91, 0.1, 0.02, 0.8])
            plt.colorbar(imshow_handle, cax=ax, orientation='vertical')
            plt.clim(-1, 1)

            pdf.savefig()
            plt.close()
Ejemplo n.º 19
0
 def retrieve_landscape(self):
     x = np.linspace(-self.scale, self.scale, self.grid_size[0])
     y = np.linspace(-self.scale, self.scale, self.grid_size[1])
     x, y = np.meshgrid(x, y)
     landscape = self.cache.get_values()
     return x, y, landscape
Ejemplo n.º 20
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