def test_two_dimensional_cluster_denmark():
    """
    Two-dimensional temporal-mode cluster state as demonstrated in https://arxiv.org/pdf/1906.08709
    """
    np.random.seed(42)
    sq_r = 3
    delay1 = 1  # number of timebins in the short delay line
    delay2 = 12  # number of timebins in the long delay line
    n = 200  # number of timebins
    shots = 10
    # first half of cluster state measured in X, second half in P

    theta_A = [0] * int(n / 2) + [np.pi / 2] * int(
        n / 2)  # measurement angles for detector A
    theta_B = theta_A  # measurement angles for detector B

    # 2D cluster
    prog = tdmprogram.TDMProgram([1, delay2 + delay1 + 1])
    with prog.context(theta_A, theta_B, shift="default") as (p, q):
        ops.Sgate(sq_r, 0) | q[0]
        ops.Sgate(sq_r, 0) | q[delay2 + delay1 + 1]
        ops.Rgate(np.pi / 2) | q[delay2 + delay1 + 1]
        ops.BSgate(np.pi / 4, np.pi) | (q[delay2 + delay1 + 1], q[0])
        ops.BSgate(np.pi / 4, np.pi) | (q[delay2 + delay1], q[0])
        ops.BSgate(np.pi / 4, np.pi) | (q[delay1], q[0])
        ops.MeasureHomodyne(p[1]) | q[0]
        ops.MeasureHomodyne(p[0]) | q[delay1]
    eng = sf.Engine("gaussian")
    result = eng.run(prog, shots=shots)
    reshaped_samples = result.samples

    for sh in range(shots):
        X_A = reshaped_samples[sh][0][:n // 2]  # X samples from detector A
        P_A = reshaped_samples[sh][0][n // 2:]  # P samples from detector A
        X_B = reshaped_samples[sh][1][:n // 2]  # X samples from detector B
        P_B = reshaped_samples[sh][1][n // 2:]  # P samples from detector B

        # nullifiers defined in https://arxiv.org/pdf/1906.08709.pdf, Eqs. (1) and (2)
        N = delay2
        ntot = len(X_A) - delay2 - 1
        nX = np.array([
            X_A[k] + X_B[k] - X_A[k + 1] - X_B[k + 1] - X_A[k + N] +
            X_B[k + N] - X_A[k + N + 1] + X_B[k + N + 1] for k in range(ntot)
        ])
        nP = np.array([
            P_A[k] + P_B[k] + P_A[k + 1] + P_B[k + 1] - P_A[k + N] +
            P_B[k + N] + P_A[k + N + 1] - P_B[k + N + 1] for k in range(ntot)
        ])
        nXvar = np.var(nX)
        nPvar = np.var(nP)

        assert np.allclose(nXvar,
                           4 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
        assert np.allclose(nPvar,
                           4 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
def test_one_dimensional_cluster_tokyo():
    """
    One-dimensional temporal-mode cluster state as demonstrated in
    https://aip.scitation.org/doi/pdf/10.1063/1.4962732
    """
    np.random.seed(42)
    sq_r = 5

    n = 10  # for an n-mode cluster state
    shots = 3

    # first half of cluster state measured in X, second half in P
    theta1 = [0] * int(n / 2) + [np.pi / 2] * int(
        n / 2)  # measurement angles for detector A
    theta2 = theta1  # measurement angles for detector B

    prog = tdmprogram.TDMProgram(N=[1, 2])
    with prog.context(theta1, theta2, shift="default") as (p, q):
        ops.Sgate(sq_r, 0) | q[0]
        ops.Sgate(sq_r, 0) | q[2]
        ops.Rgate(np.pi / 2) | q[0]
        ops.BSgate(np.pi / 4) | (q[0], q[2])
        ops.BSgate(np.pi / 4) | (q[0], q[1])
        ops.MeasureHomodyne(p[0]) | q[0]
        ops.MeasureHomodyne(p[1]) | q[1]
    eng = sf.Engine("gaussian")

    result = eng.run(prog, shots=shots)
    reshaped_samples = result.samples

    for sh in range(shots):
        X_A = reshaped_samples[sh][0][:n // 2]  # X samples from detector A
        P_A = reshaped_samples[sh][0][n // 2:]  # P samples from detector A
        X_B = reshaped_samples[sh][1][:n // 2]  # X samples from detector B
        P_B = reshaped_samples[sh][1][n // 2:]  # P samples from detector B

        # nullifiers defined in https://aip.scitation.org/doi/pdf/10.1063/1.4962732, Eqs. (1a) and (1b)
        ntot = len(X_A) - 1
        nX = np.array(
            [X_A[i] + X_B[i] + X_A[i + 1] - X_B[i + 1] for i in range(ntot)])
        nP = np.array(
            [P_A[i] + P_B[i] - P_A[i + 1] + P_B[i + 1] for i in range(ntot)])

        nXvar = np.var(nX)
        nPvar = np.var(nP)

        assert np.allclose(nXvar,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(n))
        assert np.allclose(nPvar,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(n))
 def compile_test_program(device, args=(-1, 1, 2, 3)):
     """Compiles a test program with the given gate arguments."""
     alpha = [args[1]]
     beta = [args[2]]
     gamma = [args[3]]
     prog = tdmprogram.TDMProgram(N=2)
     with prog.context(alpha, beta, gamma) as (p, q):
         ops.Sgate(args[0]) | q[
             1]  # Note that the Sgate has a second parameter that is non-zero
         ops.Rgate(p[0]) | q[0]
         ops.BSgate(p[1]) | (q[0], q[1])
         ops.MeasureHomodyne(p[2]) | q[0]
     prog.compile(device=device, compiler=device.compiler)
    def test_passing_list_of_tdmprograms(self):
        """Test that error is raised when passing a list containing TDM programs"""
        prog = tdmprogram.TDMProgram(N=2)
        with prog.context([1, 1], [1, 1], [1, 1]) as (p, q):
            ops.Sgate(0, 0) | q[1]
            ops.BSgate(p[0]) | (q[0], q[1])
            ops.Rgate(p[1]) | q[1]
            ops.MeasureHomodyne(p[2]) | q[0]

        eng = sf.Engine("gaussian")

        with pytest.raises(
                NotImplementedError,
                match="Lists of TDM programs are not currently supported"):
            eng.run([prog, prog])
 def test_at_least_one_measurement(self):
     """Checks circuit has at least one measurement operator"""
     sq_r = 1.0
     N = 3
     shots = 1
     alpha = [0] * 4
     phi = [0] * 4
     prog = tdmprogram.TDMProgram(N=N)
     with pytest.raises(ValueError,
                        match="Must be at least one measurement."):
         with prog.context(alpha, phi, shift="default") as (p, q):
             ops.Sgate(sq_r, 0) | q[2]
             ops.BSgate(p[0]) | (q[1], q[2])
             ops.Rgate(p[1]) | q[2]
         eng = sf.Engine("gaussian")
         eng.run(prog, shots=shots)
 def test_tdm_wrong_modes(self):
     """Test the correct error is raised when the tdm circuit registers don't match the device spec"""
     sq_r = 0.5643
     c = 2
     alpha = [np.pi / 4, 0] * c
     phi = [0, np.pi / 2] * c
     theta = [0, 0] + [np.pi / 2, np.pi / 2]
     prog = tdmprogram.TDMProgram(N=2)
     with prog.context(alpha, phi, theta) as (p, q):
         ops.Sgate(sq_r) | q[1]
         ops.BSgate(p[0]) | (q[0], q[1])  # The order should be (q[1], q[0])
         ops.Rgate(p[1]) | q[1]
         ops.MeasureHomodyne(p[2]) | q[0]
     eng = sf.Engine("gaussian")
     with pytest.raises(sf.program_utils.CircuitError,
                        match="due to incompatible mode ordering."):
         prog.compile(device=device, compiler="TD2")
 def test_tdm_wrong_parameter_second_argument(self):
     """Test the correct error is raised when the tdm circuit explicit parameters are not within the allowed ranges"""
     sq_r = 0.5643
     c = 2
     alpha = [np.pi / 4, 0] * c
     phi = [0, np.pi / 2] * c
     theta = [0, 0] + [np.pi / 2, np.pi / 2]
     prog = tdmprogram.TDMProgram(N=2)
     with prog.context(alpha, phi, theta) as (p, q):
         ops.Sgate(sq_r, 0.4) | q[
             1]  # Note that the Sgate has a second parameter that is non-zero
         ops.BSgate(p[0]) | (q[1], q[0])
         ops.Rgate(p[1]) | q[1]
         ops.MeasureHomodyne(p[2]) | q[0]
     eng = sf.Engine("gaussian")
     with pytest.raises(sf.program_utils.CircuitError,
                        match="due to incompatible parameter."):
         prog.compile(device=device, compiler="TD2")
def singleloop_program(r, alpha, phi, theta):
    """Single delay loop with program.

    Args:
        r (float): squeezing parameter
        alpha (Sequence[float]): beamsplitter angles
        phi (Sequence[float]): rotation angles
        theta (Sequence[float]): homodyne measurement angles
    Returns:
        (array): homodyne samples from the single loop simulation
    """
    prog = tdmprogram.TDMProgram(N=2)
    with prog.context(alpha, phi, theta) as (p, q):
        ops.Sgate(r, 0) | q[1]
        ops.BSgate(p[0]) | (q[1], q[0])
        ops.Rgate(p[1]) | q[1]
        ops.MeasureHomodyne(p[2]) | q[0]
    return prog
 def test_tdm_wrong_layout(self):
     """Test the correct error is raised when the tdm circuit gates don't match the device spec"""
     sq_r = 0.5643
     c = 2
     alpha = [np.pi / 4, 0] * c
     phi = [0, np.pi / 2] * c
     theta = [0, 0] + [np.pi / 2, np.pi / 2]
     prog = tdmprogram.TDMProgram(N=2)
     with prog.context(alpha, phi, theta) as (p, q):
         ops.Dgate(sq_r) | q[1]  # Here one should have an Sgate
         ops.BSgate(p[0]) | (q[1], q[0])
         ops.Rgate(p[1]) | q[1]
         ops.MeasureHomodyne(p[2]) | q[0]
     eng = sf.Engine("gaussian")
     with pytest.raises(
             sf.program_utils.CircuitError,
             match="The gates or the order of gates used in the Program",
     ):
         prog.compile(device=device, compiler="TD2")
 def test_spatial_modes_number_of_measurements_match(self):
     """Checks number of spatial modes matches number of measurements"""
     sq_r = 1.0
     shots = 1
     alpha = [0] * 4
     phi = [0] * 4
     theta = [0] * 4
     with pytest.raises(
             ValueError,
             match=
             "Number of measurement operators must match number of spatial modes."
     ):
         prog = tdmprogram.TDMProgram(N=[3, 3])
         with prog.context(alpha, phi, theta) as (p, q):
             ops.Sgate(sq_r, 0) | q[2]
             ops.BSgate(p[0]) | (q[1], q[2])
             ops.Rgate(p[1]) | q[2]
             ops.MeasureHomodyne(p[2]) | q[0]
         eng = sf.Engine("gaussian")
         result = eng.run(prog, shots=shots)
def singleloop(r, alpha, phi, theta, shots, shift="default"):
    """Single-loop program.

    Args:
        r (float): squeezing parameter
        alpha (Sequence[float]): beamsplitter angles
        phi (Sequence[float]): rotation angles
        theta (Sequence[float]): homodyne measurement angles
        shots (int): number of shots
        shift (string): type of shift used in the program
    Returns:
        (list): homodyne samples from the single loop simulation
    """
    prog = tdmprogram.TDMProgram(N=2)
    with prog.context(alpha, phi, theta, shift=shift) as (p, q):
        ops.Sgate(r, 0) | q[1]
        ops.BSgate(p[0]) | (q[0], q[1])
        ops.Rgate(p[1]) | q[1]
        ops.MeasureHomodyne(p[2]) | q[0]
    eng = sf.Engine("gaussian")
    result = eng.run(prog, shots=shots)

    return result.samples
Exemple #12
0
def singleloop(r, alpha, phi, theta, copies, shift="default", hbar=2):
    """Single delay loop with program.

    Args:
        r (float): squeezing parameter
        alpha (Sequence[float]): beamsplitter angles
        phi (Sequence[float]): rotation angles
        theta (Sequence[float]): homodyne measurement angles
        hbar (float): value in appearing in the commutation relation
    Returns:
        (list): homodyne samples from the single loop simulation
    """

    prog = tdmprogram.TDMProgram(N=2)
    with prog.context(alpha, phi, theta, copies=copies, shift=shift) as (p, q):
        ops.Sgate(r, 0) | q[1]
        ops.BSgate(p[0]) | (q[0], q[1])
        ops.Rgate(p[1]) | q[1]
        ops.MeasureHomodyne(p[2]) | q[0]
    eng = sf.Engine("gaussian")
    result = eng.run(prog, hbar=hbar)

    return result.samples[0]
def test_two_dimensional_cluster_tokyo():
    """
    Two-dimensional temporal-mode cluster state as demonstrated by Universtiy of Tokyo. See: https://arxiv.org/pdf/1903.03918.pdf
    """
    # temporal delay in timebins for each spatial mode
    delayA = 0
    delayB = 1
    delayC = 5
    delayD = 0

    # concurrent modes in each spatial mode
    concurrA = 1 + delayA
    concurrB = 1 + delayB
    concurrC = 1 + delayC
    concurrD = 1 + delayD

    N = [concurrA, concurrB, concurrC, concurrD]

    sq_r = 5

    # first half of cluster state measured in X, second half in P
    n = 400  # number of timebins
    theta_A = [0] * int(n / 2) + [np.pi / 2] * int(
        n / 2)  # measurement angles for detector A
    theta_B = theta_A  # measurement angles for detector B
    theta_C = theta_A
    theta_D = theta_A

    shots = 10

    # 2D cluster
    prog = tdmprogram.TDMProgram(N)
    with prog.context(theta_A, theta_B, theta_C, theta_D,
                      shift="default") as (p, q):

        ops.Sgate(sq_r, 0) | q[0]
        ops.Sgate(sq_r, 0) | q[2]
        ops.Sgate(sq_r, 0) | q[8]
        ops.Sgate(sq_r, 0) | q[9]

        ops.Rgate(np.pi / 2) | q[0]
        ops.Rgate(np.pi / 2) | q[8]

        ops.BSgate(np.pi / 4) | (q[0], q[2])
        ops.BSgate(np.pi / 4) | (q[8], q[9])
        ops.BSgate(np.pi / 4) | (q[2], q[8])
        ops.BSgate(np.pi / 4) | (q[0], q[1])
        ops.BSgate(np.pi / 4) | (q[3], q[9])

        ops.MeasureHomodyne(p[0]) | q[0]
        ops.MeasureHomodyne(p[1]) | q[1]
        ops.MeasureHomodyne(p[2]) | q[3]
        ops.MeasureHomodyne(p[3]) | q[9]

    eng = sf.Engine("gaussian")
    result = eng.run(prog, shots=shots)
    reshaped_samples = result.samples

    for sh in range(shots):

        X_A = reshaped_samples[sh][0][:n // 2]  # X samples from detector A
        P_A = reshaped_samples[sh][0][n // 2:]  # P samples from detector A
        X_B = reshaped_samples[sh][1][:n // 2]  # X samples from detector B
        P_B = reshaped_samples[sh][1][n // 2:]  # P samples from detector B
        X_C = reshaped_samples[sh][2][:n // 2]  # X samples from detector C
        P_C = reshaped_samples[sh][2][n // 2:]  # P samples from detector C
        X_D = reshaped_samples[sh][3][:n // 2]  # X samples from detector D
        P_D = reshaped_samples[sh][3][n // 2:]  # P samples from detector D

        N = delayC
        # nullifiers defined in https://arxiv.org/pdf/1903.03918.pdf, Fig. S5
        ntot = len(X_A) - N - 1
        nX1 = np.array([
            X_A[k] + X_B[k] - np.sqrt(1 / 2) *
            (-X_A[k + 1] + X_B[k + 1] + X_C[k + N] + X_D[k + N])
            for k in range(ntot)
        ])
        nX2 = np.array([
            X_C[k] - X_D[k] - np.sqrt(1 / 2) *
            (-X_A[k + 1] + X_B[k + 1] - X_C[k + N] - X_D[k + N])
            for k in range(ntot)
        ])
        nP1 = np.array([
            P_A[k] + P_B[k] + np.sqrt(1 / 2) *
            (-P_A[k + 1] + P_B[k + 1] + P_C[k + N] + P_D[k + N])
            for k in range(ntot)
        ])
        nP2 = np.array([
            P_C[k] - P_D[k] + np.sqrt(1 / 2) *
            (-P_A[k + 1] + P_B[k + 1] - P_C[k + N] - P_D[k + N])
            for k in range(ntot)
        ])

        nX1var = np.var(nX1)
        nX2var = np.var(nX2)
        nP1var = np.var(nP1)
        nP2var = np.var(nP2)

        assert np.allclose(nX1var,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
        assert np.allclose(nX2var,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
        assert np.allclose(nP1var,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
        assert np.allclose(nP2var,
                           2 * sf.hbar * np.exp(-2 * sq_r),
                           rtol=5 / np.sqrt(ntot))
def test_str_tdm_method():
    """Testing the string method"""
    prog = tdmprogram.TDMProgram(N=1)
    assert prog.__str__(
    ) == "<TDMProgram: concurrent modes=1, time bins=0, spatial modes=0>"