def metric_tensor_entry(i, j, unshifted, shift=np.pi / 2): shifted_plus = params.copy() shifted_plus[i] += shift shifted_plus[j] += shift qnode(shifted_plus) forward = dev.state ov_forward = abs(np.vdot(unshifted, forward))**2 shifted_min_plu = params.copy() shifted_min_plu[i] -= shift shifted_min_plu[j] += shift qnode(shifted_min_plu) min_plu = dev.state ov_min_plu = abs(np.vdot(unshifted, min_plu))**2 shifted_plu_min = params.copy() shifted_plu_min[i] += shift shifted_plu_min[j] -= shift qnode(shifted_plu_min) plu_min = dev.state ov_plu_min = abs(np.vdot(unshifted, plu_min))**2 shifted_minus = params.copy() shifted_minus[i] -= shift shifted_minus[j] -= shift qnode(shifted_minus) backward = dev.state ov_backward = abs(np.vdot(unshifted, backward))**2 me = (1 / 8) * (-ov_forward + ov_plu_min + ov_min_plu - ov_backward) return me
def test_qubit_unitary(self, shots, qvm, compiler): """Test that an arbitrary unitary operation works""" dev1 = qml.device("forest.qvm", device="3q-qvm", shots=shots, parametric_compilation=False) dev2 = qml.device("forest.qvm", device="9q-square-qvm", shots=shots, parametric_compilation=False) print(shots) def circuit(): """Reference QNode""" qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) qml.QubitUnitary(U2, wires=[0, 1]) return qml.expval(qml.PauliZ(0)) circuit1 = qml.QNode(circuit, dev1) circuit2 = qml.QNode(circuit, dev2) out_state = U2 @ np.array([1, 0, 0, 1]) / np.sqrt(2) obs = np.kron(np.array([[1, 0], [0, -1]]), I) self.assertAllAlmostEqual(circuit1(), np.vdot(out_state, obs @ out_state), delta=3 / np.sqrt(shots)) self.assertAllAlmostEqual(circuit2(), np.vdot(out_state, obs @ out_state), delta=3 / np.sqrt(shots))
def test_apply(self, gate, apply_unitary, shots): """Test the application of gates""" dev = plf.QVMDevice(device="3q-pyqvm", shots=shots) try: # get the equivalent pennylane operation class op = getattr(qml.ops, gate) except AttributeError: # get the equivalent pennylane-forest operation class op = getattr(plf, gate) # the list of wires to apply the operation to w = list(range(op.num_wires)) obs = qml.expval(qml.PauliZ(0)) if op.par_domain == "A": # the parameter is an array if gate == "QubitUnitary": p = np.array(U) w = [0] state = apply_unitary(U, 3) elif gate == "BasisState": p = np.array([1, 1, 1]) state = np.array([0, 0, 0, 0, 0, 0, 0, 1]) w = list(range(dev.num_wires)) circuit_graph = CircuitGraph([op(p, wires=w)] + [obs], {}) else: p = [0.432423, 2, 0.324][:op.num_params] fn = test_operation_map[gate] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn # calculate the expected output state = apply_unitary(O, 3) # Creating the circuit graph using a parametrized operation if p: circuit_graph = CircuitGraph([op(*p, wires=w)] + [obs], {}) # Creating the circuit graph using an operation that take no parameters else: circuit_graph = CircuitGraph([op(wires=w)] + [obs], {}) dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) dev.generate_samples() res = dev.expval(obs) expected = np.vdot(state, np.kron(np.kron(Z, I), I) @ state) # verify the device is now in the expected state # Note we have increased the tolerance here, since we are only # performing 1024 shots. self.assertAllAlmostEqual(res, expected, delta=3 / np.sqrt(shots))
def _exact(x_new, A_samples, B_samples, featmap, n_inp, pars): """Calculates the analytical result of the fidelity measurement, overlap_A = \sum_i p_A |<\phi(x_new)|\phi(a_i)>|^2, overlap_B = \sum_i p_B |<\phi(x_new)|\phi(b_i)>|^2, using numpy as well as pennylane to simulate the feature map. """ dev = qml.device('default.qubit', wires=n_inp) @qml.qnode(dev) def fm(weights, x=None): """Circuit to get the state after feature map""" featmap(weights, x, range(n_inp)) return qml.expval(qml.PauliZ(0)) # Compute feature states for A A_states = [] for a in A_samples: fm(pars, x=a) phi_a = dev._state A_states.append(phi_a) # Compute feature states for B B_states = [] for b in B_samples: fm(pars, x=b) phi_b = dev._state B_states.append(phi_b) # Get feature state for new input fm(pars, x=x_new) phi_x = dev._state # Put together overlap_A = sum([np.abs(np.vdot(phi_x, phi_a))**2 for phi_a in A_states]) overlap_A = overlap_A / len(A_states) overlap_B = sum([np.abs(np.vdot(phi_x, phi_b))**2 for phi_b in B_states]) overlap_B = overlap_B / len(B_states) return overlap_A, overlap_B
def getTensorValue(params, i, j, qstate): params1 = params.copy() params1[i], params1[j] = params[i] + np.pi / 2, params[j] + np.pi / 2 params2 = params.copy() params2[i], params2[j] = params[i] + np.pi / 2, params[j] - np.pi / 2 params3 = params.copy() params3[i], params3[j] = params[i] - np.pi / 2, params[j] + np.pi / 2 params4 = params.copy() params4[i], params4[j] = params[i] - np.pi / 2, params[j] - np.pi / 2 answer = ((-(np.abs(np.vdot(qstate, state(params1))))**2 + (np.abs(np.vdot(qstate, state(params2))))**2 + (np.abs(np.vdot(qstate, state(params3))))**2 - (np.abs(np.vdot(qstate, state(params4))))**2) / 8) return answer
def test_hermitian_expectation(self, tol, qvm, compiler): """Test that an arbitrary Hermitian expectation value works""" dev = qml.device('forest.wavefunction', wires=1) @qml.qnode(dev) def circuit(): """Test QNode""" qml.Hadamard(wires=0) qml.PauliY(wires=0) return qml.expval.Hermitian(H, 0) out_state = 1j*np.array([-1, 1])/np.sqrt(2) self.assertAllAlmostEqual(circuit(), np.vdot(out_state, H @ out_state), delta=tol)
def test_apply(self, gate, apply_unitary, shots): """Test the application of gates""" dev = plf.QVMDevice(device='3q-pyqvm', shots=shots) try: # get the equivalent pennylane operation class op = getattr(qml.ops, gate) except AttributeError: # get the equivalent pennylane-forest operation class op = getattr(plf, gate) # the list of wires to apply the operation to w = list(range(op.num_wires)) if op.par_domain == 'A': # the parameter is an array if gate == 'QubitUnitary': p = [U] w = [0] state = apply_unitary(U, 3) elif gate == 'BasisState': p = [np.array([1, 1, 1])] state = np.array([0, 0, 0, 0, 0, 0, 0, 1]) else: p = [0.432423, 2, 0.324][:op.num_params] fn = test_operation_map[gate] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn # calculate the expected output state = apply_unitary(O, 3) dev.apply(gate, wires=w, par=p) dev._expval_queue = [] dev.pre_expval() res = dev.expval('PauliZ', wires=[0], par=None) expected = np.vdot(state, np.kron(np.kron(Z, I), I) @ state) # verify the device is now in the expected state # Note we have increased the tolerance here, since we are only # performing 1024 shots. self.assertAllAlmostEqual(res, expected, delta=3 / np.sqrt(shots))
def test_qubit_unitary(self, tol, qvm, compiler): """Test that an arbitrary unitary operation works""" dev = qml.device('forest.wavefunction', wires=3) @qml.qnode(dev) def circuit(): """Test QNode""" qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) qml.QubitUnitary(U2, wires=[0, 1]) return qml.expval.PauliZ(0) out_state = U2 @ np.array([1, 0, 0, 1])/np.sqrt(2) obs = np.kron(np.array([[1, 0], [0, -1]]), I) self.assertAllAlmostEqual(circuit(), np.vdot(out_state, obs @ out_state), delta=tol)
def test_state_preparation_fidelity(self, tol, qubit_device_3_wires, state_vector, wires, target_state): """Tests that the template MottonenStatePreparation integrates correctly with PennyLane and produces states with correct fidelity.""" @qml.qnode(qubit_device_3_wires) def circuit(): MottonenStatePreparation(state_vector, wires) return qml.expval(qml.PauliZ(0)), qml.expval( qml.PauliZ(1)), qml.expval(qml.PauliZ(2)) circuit() state = circuit.device.state.ravel() fidelity = abs(np.vdot(state, target_state))**2 # We test for fidelity here, because the vector themselves will hardly match # due to imperfect state preparation assert np.isclose(fidelity, 1, atol=tol, rtol=0)
def state(params): qstate = np.zeros([ 8, ]) prob0 = probabs(params, 0) prob1 = probabs(params, 1) prob2 = probabs(params, 2) index = 0 for i in range(2): for j in range(2): for k in range(2): qstate[index] = np.abs( np.sqrt((prob0[i] * prob1[j] * prob2[k]))) index += 1 normalise = np.vdot(qstate, qstate) normalise = 1 / np.abs((np.sqrt(normalise))) qstate = normalise * qstate return qstate
def test_apply(self, op, apply_unitary, shots, qvm, compiler): """Test the application of gates to a state""" dev = plf.QVMDevice(device="3q-qvm", shots=shots, parametric_compilation=False) obs = qml.expval(qml.PauliZ(0)) if op.name == "QubitUnitary": state = apply_unitary(U, 3) elif op.name == "BasisState": state = np.array([0, 0, 0, 0, 0, 0, 0, 1]) elif op.name == "CPHASE": state = apply_unitary(test_operation_map["CPHASE"](0.432, 2), 3) elif op.name == "ISWAP": state = apply_unitary(test_operation_map["ISWAP"], 3) elif op.name == "PSWAP": state = apply_unitary(test_operation_map["PSWAP"](0.432), 3) else: state = apply_unitary(op.matrix, 3) with qml.tape.QuantumTape() as tape: qml.apply(op) obs dev.apply(tape.operations, rotations=tape.diagonalizing_gates) dev._samples = dev.generate_samples() res = dev.expval(obs.obs) expected = np.vdot(state, np.kron(np.kron(Z, I), I) @ state) # verify the device is now in the expected state # Note we have increased the tolerance here, since we are only # performing 1024 shots. self.assertAllAlmostEqual(res, expected, delta=3 / np.sqrt(shots))
def normalise(a1): return (np.vdot(a1, a1))
def natural_gradient(params): """Calculate the natural gradient of the qnode() cost function. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. You should evaluate the metric tensor and the gradient of the QNode, and then combine these together using the natural gradient definition. The natural gradient should be returned as a NumPy array. The metric tensor should be evaluated using the equation provided in the problem text. Hint: you will need to define a new QNode that returns the quantum state before measurement. Args: params (np.ndarray): Input parameters, of dimension 6 Returns: np.ndarray: The natural gradient evaluated at the input parameters, of dimension 6 """ natural_grad = np.zeros(6) # QHACK # dcircuit = qml.grad(qnode, argnum=0) normal_grad = dcircuit(params) def normalise(a1): return (np.vdot(a1, a1)) qml.enable_tape() @qml.qnode(dev) def circuit(params): variational_circuit(params) return qml.state() F_matrix = np.zeros([6, 6], dtype=np.float64) initial_prod = circuit(params) s = np.pi / 2 for i in range(6): for j in range(6): params[i] += s params[j] += s a11 = circuit(params) params[j] -= 2 * s a22 = circuit(params) params[i] -= 2 * s a44 = circuit(params) params[j] += 2 * s a33 = circuit(params) params[i] += s params[j] -= s aa1 = np.vdot(a11, initial_prod) a1 = normalise(aa1) aa2 = np.vdot(a22, initial_prod) a2 = normalise(aa2) aa3 = np.vdot(a33, initial_prod) a3 = normalise(aa3) aa4 = np.vdot(a44, initial_prod) a4 = normalise(aa4) #print(str((a2 + a3 - a1 - a4)/8.0)) #print(a1) F_matrix[i][j] = (np.absolute(a2) + np.absolute(a3) - np.absolute(a1) - np.absolute(a4)) / 8.0 #print(circuit(params)) final_F = np.linalg.inv(F_matrix) natural_grad = final_F.dot(normal_grad) # QHACK # return natural_grad