def circuit(X): # Create a parameter with an initial value of 2. params = [make_param(name='phi', constant=2.)] eng, q = sf.Engine(2) with eng: # Note that we are feeding 1-d tensors into gates, not scalars! Dgate(X[:, 0], 0.) | q[0] Dgate(X[:, 1], 0.) | q[1] BSgate(phi=params[0]) | (q[0], q[1]) BSgate() | (q[0], q[1]) # We have to tell the engine how big the batches (first dim of X) are # which we feed into gates num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs) # Define the output as the probability of measuring |0,2> as opposed to |2,0> p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 circuit_output = p1 / normalization return circuit_output
def qnn_layer(layer_number): with tf.name_scope('layer_{}'.format(layer_number)): BSgate(bs_variables[layer_number, 0, 0, 0], bs_variables[layer_number, 0, 0, 1]) \ | (q[0], q[1]) for i in range(mode_number): Rgate(phase_variables[layer_number, i, 0]) | q[i] for i in range(mode_number): Sgate( tf.clip_by_value(sq_magnitude_variables[layer_number, i], -sq_clip, sq_clip), sq_phase_variables[layer_number, i]) | q[i] BSgate(bs_variables[layer_number, 0, 1, 0], bs_variables[layer_number, 0, 1, 1]) \ | (q[0], q[1]) for i in range(mode_number): Rgate(phase_variables[layer_number, i, 1]) | q[i] for i in range(mode_number): Dgate( tf.clip_by_value(disp_magnitude_variables[layer_number, i], -disp_clip, disp_clip), disp_phase_variables[layer_number, i]) | q[i] for i in range(mode_number): Kgate( tf.clip_by_value(kerr_variables[layer_number, i], -kerr_clip, kerr_clip)) | q[i]
def layer(l): with tf.name_scope('layer_{}'.format(l)): BSgate(theta1[l], phi1[l]) | (q[0], q[1]) Rgate(r1[l]) | q[0] Sgate(tf.clip_by_value(sqr1[l], -sq_clip, sq_clip), sqphi1[l]) | q[0] Sgate(tf.clip_by_value(sqr2[l], -sq_clip, sq_clip), sqphi2[l]) | q[1] BSgate(theta2[l], phi2[l]) | (q[0], q[1]) Rgate(r2[l]) | q[0] Dgate(tf.clip_by_value(dr1[l], -disp_clip, disp_clip), dphi1[l]) | q[0] Dgate(tf.clip_by_value(dr2[l], -disp_clip, disp_clip), dphi2[l]) | q[1] Kgate(tf.clip_by_value(kappa1[l], -kerr_clip, kerr_clip)) | q[0] Kgate(tf.clip_by_value(kappa2[l], -kerr_clip, kerr_clip)) | q[1]
def circuit(X): params = [ make_param(name='phi', constant=2.), make_param(name="theta", constant=1.), make_param(name="theta2", constant=.1), make_param(name="theta3", constant=.1), make_param(name="theta4", constant=.1), make_param(name="theta5", constant=.1), make_param(name="theta6", constant=.1), make_param(name="theta7", constant=.1), make_param(name="theta8", constant=.1), make_param(name="theta9", constant=.1), ] eng, q = sf.Engine(2) with eng: Dgate(X[:, 0], 0.) | q[0] Dgate(X[:, 1], 0.) | q[1] BSgate(phi=params[0]) | (q[0], q[1]) BSgate() | (q[0], q[1]) Vgate(params[1]) | q[0] Vgate(params[2]) | q[1] num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs) p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 output1 = p1 / normalization with eng: X1 = output1 Dgate(X1, 0.) | q[0] Dgate(0., X1) | q[1] BSgate(phi=params[1]) | (q[0], q[1]) BSgate() | (q[0], q[1]) Vgate(params[5]) | q[0] Vgate(params[6]) | q[1] num_inputs1 = X1.get_shape().as_list()[0] state2 = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs1) p00 = state2.fock_prob([0, 2]) p01 = state2.fock_prob([2, 0]) normalization1 = p00 + p01 + 1e-10 circuit_output = p01 / normalization1 return circuit_output
def single_input_circuit(x): eng.reset() with eng: Dgate(x[0], 0.) | q[0] Dgate(x[1], 0.) | q[1] BSgate(phi=params[0]) | (q[0], q[1]) BSgate() | (q[0], q[1]) state = eng.run('fock', cutoff_dim=10, eval=True) p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 output = p1 / normalization return output
def test_cov_is_pure(): """Tests space unrolling when going into the Gaussian backend""" delays = [1, 6, 36] modes = 216 angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) net = modes + sum(delays) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): Sgate(0.8) | q[n[0]] for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() eng = sf.Engine(backend="gaussian") results = eng.run(prog) cov = results.state.cov() mu = np.zeros(len(cov)) mu_vac, cov_vac = reduced_state(mu, cov, list(range(vac_modes))) mu_comp, cov_comp = reduced_state(mu, cov, list(range(vac_modes, net))) assert np.allclose(cov_vac, 0.5 * (sf.hbar) * np.identity(2 * vac_modes)) assert is_pure_cov(cov_comp, hbar=sf.hbar)
def test_is_permutation_when_angle_pi_on_two(delays, modes): """Checks that if all the beamsplitters are cross then the absolute value output matrix is a permutation matrix""" delays = list(delays) net = modes + sum(delays) angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) angles[0] = np.pi / 2 * np.random.randint(2, size=net) angles[1] = np.pi / 2 * np.random.randint(2, size=net) angles[2] = np.pi / 2 * np.random.randint(2, size=net) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] assert np.allclose(U @ U.T.conj(), np.identity(len(U))) assert np.allclose(list(map(max, np.abs(U))), 1.0)
def test_no_entanglement_between_padding_and_computational_modes( delays, modes): """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations""" delays = list(delays) angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] # Check that it is indeed the identity U_AA = U[:vac_modes, :vac_modes] U_AB = U[vac_modes:, :vac_modes] U_BA = U[:vac_modes, vac_modes:] U_BB = U[vac_modes:, vac_modes:] assert np.allclose(U_AA, np.identity(vac_modes)) assert np.allclose(U_AB, 0) assert np.allclose(U_BA, 0) assert np.allclose(U_BB @ U_BB.T.conj(), np.identity(len(U_BB)))
def decompose(self, reg): # make BS gate theta = self.layer['BS'][0] phi = self.layer['BS'][1] BS = BSgate(theta, phi) # make Kerr gate K = Kgate(self.layer['K'][0]) # make rotation gate R = Rgate(self.layer['R'][0]) cmds = [] for i in range(self.num_layers): #pylint: disable=unused-variable for q0, q1 in self.layer['BS'][2]: cmds.append(Command(BS, (reg[q0], reg[q1]))) for mode in self.layer['K'][1]: cmds.append(Command(K, reg[mode])) for mode in self.layer['R'][1]: cmds.append(Command(R, reg[mode])) return cmds
def single_input_circuit(x): eng.reset() with eng: Dgate(x[0], 0.) | q[0] Dgate(x[1], 0.) | q[1] BSgate(phi=params[0]) | (q[0], q[1]) BSgate() | (q[0], q[1]) state = eng.run('fock', cutoff_dim=10, eval=True) # Define the output as the probability of measuring |0,2> as opposed to |2,0> p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 output = p1 / normalization return output
def circuit(params, a, m, cutoff): """Runs the constrained variational circuit with specified parameters, returning the output fidelity to the requested ON state, as well as the post-selection probability. Args: params (list): list of gate parameters for the constrained variational quantum circuit. This should contain the following values in the following order: * ``'sq0' = (r, phi)``: the squeezing magnitude and phase on mode 0 * ``'disp0' = (r, phi)``: the displacement magnitude and phase on mode 0 * ``'sq1' = (r, phi)``: the squeezing magnitude and phase on mode 1 * ``'disp1' = (r, phi)``: the displacement magnitude and phase on mode 1 * ``'BS' = (theta, phi)``: the beamsplitter angles a (float): the ON state parameter m (int): the Fock state measurement to be post-selected cutoff (int): the Fock basis truncation Returns: tuple: a tuple containing the output fidelity to the target ON state, the probability of post-selection, the state norm before entering the beamsplitter, the state norm after exiting the beamsplitter, and the density matrix of the output state. """ # define target state ONdm = on_state(a, cutoff) # unpack circuit parameters sq0_r, sq0_phi, disp0_r, disp0_phi, sq1_r, sq1_phi, disp1_r, disp1_phi, theta, phi = params # quantum circuit prior to entering the beamsplitter prog1 = sf.Program(2) with prog1.context as q1: Sgate(sq0_r, sq0_phi) | q1[0] Dgate(disp0_r, disp0_phi) | q1[0] Sgate(sq1_r, sq1_phi) | q1[1] Dgate(disp1_r, disp1_phi) | q1[1] eng = sf.Engine("fock", backend_options={"cutoff_dim": cutoff}) stateIn = eng.run(prog1).state normIn = np.abs(stateIn.trace()) # norm of output state and probability prog_BS = sf.Program(2) with prog_BS.context as q1: BSgate(theta, phi) | (q1[0], q1[1]) stateOut = eng.run(prog_BS).state normOut = np.abs(stateOut.trace()) rho = stateOut.dm() # probability of meausring m1 and m2 prob = np.abs(np.trace(rho[m, m])) # output state rhoB = rho[m, m] / prob fidelity = np.abs(np.trace(np.einsum('ij,jk->ik', rhoB, ONdm))) return (fidelity, prob, normIn, normOut, rhoB)
def test_is_channel_preparation(self, prog): """is_channel() returns False if preparations are present""" with prog.context as q: Sgate(0.4) | q[0] BSgate() | q Squeezed(0.4) | q[1] assert not utils.is_channel(prog)
def test_is_unitary_no_channel(self, prog): """test that the is_unitary function returns True if no channels are present""" assert utils.is_unitary(prog) with prog.context as q: Sgate(0.4) | q[0] BSgate(0.4) | q assert utils.is_unitary(prog)
def test_is_unitary_with_channel(self, prog): """test that the is_unitary function returns False if channels are present""" with prog.context as q: Sgate(0.4) | q[0] LossChannel(0.4) | q[0] BSgate(0.4) | q assert not utils.is_unitary(prog)
def test_is_channel_no_measurement(self, prog): """test that the is_channel function returns True if no measurements are present""" assert utils.is_channel(prog) with prog.context as q: Sgate(0.4) | q[0] LossChannel(0.4) | q[0] BSgate(0.4) | q assert utils.is_channel(prog)
def test_is_channel_measurement(self, prog): """is_channel() returns False if measurements are present""" with prog.context as q: Sgate(0.4) | q[0] BSgate() | q MeasureX | q[0] Sgate(0.4) | q[1] assert not utils.is_channel(prog)
def circuit(X): # Create a parameter with an initial value of 2. params = [make_param(name='phi', constant=2.), make_param(name="theta", constant=1.), make_param(name="theta2", constant=.1), make_param(name="theta3", constant=.1), make_param(name="theta4", constant=.1), make_param(name="theta5", constant=.1), make_param(name="theta6", constant=.1), make_param(name="theta7", constant=.1), make_param(name="theta8", constant=.1), make_param(name="theta9", constant=.1),] eng, q = sf.Engine(2) with eng: # Note that we are feeding 1-d tensors into gates, not scalars! Dgate(X[:, 0], 0.) | q[0] Dgate(X[:, 1], 0.) | q[1] BSgate(phi=params[0]) | (q[0], q[1]) BSgate() | (q[0], q[1]) Vgate(params[1]) | q[0] Vgate(params[2]) | q[1]
def layer(i, size): Rgate(rtheta_1[i, 0]) | (q[0]) BSgate(phi_1[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_1[i, 2]) | (q[1]) for j in range(size): Sgate(r[i, j]) | q[j] Rgate(rtheta_2[i, 0]) | (q[0]) BSgate(phi_2[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_2[i, 2]) | (q[2]) BSgate(phi_2[i, 2], theta_2[i, 3]) | (q[2], q[3]) Rgate(rtheta_2[i, 1]) | (q[1]) BSgate(phi_2[i, 1], 0) | (q[1], q[2]) Rgate(rtheta_2[i, 0]) | (q[0]) BSgate(phi_2[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_2[i, 0]) | (q[0]) Rgate(rtheta_2[i, 1]) | (q[1]) Rgate(rtheta_2[i, 2]) | (q[2]) Rgate(rtheta_2[i, 3]) | (q[3]) BSgate(phi_2[i, 2], 0) | (q[2], q[3]) Rgate(rtheta_2[i, 2]) | (q[2]) BSgate(phi_2[i, 1], 0) | (q[1], q[2]) Rgate(rtheta_2[i, 1]) | (q[1]) for j in range(size): Kgate(kappa[i, j]) | q[j]
def test_is_channel_no_measurement(self): """test that the is_channel function returns True if no measurements are present""" eng, q = sf.Engine(2) assert utils.is_channel(eng) with eng: Sgate(0.4) | q[0] BSgate(0.4) | q assert utils.is_channel(eng) with eng: Sgate(0.4) | q[0] LossChannel(0.4) | q[0] BSgate(0.4) | q assert utils.is_channel(eng)
def circuit(A, n_qmodes, params): I = np.eye(2 * n_qmodes) X_top = np.hstack((np.zeros((n_qmodes, n_qmodes)), np.eye(n_qmodes))) X_bot = np.hstack((np.eye(n_qmodes), np.zeros((n_qmodes, n_qmodes)))) X = np.vstack((X_top, X_bot)) # c = 0 # A = np.array([[c,-2,-10,1], # [-2,c,1,5], # [-10,1,c,-2], # [1,5,-2,c]]) zeros_4 = np.zeros((n_qmodes, n_qmodes)) c_prim = 1 A_prim = np.vstack((np.hstack((zeros_4, A)), np.hstack( (A, zeros_4)))) + np.eye(2 * n_qmodes) * c_prim d = 0.05 # Cov = np.linalg.inv(I - X@(d*A)) - I/2 Cov = np.linalg.inv(I - X @ (d * A_prim)) - I / 2 eng, q = sf.Engine(n_qmodes) with eng: # beamsplitter array Gaussian(Cov) | q Sgate(params[0]) | q[0] Sgate(params[1]) | q[1] Sgate(params[2]) | q[2] Sgate(params[3]) | q[3] Dgate(params[4]) | q[0] Dgate(params[5]) | q[1] Dgate(params[6]) | q[2] Dgate(params[7]) | q[3] BSgate(params[8], params[9]) | (q[0], q[1]) BSgate(params[10], params[11]) | (q[2], q[3]) BSgate(params[12], params[13]) | (q[1], q[2]) return eng, q
def test_is_unitary_with_channel(self): """test that the is_unitary function returns False if channels are present""" eng, q = sf.Engine(2) with eng: Sgate(0.4) | q[0] LossChannel(0.4) | q[0] BSgate(0.4) | q assert not utils.is_unitary(eng)
def prog(): """Program used for testing""" program = sf.Program(2) with program.context as q: Sgate(0.54, 0) | q[0] Sgate(0.54, 0) | q[1] BSgate(6.283, 0.6283) | (q[0], q[1]) MeasureFock() | q return program
def circuit(X): phi = make_param('phi', constant=2.) eng, q = sf.Engine(2) with eng: Dgate(X[:, 0], 0.) | q[0] Dgate(X[:, 1], 0.) | q[1] BSgate(phi=phi) | (q[0], q[1]) BSgate() | (q[0], q[1]) num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs) p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalisation = p0 + p1 + 1e-10 circuit_output = p1 / normalisation return circuit_output
def test_is_unitary_no_channel(self): """test that the is_unitary function returns True if no channels are present""" eng, q = sf.Engine(2) assert utils.is_unitary(eng) with eng: Sgate(0.4) | q[0] BSgate(0.4) | q assert utils.is_unitary(eng)
def test_is_channel_measurement(self): """test that the is_channel function returns False if measurements or preparations are present""" eng, q = sf.Engine(2) with eng: Sgate(0.4) | q[0] BSgate() | q MeasureX | q[0] Sgate(0.4) | q[1] assert not utils.is_channel(eng) eng.reset() with eng: Sgate(0.4) | q[0] BSgate() | q Squeezed(0.4) | q[1] assert not utils.is_channel(eng)
def test_beamsplitter(self, hbar): """Test beamsplitter produces correct cov and means""" H, t = beamsplitter(self.th, self.phi, hbar=hbar) resD, resV = self.H_circuit(H, t) gate = BSgate(self.th, self.phi) expD, expV = self.ref_circuit(gate) # test the covariance matrix assert np.allclose(resV, expV) # test the vector of means assert np.allclose(resD, expD)
def test_beamsplitter(self): """Test beamsplitter produces correct cov and means""" self.eng.reset() H, t = beamsplitter(self.th, self.phi, hbar=self.hbar) resD, resV = self.H_circuit(H, t) gate = BSgate(self.th, self.phi) expD, expV = self.ref_circuit(gate) # test the covariance matrix self.assertTrue(np.allclose(resV, expV)) # test the vector of means self.assertTrue(np.allclose(resD, expD))
def input_qnn_layer(): with tf.name_scope('inputlayer'): Sgate(tf.clip_by_value(output_layer[:, 0], -sq_clip, sq_clip), output_layer[:, 1]) | q[0] Sgate(tf.clip_by_value(output_layer[:, 2], -sq_clip, sq_clip), output_layer[:, 3]) | q[1] BSgate(output_layer[:, 4], output_layer[:, 5]) | (q[0], q[1]) Rgate(output_layer[:, 6]) | q[0] Rgate(output_layer[:, 7]) | q[1] Dgate(tf.clip_by_value(output_layer[:, 8], -disp_clip, disp_clip), output_layer[:, 9]) \ | q[0] Dgate(tf.clip_by_value(output_layer[:, 10], -disp_clip, disp_clip), output_layer[:, 11]) \ | q[0] Kgate(tf.clip_by_value(output_layer[:, 12], -kerr_clip, kerr_clip)) | q[0] Kgate(tf.clip_by_value(output_layer[:, 13], -kerr_clip, kerr_clip)) | q[0]
def decompose(self, reg): # make BS gate theta = self.layer['BS'][0] phi = self.layer['BS'][1] BS = BSgate(theta, phi) # make cross-Kerr gate CK = None param = self.layer.get('CK', [0])[0] if param != 0: CK = CKgate(param) # make Kerr gate K = None param = self.layer.get('K', [0])[0] if param != 0: K = Kgate(param) # make rotation gate R = None param = self.layer.get('R', [0])[0] if param != 0: R = Rgate(param) cmds = [] for i in range(self.num_layers): #pylint: disable=unused-variable for q0, q1 in self.layer['BS'][2]: cmds.append(Command(BS, (reg[q0], reg[q1]))) if CK is not None: for q0, q1 in self.layer['CK'][1]: cmds.append(Command(CK, (reg[q0], reg[q1]))) if K is not None: for mode in self.layer['K'][1]: cmds.append(Command(K, reg[mode])) if R is not None: for mode in self.layer['R'][1]: cmds.append(Command(R, reg[mode])) return cmds
def test_lossless_no_mixing_no_rotation_U(delays, modes): """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations""" delays = list(delays) angles = np.zeros([2 * len(delays), modes + sum(delays)]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] # Check that it is indeed the identity assert np.allclose(U, np.identity(len(U)))