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_loss_complete_random(self, hbar, tol): """Test loss on random state""" T = 0 mu = np.random.random(size=[4]) cov = np.array([ [10.4894171, 4.44832813, 7.35223928, -14.0593551], [4.44832813, 5.29244335, -1.48437419, -4.79381772], [7.35223928, -1.48437419, 11.92921345, -11.47687254], [-14.0593551, -4.79381772, -11.47687254, 19.67522694], ]) res = symplectic.loss(mu, cov, T, mode=0, hbar=hbar) # the loss reduces the fractional mean photon number of mode 1 mu_exp = mu.copy() mu_exp[0] = 0 mu_exp[2] = 0 cov_exp = np.array([ [hbar / 2, 0, 0, 0], [0, 5.29244335, 0, -4.79381772], [0, 0, hbar / 2, 0], [0, -4.79381772, 0, 19.67522694], ]) assert np.allclose(res[1], cov_exp, atol=tol, rtol=0) assert np.allclose(res[0], mu_exp, atol=tol, rtol=0)
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_loss_none(self, hbar, tol): """Test no loss on half a TMS leaves state unchanged""" r = 0.543 phi = 0.432 T = 1 S = symplectic.two_mode_squeezing(r, phi) mu = np.zeros([4]) cov = S @ S.T * (hbar / 2) res = symplectic.loss(mu, cov, T, mode=0, hbar=hbar) expected = mu, cov assert np.allclose(res[0], expected[0], atol=tol, rtol=0) assert np.allclose(res[1], expected[1], atol=tol, rtol=0)
def test_four_modes(hbar): """Test that probabilities are correctly updates for a four modes system under loss""" # All this block is to generate the correct covariance matrix. # It correnponds to num_modes=4 modes that undergo two mode squeezing between modes i and i + (num_modes / 2). # Then they undergo displacement. # The signal and idlers see and interferometer with unitary matrix u2x2. # And then they see loss by amount etas[i]. num_modes = 4 theta = 0.45 phi = 0.7 u2x2 = np.array([ [np.cos(theta / 2), np.exp(1j * phi) * np.sin(theta / 2)], [-np.exp(-1j * phi) * np.sin(theta / 2), np.cos(theta / 2)], ]) u4x4 = block_diag(u2x2, u2x2) cov = np.identity(2 * num_modes) * hbar / 2 means = 0.5 * np.random.rand(2 * num_modes) * np.sqrt(hbar / 2) rs = [0.1, 0.9] n_half = num_modes // 2 for i, r_val in enumerate(rs): Sexpanded = expand(two_mode_squeezing(r_val, 0.0), [i, n_half + i], num_modes) cov = Sexpanded @ cov @ (Sexpanded.T) Su = expand(interferometer(u4x4), range(num_modes), num_modes) cov = Su @ cov @ (Su.T) cov_lossless = np.copy(cov) means_lossless = np.copy(means) etas = [0.9, 0.7, 0.9, 0.1] for i, eta in enumerate(etas): means, cov = loss(means, cov, eta, i, hbar=hbar) cutoff = 3 probs_lossless = probabilities(means_lossless, cov_lossless, 4 * cutoff, hbar=hbar) probs = probabilities(means, cov, cutoff, hbar=hbar) probs_updated = update_probabilities_with_loss(etas, probs_lossless) assert np.allclose(probs, probs_updated[:cutoff, :cutoff, :cutoff, :cutoff], atol=1e-6)
def test_loss_thermal_state(self, hbar, tol): """Test loss on part of a thermal state""" nbar = np.array([0.4532, 0.123, 0.432]) T = 0.54 mu = np.zeros([2 * len(nbar)]) cov = np.diag(2 * np.tile(nbar, 2) + 1) * (hbar / 2) res = symplectic.loss(mu, cov, T, mode=1, hbar=hbar) # the loss reduces the fractional mean photon number of mode 1 new_nbar = nbar * np.array([1, T, 1]) expected = mu, np.diag(2 * np.tile(new_nbar, 2) + 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)