Пример #1
0
    def test_step_and_cost_with_grad_fn_grouped_input(self, tol):
        """Test that the correct cost and update is returned via the step_and_cost
        method for the QNG optimizer when providing an explicit grad_fn.
        Using a circuit with a single input containing all parameters."""
        dev = qml.device("default.qubit", wires=1)

        @qml.qnode(dev)
        def circuit(params):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            return qml.expval(qml.PauliZ(0))

        var = np.array([0.011, 0.012])
        opt = qml.QNGOptimizer(stepsize=0.01)

        # With autograd gradient function
        grad_fn = qml.grad(circuit)
        step1, cost1 = opt.step_and_cost(circuit, var, grad_fn=grad_fn)
        step2 = opt.step(circuit, var, grad_fn=grad_fn)

        # With more custom gradient function, forward has to be computed explicitly.
        grad_fn = lambda param: np.array(qml.grad(circuit)(param))
        step3, cost2 = opt.step_and_cost(circuit, var, grad_fn=grad_fn)
        step4 = opt.step(circuit, var, grad_fn=grad_fn)
        expected_step = var - opt.stepsize * 4 * grad_fn(var)
        expected_cost = circuit(var)

        for step in [step1, step2, step3, step3]:
            assert np.allclose(step, expected_step)
        assert np.isclose(cost1, expected_cost)
        assert np.isclose(cost2, expected_cost)
Пример #2
0
    def test_deprecate_diag_approx(self, diag_approx, approx_expected):
        """Test that using the diag_approx argument raises a warning due to
        deprecation."""
        with pytest.warns(UserWarning,
                          match="keyword argument diag_approx is deprecated"):
            opt = qml.QNGOptimizer(0.1, diag_approx=True)

        assert opt.approx == "diag"
Пример #3
0
    def test_single_qubit_vqe_using_expval_h_multiple_input_params(
            self, tol, recwarn):
        """Test single-qubit VQE by returning qml.expval(H) in the QNode and
        check for the correct QNG value every step, the correct parameter updates, and
        correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)
        coeffs = [1, 1]
        obs_list = [qml.PauliX(0), qml.PauliZ(0)]

        H = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)

        @qml.qnode(dev)
        def circuit(x, y, wires=0):
            qml.RX(x, wires=wires)
            qml.RY(y, wires=wires)
            return qml.expval(H)

        eta = 0.01
        x = np.array(0.011, requires_grad=True)
        y = np.array(0.022, requires_grad=True)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        num_steps = 200

        opt = qml.QNGOptimizer(eta)

        # optimization for 200 steps total
        for t in range(num_steps):
            theta = np.array([x, y])
            x, y = opt.step(circuit, x, y)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(x)**2) / 4])
            assert np.allclose(res, exp, atol=0.00001, rtol=0)

            # check parameter update
            theta_new = np.array([x, y])
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta,
                               theta - theta_new,
                               atol=0.000001,
                               rtol=0)

        # check final cost
        assert np.allclose(circuit(x, y), -1.41421356, atol=tol, rtol=0)
        assert len(recwarn) == 0
Пример #4
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)

    max_iterations = 500
    conv_tol = 1e-09

    #opt = qml.GradientDescentOptimizer(0.08)
    opt = qml.QNGOptimizer(0.01, diag_approx=False, lam=0.001)

    energies = []

    for n in range(max_iterations):
        params, prev_energy = opt.step_and_cost(cost_fn, params)
        energies.append(cost_fn(params))
        conv = np.abs(energies[-1] - prev_energy)

        if conv <= conv_tol:
            break

    energy = energies[-1]

    # QHACK #

    # Return the ground state energy
    return energy
Пример #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 #
    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))

    # Initialize the quantum device
    dev = qml.device("default.qubit", wires=num_qubits)

    # Set up a cost function
    cost_fn = qml.ExpvalCost(variational_ansatz, H, dev)

    # Set up an optimizer
    #opt = qml.QNGOptimizer(0.01, diag_approx=False, lam=0.001)
    #opt = qml.GradientDescentOptimizer(0.8)
    opt = qml.QNGOptimizer(0.1, diag_approx=False, lam=0.001)

    # Run the VQE by iterating over many steps of the optimizer
    max_iterations = 30
    conv_tol = 1e-09

    energies = []

    for n in range(max_iterations):
        params, prev_energy = opt.step_and_cost(cost_fn, params)
        energies.append(cost_fn(params))
        conv = np.abs(energies[-1] - prev_energy)

        if conv <= conv_tol:
            break

    energy = energies[-1]

    # QHACK #

    # Return the ground state energy
    return energies, dev.state
Пример #6
0
    def test_single_qubit_vqe(self, tol):
        """Test single-qubit VQE has the correct QNG value
        every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(params, wires=0):
            qml.RX(params[0], wires=wires)
            qml.RY(params[1], wires=wires)

        coeffs = [1, 1]
        obs_list = [
            qml.PauliX(0),
            qml.PauliZ(0)
        ]

        qnodes = qml.map(circuit, obs_list, dev, measure='expval')
        cost_fn = qml.dot(coeffs, qnodes)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012])
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(cost_fn, theta,
                                 metric_tensor_fn=qnodes.qnodes[0].metric_tensor)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(theta[0]) ** 2)/4])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
Пример #7
0
    def test_single_qubit_vqe_using_vqecost(self, tol, recwarn):
        """Test single-qubit VQE using ExpvalCost
        has the correct QNG value every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(params, wires=0):
            qml.RX(params[0], wires=wires)
            qml.RY(params[1], wires=wires)

        coeffs = [1, 1]
        obs_list = [qml.PauliX(0), qml.PauliZ(0)]

        h = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)

        cost_fn = qml.ExpvalCost(ansatz=circuit, hamiltonian=h, device=dev)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012], requires_grad=True)
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(cost_fn, theta)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(theta[0])**2) / 4])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
        assert len(recwarn) == 0
Пример #8
0
    def test_step_and_cost_autograd(self, tol):
        """Test that the correct cost is returned via the step_and_cost method for the QNG
        optimizer"""
        dev = qml.device("default.qubit", wires=1)

        @qml.qnode(dev)
        def circuit(params):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            return qml.expval(qml.PauliZ(0))

        var = np.array([0.011, 0.012])
        opt = qml.QNGOptimizer(stepsize=0.01)

        _, res = opt.step_and_cost(circuit, var)

        expected = circuit(var)
        assert np.all(res == expected)
Пример #9
0
    def QNGOPT():
        for x_idx, shotnum in enumerate(SHOTRANGE):
            #%% Quantum natural gradient
            circuit = initialize(shotnum)
            print("Doing the quantum natural gradient")
            thetaqng = init_params
            opt = qml.QNGOptimizer(0.01)

            for y_idx in range(STEPS):
                working = False
                while not working:
                    try:
                        thetaqng = opt.step(circuit, thetaqng)
                        QNG_COST[x_idx, y_idx] = circuit(thetaqng)
                        working = True
                    except:
                        pass
                working = False
        np.save("./datafiles/qngshotcosttoy.npy", QNG_COST)
Пример #10
0
    def test_qubit_rotation(self, tol):
        """Test qubit rotation has the correct QNG value
        every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        @qml.qnode(dev)
        def circuit(params):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            return qml.expval(qml.PauliZ(0))

        def gradient(params):
            """Returns the gradient of the above circuit"""
            da = -np.sin(params[0]) * np.cos(params[1])
            db = -np.cos(params[0]) * np.sin(params[1])
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012])
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(circuit, theta)

            # check metric tensor
            res = opt.metric_tensor_inv
            exp = np.diag([4, 4 / (np.cos(theta[0])**2)])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * exp @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(circuit(theta), -0.9963791, atol=tol, rtol=0)
Пример #11
0
    def test_obj_func_not_a_qnode(self):
        """Test that if the objective function is not a
        QNode, an error is raised."""

        dev = qml.device("default.qubit", wires=1)

        @qml.qnode(dev)
        def circuit(a):
            qml.RX(a, wires=0)
            return qml.expval(qml.PauliZ(0))

        def cost(a):
            return circuit(a)

        opt = qml.QNGOptimizer()
        params = 0.5

        with pytest.raises(
                ValueError,
                match="Objective function must be encoded as a single QNode"):
            opt.step(cost, params)
Пример #12
0
        print(
            "Iteration = {:},  Energy = {:.8f} Ha,  Convergence parameter = {"
            ":.8f} Ha".format(n, energy, conv)
        )

    if conv <= conv_tol:
        break

print()
print("Final value of the energy = {:.8f} Ha".format(energy))
print("Number of iterations = ", n)

##############################################################################
# We then repeat the process for the optimizer employing quantum natural gradients:

opt = qml.QNGOptimizer(stepsize=step_size, diag_approx=False)

params = init_params

qngd_param_history = [params]
qngd_cost_history = []

for n in range(max_iterations):

    # Take step
    params, prev_energy = opt.step_and_cost(cost_fn, params)
    qngd_param_history.append(params)
    qngd_cost_history.append(prev_energy)

    # Compute energy
    energy = cost_fn(params)
##############################################################################
# Performing vanilla gradient descent:

gd_cost = []
opt = qml.GradientDescentOptimizer(0.01)

theta = init_params
for _ in range(steps):
    theta = opt.step(circuit, theta)
    gd_cost.append(circuit(theta))

##############################################################################
# Performing quantum natural gradient descent:

qng_cost = []
opt = qml.QNGOptimizer(0.01)

theta = init_params
for _ in range(steps):
    theta = opt.step(circuit, theta)
    qng_cost.append(circuit(theta))

##############################################################################
# Plotting the cost vs optimization step for both optimization strategies:
from matplotlib import pyplot as plt

plt.style.use("seaborn")
plt.plot(gd_cost, "b", label="Vanilla gradient descent")
plt.plot(qng_cost, "g", label="Quantum natural gradient descent")

plt.ylabel("Cost function value")
Пример #14
0
def run_vqe(cost_fn, max_iter, initial_params, opt_name, step_size, 
            conv_tol=1e-6, diag_approx=False, lam=0, print_freq=20):
    """Launches a VQE calculation.
    
    Args:
    =====
    cost_fn : VQECost
        VQE cost function we are trying to optimize
    max_iter : int
        Maximum number of optimization iterations
    initial_params : numpy.ndarray
        Vector of initial parameter values
    opt_name : str
        Name of optimizer. Valid options are: QNGOptimizer or GradientDescentOptimizer.
    step_size : float
        Stepsize or learning rate of the optimizer
    conv_tol : float
        Convergence tolerance for optimizer (relative improvement in energy)
    diag_approx : bool
        If using QNGOptimizer, diag_approx is an option for using the block-diagonal 
        approximation to the Fubini-Study metric. If false, the diagonal approximation
        is used.
    lam : float
        Regularizer term for QNGOptimizer
    print_freq : int
        Optimizer progress printing frequency
       
    Returns:
    ========
    energy_history : list/numpy.ndarray
        History of energies
    n : int
        Number of steps taken to optimize (could be less than max_iter if converged)
    
    """
    energy_history = []

    if opt_name =='GradientDescentOptimizer':
        opt = qml.GradientDescentOptimizer(stepsize=step_size)

    elif opt_name =='QNGOptimizer':
        opt = qml.QNGOptimizer(stepsize=step_size, diag_approx=diag_approx, lam=lam)

    else:
        raise ValueError('Use either QNGOptimizer of GradientDescentOptimizer.')

    params = initial_params
    prev_energy = cost_fn(params)
    energy_history = [prev_energy]

    for n in range(max_iter):
        params = opt.step(cost_fn, params)
        energy = cost_fn(params)
        conv = np.abs(energy - prev_energy)

        if n % print_freq == 0:
            print('Iteration = {:},  Energy = {:.8f} Ha,  Convergence parameter = {'
                  ':.8f} Ha'.format(n, energy, conv))

        if conv <= conv_tol:
            break
        
        energy_history.append(energy)
        
        # Update energy
        prev_energy = energy
        
    print()
    print("Final value of the energy = {:.8f}".format(energy))
    print("Number of iterations = ", n)
        
    return energy_history, n
Пример #15
0
            qml.Hadamard(q)
        base_ansatz(params, wires)

    ansatz = ansatz_f
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)
Пример #16
0
def run_single_qubit_vqe(cost_fn, dev, max_iter, initial_params, opt_name, 
                         step_size, conv_tol=1e-6, diag_approx=False):
    """Launches a VQE calculation for single-qubit systems, where we may be interested
    in plotting the optimization path on the Bloch sphere, and thus, need to save the 
    statevector and circuit parameter history.
    
    Args:
    =====
    cost_fn : VQECost
        VQE cost function we are trying to optimize
    dev : qml.Device
        Quantum simulator/device
    max_iter : int
        Maximum number of optimization iterations
    initial_params : numpy.ndarray
        Vector of initial parameter values
    opt_name : str
        Name of optimizer. Valid options are: QNGOptimizer and GradientDescentOptimizer.
    step_size : float
        Stepsize or learning rate of the optimizer
    conv_tol : float
        Convergence tolerance for optimizer (relative improvement in energy)
    diag_approx : bool
        If using QNGOptimizer, diag_approx is an option for using the block-diagonal 
        approximation to the Fubini-Study metric. If false, the diagonal approximation
        is used.
       
    Returns:
    ========
    energy_history : list/numpy.ndarray
        History of energies
    n : int
        Number of steps taken to optimize (could be less than max_iter if converged)
    state_history : list
        History of state vectors/wavefunctions
    param_history : list
        History of parameters
    """
    energy_history = []

    if opt_name =='GradientDescentOptimizer':
        opt = qml.GradientDescentOptimizer(stepsize=step_size)

    elif opt_name =='QNGOptimizer':
        opt = qml.QNGOptimizer(stepsize=step_size, diag_approx=diag_approx)

    else:
        raise ValueError('Use either QNGOptimizer of GradientDescentOptimizer.')

    params = initial_params
    prev_energy = cost_fn(params)
    energy_history = [prev_energy]
    state_history = [dev.state]
    param_history = [params]

    for n in range(max_iter):
        params = opt.step(cost_fn, params)
        energy = cost_fn(params)
        conv = np.abs(energy - prev_energy)
        
        if n % 20 == 0:
            print('Iteration = {:},  Energy = {:.8f} Ha,  Convergence parameter = {'
                  ':.8f} Ha'.format(n, energy, conv))

        if conv <= conv_tol:
            break
        
        energy_history.append(energy)
        state_history.append(dev.state)
        param_history.append(params)
        
        # Update energy
        prev_energy = energy
        
    print()
    print("Final value of the energy = {:.8f}".format(energy))
    print("Number of iterations = ", n)

    return energy_history, n, state_history, param_history
Пример #17
0
    # Define the QNodeCollection
    qnodes = qml.map(circuit, operators, dev, measure="expval")

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

    ###################
    # TODO Add a simple option for optimizers
    # TODO Randomize the params -> Save later
    # TODO Manage experimental results using comet.ml or tensorboard
    ###################

    if args.optim == "qng":
        optim = qml.QNGOptimizer()
    elif args.optim == "adam":
        optim = qml.AdamOptimizer()
    else:
        optim = qml.GradientDescentOptimizer()

    for _ in range(100):
        print(f"Hamiltonian VEV: {Hamiltonian(params)}")
        params = optim.step(Hamiltonian, params)

    # model.quantum_circuits(torch.zeros(6), [1], 3)

    # node = qml.map(model.quantum_circuits, [qml.PauliZ(0) @ qml.PauliZ(1)], dev,
    #                measure="expval", interface="torch")
    # qml.map()
Пример #18
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