예제 #1
0
    def runJob(self, eng):
        num_subsystem = 8
        prog = sf.Program(num_subsystem, name="remote_job")
        U = random_interferometer(4)
        with prog.context as q:
            # Initial squeezed states
            # Allowed values are r=1.0 or r=0.0
            ops.S2gate(1.0) | (q[0], q[4])
            ops.S2gate(1.0) | (q[1], q[5])
            ops.S2gate(1.0) | (q[3], q[7])

            # Interferometer on the signal modes (0-3)
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3])
            ops.BSgate(0.543, 0.123) | (q[2], q[0])
            ops.Rgate(0.453) | q[1]
            ops.MZgate(0.65, -0.54) | (q[2], q[3])

            # *Same* interferometer on the idler modes (4-7)
            ops.Interferometer(U) | (q[4], q[5], q[6], q[7])
            ops.BSgate(0.543, 0.123) | (q[6], q[4])
            ops.Rgate(0.453) | q[5]
            ops.MZgate(0.65, -0.54) | (q[6], q[7])

            ops.MeasureFock() | q


        eng = eng
        results =eng.run(prog, shots=10)
        # state = results.state
        # measurements = results.samples
        return results.samples
예제 #2
0
    def test_run_optimizations(self):
        """Test that circuit is optimized when optimize is True"""
        class DummyCompiler(Compiler):
            """A circuit with 2 modes"""

            interactive = True
            primitives = {"Rgate"}
            decompositions = set()

        device_dict = {
            "target": "dummy_target",
            "modes": 3,
            "layout": "",
            "gate_parameters": {},
            "compiler": ["gaussian"],
        }

        prog = sf.Program(3)
        with prog.context as q:
            ops.Rgate(0.3) | q[0]
            ops.Rgate(0.4) | q[0]

        new_prog = prog.compile(
            compiler=DummyCompiler(),
            optimize=True,
        )
        assert new_prog.circuit[0].__str__() == "Rgate(0.7) | (q[0])"
예제 #3
0
    def test_run_optimizations(self):
        """Test that circuit is optimized when optimize is True"""
        class DummyCircuit(Compiler):
            """A circuit with 2 modes"""
            interactive = True
            primitives = {'Rgate'}
            decompositions = set()

        device_dict = {
            "modes": 3,
            "layout": None,
            "gate_parameters": None,
            "compiler": [None]
        }
        spec = sf.api.DeviceSpec(target="dummy_target",
                                 connection=None,
                                 spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.Rgate(0.3) | q[0]
            ops.Rgate(0.4) | q[0]

        new_prog = prog.compile(
            compiler=DummyCircuit(),
            optimize=True,
        )
        assert new_prog.circuit[0].__str__() == "Rgate(0.7) | (q[0])"
예제 #4
0
def get_unitary(gate_args, device, phi_loop=None, delays=[1, 6, 36]):
    """Computes the unitary corresponding to a given set of gates

    Args:
        gate_args (_type_): dictionary with the collected arguments for
            squeezing gate, phase gates and beamsplitter gates
        device (sf.Device): the Borealis device
        phi_loop (list, optional): list containing the three loop offsets.
            Defaults to None.
        delays (list, optional): the delay applied by each loop in time bins.

    Returns:
        np.ndarray: a unitary matrix
    """
    args_list = to_args_list(gate_args, device)

    n, N = get_mode_indices(delays)

    prog = sf.TDMProgram(N)
    with prog.context(*args_list) as (p, q):
        for i in range(len(delays)):
            ops.Rgate(p[2 * i + 1]) | q[n[i]]
            ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i]], q[n[i + 1]])
            if phi_loop is not None:
                ops.Rgate(phi_loop[i]) | q[n[i]]
    prog.space_unroll()
    prog = prog.compile(compiler="passive")

    # unitary matrix
    assert prog.circuit
    U = prog.circuit[0].op.p[0]

    return U
예제 #5
0
def prog():
    """Program fixture."""
    program = Program(8)
    with program.context as q:
        ops.Rgate(0.5) | q[0]
        ops.Rgate(0.5) | q[4]
        ops.MeasureFock() | q
    return program
예제 #6
0
def test_all_passive_gates(hbar, tol):
    """test that all gates run and do not cause anything to crash"""

    eng = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(4)

    with circuit.context as q:
        for i in range(4):
            ops.Sgate(1, 0.3) | q[i]
        ops.Rgate(np.pi) | q[0]
        ops.PassiveChannel(np.ones((2, 2))) | (q[1], q[2])
        ops.LossChannel(0.9) | q[1]
        ops.MZgate(0.25 * np.pi, 0) | (q[2], q[3])
        ops.PassiveChannel(np.array([[0.83]])) | q[0]
        ops.sMZgate(0.11, -2.1) | (q[0], q[3])
        ops.Interferometer(np.array([[np.exp(1j * 2)]])) | q[1]
        ops.BSgate(0.8, 0.4) | (q[1], q[3])
        ops.Interferometer(0.5**0.5 * np.fft.fft(np.eye(2))) | (q[0], q[2])
        ops.PassiveChannel(0.1 * np.ones((3, 3))) | (q[3], q[1], q[0])

    cov = eng.run(circuit).state.cov()

    circuit = sf.Program(4)
    with circuit.context as q:
        ops.Rgate(np.pi) | q[0]
        ops.PassiveChannel(np.ones((2, 2))) | (q[1], q[2])
        ops.LossChannel(0.9) | q[1]
        ops.MZgate(0.25 * np.pi, 0) | (q[2], q[3])
        ops.PassiveChannel(np.array([[0.83]])) | q[0]
        ops.sMZgate(0.11, -2.1) | (q[0], q[3])
        ops.Interferometer(np.array([[np.exp(1j * 2)]])) | q[1]
        ops.BSgate(0.8, 0.4) | (q[1], q[3])
        ops.Interferometer(0.5**0.5 * np.fft.fft(np.eye(2))) | (q[0], q[2])
        ops.PassiveChannel(0.1 * np.ones((3, 3))) | (q[3], q[1], q[0])

    compiled_circuit = circuit.compile(compiler="passive")
    T = compiled_circuit.circuit[0].op.p[0]

    S_sq = np.eye(8, dtype=np.complex128)
    r = 1
    phi = 0.3
    for i in range(4):
        S_sq[i, i] = np.cosh(r) - np.sinh(r) * np.cos(phi)
        S_sq[i, i + 4] = -np.sinh(r) * np.sin(phi)
        S_sq[i + 4, i] = -np.sinh(r) * np.sin(phi)
        S_sq[i + 4, i + 4] = np.cosh(r) + np.sinh(r) * np.cos(phi)

    cov_sq = (hbar / 2) * S_sq @ S_sq.T
    mu = np.zeros(8)

    P = interferometer(T)
    L = (hbar / 2) * (np.eye(P.shape[0]) - P @ P.T)
    cov2 = P @ cov_sq @ P.T + L

    assert np.allclose(cov, cov2, atol=tol, rtol=0)
    def test_CZgate_decomp_equal(self, setup_eng, s, tol):
        """Tests that the CZgate gives the same transformation as its decomposition."""
        eng, prog = setup_eng(2)

        with prog.context as q:
            ops.CZgate(s) | q
            # run decomposition with reversed arguments
            ops.Rgate(-np.pi / 2) | q[1]
            ops.CXgate(-s) | q
            ops.Rgate(np.pi / 2) | q[1]

        eng.run(prog)
        assert np.all(eng.backend.is_vacuum(tol))
예제 #8
0
    def test_apply_history(self, setup_eng, pure):
        """Tests the reapply history argument works correctly with a backend"""
        eng, q = setup_eng(2)

        a = 0.23
        r = 0.1

        def inspect():
            res = []
            print_fn = lambda x: res.append(x.__str__())
            eng.print_applied(print_fn)
            return res

        with eng:
            ops.Dgate(a) | q[0]
            ops.Sgate(r) | q[1]

        state1 = eng.run()
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
        ]

        assert inspect() == expected

        # reset backend, but reapply history
        eng.backend.reset(pure=pure)
        state2 = eng.run(apply_history=True)
        assert inspect() == expected
        assert state1 == state2

        # append more commands to the same backend
        with eng:
            ops.Rgate(r) | q[0]

        state3 = eng.run()
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
            "Run 1:",
            "Rgate({}) | (q[0])".format(r),
        ]

        assert inspect() == expected
        assert not state2 == state3

        # reset backend, but reapply history
        eng.backend.reset(pure=pure)
        state4 = eng.run(apply_history=True)
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
            "Rgate({}) | (q[0])".format(r),
        ]

        assert inspect() == expected
        assert state3 == state4
예제 #9
0
    def test_invalid_decompositions(self, monkeypatch):
        """Test that an exception is raised if the device spec
        requests a decomposition that doesn't exist"""

        class DummyDevice(DeviceSpecs):
            modes = None
            remote = False
            local = True
            interactive = True
            primitives = {'Rgate', 'Interferometer'}
            decompositions = {'Rgate': {}}

        dev = DummyDevice()

        prog = sf.Program(3)
        U = np.array([[0, 1], [1, 0]])

        with prog.context as q:
            ops.Rgate(0.6) | q[0]
            ops.Interferometer(U) | [q[0], q[1]]

        with monkeypatch.context() as m:
            # monkeypatch our DummyDevice into the
            # backend database
            db =  {'dummy': DummyDevice}
            m.setattr("strawberryfields.devicespecs.backend_specs", db)

            with pytest.raises(NotImplementedError, match="No decomposition available: Rgate"):
                new_prog = prog.compile(backend='dummy')
예제 #10
0
    def test_program_subroutine(self, setup_eng, tol):
        """Simple quantum program with a subroutine and references."""
        eng, prog = setup_eng(2)

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(0.7 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi / 3)
        def subroutine(a, b):
            """Subroutine for the quantum program"""
            R | a
            BS | (a, b)
            R.H | a

        # main program
        with prog.context as q:
            # get register references
            alice, bob = q
            ops.All(ops.Vacuum()) | (alice, bob)
            D | alice
            subroutine(alice, bob)
            BS | (alice, bob)
            subroutine(bob, alice)

        state = eng.run(prog).state

        # state norm must be invariant
        if isinstance(eng.backend, BaseFock):
            assert np.allclose(state.trace(), 1, atol=tol, rtol=0)
예제 #11
0
    def test_unroll_shots(self):
        """Test unrolling program several times using different number of shots."""
        n = 2
        shots = 2

        prog = sf.TDMProgram(N=2)
        with prog.context([0] * n, [0] * n, [0] * n) as (p, q):
            ops.Sgate(0.5643, 0) | q[1]
            ops.BSgate(p[0]) | (q[1], q[0])
            ops.Rgate(p[1]) | q[1]
            ops.MeasureHomodyne(p[2]) | q[0]

        prog_length = len(prog.circuit)
        assert prog_length == 4

        prog.unroll(shots=shots)
        assert len(prog.circuit) == n * shots * prog_length

        # unroll once more with the same number of shots to cover caching
        prog.unroll(shots=shots)
        assert len(prog.circuit) == n * shots * prog_length

        # unroll once more with a different number of shots
        shots = 3
        prog.unroll(shots=shots)
        assert len(prog.circuit) == n * shots * prog_length

        prog.roll()
        assert len(prog.circuit) == prog_length
예제 #12
0
    def borealis_program_with_offsets(gate_args):
        delays = [1, 6, 36]
        cert = borealis_device.certificate or {"loop_phases": []}
        loop_phases = cert["loop_phases"]

        n, N = get_mode_indices(delays)
        prog = sf.TDMProgram(N)

        with prog.context(*gate_args) as (p, q):
            ops.Sgate(p[0]) | q[n[0]]
            for i in range(len(delays)):
                ops.Rgate(p[2 * i + 1]) | q[n[i]]
                ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]])
                ops.Rgate(loop_phases[i]) | q[n[i]]
            ops.MeasureFock() | q[0]

        return prog
예제 #13
0
def get_photon_number_moments(gate_args,
                              device,
                              phi_loop=None,
                              delays=[1, 6, 36]):
    """Computes first and second moment of the photon number distribution
    obtained from a single

    Args:
        gate_args (_type_): dictionary with the collected arguments for
            squeezing gate, phase gates and beamsplitter gates
        device (sf.Device): the Borealis device
        phi_loop (list, optional): list containing the three loop offsets.
            Defaults to None.
        delays (list, optional): the delay applied by each loop in time bins.

    Returns:
        tuple: two np.ndarrays with the mean photon numbers and photon-number
            covariance matrix, respectively
    """
    args_list = to_args_list(gate_args, device)

    n, N = get_mode_indices(delays)

    prog = sf.TDMProgram(N)
    with prog.context(*args_list) as (p, q):
        ops.Sgate(p[0]) | q[n[0]]
        for i in range(len(delays)):
            ops.Rgate(p[2 * i + 1]) | q[n[i]]
            ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i]], q[n[i + 1]])
            if phi_loop is not None:
                ops.Rgate(phi_loop[i]) | q[n[i]]
    prog.space_unroll()
    eng = sf.Engine("gaussian")
    results = eng.run(prog)

    assert isinstance(results.state, BaseGaussianState)

    # quadrature mean vector and covariance matrix
    mu = results.state.means()
    cov_q = results.state.cov()

    # photon-number mean vector and covariance matrix
    mean_n = photon_number_mean_vector(mu, cov_q)
    cov_n = photon_number_covmat(mu, cov_q)

    return mean_n, cov_n
예제 #14
0
    def test_checkpoints(self, setup_eng, tol):
        """Test history checkpoints work when creating and deleting modes."""
        eng, q = setup_eng(2)
        alice, bob = q

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        with eng:
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Del | bob
            D.H | charlie
            ops.MeasureX | charlie

        eng.optimize()
        state = eng.run()

        # state norm must be invariant
        if isinstance(eng.backend, BaseFock):
            assert np.allclose(state.trace(), 1, atol=tol, rtol=0)

        def check_reg(self, expected_n=None):
            """Compare Engine.register with the mode list returned by the backend.
            They should always be in agreement after Engine.run(), Engine.reset_queue()
            and Engine.reset().
            """
            rr = eng.register
            modes = eng.backend.get_modes()
            # number of elements
            assert len(rr) == len(modes)

            if expected_n is not None:
                assert len(rr) == expected_n

            # check indices match
            assert np.all([r.ind for r in rr] == modes)
            # activity
            assert np.all([r.active for r in rr])

        # check that reset() works
        check_reg(1)
        eng.reset()

        new_reg = eng.register
        # original number of modes
        assert len(new_reg) == len(q)

        # the regrefs are reset as well
        assert np.all([r.val is None for r in new_reg])
        check_reg(2)
예제 #15
0
    def test_GBS_compile_ops_after_measure(self):
        """Tests that GBS compilation fails when there are operations following a Fock measurement."""
        prog = sf.Program(2)
        with prog.context as q:
            ops.MeasureFock() | q
            ops.Rgate(1.0)  | q[0]

        with pytest.raises(program.CircuitError, match="Operations following the Fock measurements."):
            prog.compile('gbs')
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))
예제 #17
0
    def test_apply_history(self, eng):
        """Tests the reapply history argument"""
        a = 0.23
        r = 0.1

        def inspect():
            res = []
            print_fn = lambda x: res.append(x.__str__())
            eng.print_applied(print_fn)
            return res

        p1 = sf.Program(2)
        with p1.context as q:
            ops.Dgate(a) | q[1]
            ops.Sgate(r) | q[1]

        eng.run(p1)
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[1])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
        ]
        assert inspect() == expected

        # run the program again
        eng.reset()
        eng.run(p1)
        assert inspect() == expected

        # apply more commands to the same backend
        p2 = sf.Program(2)
        with p2.context as q:
            ops.Rgate(r) | q[1]

        eng.run(p2)
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[1])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
            "Run 1:",
            "Rgate({}) | (q[1])".format(r),
        ]

        assert inspect() == expected

        # reapply history
        eng.reset()
        eng.run([p1, p2])
        #expected = [
        #    "Run 0:",
        #    "Dgate({}, 0) | (q[1])".format(a),
        #    "Sgate({}, 0) | (q[1])".format(r),
        #    "Rgate({}) | (q[1])".format(r),
        #]

        assert inspect() == expected
예제 #18
0
    def test_apply_history(self, backend):
        """Tests the reapply history argument"""
        eng, q = sf.Engine(2)

        a = 0.23
        r = 0.1

        def inspect():
            res = []
            print_fn = lambda x: res.append(x.__str__())
            eng.print_applied(print_fn)
            return res

        with eng:
            ops.Dgate(a) | q[0]
            ops.Sgate(r) | q[1]

        eng.run(backend)
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
        ]

        assert inspect() == expected

        # reapply history
        state2 = eng.run(apply_history=True)
        assert inspect() == expected

        # append more commands to the same backend
        with eng:
            ops.Rgate(r) | q[0]

        eng.run()
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
            "Run 1:",
            "Rgate({}) | (q[0])".format(r),
        ]

        assert inspect() == expected

        # reapply history
        eng.run(apply_history=True)
        expected = [
            "Run 0:",
            "Dgate({}, 0) | (q[0])".format(a),
            "Sgate({}, 0) | (q[1])".format(r),
            "Rgate({}) | (q[0])".format(r),
        ]

        assert inspect() == expected
예제 #19
0
    def test_print_commands(self, eng, prog):
        """Program.print and Engine.print_applied return correct strings."""
        prog = sf.Program(2)

        # store the result of the print command in list res
        res = []
        # use a print function that simply appends the operation
        # name to the results list
        print_fn = lambda x: res.append(x.__str__())

        # prog should now be empty
        prog.print(print_fn)
        assert res == []

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        with prog.context as q:
            alice, bob = q
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Dgate(bob.par).H | charlie
            ops.Del | bob
            ops.MeasureX | charlie

        res = []
        prog.print(print_fn)

        expected = [
            "Dgate(0.5, 0) | (q[0])",
            "BSgate(6.283, 1.571) | (q[0], q[1])",
            "Del | (q[0])",
            "Rgate(3.142) | (q[1])",
            "New(1)",
            "BSgate(6.283, 1.571) | (q[1], q[2])",
            "MeasureX | (q[1])",
            "Dgate(q1, 0).H | (q[2])",
            "Del | (q[1])",
            "MeasureX | (q[2])",
        ]

        assert res == expected

        # NOTE optimization can change gate order
        result = eng.run(prog, compile_options={'optimize': False})
        res = []
        eng.print_applied(print_fn)
        assert res == ["Run 0:"] + expected
예제 #20
0
    def test_print_commands(self, backend):
        """Test print_queue and print_applied returns correct strings."""
        eng, q = sf.Engine(2)

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        # get register references
        alice, bob = q

        with eng:
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Dgate(bob).H | charlie
            ops.Del | bob
            ops.MeasureX | charlie

        res = []
        print_fn = lambda x: res.append(x.__str__())
        eng.print_queue(print_fn)

        expected = [
            "Dgate(0.5, 0) | (q[0])",
            "BSgate(6.283, 1.571) | (q[0], q[1])",
            "Del | (q[0])",
            "Rgate(3.142) | (q[1])",
            "New(1) ",
            "BSgate(6.283, 1.571) | (q[1], q[2])",
            "MeasureX | (q[1])",
            "Dgate(RR(q[1]), 0).H | (q[2])",
            "Del | (q[1])",
            "MeasureX | (q[2])",
        ]

        assert res == expected
        state = eng.run(backend)

        # queue should now be empty
        res = []
        eng.print_queue(print_fn)
        assert res == []

        # print_applied should now not be empty
        res = []
        eng.print_applied(print_fn)
        assert res == ["Run 0:"] + expected
예제 #21
0
    def test_subsystems(self, setup_eng, tol):
        """Check that the backend keeps in sync with the program when creating and deleting modes."""
        null = sf.Program(2)  # empty program
        eng, prog = setup_eng(2)

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        with prog.context as q:
            alice, bob = q
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Del | bob
            D.H | charlie
            ops.MeasureX | charlie

        def check_reg(p, expected_n=None):
            """Compare Program.register with the mode list returned by the backend.
            They should always be in agreement after Engine.run() and Engine.reset().
            """
            rr = p.register
            modes = eng.backend.get_modes()
            # number of elements
            assert len(rr) == len(modes)

            if expected_n is not None:
                assert len(rr) == expected_n

            # check indices match
            assert np.all([r.ind for r in rr] == modes)
            # activity
            assert np.all([r.active for r in rr])

        state = eng.run(null)
        check_reg(null, 2)
        state = eng.run(prog).state
        check_reg(prog, 1)

        # state norm must be invariant
        if isinstance(eng.backend, BaseFock):
            assert np.allclose(state.trace(), 1, atol=tol, rtol=0)

        # check that reset() works
        eng.reset()
        # the regrefs are reset as well
        assert np.all([r.val is None for r in prog.register])
예제 #22
0
    def test_checkpoints(self, backend):
        """Test history checkpoints work when creating and deleting modes."""
        eng, q = sf.Engine(2)
        alice, bob = q

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        with eng:
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Del | bob
            D.H | charlie
            ops.MeasureX | charlie

        eng.optimize()
        eng.run(backend)

        assert not alice.active
        assert charlie.active

        # check that reset_queue() restores the latest RegRef checkpoint
        # (created by eng.run() above)
        eng.reset_queue()

        assert not alice.active
        assert charlie.active

        with eng:
            diana, = ops.New(1)
            ops.Del | charlie

        assert not charlie.active
        assert diana.active
        eng.reset_queue()
        assert charlie.active
        assert not diana.active

        eng.reset()

        new_reg = eng.register
        # original number of modes
        assert len(new_reg) == len(q)

        # the regrefs are reset as well
        assert np.all([r.val is None for r in new_reg])
예제 #23
0
    def test_print_commands(self, eng, prog):
        """Program.print and Engine.print_applied return correct strings."""
        prog = sf.Program(2)
        res = []
        print_fn = lambda x: res.append(x.__str__())

        # prog should now be empty
        prog.print(print_fn)
        assert res == []

        # define some gates
        D = ops.Dgate(0.5)
        BS = ops.BSgate(2 * np.pi, np.pi / 2)
        R = ops.Rgate(np.pi)

        with prog.context as q:
            alice, bob = q
            D | alice
            BS | (alice, bob)
            ops.Del | alice
            R | bob
            charlie, = ops.New(1)
            BS | (bob, charlie)
            ops.MeasureX | bob
            ops.Dgate(bob).H | charlie
            ops.Del | bob
            ops.MeasureX | charlie

        res = []
        prog.print(print_fn)

        expected = [
            "Dgate(0.5, 0) | (q[0])",
            "BSgate(6.283, 1.571) | (q[0], q[1])",
            "Del | (q[0])",
            "Rgate(3.142) | (q[1])",
            "New(1)",
            "BSgate(6.283, 1.571) | (q[1], q[2])",
            "MeasureX | (q[1])",
            "Dgate(RR(q[1]), 0).H | (q[2])",
            "Del | (q[1])",
            "MeasureX | (q[2])",
        ]

        assert res == expected

        state = eng.run(
            prog, compile=False
        )  # FIXME optimization can change gate order, however this is not a good way of avoiding it
        res = []
        eng.print_applied(print_fn)
        assert res == ["Run 0:"] + expected
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))
예제 #25
0
    def test_reset(self, eng, prog):
        """Running independent programs with an engine reset in between."""
        assert not eng.run_progs
        eng.run(prog)
        assert len(eng.run_progs) == 1

        eng.reset()
        assert not eng.run_progs
        p2 = sf.Program(3)
        with p2.context as q:
            ops.Rgate(1.0) | q[2]
        eng.run(p2)
        assert len(eng.run_progs) == 1
예제 #26
0
 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)
예제 #27
0
    def borealis_program(gate_args):
        delays = [1, 6, 36]

        n, N = get_mode_indices(delays)
        prog = sf.TDMProgram(N)

        with prog.context(*gate_args) as (p, q):
            ops.Sgate(p[0]) | q[n[0]]
            for i in range(len(delays)):
                ops.Rgate(p[2 * i + 1]) | q[n[i]]
                ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]])
            ops.MeasureFock() | q[0]

        return prog
def interferometer(params, q):
    """Parameterised interferometer acting on ``N`` modes.

    Args:
        params (list[float]): list of length ``max(1, N-1) + (N-1)*N`` parameters.

            * The first ``N(N-1)/2`` parameters correspond to the beamsplitter angles
            * The second ``N(N-1)/2`` parameters correspond to the beamsplitter phases
            * The final ``N-1`` parameters correspond to local rotation on the first N-1 modes

        q (list[RegRef]): list of Strawberry Fields quantum registers the interferometer
            is to be applied to
    """
    N = len(q)
    theta = params[:N * (N - 1) // 2]
    phi = params[N * (N - 1) // 2:N * (N - 1)]
    rphi = params[-N + 1:]

    if N == 1:
        # the interferometer is a single rotation
        ops.Rgate(rphi[0]) | q[0]
        return

    n = 0  # keep track of free parameters

    # Apply the rectangular beamsplitter array
    # The array depth is N
    for l in range(N):
        for k, (q1, q2) in enumerate(zip(q[:-1], q[1:])):
            # skip even or odd pairs depending on layer
            if (l + k) % 2 != 1:
                ops.BSgate(theta[n], phi[n]) | (q1, q2)
                n += 1

    # apply the final local phase shifts to all modes except the last one
    for i in range(max(1, N - 1)):
        ops.Rgate(rphi[i]) | q[i]
예제 #29
0
    def test_tdm_program(self):
        prog = TDMProgram(2)

        with prog.context([1, 2], [3, 4], [5, 6]) as (p, q):
            ops.Sgate(0.7, 0) | q[1]
            ops.BSgate(p[0]) | (q[0], q[1])
            ops.Rgate(p[1]) | q[1]
            ops.MeasureHomodyne(p[2]) | q[0]

        bb = io.to_blackbird(prog)

        assert bb.operations[0] == {
            'kwargs': {},
            'args': [0.7, 0],
            'op': 'Sgate',
            'modes': [1]
        }
        assert bb.operations[1] == {
            'kwargs': {},
            'args': ['p0', 0.0],
            'op': 'BSgate',
            'modes': [0, 1]
        }
        assert bb.operations[2] == {
            'kwargs': {},
            'args': ['p1'],
            'op': 'Rgate',
            'modes': [1]
        }
        assert bb.operations[3] == {
            'kwargs': {
                'phi': 'p2'
            },
            'args': [],
            'op': 'MeasureHomodyne',
            'modes': [0]
        }

        assert bb.programtype == {
            'name': 'tdm',
            'options': {
                'temporal_modes': 2
            }
        }
        assert list(bb._var.keys()) == ["p0", "p1", "p2"]
        assert np.all(bb._var["p0"] == np.array([[1, 2]]))
        assert np.all(bb._var["p1"] == np.array([[3, 4]]))
        assert np.all(bb._var["p2"] == np.array([[5, 6]]))
        assert bb.modes == [0, 1]
    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])