def test_gaussian_symplectic(self, r, phi):
        r"""Checks symplectic transformations, and get_covmat_xp, get_mean_xp."""
        example = circuit.BosonicModes(2, 1)

        # Create a symplectic and apply to first mode
        Smat = np.array([[np.exp(-r), 0], [0, np.exp(r)]])
        Rmat = np.array([[np.cos(phi / 2), -np.sin(phi / 2)],
                         [np.sin(phi / 2), np.cos(phi / 2)]])
        Rmat2 = np.array([[np.cos(phi), -np.sin(phi)],
                          [np.sin(phi), np.cos(phi)]])
        symp = Rmat2 @ Rmat @ Smat @ Rmat.T
        symp = example.expandS([0], symp)
        example.apply_channel(symp, np.zeros(symp.shape))

        # Apply the corresponding operations to second mode
        example.squeeze(r, phi, 1)
        example.phase_shift(phi, 1)

        # Check that same modes are in both states
        assert np.allclose(example.get_covmat()[:, 0:2, 0:2],
                           example.get_covmat()[:, 2:4, 2:4])
        assert np.allclose(example.get_mean()[:, 0:2],
                           example.get_mean()[:, 2:4])
        assert np.allclose(example.get_covmat_xp()[:, 0::2, 0::2],
                           example.get_covmat_xp()[:, 1::2, 1::2])
        assert np.allclose(example.get_mean_xp()[:, 0::2],
                           example.get_mean_xp()[:, 1::2])
    def test_from_cov_and_mean(self, r, phi):
        r"""Checks Gaussian states can be made from mean and covariance."""
        example = circuit.BosonicModes(1, 1)

        # Create a state with a given mean and cov
        d = example.hbar * (np.array(
            [r * np.exp(1j * phi).real, r * np.exp(1j * phi).imag]))
        Smat = np.array([[np.exp(-r), 0], [0, np.exp(r)]])
        Rmat = np.array([[np.cos(phi / 2), -np.sin(phi / 2)],
                         [np.sin(phi / 2), np.cos(phi / 2)]])
        Rmat2 = np.array([[np.cos(phi), -np.sin(phi)],
                          [np.sin(phi), np.cos(phi)]])
        cov_new = Rmat2 @ Rmat @ Smat @ Rmat.T @ Rmat @ Smat.T @ Rmat.T @ Rmat2.T
        d_new = Rmat2 @ Rmat @ Smat @ Rmat.T @ d

        example.from_mean(d_new)
        example.from_covmat(cov_new)

        # Undo all the operations that would have in principle prepared the same state
        example.phase_shift(-phi, 0)
        example.squeeze(-r, phi, 0)
        example.displace(-r, phi, 0)
        # Check that you are left with vacuum
        assert example.is_vacuum(tol=1e-10)

        # Do again specifying the mode this time
        example.from_mean(d_new, modes=[0])
        example.from_covmat(cov_new, modes=[0])
        example.phase_shift(-phi, 0)
        example.squeeze(-r, phi, 0)
        example.displace(-r, phi, 0)
        # Check that you are left with vacuum
        assert example.is_vacuum(tol=1e-10)
    def test_circuit_init(self, num_weights, num_modes):
        r"""Checks that the reset method instantiates the correct number of modes
        as vacuum, each with a given number of weights."""
        # Create a state over num_modes, each with num_weights
        example = circuit.BosonicModes(num_modes, num_weights)
        tot_num_weights = num_weights**num_modes
        num_quad = 2 * num_modes

        # Confirm number of modes
        assert example.nlen == num_modes

        # Confirm normalization
        assert np.isclose(sum(example.get_weights()), 1)

        # Confirm each mode initialized to vacuum
        assert example.is_vacuum(tol=1e-10)
        for i in range(num_modes):
            assert np.isclose(example.fidelity_vacuum([i]), 1)

        # Confirm active modes
        assert example.active == list(range(num_modes))
        # Check mode-wise and quadrature-wise ordering
        to_xp_list = list(range(0, num_quad, 2)) + list(
            range(1, num_quad + 1, 2))
        from_xp_list = []
        for i in range(num_modes):
            from_xp_list += [i, i + num_modes]
        assert np.allclose(example.to_xp, np.array(to_xp_list))
        assert np.allclose(example.from_xp, np.array(from_xp_list))

        # Confirm number of weights, means and covs
        assert example.get_weights().shape == (tot_num_weights, )
        assert example.get_mean().shape == (tot_num_weights, num_quad)
        assert example.get_covmat().shape == circuit.c_shape(
            num_modes, num_weights)
    def test_complex_weight(self, r):
        r"""Checks that a cat state has correct parity and that the mean from homodyne sampling is 0."""
        # Make a cat state
        example = circuit.BosonicModes(1, 4)

        r = abs(r)

        example.weights[0] = 1.0 + 0j
        example.weights[1] = 1.0 + 0j
        example.weights[2] = np.exp(-2 * abs(r)**2)
        example.weights[3] = np.exp(-2 * abs(r)**2)
        example.weights /= np.sum(example.weights)

        hbar = example.hbar
        example.means[0, :] = np.sqrt(2 * hbar) * np.array([r.real, r.imag])
        example.means[1, :] = -np.sqrt(2 * hbar) * np.array([r.real, r.imag])
        example.means[2, :] = 1j * np.sqrt(2 * hbar) * np.array(
            [r.imag, -r.real])
        example.means[3, :] = -1j * np.sqrt(2 * hbar) * np.array(
            [r.imag, -r.real])

        assert np.isclose(example.parity_val(), 1)

        # Perform homodyne
        vals = example.homodyne(0, shots=500)
        mean = vals[:, 0].mean()
        assert np.isclose(mean,
                          0,
                          rtol=0,
                          atol=10 * np.sqrt(abs(r)) / np.sqrt(500))
    def test_reset_circuit(self, num_weights, num_modes):
        r"""Checks that the reset method instantiates the correct number of modes
        as vacuum, each with a given number of weights."""
        # Create a state over 1 mode, 1 weight per mode
        example = circuit.BosonicModes(1, 1)

        # Reset with number of weights and modes and perform the same check
        # as test_circuit_init
        example.reset(num_modes, num_weights)
        tot_num_weights = num_weights**num_modes
        num_quad = 2 * num_modes
        assert example.nlen == num_modes
        assert np.isclose(sum(example.get_weights()), 1)
        assert example.is_vacuum(tol=1e-10)
        for i in range(num_modes):
            assert np.isclose(example.fidelity_vacuum([i]), 1)
        assert example.active == list(range(num_modes))
        assert example.get_mean().shape == (tot_num_weights, num_quad)
        assert example.get_covmat().shape == circuit.c_shape(
            num_modes, num_weights)
        to_xp_list = list(range(0, num_quad, 2)) + list(
            range(1, num_quad + 1, 2))
        from_xp_list = []
        for i in range(num_modes):
            from_xp_list += [i, i + num_modes]
        assert np.allclose(example.to_xp, np.array(to_xp_list))
        assert np.allclose(example.from_xp, np.array(from_xp_list))
    def test_parity(self, num_weights, num_modes):
        r"""Checks parity function for vacuum state."""
        # Create state with number of weights and means
        example = circuit.BosonicModes(num_modes, num_weights)

        # Assert that it has parity of 1 (since it is vacuum)
        assert np.isclose(example.parity_val(), 1)
        for i in range(num_modes):
            assert np.isclose(example.parity_val([i]), 1)
    def test_displace(self, r, phi):
        r"""Checks the displacement operation."""
        # Ensure zero displacement yields vacuum
        example = circuit.BosonicModes(1, 1)
        example.displace(0, 0, 0)
        assert example.is_vacuum(tol=1e-10)

        # Displace and check it is a coherent state
        example.displace(r, phi, 0)
        assert np.isclose(
            example.fidelity_coherent(np.array([r * np.exp(1j * phi)])), 1)
 def test_beamsplitter(self, r, phi):
     r"""Checks applying a beamsplitter."""
     example = circuit.BosonicModes(2, 1)
     # Send two coherent states through beamspliter
     example.displace(r, 0, 0)
     example.displace(r, 0, 1)
     example.beamsplitter(phi, phi, 0, 1)
     alpha = r * (np.cos(phi) - np.exp(-1j * phi) * np.sin(phi))
     beta = r * (np.cos(phi) + np.exp(1j * phi) * np.sin(phi))
     assert np.isclose(example.fidelity_coherent(np.array([alpha, beta])),
                       1)
 def test_linear_optical_unitary(self, r, phi):
     r"""Checks the linear optical unitary implementation of a phase shift."""
     # Create two displaced states
     example = circuit.BosonicModes(2, 1)
     example.displace(r, 0, 0)
     example.displace(r, 0, 1)
     # Rotate them using different techniques
     example.apply_u(np.diag([np.exp(1j * phi), 1]))
     example.phase_shift(phi, 1)
     # Check they have the same means
     assert np.allclose(example.get_mean()[:, 0:2],
                        example.get_mean()[:, 2:4])
 def test_heterodyne(self, num_weights, r, phi):
     r"""Checks a heterodyne measurement."""
     # Create coherent state with multiple weights
     example = circuit.BosonicModes(1, num_weights)
     example.displace(r, phi, 0)
     # Perform heterodyne on mode 0
     vals = example.heterodyne(0, shots=500)
     mean = vals[:, 0].mean()
     assert np.isclose(mean,
                       example.hbar * r * np.cos(phi),
                       rtol=0,
                       atol=10 / np.sqrt(500))
    def test_mb_squeeze_single_shot(self, r, phi, r_anc, eta_anc):
        r"""Checks measurement-based squeezing."""
        example = circuit.BosonicModes(2, 1)

        # Test zero mbsqueezing
        example.mb_squeeze_single_shot(0, 0, 0, 5, 1)
        assert example.is_vacuum(tol=1e-10)

        # Test high-quality mbsqueezing
        example.mb_squeeze_single_shot(0, r, phi, 9, 1)
        example.squeeze(r, phi, 1)
        assert np.allclose(example.get_covmat()[:, 0:2, 0:2],
                           example.get_covmat()[:, 2:4, 2:4])
    def test_post_select_homodyne(self, num_weights, r):
        r"""Checks post_select_homodyne."""
        # Create maximally entangled states from highly squeezed states
        example = circuit.BosonicModes(2, num_weights)
        example.squeeze(8, 0, 0)
        example.squeeze(-8, 0, 1)
        example.beamsplitter(np.pi / 4, 0, 0, 1)

        # Post select homodyne on one
        example.post_select_homodyne(0, r, phi=np.pi / 2)
        # Check other mode ended up in correlated state
        mean_compare = np.tile(np.array([0, 0, 0, -r]), (num_weights**2, 1))
        assert np.allclose(example.get_mean(), mean_compare)
 def test_del_modes(self, num_weights, num_modes):
     r"""Checks that modes can be deleted."""
     # Create a state with the pre-defined number of weights and modes
     example = circuit.BosonicModes(num_modes, num_weights)
     # Delete first mode and check active modes changed
     example.del_mode(0)
     tot_weights = num_weights**num_modes
     assert example.get_modes() == list(range(1, num_modes))
     # Check numbers of weights, means and covs
     assert np.isclose(sum(example.get_weights()), 1)
     assert example.get_weights().shape == (tot_weights, )
     assert example.get_mean().shape == (tot_weights, 2 * (num_modes))
     assert example.get_covmat().shape == (tot_weights, 2 * (num_modes),
                                           2 * (num_modes))
 def test_expandXY(self, num_modes, r):
     r"""Tests the expandXY function"""
     example = circuit.BosonicModes(num_modes, 1)
     X = r * np.eye(2)
     Y = r * np.eye(2)
     X2, Y2 = example.expandXY([0], X, Y)
     X2_diag = np.ones(2 * num_modes)
     X2_diag[0] = r
     X2_diag[num_modes] = r
     Y2_diag = np.zeros(2 * num_modes)
     Y2_diag[0] = r
     Y2_diag[num_modes] = r
     assert np.allclose(X2, np.diag(X2_diag))
     assert np.allclose(Y2, np.diag(Y2_diag))
    def test_add_modes(self, num_weights, num_modes, num_weights_new):
        r"""Checks that modes can be added."""
        # Create a state with the pre-defined number of weights and modes
        example = circuit.BosonicModes(num_modes, num_weights)

        # Add new mode with new number of weights
        example.add_mode([num_weights_new])
        tot_weights = (num_weights**num_modes) * num_weights_new
        # Check numbers of weights, means and covs
        assert example.get_modes() == list(range(num_modes + 1))
        assert np.isclose(sum(example.get_weights()), 1)
        assert example.get_weights().shape == (tot_weights, )
        assert example.get_mean().shape == (tot_weights, 2 * (num_modes + 1))
        assert example.get_covmat().shape == (tot_weights, 2 * (num_modes + 1),
                                              2 * (num_modes + 1))
    def test_generaldyne(self, num_weights, r, num_shots):
        r"""Checks a generaldyne measurement."""
        # Create two vacuums with multiple weights
        example = circuit.BosonicModes(2, num_weights)
        # Create squeezed covariance matrix
        covmat = np.diag(np.array([np.exp(-r), np.exp(r)]))
        # Send two squeezed states through beamspliter
        example.squeeze(r, 0, 0)
        example.squeeze(-r, 0, 1)
        example.beamsplitter(np.pi / 4, 0, 0, 1)
        # Perform generaldyne on mode 0
        vals = example.measure_dyne(covmat, [0], shots=num_shots)

        # Check output has right shape
        assert vals.shape == (num_shots, 2)
        # Check measured mode was set to vacuum
        assert np.isclose(example.fidelity_vacuum(modes=[0]), 1)
    def test_add_del_modes_together(self, num_weights, num_modes,
                                    num_weights_new):
        r"""Checks that modes can be added and deleted in sequence."""
        # Create a state with the pre-defined number of weights and modes
        example = circuit.BosonicModes(num_modes, num_weights)
        # Delete first mode and check active modes changed
        example.del_mode(0)
        assert example.get_modes() == list(range(1, num_modes))

        # Add new mode with new number of weights
        example.add_mode([num_weights_new])
        tot_weights = (num_weights**num_modes) * num_weights_new
        # Check numbers of weights, means and covs
        assert example.get_modes() == list(range(1, num_modes + 1))
        assert np.isclose(sum(example.get_weights()), 1)
        assert example.get_weights().shape == (tot_weights, )
        assert example.get_mean().shape == (tot_weights, 2 * (num_modes + 1))
        assert example.get_covmat().shape == (tot_weights, 2 * (num_modes + 1),
                                              2 * (num_modes + 1))
    def test_rotate(self, r, phi):
        r"""Checks the phase shift operation."""
        # Ensure zero rotation yields vacuum
        example = circuit.BosonicModes(1, 1)
        example.phase_shift(0, 0)
        assert example.is_vacuum(tol=1e-10)

        # Displace
        example.displace(r, phi, 0)
        d = example.hbar * (np.array(
            [r * np.exp(1j * phi).real, r * np.exp(1j * phi).imag]))

        # Rotate state and confirm correct mean and covs produced
        example.phase_shift(phi, 0)
        Rmat2 = np.array([[np.cos(phi), -np.sin(phi)],
                          [np.sin(phi), np.cos(phi)]])
        cov_new = Rmat2 @ Rmat2.T
        d_new = Rmat2 @ d
        assert np.allclose(example.get_mean()[0], d_new)
        assert np.allclose(example.get_covmat()[0], cov_new)
    def test_squeeze(self, r, phi):
        r"""Checks the squeezing operation."""
        # Ensure zero squeezing yields vacuum
        example = circuit.BosonicModes(1, 1)
        example.squeeze(0, 0, 0)
        assert example.is_vacuum(tol=1e-10)

        # Displace
        example.displace(r, phi, 0)

        # Squeeze state and confirm correct mean and covs produced
        example.squeeze(r, phi, 0)
        d = example.hbar * (np.array(
            [r * np.exp(1j * phi).real, r * np.exp(1j * phi).imag]))
        Smat = np.array([[np.exp(-r), 0], [0, np.exp(r)]])
        Rmat = np.array([[np.cos(phi / 2), -np.sin(phi / 2)],
                         [np.sin(phi / 2), np.cos(phi / 2)]])
        cov_new = Rmat @ Smat @ Rmat.T @ Rmat @ Smat.T @ Rmat.T
        d_new = Rmat @ Smat @ Rmat.T @ d
        assert np.allclose(example.get_mean()[0], d_new)
        assert np.allclose(example.get_covmat()[0], cov_new)
    def test_losses(self, r, phi, nbar, num_weights):
        r"""Checks loss, thermal_loss, and init_thermal."""
        # Create state with random number of weights and means
        example = circuit.BosonicModes(3, num_weights)

        # Displace, apply total loss to first mode and check it is vacuum
        example.displace(r, phi, 0)
        example.loss(0, 0)
        assert example.is_vacuum(tol=1e-10)

        # Apply thermal loss to second mode and check the covariance is as expected
        example.thermal_loss(0, nbar, 1)
        assert np.allclose(
            np.tile((2 * nbar + 1) * np.eye(2), (num_weights**3, 1, 1)),
            example.get_covmat()[:, 2:4, 2:4],
        )

        # Initialize third mode as thermal and check it's the same as second mode
        example.init_thermal(nbar, 2)
        assert np.allclose(example.get_covmat()[:, 4:6, 4:6],
                           example.get_covmat()[:, 2:4, 2:4])