def matrix(self, **kwargs) -> tf.Tensor: Us = [] for i in range(self.thetas.shape[0]): Us.append(qc.U(self.thetas[i])) if self.targets: return tensor([self.pre_identity, *Us, self.post_identity]) else: return tensor(Us)
def layer1(w1, w2, w3, x1, x2, x3, t1, t2, t3): state_1_in = tensor([w1, x1]) state_2_in = tensor([w2, x2]) state_3_in = tensor([w3, x3]) state_in = tensor([state_1_in, state_2_in, state_3_in]) U_layer1.thetas.assign([t1, t2, t3]) state_out = U_layer1.matrix() @ state_in out_layer1 = density_matrix(state_out, [0, 4, 8]) return out_layer1
def test_non_last_qubits_trace(self): # Test trace for two conseg. non-last qubits state1 = tensor([s0, s1]) state2 = tensor([(s0 + s1)/np.sqrt(2), (s0 - s1)/np.sqrt(2)]) state = tensor([state1, state2]) dm1 = trace(density_matrix(state), [0, 1]) self.assertTrue(almost_equal(dm1, density_matrix(state2))) # Test trace for two separated non-last qubits state_test = tensor([s0, (s0 + s1)/np.sqrt(2)]) dm2 = trace(density_matrix(state), [1, 3]) self.assertTrue(almost_equal(dm2, density_matrix(state_test)))
def layer2(state_w, dm_in, t4): dm_w = density_matrix(state_w) dm_in = tensor([dm_w, dm_in]) # ndtotext_print(dm_in) # Remember this is a density matrix, so we must apply U on both sides U_layer2.thetas.assign([t4]) U_matrix = U_layer2.matrix() dm_out = U_matrix @ dm_in @ tf.math.conj(U_matrix) return trace(dm_out, [1, 2, 3])
def call(self, inputs, training=None, mask=None): x1, x2, x3 = inputs[..., 0, :, :], inputs[..., 1, :, :], inputs[..., 2, :, :] state_1_in = tensor([self.w1, x1]) state_2_in = tensor([self.w2, x2]) state_3_in = tensor([self.w3, x3]) state_in = tensor([state_1_in, state_2_in, state_3_in]) state_out = self.U_layer1.matrix() @ state_in dm_out_layer1 = density_matrix(state_out, [0, 4, 8]) dm_w = density_matrix(self.w4) dm_in = tensor([dm_w, dm_out_layer1]) U_matrix = self.U_layer2.matrix() dm_out = U_matrix @ dm_in @ tf.math.conj(U_matrix) dm_output = trace(dm_out, [1, 2, 3]) P0 = dm_output[..., 0, 0] return P0
def apply_qc_to_data(fea): opers = [] # Encoding is Z(acos(x**2)) * Y(asin(x)) on all qubits encoding_oper = qc.RZ(fea[0]) @ qc.RY(fea[1]) opers.append(tensor([ encoding_oper, encoding_oper, encoding_oper, encoding_oper ])) return apply_operators2state(opers, s0000)
def apply_qc_to_data(data): opers = [] opers.append( tensor([ U3([data[0], π / 2, π / 2]), # ERR NOT THE SAME AS XYZ!!! U3([data[1], π / 2, π / 2]), U3([data[0], π / 2, π / 2]), U3([data[1], π / 2, π / 2]) ])) opers.append(u_1) return apply_operators2state(opers, s0000)
def apply_qc_to_data(data): opers = [] opers.append(tensor([ U3([data[0], π/2, π/2]), U3([data[1], π/2, π/2]), U3([data[2], π/2, π/2]), U3([data[3], π/2, π/2]) ])) opers.append(u_1) # opers.append(u3_1) # opers.append(u_2) # opers.append(u3_2) return apply_operators2state(opers, s0000)
def apply_qc_to_data(data): opers = [] opers.append( tensor([ RX(data[0]) @ RY(π / 4) @ RZ(π / 4), RX(data[1]) @ RY(π / 4) @ RZ(π / 4), RX(data[0]) @ RY(π / 4) @ RZ(π / 4), RX(data[1]) @ RY(π / 4) @ RZ(π / 4) ])) opers.append(u3_1) opers.append(u_1) opers.append(u3_2) opers.append(u_2) opers.append(u3_3) opers.append(u_3) # opers.append(uXY_4) # opers.append(u_4) return apply_operators2state(opers, s0000)
data = lambda N: random_pure_states((2, 2**N, 1)) l1 = ULayer() l2 = ULayer([[0, 1, 2, 3], [4, 5, 6, 7]]) l1(data(8)) l2(data(8)) l1.thetas = l2.thetas assert tf.reduce_all(l1.matrix() == l2.matrix()) l1 = ULayer() l_I1 = ILayer() l2 = ULayer([[0, 1, 2, 3], [4, 5, 6, 7]]) l1(data(8)) l_I1(data(1)) l2(data(9)) l1.thetas = l2.thetas assert tf.reduce_all(tensor([l1.matrix(), l_I1.matrix()]) == l2.matrix()) l1 = ULayer() l_I1 = ILayer() l2 = ULayer([[1, 2, 3, 4], [5, 6, 7, 8]]) l1(data(8)) l_I1(data(1)) l2(data(10)) l1.thetas = l2.thetas assert tf.reduce_all( tensor([l_I1.matrix(), l1.matrix(), l_I1.matrix()]) == l2.matrix()) l1 = ULayer() l_I2 = ILayer() l2 = ULayer([0, 1, 2, 3])
from tf_qc import complex_type from tf_qc.layers import ULayer from tf_qc.utils import random_pure_states, normalize_state_vectors from tf_qc.qc import s0, s1, tensor, density_matrix, trace from txtutils import ndtotext_print import tensorflow as tf import math π = math.pi s000 = tensor([s0, s0, s0]) s001 = tensor([s0, s0, s1]) s010 = tensor([s0, s1, s0]) s011 = tensor([s0, s1, s1]) s100 = tensor([s1, s0, s0]) s101 = tensor([s1, s0, s1]) s110 = tensor([s1, s1, s0]) s111 = tensor([s1, s1, s1]) targets = [0, 1, 2, 3] u_layer = ULayer(targets) data = random_pure_states((10, 2**4, 1), post_zeros=0) data_shape = data.shape u_layer.build(data_shape) def tree_qubit_state(amps): amps = tf.reshape(amps, (-1, 2**3, 1)) return tf.cast(normalize_state_vectors(amps), complex_type)
def call(self, inputs, training=None, mask=None): uXY_1 = tensor([ RX(self.wXY_1[0]) @ RZ(self.wXY_1[1]), RX(self.wXY_1[2]) @ RZ(self.wXY_1[3]), RX(self.wXY_1[4]) @ RZ(self.wXY_1[5]), RX(self.wXY_1[6]) @ RZ(self.wXY_1[7]) ]) uXY_2 = tensor([ RX(self.wXY_2[0]) @ RZ(self.wXY_2[1]), RX(self.wXY_2[2]) @ RZ(self.wXY_2[3]), RX(self.wXY_2[4]) @ RZ(self.wXY_2[5]), RX(self.wXY_2[6]) @ RZ(self.wXY_2[7]) ]) uXY_3 = tensor([ RX(self.wXY_3[0]) @ RZ(self.wXY_3[1]), RX(self.wXY_3[2]) @ RZ(self.wXY_3[3]), RX(self.wXY_3[4]) @ RZ(self.wXY_3[5]), RX(self.wXY_3[6]) @ RZ(self.wXY_3[7]) ]) uXY_4 = tensor([ RX(self.wXY_4[0]) @ RZ(self.wXY_4[1]), RX(self.wXY_4[2]) @ RZ(self.wXY_4[3]), RX(self.wXY_4[4]) @ RZ(self.wXY_4[5]), RX(self.wXY_4[6]) @ RZ(self.wXY_4[7]) ]) u3_1 = tensor([ U3(self.wU3_1[0:3]), U3(self.wU3_1[3:6]), U3(self.wU3_1[6:9]), U3(self.wU3_1[9:12]) ]) u3_2 = tensor([ U3(self.wU3_2[0:3]), U3(self.wU3_2[3:6]), U3(self.wU3_2[6:9]), U3(self.wU3_2[9:12]) ]) u3_3 = tensor([ U3(self.wU3_3[0:3]), U3(self.wU3_3[3:6]), U3(self.wU3_3[6:9]), U3(self.wU3_3[9:12]) ]) u_1 = qc.U(self.w_U_iswap[0]) u_2 = qc.U(self.w_U_iswap[1]) u_3 = qc.U(self.w_U_iswap[2]) u_4 = qc.U(self.w_U_iswap[3]) # iswap_C1T1 = ISWAPLayer.matrix_static(4, [0, 2], self.w_U_iswap[0]) # iswap_C1T2 = ISWAPLayer.matrix_static(4, [0, 3], self.w_U_iswap[1]) # iswap_C2T1 = ISWAPLayer.matrix_static(4, [1, 2], self.w_U_iswap[2]) # iswap_C2T2 = ISWAPLayer.matrix_static(4, [1, 3], self.w_U_iswap[3]) # u_2 = qc.U(self.wU[1]) # u_3 = qc.U(self.wU[2]) def apply_qc_to_data(data): opers = [] opers.append( tensor([ RX(data[0]) @ RY(π / 4) @ RZ(π / 4), RX(data[1]) @ RY(π / 4) @ RZ(π / 4), RX(data[0]) @ RY(π / 4) @ RZ(π / 4), RX(data[1]) @ RY(π / 4) @ RZ(π / 4) ])) opers.append(u3_1) opers.append(u_1) opers.append(u3_2) opers.append(u_2) opers.append(u3_3) opers.append(u_3) # opers.append(uXY_4) # opers.append(u_4) return apply_operators2state(opers, s0000) outputs = tf.map_fn(apply_qc_to_data, inputs, dtype=complex_type, parallel_iterations=12) # Make a linearcombi. of the output probabilities # we measure all of them as combine them in linear combi. + bias and then apply sigmoid # print(outputs) Ps = tf.cast(measure(outputs, [0]), float_type) # print(Ps) return Ps
def naive_impl(states, n): tensor([states] + [s0] * n)
def test_gate_expand_2to_n(self): swap2 = SWAP(2, 0, 1) self.assertTrue(almost_equal(gate_expand_2toN(swap2, 3, targets=[0, 1]), tensor([swap2, I1]))) self.assertTrue(almost_equal(gate_expand_2toN(swap2, 4, targets=[1, 2]), tensor([I1, swap2, I1]))) self.assertTrue(almost_equal(gate_expand_2toN(swap2, 4, targets=[2, 3]), tensor([I1, I1, swap2])))
def call(self, inputs, training=None, mask=None): u_1 = ISWAPLayer.matrix_static(4, [1,2], self.w_U[0]) u_2 = ISWAPLayer.matrix_static(4, [1,3], self.w_U[1]) u_3 = ISWAPLayer.matrix_static(4, [1,2], self.w_U[2]) u_4 = ISWAPLayer.matrix_static(4, [1,3], self.w_U[3]) u_5 = ISWAPLayer.matrix_static(4, [0,3], self.w_U[4]) u_6 = ISWAPLayer.matrix_static(4, [0,2], self.w_U[5]) # u_1 = qc.U(self.w_U[0]) # u_2 = qc.U(self.w_U[1]) # u_3 = qc.U(self.w_U[2]) # u_4 = qc.U(self.w_U[3]) # u_5 = qc.U(self.w_U[4]) # u_6 = qc.U(self.w_U[5]) rxzx_1 = tensor([ qc.RXZX(self.w_RXZX[0,0,0], self.w_RXZX[0,0,1], self.w_RXZX[0,0,2]), qc.RXZX(self.w_RXZX[0,1,0], self.w_RXZX[0,1,1], self.w_RXZX[0,1,2]), qc.RXZX(self.w_RXZX[0,2,0], self.w_RXZX[0,2,1], self.w_RXZX[0,2,2]), qc.RXZX(self.w_RXZX[0,3,0], self.w_RXZX[0,3,1], self.w_RXZX[0,3,2]) ]) rxzx_2 = tensor([ qc.RXZX(self.w_RXZX[1, 0, 0], self.w_RXZX[1, 0, 1], self.w_RXZX[1, 0, 2]), qc.RXZX(self.w_RXZX[1, 1, 0], self.w_RXZX[1, 1, 1], self.w_RXZX[1, 1, 2]), qc.RXZX(self.w_RXZX[1, 2, 0], self.w_RXZX[1, 2, 1], self.w_RXZX[1, 2, 2]), qc.RXZX(self.w_RXZX[1, 3, 0], self.w_RXZX[1, 3, 1], self.w_RXZX[1, 3, 2]) ]) rxzx_3 = tensor([ qc.RXZX(self.w_RXZX[2, 0, 0], self.w_RXZX[2, 0, 1], self.w_RXZX[2, 0, 2]), qc.RXZX(self.w_RXZX[2, 1, 0], self.w_RXZX[2, 1, 1], self.w_RXZX[2, 1, 2]), qc.RXZX(self.w_RXZX[2, 2, 0], self.w_RXZX[2, 2, 1], self.w_RXZX[2, 2, 2]), qc.RXZX(self.w_RXZX[2, 3, 0], self.w_RXZX[2, 3, 1], self.w_RXZX[2, 3, 2]) ]) rxzx_4 = tensor([ qc.RXZX(self.w_RXZX[3, 0, 0], self.w_RXZX[3, 0, 1], self.w_RXZX[3, 0, 2]), qc.RXZX(self.w_RXZX[3, 1, 0], self.w_RXZX[3, 1, 1], self.w_RXZX[3, 1, 2]), qc.RXZX(self.w_RXZX[3, 2, 0], self.w_RXZX[3, 2, 1], self.w_RXZX[3, 2, 2]), qc.RXZX(self.w_RXZX[3, 3, 0], self.w_RXZX[3, 3, 1], self.w_RXZX[3, 3, 2]) ]) rxzx_5 = tensor([ qc.RXZX(self.w_RXZX[4, 0, 0], self.w_RXZX[4, 0, 1], self.w_RXZX[4, 0, 2]), qc.RXZX(self.w_RXZX[4, 1, 0], self.w_RXZX[4, 1, 1], self.w_RXZX[4, 1, 2]), qc.RXZX(self.w_RXZX[4, 2, 0], self.w_RXZX[4, 2, 1], self.w_RXZX[4, 2, 2]), qc.RXZX(self.w_RXZX[4, 3, 0], self.w_RXZX[4, 3, 1], self.w_RXZX[4, 3, 2]) ]) rxzx_6 = tensor([ qc.RXZX(self.w_RXZX[5, 0, 0], self.w_RXZX[5, 0, 1], self.w_RXZX[5, 0, 2]), qc.RXZX(self.w_RXZX[5, 1, 0], self.w_RXZX[5, 1, 1], self.w_RXZX[5, 1, 2]), qc.RXZX(self.w_RXZX[5, 2, 0], self.w_RXZX[5, 2, 1], self.w_RXZX[5, 2, 2]), qc.RXZX(self.w_RXZX[5, 3, 0], self.w_RXZX[5, 3, 1], self.w_RXZX[5, 3, 2]) ]) def apply_qc_to_data(fea): opers = [] # Encoding is Z(acos(x**2)) * Y(asin(x)) on all qubits encoding_oper = qc.RZ(fea[0]) @ qc.RY(fea[1]) opers.append(tensor([ encoding_oper, encoding_oper, encoding_oper, encoding_oper ])) return apply_operators2state(opers, s0000) encoding = tf.map_fn(apply_qc_to_data, inputs, dtype=complex_type, parallel_iterations=12) W_circuit = [ # This is the parametrized circuit u_1, rxzx_1, u_2, rxzx_2, u_3, rxzx_3, u_4, rxzx_4, u_5, rxzx_5, u_6, rxzx_6 ] outputs = apply_operators2state(W_circuit, encoding) # Measure Z on 1st qubit and add a bit of noise # https://en.wikipedia.org/wiki/Density_matrix#Measurement # The expectation value <Z> = tr(density_matrix @ Z_oper) # Z_oper = [[1,0], [0,-1]] so <Z> = density_matrix[0,0] - density_matrix[1,1] # But P0 - P1 = P0 - (1-P0) = 2*P0 - 1, so <Z> = 2*density_matrix[0,0] - 1 # OK to take partial trace first: https://en.wikipedia.org/wiki/Partial_trace#Partial_trace_as_a_quantum_operation Z01 = measure(outputs, [0]) Z_exp = 2*tf.cast(Z01[..., 0], float_type) - 1 noise = tf.sqrt(2/self.samples) * (Z_exp**2 - 1)/4 measurement = self.out_scale * (Z_exp + noise) + self.out_bias return measurement