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 #Create the optimizer opt = qml.GradientDescentOptimizer(stepsize=0.4) def cost(x): return circuit(x) # initialise the optimizer opt = qml.GradientDescentOptimizer(stepsize=0.4) # set the number of steps steps = 100 # set the initial parameter values for i in range(steps): # update the circuit parameters params = opt.step(cost, params) optimal_value = cost(params) # QHACK # # Return the value of the minimized QNode return optimal_value
def test_complex(self): random.seed(1234) pnp.random.seed(1234) mp = create_freezable_circuit(3, layer_size=2) circuit = qml.QNode(variational_circuit, device(mp.mask(Axis.WIRES).size)) optimizer = qml.GradientDescentOptimizer() def cost_fn(params, masked_circuit=None): return cost( params, circuit, masked_circuit, ) mp.perturb(axis=Axis.LAYERS, amount=2, mode=Mode.SET, mask=FreezeMask) mp.perturb(axis=Axis.WIRES, amount=2, mode=Mode.SET, mask=FreezeMask) last_changeable = pnp.sum(mp.parameters[~mp._accumulated_mask()]) frozen = pnp.sum(mp.parameters[mp._accumulated_mask()]) for _ in range(10): params = optimizer.step(cost_fn, mp.differentiable_parameters, masked_circuit=mp) mp.differentiable_parameters = params current_changeable = pnp.sum( mp.parameters[~mp._accumulated_mask()]) assert last_changeable - current_changeable != 0 assert frozen - pnp.sum(mp.parameters[mp._accumulated_mask()]) == 0 last_changeable = current_changeable
def benchmark_casual(dev_name, s3=None): """A simple optimization workflow Args: dev_name (str): Either "local", "sv1", "tn1", or "ionq" s3 (tuple): A tuple of (bucket, prefix) to specify the s3 storage location """ n_steps = 2 n_wires = 4 n_layers = 6 interface = "autograd" diff_method = "best" if dev_name == "local": device = qml.device("braket.local.qubit", wires=n_wires, shots=None) elif dev_name == "sv1": device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1", s3_destination_folder=s3, wires=n_wires, shots=None, ) elif dev_name == "tn1": shots = 1000 device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/quantum-simulator/amazon/tn1", s3_destination_folder=s3, wires=n_wires, shots=shots, ) elif dev_name == "ionq": shots = 100 device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/qpu/ionq/ionQdevice", s3_destination_folder=s3, wires=n_wires, shots=shots, ) else: raise ValueError("dev_name not 'local', 'sv1', 'ionq', or 'tn1'") @qml.qnode(device, interface=interface, diff_method=diff_method) def circuit(params_): qml.templates.BasicEntanglerLayers(params_, wires=range(n_wires)) return qml.expval(qml.PauliZ(0)) rng = pnp.random.default_rng(seed=42) params = pnp.array(rng.standard_normal((n_layers, n_wires)), requires_grad=True) opt = qml.GradientDescentOptimizer(stepsize=0.1) print("starting optimization") for i in range(n_steps): params = opt.step(circuit, params) print("step: ", i)
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=WIRES) # Instantiate the QNode circuit = qml.QNode(variational_circuit, dev) #Get Gradient def parameter_shift_term(qnode, params, pos): shifted = params.copy() shifted[pos] += np.pi / 2 forward = qnode(shifted) shifted[pos] -= np.pi backward = qnode(shifted) return 0.5 * (forward - backward) def get_grads(qnode, params): gradients = np.zeros_like(params) for i in range(30): gradients[i] = parameter_shift_term(qnode, params, i) return gradients # Minimize the circuit steps = 1200 for i in range(steps): params = qml.GradientDescentOptimizer(stepsize=0.02).step( circuit, params) optimal_value = circuit(params) # QHACK # # Return the value of the minimized QNode return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) # Device Creation dev = qml.device('default.qubit', wires=H.wires) # Cost Function. Using ExpvalCost useful to use with hamiltonians cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) # Optimizer opt = qml.GradientDescentOptimizer(stepsize=0.1) # Optimization loop max_iterations = 800 conv_tol = 1e-05 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 n % 20 == 0: # print('Iteration = {:}, Energy = {:.8f} Ha'.format(n, energy)) if conv <= conv_tol: break # # QHACK # # Return the ground state energy return energy
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # # Initialize the quantum device num_qubits = len(H.wires) dev = qml.device('default.qubit', wires=num_qubits) #print(H) # Randomly choose initial parameters (how many do you need?) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_qubits - 1)) # Set up a cost function cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) # Set up an optimizer opt = qml.GradientDescentOptimizer(stepsize=0.01) # Run the VQE by iterating over many steps of the optimizer max_iterations = 500 conv_tol = 1e-06 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) # # DEBUG PRINT # if n % 20 == 0: # print('Iteration = {:}, Energy = {:.8f} Ha'.format(n, energy)) # print(params) if conv <= conv_tol: break # @qml.qnode(dev) # def circuit(params): # variational_ansatz(params, H.wires) # return qml.probs(H.wires) # print(circuit(params)) # QHACK # # Return the ground state energy return energy
def GDO(steps, cost): print(f'doing measurement for {steps} times') params = np.array([0.0000001, 0.0000001]) cost(params) opt = qml.GradientDescentOptimizer(stepsize=0.5) for i in range(steps): params = opt.step(cost, params) #print('Cost after step {:5d}: {: .7f}'.format(i+1, cost(params))) #print('Optimized rotation angle: {}'.format(params),'\n') print('probability of [00,01,10,11] is :') print(circuit(params), '\n')
def benchmark_qchem(dev_name, s3=None): """A basic qchem workflow Args: dev_name (str): Either "local", "sv1", "tn1", or "ionq" s3 (tuple): A tuple of (bucket, prefix) to specify the s3 storage location """ n_wires = 4 if dev_name == "local": device = qml.device("braket.local.qubit", wires=n_wires, shots=None) elif dev_name == "sv1": device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1", s3_destination_folder=s3, wires=n_wires, shots=None, ) elif dev_name == "tn1": shots = 1000 device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/quantum-simulator/amazon/tn1", s3_destination_folder=s3, wires=n_wires, shots=shots, ) elif dev_name == "ionq": shots = 100 device = qml.device( "braket.aws.qubit", device_arn="arn:aws:braket:::device/qpu/ionq/ionQdevice", s3_destination_folder=s3, wires=n_wires, shots=shots, ) else: raise ValueError("dev_name not 'local', 'sv1','tn1', or 'ionq'") def circuit(params, wires): qml.PauliX(0) qml.PauliX(1) qml.DoubleExcitation(params[0], wires=[0, 1, 2, 3]) qml.SingleExcitation(params[1], wires=[0, 2]) qml.SingleExcitation(params[2], wires=[1, 3]) params = [0.0] * 3 cost_fn = qml.ExpvalCost(circuit, ham_h2, device, optimize=True) opt = qml.GradientDescentOptimizer(stepsize=0.5) for _ in range(1): params, energy = opt.step_and_cost(cost_fn, params)
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 steps = 200 gd_cost = [] opt = qml.GradientDescentOptimizer(0.1) theta = params.copy() for i in range(steps): theta = opt.step(circuit, theta) gd_cost.append(circuit(theta)) #print(theta) #print(gd_cost[-1]) #optimal_value=np.array(optimal_value) #qnp_cost=[] #opt = qml.QNGOptimizer(0.01) #theta=params.copy() #for _ in range(steps): # theta=opt.step(circuit,theta) # qnp_cost.append(circuit(theta)) optimal_value = circuit(theta) # QHACK # # Return the value of the minimized QNode return optimal_value
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 opt = qml.GradientDescentOptimizer(stepsize=0.4) steps = 100 #init_params = np.array([0.011 for i in range(30)]) #params = init_params def cost(params): return (circuit(params)) for i in range(steps): params = opt.step(cost, params) #if (i + 1) % 5 == 0: # print("Cost after step {:5d}: {: .7f}".format(i + 1, cost(params))) # QHACK # # Return the value of the minimized QNode optimal_value = cost(params) return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2**num_qubits) - 1 np.random.seed(10) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # dev0 = qml.device('default.qubit', wires=num_qubits) cost_fn = qml.ExpvalCost(variational_ansatz, H, dev0, optimize=True) opt = qml.GradientDescentOptimizer(stepsize=0.1) #np.random.seed(0) max_iterations = 550 #conv_tol = 1e-06 n = 0 #for n in range(max_iterations): while True: params = opt.step(cost_fn, params) previous = float(energy) energy = cost_fn(params) if round(float(energy), 8) == round(previous, 8) or n == 500: break n += 1 #conv = np.abs(energy - prev_energy) # 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 # # Return the ground state energy return energy
def GDOPT(): for x_idx, shotnum in enumerate(SHOTRANGE): circuit = initialize(shotnum) print(f"round {x_idx} of {DENSITY} with {shotnum} shots") opt = qml.GradientDescentOptimizer(0.01) print("Doing the gradient descent") thetagd = init_params for y_idx in range(STEPS): thetagd = opt.step(circuit, thetagd) GD_COST[x_idx, y_idx] = circuit(thetagd) print("finally save GD cost file") np.save("./datafiles/gdshotcosttoy.npy", GD_COST) return # exit for GC
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=WIRES) # Instantiate the QNode circuit = qml.QNode(variational_circuit, dev) # Minimize the circuit #optimizer opt = qml.GradientDescentOptimizer(stepsize=0.3) #training parameters max_iterations = 500 conv_tol = 1e-12 for n in range(max_iterations): params, prev_energy = opt.step_and_cost(circuit, params) energy = circuit(params) conv = np.abs(energy - prev_energy) if n % 50 == 0: print('Iteration = {:}, Energy = {:.8f} Ha'.format(n, energy)) if conv <= conv_tol: break optimal_value = energy # QHACK # # Return the value of the minimized QNode return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # num_qubits = len(H.wires) # Initialize the quantum device dev = qml.device("default.qubit", wires=num_qubits) # Randomly choose initial parameters (how many do you need?) params = np.random.uniform(0, np.pi, size=(num_qubits-1)) # Set up a cost function cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) # Set up an optimizer opt = qml.GradientDescentOptimizer(0.02) # Run the VQE by iterating over many steps of the optimizer max_iterations = 500 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 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=WIRES) # Instantiate the QNode circuit = qml.QNode(variational_circuit, dev) # Minimize the circuit print('circuit...', circuit(params)) print('hamiltonian..', hamiltonian) # def cost(var): # return 1-circuit(var) steps = 100 opt = qml.GradientDescentOptimizer(0.25) qng_cost = [] theta = params print(params) for _ in range(steps): theta = opt.step(circuit, theta) # qng_cost.append(circuit(theta)) print(circuit(theta)) # QHACK # # Return the value of the minimized QNode return optimal_value
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # num_qubits = len(H.wires) num_params = num_qubits # Initialize the quantum device dev = qml.device('default.qubit', wires=num_qubits) # Randomly choose initial parameters (how many do you need?) alphas = np.random.uniform(low=-1, high=1, size=(num_params,)) params = np.zeros(num_qubits-1) params[0] = 2. * np.arccos(alphas[0]) prod_sins = np.sin(params[0]/2) for i in range(1,num_qubits-1): params[i] = 2 * np.arccos(alphas[i] * (-1)**(i+1) / prod_sins) prod_sins *= np.sin(params[i] / 2) cost = qml.ExpvalCost(variational_ansatz, H, dev) steps = 500 opt = qml.GradientDescentOptimizer(stepsize=0.02) for i in range(steps): params = opt.step(cost, params) energy = cost(params) # QHACK # # Return the ground state energy return energy
def benchmark_vqe(hyperparams={}): """ Performs VQE optimizations. Args: hyperparams (dict): hyperparameters to configure this benchmark * 'ham': Molecular Hamiltonian represented as a PennyLane Hamiltonian class * 'ansatz': VQE ansatz * 'params': Numpy array of trainable parameters that is fed into the ansatz. * 'n_steps': Number of VQE steps * 'device': Device on which the circuit is run, or valid device name * 'interface': Name of the interface to use * 'diff_method': Name of differentiation method * 'optimize': argument for grouping the observables composing the Hamiltonian """ ham, ansatz, params, n_steps, device, interface, diff_method, grouping = _vqe_defaults( hyperparams) if version.parse(qml.__version__) > version.parse("0.17"): if grouping: ham.compute_grouping() @qml.qnode(device) def cost_fn(weights): ansatz(weights, wires=device.wires) return qml.expval(ham) else: cost_fn = qml.ExpvalCost(ansatz, ham, device, interface=interface, diff_method=diff_method, optimize=grouping) opt = qml.GradientDescentOptimizer(stepsize=0.4) for _ in range(n_steps): params, energy = opt.step_and_cost(cost_fn, params)
def optimize(): # initialise the optimizer opt = qml.GradientDescentOptimizer(stepsize=0.4) # set the number of steps steps = 100 # set the initial parameter values params = np.array([0.01, 0.01]) for i in range(steps): # update the circuit parameters params = opt.step(cost, params) if (i + 1) % 5 == 0: print('Cost after step {:5d}: {: .7f}'.format(i + 1, cost(params))) print('Optimized rotation angles: {}'.format(params))
def train(): # initialise the optimizer opt = qml.GradientDescentOptimizer(stepsize=0.4) # set the number of steps steps = 100 # set the initial parameter values params = init_params for i in range(steps): # update the circuit parameters params = opt.step(cost, params) if (i + 1) % 5 == 0: print("Cost after step {:5d}: {: .7f}".format(i + 1, cost(params))) print("Optimized rotation angles: {}".format(params))
def optimize(init_params): opt = qml.GradientDescentOptimizer(stepsize=0.1) # set the number of steps steps = 20 # set the initial parameter values params = init_params for i in range(steps): # update the circuit parameters params = opt.step(cost, params) print('Cost after step {:5d}: {:8f}'.format(i+1, cost(params)) ) print('Optimized mag_alpha:{:8f}'.format(params[0])) print('Optimized phase_alpha:{:8f}'.format(params[1])) print('Optimized phi:{:8f}'.format(params[2]))
def optimize_graddesc(init_params): opt = qml.GradientDescentOptimizer(stepsize=0.4) steps = 100 params = init_params var = [] var.append(params) for i in range(steps): # update the circuit parameters params = opt.step(cost, params) var.append(params) if (i + 1) % 5 == 0: print('Cost after step {:5d}: {: .7f}'.format(i + 1, cost(params))) print('Optimized rotation angles: {}, {}'.format(params, cost(params))) return var
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 num_qubits = len(H.wires) dev = qml.device("default.qubit", wires=num_qubits) # Randomly choose initial parameters (how many do you need?) params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_qubits - 1)) # Set up a cost function cost = qml.ExpvalCost(variational_ansatz, H, dev) # Set up an optimizer opt = qml.GradientDescentOptimizer(0.1) # Run the VQE by iterating over many steps of the optimizer for i in range(400): #if i % 10: print(f"step {i}, cost {cost(params)}") params = opt.step(cost, params) energy = cost(params) # QHACK # # Return the ground state energy return energy
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ energy = 0 # QHACK # num_qubits = len(H.wires) # Initialize the quantum device dev = qml.device('default.qubit', wires=num_qubits) # Randomly choose initial parameters (how many do you need?) params = np.random.normal(size=num_qubits - 1) # Set up a cost function cost_fn = qml.ExpvalCost(variational_ansatz, H, dev) # Set up an optimizer opt = qml.GradientDescentOptimizer() # Run the VQE by iterating over many steps of the optimizer max_iterations = 500 for n in range(max_iterations): params = opt.step(cost_fn, params) energy = cost_fn(params) # if n % 20 == 0: # print('Iteration = {:}, Energy = {:.8f} Ha, Params = {:}'.format(n, energy, params)) # QHACK # # Return the ground state energy return energy
def run_vqe(H): """Runs the variational quantum eigensolver on the problem Hamiltonian using the variational ansatz specified above. Fill in the missing parts between the # QHACK # markers below to run the VQE. Args: H (qml.Hamiltonian): The input Hamiltonian Returns: The ground state energy of the Hamiltonian. """ # Initialize parameters num_qubits = len(H.wires) num_param_sets = (2 ** num_qubits) - 1 params = np.random.uniform(low=-np.pi / 2, high=np.pi / 2, size=(num_param_sets, 3)) energy = 0 # QHACK # # Create a quantum device, set up a cost funtion and optimizer, and run the VQE. # (We recommend ~500 iterations to ensure convergence for this problem, # or you can design your own convergence criteria) dev = qml.device("default.qubit", wires=num_qubits) cost = qml.ExpvalCost(variational_ansatz, H, dev) opt = qml.GradientDescentOptimizer(0.1) #opt = qml.AdamOptimizer() for i in range(400): #if i % 10: print(f"step {i}, cost {cost(params)}") params = opt.step(cost, params) energy = cost(params) # QHACK # # Return the ground state energy return energy
# As explained above, we express it in terms of expectation values through Bayes' theorem. def cost(weights): """Cost function which tends to zero when A |x> tends to |b>.""" p_global_ground = global_ground(weights) p_ancilla_ground = ancilla_ground(weights) p_cond = p_global_ground / p_ancilla_ground return 1 - p_cond ############################################################################## # To minimize the cost function we use the gradient-descent optimizer. opt = qml.GradientDescentOptimizer(eta) ############################################################################## # We initialize the variational weights with random parameters (with a fixed seed). np.random.seed(rng_seed) w = q_delta * np.random.randn(n_qubits) ############################################################################## # We are ready to perform the optimization loop. cost_history = [] for it in range(steps): w = opt.step(cost, w) _cost = cost(w) print("Step {:3d} Cost = {:9.7f}".format(it, _cost))
############################################################################## # Now, we create three QNodes, each corresponding to a device above, # and optimize them using gradient descent via the parameter-shift rule. qnode_analytic = qml.QNode(circuit, dev_analytic) qnode_stochastic = qml.QNode(circuit, dev_stochastic) init_params = strong_ent_layers_uniform(num_layers, num_wires) # Optimizing using exact gradient descent cost_GD = [] params_GD = init_params opt = qml.GradientDescentOptimizer(eta) for _ in range(steps): cost_GD.append(qnode_analytic(params_GD)) params_GD = opt.step(qnode_analytic, params_GD) # Optimizing using stochastic gradient descent with shots=1 dev_stochastic.shots = 1 cost_SGD1 = [] params_SGD1 = init_params opt = qml.GradientDescentOptimizer(eta) for _ in range(steps): cost_SGD1.append(qnode_stochastic(params_SGD1)) params_SGD1 = opt.step(qnode_stochastic, params_SGD1)
def square_loss(X, Y): loss = 0 for x, y in zip(X, Y): loss = loss + (x - y)**2 return loss / len(X) def cost(theta, p): # p is prob. of ket zero, 1-p is prob of ket 1 return square_loss(W(theta), [p, 1 - p]) # initialise the optimizer opt = qml.GradientDescentOptimizer(stepsize=0.4) # set the number of steps steps = 100 # set the initial theta value of variational circuit state theta = np.random.randn(2) print("Initial probs. of basis states: {}".format(W(theta))) def update(theta, p): # p = probability for i in range(steps): # update the circuit parameters theta = opt.step(lambda v: cost(v, p), theta) #if (i + 1) % 10 == 0: #print("Cost @ step {:5d}: {: .7f}".format(i, cost(theta,p))) print("Probs. of basis states: {}".format(W(theta)))
# Let's now come back to the actual implementation. # PennyLane's `EmbeddingKernel` class allows you to easily evaluate the kernel target alignment: print( "The kernel-target-alignment for our dataset with random parameters is {:.3f}" .format(k.target_alignment(dataset.X, dataset.Y, init_params))) # Now let's code up an optimization loop and improve this! # # We will make use of regular gradient descent optimization. # To speed up the optimization we will not use the entire training set but rather sample smaller subsets of the data at each step, we choose $4$ datapoints at random. # Remember that PennyLane's inbuilt optimizer works to _minimize_ the cost function that is given to it, which is why we have to multiply the kernel target alignment by $-1$ to actually _maximize_ it in the process. # + params = init_params opt = qml.GradientDescentOptimizer(2.5) for i in range(500): subset = np.random.choice(list(range(len(dataset.X))), 4) params = opt.step( lambda _params: -k.target_alignment(dataset.X[subset], dataset.Y[ subset], _params), params) if (i + 1) % 50 == 0: print("Step {} - Alignment = {:.3f}".format( i + 1, k.target_alignment(dataset.X, dataset.Y, params))) # - # We want to assess the impact of training the parameters of the quantum kernel. # Thus, let's build a second support vector classifier with the trained kernel:
# Quantum natural gradient optimization # ------------------------------------- # # PennyLane provides an implementation of the quantum natural gradient # optimizer, :class:`~.QNGOptimizer`. Let's compare the optimization convergence # of the QNG Optimizer and the :class:`~.GradientDescentOptimizer` for the simple variational # circuit above. steps = 200 init_params = np.array([0.432, -0.123, 0.543, 0.233]) ############################################################################## # 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)
init_params = np.array([3.97507603, 3.00854038]) ############################################################################## # We will carry out each optimization over a maximum of 500 steps. As was done in the VQE # tutorial, we aim to reach a convergence tolerance of around :math:`10^{-6}`. # We use a step size of 0.01. max_iterations = 500 conv_tol = 1e-06 step_size = 0.01 ############################################################################## # First, we carry out the VQE optimization using the standard gradient descent method. opt = qml.GradientDescentOptimizer(stepsize=step_size) params = init_params gd_param_history = [params] gd_cost_history = [] for n in range(max_iterations): # Take step params, prev_energy = opt.step_and_cost(cost_fn, params) gd_param_history.append(params) gd_cost_history.append(prev_energy) energy = cost_fn(params)