Example #1
0
    def test_squeezing_no_phi_array(self, tol):
        """Test multimode squeezing symplectic transform without specifying phi"""
        r = np.random.randn(6)
        phi = np.zeros_like(r)

        S = symplectic.squeezing(r)
        S_phi = symplectic.squeezing(r, phi)

        assert np.allclose(S, S_phi, atol=tol, rtol=0)
Example #2
0
    def test_dtype(self, tol):
        """Test multimode version gives symplectic matrix"""
        r = [0.543] * 4
        phi = [0.123] * 4
        S = symplectic.squeezing(r, phi)

        S32_bit = symplectic.squeezing(r, phi, dtype=np.float32)

        # the symplectic matrix
        O = symplectic.sympmat(4)

        assert np.allclose(S32_bit @ O @ S32_bit.T, O, atol=tol, rtol=0)
        assert np.allclose(S, S32_bit, atol=tol, rtol=0)
    def test_symplectic(self, tol):
        """Test that the squeeze operator is symplectic"""
        r = 0.543
        phi = 0.123
        S = symplectic.squeezing(r, phi)

        # the symplectic matrix
        O = np.array([[0, 1], [-1, 0]])

        assert np.allclose(S @ O @ S.T, O, atol=tol, rtol=0)
Example #4
0
    def test_symplectic_multimode(self, tol):
        """Test multimode version gives symplectic matrix"""
        r = [0.543] * 4
        phi = [0.123] * 4
        S = symplectic.squeezing(r, phi)

        # the symplectic matrix
        O = symplectic.sympmat(4)

        assert np.allclose(S @ O @ S.T, O, atol=tol, rtol=0)
    def test_squeezing(self, tol):
        """Test the squeezing symplectic transform."""
        r = 0.543
        phi = 0.123
        S = symplectic.squeezing(r, phi)
        out = S @ S.T

        # apply to an identity covariance matrix
        rotation = np.array([[np.cos(phi / 2), -np.sin(phi / 2)],
                             [np.sin(phi / 2),
                              np.cos(phi / 2)]])
        expected = rotation @ np.diag(np.exp([-2 * r, 2 * r])) @ rotation.T
        assert np.allclose(out, expected, atol=tol, rtol=0)
Example #6
0
    def squeeze(self, r, phi, k):
        r"""Squeeze mode ``k`` by the amount ``r*exp(1j*phi)``.

        Args:
            r (float): squeezing magnitude
            phi (float): squeezing phase
            k (int): mode to be squeezed

        Raises:
            ValueError: if the mode is not in the list of active modes
        """
        if self.active[k] is None:
            raise ValueError("Cannot squeeze mode, mode does not exist")

        sq = symp.expand(symp.squeezing(r, phi), k, self.nlen)
        self.means = update_means(self.means, sq, self.from_xp)
        self.covs = update_covs(self.covs, sq, self.from_xp)
Example #7
0
def test_pnd_squeeze_displace(tol, r, phi, alpha, hbar):
    """Test the photon number distribution for the squeezed displaced state

    Eq. (17) in 'Benchmarking of Gaussian boson sampling using two-point correlators',
    Phillips et al. (https://ris.utwente.nl/ws/files/122721825/PhysRevA.99.023836.pdf).
    """
    S = squeezing(r, phi)
    mu = [np.sqrt(2 * hbar) * np.real(alpha), np.sqrt(2 * hbar) * np.imag(alpha)]

    cov = hbar / 2 * (S @ S.T)
    pnd_cov = photon_number_covmat(mu, cov, hbar=hbar)

    pnd_cov_analytic = np.sinh(r) ** 2 * np.cosh(r) ** 2 + np.sinh(r) ** 4 \
        + np.sinh(r) ** 2 + np.abs(alpha) ** 2 * (1 + 2 * np.sinh(r) ** 2) \
        - 2 * np.real(alpha ** 2 * np.exp(-1j * phi) * np.sinh(r) * np.cosh(r))

    assert np.isclose(float(pnd_cov), pnd_cov_analytic, atol=tol, rtol=0)
def test_is_symplectic():
    """ Tests that the matrices generated in the symplectic module are indeed symplectic"""
    theta = np.pi / 6
    r = np.arcsinh(1.0)
    phi = np.pi / 8
    S = symplectic.rotation(theta)
    assert symplectic.is_symplectic(S)
    S = symplectic.squeezing(r, theta)
    assert symplectic.is_symplectic(S)
    S = symplectic.beam_splitter(theta, phi)
    assert symplectic.is_symplectic(S)
    S = symplectic.two_mode_squeezing(r, theta)
    assert symplectic.is_symplectic(S)
    A = np.array([[2.0, 3.0], [4.0, 6.0]])
    assert not symplectic.is_symplectic(A)
    A = np.identity(3)
    assert not symplectic.is_symplectic(A)
    A = np.array([[2.0, 3.0], [4.0, 6.0], [4.0, 6.0]])
    assert not symplectic.is_symplectic(A)
Example #9
0
def test_single_mode_displacement_squeezing(choi_r, tol):
    """Tests the correct construction of the single mode squeezing operation followed by the single mode displacement operation"""
    nmodes = 1
    s = 1.0
    cutoff = 5
    S = squeezing(s, 0.0)
    alphas = (0.5 + 0.4 * 1j) * np.ones([nmodes])
    # This data is obtained by using qutip
    # np.array((displace(40,alpha)*squeeze(40,r)).data.todense())[0:5,0:5]
    expected = np.array(
        [
            [0.6263739 + 0.09615331j, -0.22788717 + 0.13121343j, 0.36548296 - 0.0200537j, -0.20708137 + 0.14004403j, 0.25645667 - 0.06275564j],
            [0.5425389 + 0.14442404j, 0.19268911 + 0.15615312j, 0.11497303 + 0.13744549j, 0.21448948 + 0.08109308j, -0.03652914 + 0.15069359j],
            [-0.00915607 + 0.07475267j, 0.48081922 + 0.10576742j, -0.00961086 + 0.20535144j, 0.33089303 + 0.09864247j, 0.02555522 + 0.19950786j],
            [-0.34614367 - 0.05229875j, 0.11543956 + 0.01112537j, 0.16672961 + 0.07439407j, 0.02323121 + 0.15103267j, 0.27233637 + 0.08297028j],
            [-0.14390852 - 0.08884069j, -0.37898007 - 0.07630228j, 0.12911863 - 0.08963054j, -0.12164023 + 0.04431394j, 0.1141808 + 0.01581529j],
        ]
    )
    T = fock_tensor(S, alphas, cutoff, choi_r=choi_r)
    assert np.allclose(T, expected, atol=tol, rtol=0)
Example #10
0
def test_single_mode_squeezing(choi_r, tol):
    """Tests the correct construction of the single mode squeezing operation"""
    nmodes = 1
    s = 1.0
    cutoff = 5
    S = squeezing(s, 0.0)
    alphas = np.zeros([nmodes])
    # This data is obtained by using qutip
    # np.array(squeeze(40,r).data.todense())[0:5,0:5]
    expected = np.array(
        [
            [0.80501818 + 0.0j, 0.0 + 0.0j, 0.43352515 + 0.0j, 0.0 + 0.0j, 0.2859358 + 0.0j],
            [0.0 + 0.0j, 0.52169547 + 0.0j, 0.0 + 0.0j, 0.48661591 + 0.0j, 0.0 + 0.0j],
            [-0.43352515 + 0.0j, 0.0 + 0.0j, 0.10462138 + 0.0j, 0.0 + 0.0j, 0.29199268 + 0.0j],
            [0.0 + 0.0j, -0.48661591 + 0.0j, 0.0 + 0.0j, -0.23479643 + 0.0j, 0.0 + 0.0j],
            [0.2859358 + 0.0j, 0.0 + 0.0j, -0.29199268 + 0.0j, 0.0 + 0.0j, -0.34474749 + 0.0j],
        ]
    )
    T = fock_tensor(S, alphas, cutoff, choi_r=choi_r)
    assert np.allclose(T, expected, atol=tol, rtol=0)
Example #11
0
    def compile(self, seq, registers):
        """Try to arrange a quantum circuit into the canonical Symplectic form.

        This method checks whether the circuit can be implemented as a sequence of Gaussian operations.
        If the answer is yes it arranges them in the canonical order with displacement at the end.

        Args:
            seq (Sequence[Command]): quantum circuit to modify
            registers (Sequence[RegRefs]): quantum registers
        Returns:
            List[Command]: modified circuit
        Raises:
            CircuitError: the circuit does not correspond to a Gaussian unitary
        """

        # Check which modes are actually being used
        used_modes = []
        for operations in seq:
            modes = [modes_label.ind for modes_label in operations.reg]
            used_modes.append(modes)
        # pylint: disable=consider-using-set-comprehension
        used_modes = list(
            set([item for sublist in used_modes for item in sublist]))

        # dictionary mapping the used modes to consecutive non-negative integers
        dict_indices = {used_modes[i]: i for i in range(len(used_modes))}
        nmodes = len(used_modes)

        # This is the identity transformation in phase-space, multiply by the identity and add zero
        Snet = np.identity(2 * nmodes)
        rnet = np.zeros(2 * nmodes)

        # Now we will go through each operation in the sequence `seq` and apply it in quadrature space
        # We will keep track of the net transforation in the Symplectic matrix `Snet` and the quadrature
        # vector `rnet`.
        for operations in seq:
            name = operations.op.__class__.__name__
            params = par_evaluate(operations.op.p)
            modes = [modes_label.ind for modes_label in operations.reg]
            if name == "Dgate":
                rnet = rnet + expand_vector(
                    params[0] *
                    (np.exp(1j * params[1])), dict_indices[modes[0]], nmodes)
            else:
                if name == "Rgate":
                    S = expand(rotation(params[0]), dict_indices[modes[0]],
                               nmodes)
                elif name == "Sgate":
                    S = expand(squeezing(params[0], params[1]),
                               dict_indices[modes[0]], nmodes)
                elif name == "S2gate":
                    S = expand(
                        two_mode_squeezing(params[0], params[1]),
                        [dict_indices[modes[0]], dict_indices[modes[1]]],
                        nmodes,
                    )
                elif name == "Interferometer":
                    S = expand(interferometer(params[0]),
                               [dict_indices[mode] for mode in modes], nmodes)
                elif name == "GaussianTransform":
                    S = expand(params[0],
                               [dict_indices[mode] for mode in modes], nmodes)
                elif name == "BSgate":
                    S = expand(
                        beam_splitter(params[0], params[1]),
                        [dict_indices[modes[0]], dict_indices[modes[1]]],
                        nmodes,
                    )
                elif name == "MZgate":
                    v = np.exp(1j * params[0])
                    u = np.exp(1j * params[1])
                    U = 0.5 * np.array([[u * (v - 1), 1j *
                                         (1 + v)], [1j * u * (1 + v), 1 - v]])
                    S = expand(
                        interferometer(U),
                        [dict_indices[modes[0]], dict_indices[modes[1]]],
                        nmodes,
                    )
                Snet = S @ Snet
                rnet = S @ rnet

        # Having obtained the net displacement we simply convert it into complex notation
        alphas = 0.5 * (rnet[0:nmodes] + 1j * rnet[nmodes:2 * nmodes])
        # And now we just pass the net transformation as a big Symplectic operation plus displacements
        ord_reg = [r for r in list(registers) if r.ind in used_modes]
        ord_reg = sorted(list(ord_reg), key=lambda x: x.ind)
        if np.allclose(Snet, np.identity(2 * nmodes)):
            A = []
        else:
            A = [Command(ops.GaussianTransform(Snet), ord_reg)]
        B = [
            Command(ops.Dgate(np.abs(alphas[i]), np.angle(alphas[i])),
                    ord_reg[i]) for i in range(len(ord_reg))
            if not np.allclose(alphas[i], 0.0)
        ]
        return A + B
Example #12
0
def test_cumulants_three_mode_random_state(hbar):  # pylint: disable=too-many-statements
    """Tests third order cumulants for a random state"""
    M = 3
    O = interferometer(random_interferometer(3))
    mu = np.random.rand(2 * M) - 0.5
    hbar = 2
    cov = 0.5 * hbar * O @ squeezing(np.random.rand(M)) @ O.T
    cutoff = 50
    probs = probabilities(mu, cov, cutoff, hbar=hbar)
    n = np.arange(cutoff)
    probs0 = np.sum(probs, axis=(1, 2))
    probs1 = np.sum(probs, axis=(0, 2))
    probs2 = np.sum(probs, axis=(0, 1))

    # Check one body cumulants
    n0_1 = n @ probs0
    n1_1 = n @ probs1
    n2_1 = n @ probs2
    assert np.allclose(photon_number_cumulant(mu, cov, [0], hbar=hbar), n0_1)
    assert np.allclose(photon_number_cumulant(mu, cov, [1], hbar=hbar), n1_1)
    assert np.allclose(photon_number_cumulant(mu, cov, [2], hbar=hbar), n2_1)

    n0_2 = n**2 @ probs0
    n1_2 = n**2 @ probs1
    n2_2 = n**2 @ probs2
    var0 = n0_2 - n0_1**2
    var1 = n1_2 - n1_1**2
    var2 = n2_2 - n2_1**2
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 0], hbar=hbar),
                       var0)
    assert np.allclose(photon_number_cumulant(mu, cov, [1, 1], hbar=hbar),
                       var1)
    assert np.allclose(photon_number_cumulant(mu, cov, [2, 2], hbar=hbar),
                       var2)

    n0_3 = n**3 @ probs0 - 3 * n0_2 * n0_1 + 2 * n0_1**3
    n1_3 = n**3 @ probs1 - 3 * n1_2 * n1_1 + 2 * n1_1**3
    n2_3 = n**3 @ probs2 - 3 * n2_2 * n2_1 + 2 * n2_1**3
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 0, 0], hbar=hbar),
                       n0_3)
    assert np.allclose(photon_number_cumulant(mu, cov, [1, 1, 1], hbar=hbar),
                       n1_3)
    assert np.allclose(photon_number_cumulant(mu, cov, [2, 2, 2], hbar=hbar),
                       n2_3)

    # Check two body cumulants
    probs01 = np.sum(probs, axis=(2))
    probs02 = np.sum(probs, axis=(1))
    probs12 = np.sum(probs, axis=(0))

    n0n1 = n @ probs01 @ n
    n0n2 = n @ probs02 @ n
    n1n2 = n @ probs12 @ n
    covar01 = n0n1 - n0_1 * n1_1
    covar02 = n0n2 - n0_1 * n2_1
    covar12 = n1n2 - n1_1 * n2_1

    assert np.allclose(photon_number_cumulant(mu, cov, [0, 1], hbar=hbar),
                       covar01)
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 2], hbar=hbar),
                       covar02)
    assert np.allclose(photon_number_cumulant(mu, cov, [1, 2], hbar=hbar),
                       covar12)

    kappa001 = n**2 @ probs01 @ n - 2 * n0n1 * n0_1 - n0_2 * n1_1 + 2 * n0_1**2 * n1_1
    kappa011 = n @ probs01 @ n**2 - 2 * n0n1 * n1_1 - n1_2 * n0_1 + 2 * n1_1**2 * n0_1
    kappa002 = n**2 @ probs02 @ n - 2 * n0n2 * n0_1 - n0_2 * n2_1 + 2 * n0_1**2 * n2_1
    kappa022 = n @ probs02 @ n**2 - 2 * n0n2 * n2_1 - n2_2 * n0_1 + 2 * n2_1**2 * n0_1
    kappa112 = n**2 @ probs12 @ n - 2 * n1n2 * n1_1 - n1_2 * n2_1 + 2 * n1_1**2 * n2_1
    kappa122 = n @ probs12 @ n**2 - 2 * n1n2 * n2_1 - n2_2 * n1_1 + 2 * n2_1**2 * n1_1

    assert np.allclose(photon_number_cumulant(mu, cov, [0, 0, 1], hbar=hbar),
                       kappa001)
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 1, 1], hbar=hbar),
                       kappa011)
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 0, 2], hbar=hbar),
                       kappa002)
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 2, 2], hbar=hbar),
                       kappa022)
    assert np.allclose(photon_number_cumulant(mu, cov, [1, 1, 2], hbar=hbar),
                       kappa112)
    assert np.allclose(photon_number_cumulant(mu, cov, [1, 2, 2], hbar=hbar),
                       kappa122)

    # Finally, the three body cumulant
    n0n1n2 = np.einsum("ijk, i, j, k", probs, n, n, n)
    kappa012 = n0n1n2 - n0n1 * n2_1 - n0n2 * n1_1 - n1n2 * n0_1 + 2 * n0_1 * n1_1 * n2_1
    assert np.allclose(photon_number_cumulant(mu, cov, [0, 1, 2], hbar=hbar),
                       kappa012)