def Fubini_elem(qnode2, params, i, j): # print(dev._state) #elem 1 shifted = params.copy() shifted[i] += np.pi / 2 shifted[j] += np.pi / 2 ket = qnode2(shifted) # forward evaluation bra = qnode2(params) inner_prod_sq1 = np.abs(bra.T @ np.conjugate(ket))**2 #elem 2 shifted = params.copy() shifted[i] += np.pi / 2 shifted[j] -= np.pi / 2 ket = qnode2(shifted) # forward evaluation inner_prod_sq2 = np.abs(bra.T @ np.conjugate(ket))**2 #elem 3 shifted = params.copy() shifted[i] -= np.pi / 2 shifted[j] += np.pi / 2 ket = qnode2(shifted) # forward evaluation inner_prod_sq3 = np.abs(bra.T @ np.conjugate(ket))**2 #elem 4 shifted = params.copy() shifted[i] -= np.pi / 2 shifted[j] -= np.pi / 2 ket = qnode2(shifted) # forward evaluation inner_prod_sq4 = np.abs(bra.T @ np.conjugate(ket))**2 return (1 / 8) * (-inner_prod_sq1 + inner_prod_sq2 + inner_prod_sq3 - inner_prod_sq4)
def target_function(x): """Generate a truncated Fourier series, where the data gets re-scaled.""" res = coeff0 for idx, coeff in enumerate(coeffs): exponent = np.complex(0, scaling * (idx + 1) * x) conj_coeff = np.conjugate(coeff) res += coeff * np.exp(exponent) + conj_coeff * np.exp(-exponent) return np.real(res)
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 """ # QHACK # def get_state(params): """ Get the state before a measurement """ qnode(params) return dev.state # Calculate the unshifted state (its conjugate transpose) state_unshifted = np.conjugate(get_state(params)).T def shift_vector(i): vector = np.zeros(6) vector[i] = 1 return vector metric_tensor = np.zeros((6, 6)) for i in range(6): for j in range(i + 1): state_shifted_1 = get_state(params + (shift_vector(i) + shift_vector(j)) * np.pi / 2) state_shifted_2 = get_state(params + (shift_vector(i) - shift_vector(j)) * np.pi / 2) state_shifted_3 = get_state(params + (-shift_vector(i) + shift_vector(j)) * np.pi / 2) state_shifted_4 = get_state(params - (shift_vector(i) + shift_vector(j)) * np.pi / 2) metric_tensor[ i, j] = (-np.abs(np.dot(state_unshifted, state_shifted_1))**2 + np.abs(np.dot(state_unshifted, state_shifted_2))**2 + np.abs(np.dot(state_unshifted, state_shifted_3))**2 - np.abs(np.dot(state_unshifted, state_shifted_4))**2) / 8 if i != j: metric_tensor[j, i] = metric_tensor[i, j] grad = qml.grad(qnode) gradient = grad(params)[0] metric_tensor_inv = np.linalg.inv(metric_tensor) natural_grad = np.dot(metric_tensor_inv, gradient) # QHACK # return natural_grad
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], dtype=np.float64) # QHACK # gradient = np.zeros([6], dtype=np.float64) for i in range(len(params)): shifted = params.copy() shifted[i] += np.pi / 2 forward = qnode(shifted) # forward evaluation:f(theta+s e_i) shifted[i] -= np.pi backward = qnode(shifted) # backward evaluation: f(theta-s e_i) gradient[i] = 0.5 * (forward - backward) @qml.qnode(dev) def qnode_shifted(params, i, j, s1, s2): shifted = params.copy() shifted[i] += s1 shifted[j] += s2 variational_circuit(shifted) return qml.state() #qml.expval(qml.PauliX(1)) F = np.zeros([6, 6]) for j in range(len(params)): for i in range(len(params)): F[i, j] = 0.125 * (-np.abs( np.conjugate(np.transpose(qnode_shifted(params, i, j, 0, 0))) @ qnode_shifted(params, i, j, np.pi / 2, np.pi / 2) )**2 + np.abs( np.conjugate(np.transpose(qnode_shifted(params, i, j, 0, 0))) @ qnode_shifted(params, i, j, np.pi / 2, -np.pi / 2) )**2 + np.abs( np.conjugate(np.transpose(qnode_shifted(params, i, j, 0, 0))) @ qnode_shifted(params, i, j, -np.pi / 2, np.pi / 2) )**2 - np.abs( np.conjugate(np.transpose(qnode_shifted(params, i, j, 0, 0))) @ qnode_shifted(params, i, j, -np.pi / 2, -np.pi / 2))**2) #natural_grad=np.linalg.pinv(F)@gradient #print(np.round(F,8)) #print() #print(qml.metric_tensor(qnode)(params)) natural_grad = np.linalg.pinv(F) @ gradient # QHACK # return natural_grad
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) gradient = np.zeros([natural_grad.shape[0]]) fim = np.zeros([natural_grad.shape[0], natural_grad.shape[0]]) eps = np.pi / 2 for k in range(gradient.shape[0]): eps_plus = params.copy() eps_plus[k] += eps exp_value_plus = qnode(eps_plus) eps_minus = params.copy() eps_minus[k] -= eps exp_value_minus = qnode(eps_minus) gradient[k] = (exp_value_plus - exp_value_minus) / (2 * np.sin(eps)) eps = np.pi / 2 qnode(params) state = dev.state for k in range(natural_grad.shape[0]): for l in range(gradient.shape[0]): if l <= k: eps_pp = params.copy() eps_pp[k] += eps eps_pp[l] += eps eps_pm = params.copy() eps_pm[k] += eps eps_pm[l] -= eps eps_mp = params.copy() eps_mp[k] -= eps eps_mp[l] += eps eps_mm = params.copy() eps_mm[k] -= eps eps_mm[l] -= eps qnode(eps_pp) state_pp = dev.state measure_pp = np.abs(np.conjugate(state) @ state_pp) qnode(eps_pm) state_pm = dev.state measure_pm = np.abs(np.conjugate(state) @ state_pm) qnode(eps_mp) state_mp = dev.state measure_mp = np.abs(np.conjugate(state) @ state_mp) qnode(eps_mm) state_mm = dev.state measure_mm = np.abs(np.conjugate(state) @ state_mm) fim[k, l] = (-measure_pp**2 - measure_mm**2 + measure_mp**2 + measure_pm**2) / 8 for k in range(natural_grad.shape[0]): for l in range(natural_grad.shape[0]): if l > k: fim[k, l] = fim[l, k] natural_grad = np.dot(np.linalg.inv(fim), gradient) return natural_grad