def test_loss_complete(self, hbar, tol): """Test full loss on half a TMS""" r = 0.543 phi = 0.432 T = 0 S = symplectic.two_mode_squeezing(r, phi) mu = np.zeros([4]) cov = S @ S.T * (hbar / 2) mu, cov = symplectic.loss(mu, cov, T, mode=0, hbar=hbar) # expected state mode 0 expected0 = np.zeros([2]), np.identity(2) * hbar / 2 res0 = symplectic.reduced_state(mu, cov, 0) # expected state mode 1 nbar = np.sinh(r)**2 expected1 = np.zeros([2]), np.identity(2) * (2 * nbar + 1) * hbar / 2 res1 = symplectic.reduced_state(mu, cov, 1) assert np.allclose(res0[0], expected0[0], atol=tol, rtol=0) assert np.allclose(res0[1], expected0[1], atol=tol, rtol=0) assert np.allclose(res1[0], expected1[0], atol=tol, rtol=0) assert np.allclose(res1[1], expected1[1], atol=tol, rtol=0)
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_displaced_loss_against_interferometer(self, hbar, tol): """Test that the loss channel on a displaced state corresponds to a beamsplitter acting on the mode with loss and an ancilla vacuum state""" T = 0.812 alpha = np.random.random(size=[2]) + np.random.random(size=[2]) * 1j mu = np.concatenate([alpha.real, alpha.imag]) # perform loss mu_res, _ = symplectic.loss(mu, np.identity(4), T, mode=0, hbar=hbar) # create a two mode beamsplitter acting on modes 0 and 2 B = np.array([[np.sqrt(T), -np.sqrt(1 - T), 0, 0], [np.sqrt(1 - T), np.sqrt(T), 0, 0], [0, 0, np.sqrt(T), -np.sqrt(1 - T)], [0, 0, np.sqrt(1 - T), np.sqrt(T)]]) B = symplectic.expand(B, modes=[0, 2], N=3) # apply the beamsplitter to modes 0 and 2 mu_expand = np.zeros([6]) mu_expand[np.array([0, 1, 3, 4])] = mu mu_expected, _ = symplectic.reduced_state(B @ mu_expand, np.identity(6), modes=[0, 1]) # compare loss function result to an interferometer mixing mode 0 with the vacuum assert np.allclose(mu_expected, mu_res, atol=tol, rtol=0)
def test_all(self, hbar, tol): """Test requesting all wires returns the full state""" mu, cov = symplectic.vacuum_state(4, hbar=hbar) res = symplectic.reduced_state(mu, cov, [0, 1, 2, 3]) expected = np.zeros([8]), np.identity(8) * hbar / 2 assert np.allclose(res[0], expected[0], atol=tol, rtol=0) assert np.allclose(res[1], expected[1], atol=tol, rtol=0)
def test_integer(self, hbar, tol): """Test requesting via an integer""" mu, cov = symplectic.vacuum_state(4, hbar=hbar) res = symplectic.reduced_state(mu, cov, 0) expected = np.zeros([2]), np.identity(2) * hbar / 2 assert np.allclose(res[0], expected[0], atol=tol, rtol=0) assert np.allclose(res[1], expected[1], atol=tol, rtol=0)
def test_tms(self, hbar, tol): """Test reduced state of a TMS state is a thermal state""" r = 0.543 phi = 0.432 S = symplectic.two_mode_squeezing(r, phi) mu = np.zeros([4]) cov = S @ S.T * (hbar / 2) res = symplectic.reduced_state(mu, cov, 0) # expected state nbar = np.sinh(r)**2 expected = np.zeros([2]), np.identity(2) * (2 * nbar + 1) * hbar / 2 assert np.allclose(res[0], expected[0], atol=tol, rtol=0) assert np.allclose(res[1], expected[1], atol=tol, rtol=0)
def test_TMS_against_interferometer(self, hbar, tol): """Test that the loss channel on a TMS state corresponds to a beamsplitter acting on the mode with loss and an ancilla vacuum state""" r = 0.543 phi = 0.432 T = 0.812 S = symplectic.two_mode_squeezing(r, phi) cov = S @ S.T * (hbar / 2) # perform loss _, cov_res = symplectic.loss(np.zeros([4]), cov, T, mode=0, hbar=hbar) # create a two mode beamsplitter acting on modes 0 and 2 B = np.array([ [np.sqrt(T), -np.sqrt(1 - T), 0, 0], [np.sqrt(1 - T), np.sqrt(T), 0, 0], [0, 0, np.sqrt(T), -np.sqrt(1 - T)], [0, 0, np.sqrt(1 - T), np.sqrt(T)], ]) B = symplectic.expand(B, modes=[0, 2], N=3) # add an ancilla vacuum state in mode 2 cov_expand = np.identity(6) * hbar / 2 cov_expand[:2, :2] = cov[:2, :2] cov_expand[3:5, :2] = cov[2:, :2] cov_expand[:2, 3:5] = cov[:2, 2:] cov_expand[3:5, 3:5] = cov[2:, 2:] # apply the beamsplitter to modes 0 and 2 cov_expand = B @ cov_expand @ B.T # compare loss function result to an interferometer mixing mode 0 with the vacuum _, cov_expected = symplectic.reduced_state(np.zeros([6]), cov_expand, modes=[0, 1]) assert np.allclose(cov_expected, cov_res, atol=tol, rtol=0)
def test_exception(self, ): """Test error is raised if requesting a non-existant subsystem""" with pytest.raises(ValueError, match="cannot be larger than number of subsystems"): symplectic.reduced_state(np.array([0, 0]), np.identity(2), [6, 4])