Example #1
0
def vibronic(
    t: np.ndarray,
    U1: np.ndarray,
    r: np.ndarray,
    U2: np.ndarray,
    alpha: np.ndarray,
    n_samples: int,
    loss: float = 0.0,
) -> list:
    """Generate samples for computing vibronic spectra. The following gates are applied to input
    vacuum states:

    1. Two-mode squeezing on all :math:`2N` modes with parameters ``t``
    2. Interferometer ``U1`` on the first :math:`N` modes
    3. Squeezing on the first :math:`N` modes with parameters ``r``
    4. Interferometer ``U2`` on the first :math:`N` modes
    5. Displacement on the first :math:`N` modes with parameters ``alpha``

    A sample is generated by measuring the number of photons in each of the :math:`2N` modes. In
    the special case that all of the two-mode squeezing parameters ``t`` are zero, only :math:`N`
    modes are considered, which speeds up calculations.

    **Example usage:**

>>> t = np.array([0., 0., 0., 0., 0., 0., 0.])
>>> U1 = np.array(
>>>     [[-0.07985219, 0.66041032, -0.19389188, 0.01340832, 0.70312675, -0.1208423, -0.10352726],
>>>     [0.19216669, -0.12470466, -0.81320519, 0.52045174, -0.1066017, -0.06300751, -0.00376173],
>>>     [0.60838109, 0.0835063, -0.14958816, -0.34291399, 0.06239828, 0.68753918, -0.07955415],
>>>     [0.63690134, -0.03047939, 0.46585565, 0.50545897, 0.21194805, -0.20422433, 0.18516987],
>>>     [0.34556293, 0.22562207, -0.1999159, -0.50280235, -0.25510781, -0.55793978, 0.40065893],
>>>     [-0.03377431, -0.66280536, -0.14740447, -0.25725325, 0.6145946, -0.07128058, 0.29804963],
>>>     [-0.24570365, 0.22402764, 0.003273, 0.19204683, -0.05125235, 0.3881131, 0.83623564]])
>>> r = np.array(
>>>     [0.09721339, 0.07017918, 0.02083469, -0.05974357, -0.07487845, -0.1119975, -0.1866708])
>>> U2 = np.array(
>>>     [[-0.07012006, 0.14489772, 0.17593463, 0.02431155, -0.63151781, 0.61230046, 0.41087368],
>>>     [0.5618538, -0.09931968, 0.04562272, 0.02158822, 0.35700706, 0.6614837, -0.326946],
>>>     [-0.16560687, -0.7608465, -0.25644606, -0.54317241, -0.12822903, 0.12809274, -0.00597384],
>>>     [0.01788782, 0.60430409, -0.19831443, -0.73270964, -0.06393682, 0.03376894, -0.23038293],
>>>     [0.78640978, -0.11133936, 0.03160537, -0.09188782, -0.43483738, -0.4018141, 0.09582698],
>>>     [-0.13664887, -0.11196486, 0.86353995, -0.19608061, -0.12313513, -0.08639263, -0.40251231],
>>>     [-0.12060103, -0.01169781, -0.33937036, 0.34662981, -0.49895371, 0.03257453, -0.70709135]])
>>> alpha = np.array(
>>>     [0.15938187, 0.10387399, 1.10301587, -0.26756921, 0.32194572, -0.24317402, 0.0436992])
>>> vibronic(t, U1, S, U2, alpha, 2, 0.0)
[[0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

    Args:
        t (array): two-mode squeezing parameters
        U1 (array): unitary matrix for the first interferometer
        r (array): squeezing parameters
        U2 (array): unitary matrix for the second interferometer
        alpha (array): displacement parameters
        n_samples (int): number of samples to be generated
        loss (float): loss parameter denoting the fraction of generated photons that are lost


    Returns:
        list[list[int]]: a list of samples from GBS
    """
    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")
    if not 0 <= loss <= 1:
        raise ValueError(
            "Loss parameter must take a value between zero and one")

    n_modes = len(t)

    eng = sf.LocalEngine(backend="gaussian")

    if np.any(t != 0):
        gbs = sf.Program(n_modes * 2)
    else:
        gbs = sf.Program(n_modes)

    # pylint: disable=expression-not-assigned,pointless-statement
    with gbs.context as q:

        if np.any(t != 0):
            for i in range(n_modes):
                sf.ops.S2gate(t[i]) | (q[i], q[i + n_modes])

        sf.ops.Interferometer(U1) | q[:n_modes]

        for i in range(n_modes):
            sf.ops.Sgate(r[i]) | q[i]

        sf.ops.Interferometer(U2) | q[:n_modes]

        for i in range(n_modes):
            sf.ops.Dgate(alpha[i]) | q[i]

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")

        s = eng.run(gbs, run_options={"shots": n_samples}).samples

    s = np.array(s).tolist()  # convert all generated samples to list

    if n_samples == 1:
        s = [s]
    if np.any(t == 0):
        s = np.pad(s, ((0, 0), (0, n_modes))).tolist()
    return s
def prog():
    """Program fixture."""
    return sf.Program(2)
    tf.random.set_seed(137)
    np.random.seed(137)

    # define width and depth of CV quantum neural network
    modes = 1
    layers = 8
    cutoff_dim = 6

    # defining desired state (single photon state)
    target_state = np.zeros(cutoff_dim)
    target_state[1] = 1
    target_state = tf.constant(target_state, dtype=tf.complex64)

    # initialize engine and program
    eng = sf.Engine(backend="tf", backend_options={"cutoff_dim": cutoff_dim})
    qnn = sf.Program(modes)

    # initialize QNN weights
    weights = init_weights(modes, layers)
    num_params = np.prod(weights.shape)

    # Create array of Strawberry Fields symbolic gate arguments, matching
    # the size of the weights Variable.
    sf_params = np.arange(num_params).reshape(weights.shape).astype(np.str)
    sf_params = np.array([qnn.params(*i) for i in sf_params])

    # Construct the symbolic Strawberry Fields program by
    # looping and applying layers to the program.
    with qnn.context as q:
        for k in range(layers):
            layer(sf_params[k], q)
Example #4
0
def sample_tmsv(
    r: list,
    t: float,
    Ul: np.ndarray,
    w: np.ndarray,
    n_samples: int,
    loss: float = 0.0,
) -> list:
    r"""Generate samples for simulating vibrational quantum dynamics with a two-mode squeezed
    vacuum input state.

    This function generates samples from a GBS device with two-mode squeezed vacuum input states.
    Given :math:`N` squeezing parameters and an :math:`N`-dimensional normal-to-local transformation
    matrix, a GBS device with :math:`2N` modes is simulated. The :func:`~.TimeEvolution` operator
    acts only on the first :math:`N` modes in the device. Samples are generated by measuring the
    number of photons in each of the :math:`2N` modes.

    **Example usage:**

    >>> r = [[0.2, 0.1], [0.8, 0.2]]
    >>> t = 10.0
    >>> Ul = np.array([[0.707106781, -0.707106781],
    ...                [0.707106781, 0.707106781]])
    >>> w = np.array([3914.92, 3787.59])
    >>> n_samples = 5
    >>> sample_tmsv(r, t, Ul, w, n_samples)
    [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 1], [0, 1, 0, 1], [0, 2, 0, 2]]

    Args:
        r (list[list[float]]): list of two-mode squeezing gate parameters given as ``[amplitude, phase]`` for all modes
        t (float): time in femtoseconds
        Ul (array): normal-to-local transformation matrix
        w (array): normal mode frequencies :math:`\omega` in units of :math:`\mbox{cm}^{-1}`
        n_samples (int): number of samples to be generated
        loss (float): loss parameter denoting the fraction of lost photons

    Returns:
        list[list[int]]: a list of samples
    """
    if np.any(np.iscomplex(Ul)):
        raise ValueError(
            "The normal mode to local mode transformation matrix must be real")

    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")

    if not len(r) == len(Ul):
        raise ValueError(
            "Number of squeezing parameters and the number of modes in the normal-to-local"
            " transformation matrix must be equal")

    N = len(Ul)

    eng = sf.LocalEngine(backend="gaussian")
    prog = sf.Program(2 * N)

    # pylint: disable=expression-not-assigned
    with prog.context as q:

        for i in range(N):
            sf.ops.S2gate(r[i][0], r[i][1]) | (q[i], q[i + N])

        sf.ops.Interferometer(Ul.T) | q[:N]

        TimeEvolution(w, t) | q[:N]

        sf.ops.Interferometer(Ul) | q[:N]

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")

        s = eng.run(prog, shots=n_samples).samples

    return s.tolist()
Example #5
0
def prog(backend):
    """Program fixture."""
    prog = sf.Program(2)
    with prog.context as q:
        ops.Dgate(0.5) | q[0]
    return prog
def sample(A: np.ndarray,
           n_mean: float,
           n_samples: int = 1,
           threshold: bool = True,
           loss: float = 0.0) -> list:
    r"""Generate simulated samples from GBS encoded with a symmetric matrix :math:`A`.

    **Example usage:**

    >>> g = nx.erdos_renyi_graph(5, 0.7)
    >>> a = nx.to_numpy_array(g)
    >>> sample(a, 3, 4)
    [[1, 1, 1, 1, 1], [1, 1, 0, 1, 1], [0, 0, 0, 0, 0], [1, 0, 0, 0, 1]]

    Args:
        A (array): the symmetric matrix to sample from
        n_mean (float): mean photon number
        n_samples (int): number of samples
        threshold (bool): perform GBS with threshold detectors if ``True`` or photon-number
            resolving detectors if ``False``
        loss (float): fraction of generated photons that are lost while passing through device.
            Parameter should range from ``loss=0`` (ideal noise-free GBS) to ``loss=1``.

    Returns:
        list[list[int]]: a list of samples from GBS with respect to the input symmetric matrix
    """
    if not np.allclose(A, A.T):
        raise ValueError(
            "Input must be a NumPy array corresponding to a symmetric matrix")
    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")
    if n_mean < 0:
        raise ValueError("Mean photon number must be non-negative")
    if not 0 <= loss <= 1:
        raise ValueError(
            "Loss parameter must take a value between zero and one")

    nodes = len(A)

    p = sf.Program(nodes)
    eng = sf.LocalEngine(backend="gaussian")

    mean_photon_per_mode = n_mean / float(nodes)

    # pylint: disable=expression-not-assigned,pointless-statement
    with p.context as q:
        sf.ops.GraphEmbed(A, mean_photon_per_mode=mean_photon_per_mode) | q

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        if threshold:
            sf.ops.MeasureThreshold() | q
        else:
            sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")
        s = eng.run(p, run_options={"shots": n_samples}).samples

    if n_samples == 1:
        return [s]

    return s.tolist()
    def test_no_unitary(self, tol):
        """Test compilation works with no unitary provided"""
        prog = sf.Program(8)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[7])
            ops.MeasureFock() | q

        res = prog.compile("Xunitary")
        expected = sf.Program(8)

        with expected.context as q:
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[7])

            # corresponds to an identity on modes [0, 1, 2, 3]
            # This can be easily seen from below by noting that:
            # MZ(pi, pi) = R(0) = I
            # MZ(pi, 0) @ MZ(pi, 0) = I
            # [R(pi) \otimes I] @ MZ(pi, 0) = I
            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.MZgate(np.pi, np.pi) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.Rgate(np.pi) | (q[0])
            ops.Rgate(0) | (q[1])
            ops.Rgate(0) | (q[2])
            ops.Rgate(0) | (q[3])

            # corresponds to an identity on modes [4, 5, 6, 7]
            ops.MZgate(np.pi, 0) | (q[4], q[5])
            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, np.pi) | (q[5], q[6])
            ops.MZgate(np.pi, np.pi) | (q[4], q[5])
            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, np.pi) | (q[5], q[6])
            ops.Rgate(np.pi) | (q[4])
            ops.Rgate(0) | (q[5])
            ops.Rgate(0) | (q[6])
            ops.Rgate(0) | (q[7])

            ops.MeasureFock() | q

        assert program_equivalence(res, expected, atol=tol, compare_params=False)

        # double check that the applied symplectic is correct

        # remove the Fock measurements
        res.circuit = res.circuit[:-1]

        # extract the Gaussian symplectic matrix
        O = res.compile("gaussian_unitary").circuit[0].op.p[0]

        # construct the expected symplectic matrix corresponding
        # to just the initial two mode squeeze gates
        S = two_mode_squeezing(SQ_AMPLITUDE, 0)
        num_modes = 8
        expected = np.identity(2 * num_modes)
        for i in range(num_modes // 2):
            expected = expand(S, [i, i + num_modes // 2], num_modes) @ expected
        # Note that the comparison has to be made at the level of covariance matrices
        # Not at the level of symplectic matrices
        assert np.allclose(O @ O.T, expected @ expected.T, atol=tol)
#!/usr/bin/env python3
import strawberryfields as sf
from strawberryfields.ops import *

# initialize engine and program objects
eng = sf.Engine(backend="gaussian")
gate_teleportation = sf.Program(4)

with gate_teleportation.context as q:
    # create initial states
    Squeezed(0.1) | q[0]
    Squeezed(-2) | q[1]
    Squeezed(-2) | q[2]

    # apply the gate to be teleported
    Pgate(0.5) | q[1]

    # conditional phase entanglement
    CZgate(1) | (q[0], q[1])
    CZgate(1) | (q[1], q[2])

    # projective measurement onto
    # the position quadrature
    Fourier.H | q[0]
    MeasureX | q[0]
    Fourier.H | q[1]
    MeasureX | q[1]
    # compare against the expected output
    # X(q1/sqrt(2)).F.P(0.5).X(q0/sqrt(0.5)).F.|z>
    # not including the corrections
    Squeezed(0.1) | q[3]
Example #9
0
# y0ka1
# Made on July 14th, 2021

''' This is a script to run test-circuits to help me learn quantum computing, it will not have a certian structured circuit, 
    rather I will be testing many circuits so this will be constantly running while I run the code from a seperate terminal. '''

import qiskit
import strawberryfields as sf
import pennylane

from strawberryfields import ops

# Creating a 3-mode quantum program
prog = sf.Program(3)

with prog.context as q:
    ops.Sgate(0.54) | q[0]
    ops.Sgate(0.54) | q[1]
    ops.Sgate(0.54) | q[2]
    ops.BSgate(0.43, 0.1) | (q[0], q[2])
    ops.BSgate(0.43, 0.1) | (q[1], q[2])
    ops.MeasureFock() | q

Example #10
0
    def test_decomposition(self, tol):
        """Test that a graph is correctly decomposed"""
        n = 3
        prog = sf.Program(2*n)

        A = np.zeros([2*n, 2*n])
        B = np.random.random([n, n])

        A[:n, n:] = B
        A += A.T

        sq, U, V = dec.bipartite_graph_embed(B)

        G = ops.BipartiteGraphEmbed(A)
        cmds = G.decompose(prog.register)

        S = np.identity(4 * n)

        # calculating the resulting decomposed symplectic
        for cmd in cmds:
            # all operations should be BSgates, Rgates, or S2gates
            assert isinstance(
                cmd.op, (ops.Interferometer, ops.S2gate)
            )

            # build up the symplectic transform
            modes = [i.ind for i in cmd.reg]

            if isinstance(cmd.op, ops.S2gate):
                # check that the registers are i, i+n
                assert len(modes) == 2
                assert modes[1] == modes[0] + n

                r, phi = par_evaluate(cmd.op.p)
                assert -r in sq
                assert phi == 0

                S = _two_mode_squeezing(r, phi, modes, 2*n) @ S

            if isinstance(cmd.op, ops.Interferometer):
                # check that each unitary only applies to half the modes
                assert len(modes) == n
                assert modes in ([0, 1, 2], [3, 4, 5])

                # check matrix is unitary
                U1 = par_evaluate(cmd.op.p[0])
                assert np.allclose(U1 @ U1.conj().T, np.identity(n), atol=tol, rtol=0)

                if modes[0] == 0:
                    assert np.allclose(U1, U, atol=tol, rtol=0)
                else:
                    assert modes[0] == 3
                    assert np.allclose(U1, V, atol=tol, rtol=0)

                S_U = np.vstack(
                    [np.hstack([U1.real, -U1.imag]), np.hstack([U1.imag, U1.real])]
                )

                S = expand(S_U, modes, 2*n) @ S

        # the resulting covariance state
        cov = S @ S.T
        A_res = Amat(cov)[:2*n, :2*n]

        # The bottom right corner of A_res should be identical to A,
        # up to some constant scaling factor. Check if the ratio
        # of all elements is one
        ratio = np.real_if_close(A_res[n:, :n] / B.T)
        ratio /= ratio[0, 0]

        assert np.allclose(ratio, np.ones([n, n]), atol=tol, rtol=0)
Example #11
0
import strawberryfields as sf
from strawberryfields.ops import *
import tensorflow as tf

# Define the variational circuit and its output.
X = tf.placeholder(tf.float32, shape=[2])
y = tf.placeholder(tf.float32, shape=[2])
sdev = 0.05
depth = 50
phi = tf.Variable(tf.random_normal(shape=[depth], stddev=sdev))

# eng, q = sf.Engine(3)
eng = sf.Engine(backend="tf", backend_options={"cutoff_dim": 10})
circuit = sf.Program(2)

with circuit.context as q:  #with eng:
    # Note that we are feeding 1-d tensors into gates, not scalars!
    Dgate(X[0], 0.) | q[0]
    Dgate(X[1], 0.) | q[1]
    # Dgate(X[0], X[1]) | q[2]
    Dgate(phi[0], phi[1]) | q[0]
    Dgate(phi[2], phi[3]) | q[1]
    # Dgate(phi[4], phi[5]) | q[2]
    Sgate(phi[6], phi[7]) | q[0]
    Sgate(phi[8], phi[9]) | q[1]
    # Sgate(phi[10], phi[11]) | q[2]
    Kgate(phi[30]) | q[0]
    Kgate(phi[31]) | q[1]
    # Kgate(phi[32]) | q[2]
    BSgate(phi[12]) | (q[0], q[1])
    BSgate() | (q[0], q[1])
Example #12
0
def test_gradients2to1():
    ket_in = np.zeros((5, 5, 5, 5), dtype=np.complex128)
    ket_in[1, 2, 1, 0] = np.sqrt(1 / 3)
    ket_in[1, 0, 0, 3] = 1j * np.sqrt(2 / 3)
    test_prog = sf.Program(4)  # 4 modes
    with test_prog.context as q:
        sf.ops.Ket(ket_in) | (q[0], q[1], q[2], q[3])
        sf.ops.BSgate(0.1804, 0.0578) | (q[0], q[1])
        sf.ops.BSgate(0.06406, 1.5165) | (q[2], q[3])
        sf.ops.BSgate(1.473, 0.1176) | (q[1], q[2])
        sf.ops.BSgate(0.263, -0.2517) | (q[0], q[1])
        sf.ops.BSgate(0.3323, 1.9946) | (q[2], q[3])
        sf.ops.BSgate(0.311, -0.3231) | (q[1], q[2])
        sf.ops.BSgate(0.4348, 0.0798) | (q[0], q[1])
        sf.ops.BSgate(0.0368, 0.6157) | (q[2], q[3])

    # eng = sf.Engine(backend="fock", backend_options={"cutoff_dim": 5})
    # ket_out_1 = eng.run(test_prog).state.ket()
    prog_unitary = sf.Program(4)
    prog_unitary.circuit = test_prog.circuit[1:]
    prog_compiled = prog_unitary.compile(compiler="gaussian_unitary")
    S = prog_compiled.circuit[0].op.p[0]
    U1 = S[:4, :4] + 1j * S[4:, :4]

    ket_in = np.zeros((5, 5, 5, 5), dtype=np.complex128)
    ket_in[1, 2, 1, 0] = np.sqrt(1 / 3)
    ket_in[1, 0, 0, 3] = 1j * np.sqrt(2 / 3)
    test_prog = sf.Program(4)  # 4 modes
    with test_prog.context as q:
        sf.ops.Ket(ket_in) | (q[0], q[1], q[2], q[3])
        sf.ops.BSgate(0.1804, 0.0568) | (q[0], q[1])
        sf.ops.BSgate(0.06306, 1.5165) | (q[2], q[3])
        sf.ops.BSgate(1.462, 0.1176) | (q[1], q[2])
        sf.ops.BSgate(0.263, -0.2507) | (q[0], q[1])
        sf.ops.BSgate(0.3333, 1.9946) | (q[2], q[3])
        sf.ops.BSgate(0.312, -0.3231) | (q[1], q[2])
        sf.ops.BSgate(0.4358, 0.0788) | (q[0], q[1])
        sf.ops.BSgate(0.0358, 0.6157) | (q[2], q[3])

    # eng = sf.Engine(backend="fock", backend_options={"cutoff_dim": 5})
    # ket_out_2 = eng.run(test_prog).state.ket()
    prog_unitary = sf.Program(4)
    prog_unitary.circuit = test_prog.circuit[1:]
    prog_compiled = prog_unitary.compile(compiler="gaussian_unitary")
    S = prog_compiled.circuit[0].op.p[0]
    U2 = S[:4, :4] + 1j * S[4:, :4]
    dU = U2 - U1

    s_in = State({
        (1, 2, 1, 0): np.sqrt(1 / 3),
        (1, 0, 0, 3): 1j * np.sqrt(2 / 3)
    })
    s_out = State({(4, 0, 0, 0): 1.0})
    io = IOSpec(input_state=s_in, output_state=s_out)
    tree1 = Tree(io=io, covariance_matrix=U1)
    tree2 = Tree(io=io, covariance_matrix=U1 + dU)

    amp1, grad1 = tree1.amplitude()
    amp2, grad2 = tree2.amplitude()
    print(amp2, amp1 + np.sum(grad1 * dU))
    print(np.sum(grad1 * dU), amp2 - amp1)
    assert np.isclose(amp2, amp1 + np.sum(grad1 * dU))
    assert np.isclose(amp1, amp2 - np.sum(grad2 * dU))
Example #13
0
import strawberryfields as sf
from strawberryfields.ops import *

prog = sf.Program(2)

import tensorflow as tf

input_ = tf.placeholder(tf.float32, shape=(2, 1))
weights = tf.Variable([[0.1, 0.1]])
bias = tf.Variable(0.0)
NN = tf.sigmoid(tf.matmul(weights, input_) + bias)
NNDgate = Dgate(NN)


@sf.convert
def sigmoid(x):
    return tf.sigmoid(x)


prog = sf.Program(2)

with prog.context as q:
    MeasureX | q[0]
    Dgate(sigmoid(q[0])) | q[1]

batch_size = 3
prog = sf.Program(2)
eng = sf.Engine('tf',
                backend_options={
                    "cutoff_dim": 7,
                    "batch_size": batch_size
Example #14
0
                tf.clip_by_value(disp_magnitude_variables[layer_number, i],
                                 -disp_clip, disp_clip),
                disp_phase_variables[layer_number, i]) | q[i]

        for i in range(mode_number):
            Kgate(
                tf.clip_by_value(kerr_variables[layer_number, i], -kerr_clip,
                                 kerr_clip)) | q[i]


# ===================================================================================
#                                   Defining QNN
# ===================================================================================

# construct the two-mode Strawberry Fields program
prog = sf.Program(mode_number)

# construct the circuit
with prog.context as q:
    input_qnn_layer()

    for i in range(depth):
        qnn_layer(i)

# create an engine
eng = sf.Engine('tf',
                backend_options={
                    "cutoff_dim": cutoff,
                    "batch_size": batch_size
                })
Example #15
0
def vibronic(
    t: np.ndarray,
    U1: np.ndarray,
    r: np.ndarray,
    U2: np.ndarray,
    alpha: np.ndarray,
    n_samples: int,
    loss: float = 0.0,
) -> list:
    """Generate samples for computing vibronic spectra. The following gates are applied to input
    vacuum states:

    1. Two-mode squeezing on all :math:`2N` modes with parameters ``t``
    2. Interferometer ``U1`` on the first :math:`N` modes
    3. Squeezing on the first :math:`N` modes with parameters ``r``
    4. Interferometer ``U2`` on the first :math:`N` modes
    5. Displacement on the first :math:`N` modes with parameters ``alpha``

    A sample is generated by measuring the number of photons in each of the :math:`2N` modes. In
    the special case that all of the two-mode squeezing parameters ``t`` are zero, only :math:`N`
    modes are considered, which speeds up calculations.

    **Example usage:**

    >>> formic = data.Formic()
    >>> w = formic.w
    >>> wp = formic.wp
    >>> Ud = formic.Ud
    >>> delta = formic.delta
    >>> T = 0
    >>> t, U1, r, U2, alpha = vibronic.gbs_params(w, wp, Ud, delta, T)
    >>> sample.vibronic(t, U1, r, U2, alpha, 2, 0.0)
    [[0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

    Args:
        t (array): two-mode squeezing parameters
        U1 (array): unitary matrix for the first interferometer
        r (array): squeezing parameters
        U2 (array): unitary matrix for the second interferometer
        alpha (array): displacement parameters
        n_samples (int): number of samples to be generated
        loss (float): loss parameter denoting the fraction of generated photons that are lost


    Returns:
        list[list[int]]: a list of samples from GBS
    """
    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")
    if not 0 <= loss <= 1:
        raise ValueError(
            "Loss parameter must take a value between zero and one")

    n_modes = len(t)

    eng = sf.LocalEngine(backend="gaussian")

    if np.any(t != 0):
        gbs = sf.Program(n_modes * 2)
    else:
        gbs = sf.Program(n_modes)

    # pylint: disable=expression-not-assigned,pointless-statement
    with gbs.context as q:

        if np.any(t != 0):
            for i in range(n_modes):
                sf.ops.S2gate(t[i]) | (q[i], q[i + n_modes])

        sf.ops.Interferometer(U1) | q[:n_modes]

        for i in range(n_modes):
            sf.ops.Sgate(r[i]) | q[i]

        sf.ops.Interferometer(U2) | q[:n_modes]

        for i in range(n_modes):
            sf.ops.Dgate(alpha[i]) | q[i]

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")

        s = eng.run(gbs, run_options={"shots": n_samples}).samples

    s = np.array(s).tolist()  # convert all generated samples to list

    if n_samples == 1:
        s = [s]
    if np.any(t == 0):
        s = np.pad(s, ((0, 0), (0, n_modes))).tolist()
    return s
Example #16
0
#!/usr/bin/env python3
import strawberryfields as sf
from strawberryfields.ops import *
from numpy import pi

# initialize engine and program objects
eng = sf.Engine(backend="fock", backend_options={"cutoff_dim": 5})
ham_simulation = sf.Program(2)

# set the Hamiltonian parameters
J = 1  # hopping transition
U = 1.5  # on-site interaction
k = 20  # Lie product decomposition terms
t = 1.086  # timestep
theta = -J * t / k
r = -U * t / (2 * k)

with ham_simulation.context as q:
    # prepare the initial state
    Fock(2) | q[0]

    # Two node tight-binding
    # Hamiltonian simulation

    for i in range(k):
        BSgate(theta, pi / 2) | (q[0], q[1])
        Kgate(r) | q[0]
        Rgate(-r) | q[0]
        Kgate(r) | q[1]
        Rgate(-r) | q[1]
    # end circuit
Example #17
0
 def prog(self):
     """Dummy program context for each test"""
     prog = sf.Program(2)
     Program._current_context = prog
     yield prog
     Program._current_context = None
Example #18
0
#!/usr/bin/env python3
import strawberryfields as sf
from strawberryfields.ops import *
from strawberryfields.utils import scale
from numpy import pi, sqrt

# initialize engine and program objects
eng = sf.Engine(backend="gaussian")
teleportation = sf.Program(3)

with teleportation.context as q:
    psi, alice, bob = q[0], q[1], q[2]

    # state to be teleported:
    Coherent(1+0.5j) | psi

    # 50-50 beamsplitter
    BS = BSgate(pi/4, 0)

    # maximally entangled states
    Squeezed(-2) | alice
    Squeezed(2) | bob
    BS | (alice, bob)

    # Alice performs the joint measurement
    # in the maximally entangled basis
    BS | (psi, alice)
    MeasureX | psi
    MeasureP | alice

    # Bob conditionally displaces his mode
def gaussian(
    U1: np.ndarray,
    r: np.ndarray,
    U2: np.ndarray,
    alpha: np.ndarray,
    n_samples: int,
    loss: float = 0.0,
) -> list:
    r"""Generate simulated samples from pure Gaussian states.

    The pure Gaussian states are prepared by applying the following gates to the vacuum:

    #. Interferometer ``U1``
    #. Squeezing on all modes with parameters ``r``
    #. Interferometer ``U2``
    #. Displacement on all modes with parameters ``alpha``

    Note that, since the gates are applied to the vacuum, the first interferometer has no effect.
    It is nevertheless included so that the inputs to this function follow a standard decomposition
    of Gaussian unitaries shown :ref:`above <decomposition>`.

    Args:
        U1 (array): first interferometer unitary matrix
        r (array): squeezing parameters
        U2 (array): second interferometer unitary matrix
        alpha (array): displacement parameters
        n_samples (int): number of samples to be generated
        loss (float): fraction of generated photons that are lost while passing through device.
            Parameter should range from ``loss=0`` (ideal noise-free GBS) to ``loss=1``.

    Returns:
        list[list[int]]: a list of samples from GBS
    """
    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")
    if not 0 <= loss <= 1:
        raise ValueError(
            "Loss parameter must take a value between zero and one")

    n_modes = len(alpha)

    eng = sf.LocalEngine(backend="gaussian")
    gbs = sf.Program(n_modes)

    # pylint: disable=expression-not-assigned,pointless-statement
    with gbs.context as q:

        # this interferometer has no action, but is kept to follow the decomposition given in the
        # docstring
        sf.ops.Interferometer(U1) | q

        for i in range(n_modes):
            sf.ops.Sgate(r[i]) | q[i]

        sf.ops.Interferometer(U2) | q

        for i in range(n_modes):
            sf.ops.Dgate(alpha[i]) | q[i]

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")
        s = eng.run(gbs, run_options={"shots": n_samples}).samples

    if n_samples == 1:
        return [s]

    return s.tolist()
#!/usr/bin/env python3
import numpy as np
import strawberryfields as sf
from strawberryfields.ops import *

# initialize engine and program objects
eng = sf.Engine(backend="gaussian")
gbs = sf.Program(4)

# define the linear interferometer
U = np.array(
    [[
        0.219546940711 - 0.256534554457j, 0.611076853957 + 0.524178937791j,
        -0.102700187435 + 0.474478834685j, -0.027250232925 + 0.03729094623j
    ],
     [
         0.451281863394 + 0.602582912475j, 0.456952590016 + 0.01230749109j,
         0.131625867435 - 0.450417744715j, 0.035283194078 - 0.053244267184j
     ],
     [
         0.038710094355 + 0.492715562066j, -0.019212744068 - 0.321842852355j,
         -0.240776471286 + 0.524432833034j, -0.458388143039 + 0.329633367819j
     ],
     [
         -0.156619083736 + 0.224568570065j, 0.109992223305 - 0.163750223027j,
         -0.421179844245 + 0.183644837982j, 0.818769184612 + 0.068015658737j
     ]])

with gbs.context as q:
    # prepare the input squeezed states
    S = Sgate(1)
Example #21
0
def sample_fock(
    input_state: list,
    t: float,
    Ul: np.ndarray,
    w: np.ndarray,
    n_samples: int,
    cutoff: int,
    loss: float = 0.0,
) -> list:
    r"""Generate samples for simulating vibrational quantum dynamics with an input Fock state.

    **Example usage:**

    >>> input_state = [0, 2]
    >>> t = 10.0
    >>> Ul = np.array([[0.707106781, -0.707106781],
    ...                [0.707106781, 0.707106781]])
    >>> w = np.array([3914.92, 3787.59])
    >>> n_samples = 5
    >>> cutoff = 5
    >>> sample_fock(input_state, t, Ul, w, n_samples, cutoff)
    [[0, 2], [0, 2], [1, 1], [0, 2], [0, 2]]

    Args:
        input_state (list): input Fock state
        t (float): time in femtoseconds
        Ul (array): normal-to-local transformation matrix
        w (array): normal mode frequencies :math:`\omega` in units of :math:`\mbox{cm}^{-1}`
        n_samples (int): number of samples to be generated
        cutoff (int): cutoff dimension for each mode
        loss (float): loss parameter denoting the fraction of lost photons

    Returns:
        list[list[int]]: a list of samples
    """
    if np.any(np.iscomplex(Ul)):
        raise ValueError(
            "The normal mode to local mode transformation matrix must be real")

    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")

    if not len(input_state) == len(Ul):
        raise ValueError(
            "Number of modes in the input state and the normal-to-local transformation"
            " matrix must be equal")

    if np.any(np.array(input_state) < 0):
        raise ValueError("Input state must not contain negative values")

    if max(input_state) >= cutoff:
        raise ValueError(
            "Number of photons in each input mode must be smaller than cutoff")

    modes = len(Ul)

    s = []

    eng = sf.Engine("fock", backend_options={"cutoff_dim": cutoff})

    prog = sf.Program(modes)

    # pylint: disable=expression-not-assigned
    with prog.context as q:

        for i in range(modes):
            sf.ops.Fock(input_state[i]) | q[i]

        sf.ops.Interferometer(Ul.T) | q

        TimeEvolution(w, t) | q

        sf.ops.Interferometer(Ul) | q

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    for _ in range(n_samples):
        s.append(eng.run(prog).samples[0].tolist())

    return s
Example #22
0
    def test_decomposition(self, hbar, tol):
        """Test that an graph is correctly decomposed"""
        n = 3
        prog = sf.Program(n)

        A = np.random.random([n, n]) + 1j * np.random.random([n, n])
        A += A.T
        A -= np.trace(A) * np.identity(n) / n

        sq, U = dec.graph_embed(A)

        G = ops.GraphEmbed(A)
        cmds = G.decompose(prog.register)

        assert np.all(sq == G.sq)
        assert np.all(U == G.U)

        S = np.identity(2 * n)

        # calculating the resulting decomposed symplectic
        for cmd in cmds:
            # all operations should be BSgates, Rgates, or Sgates
            assert isinstance(
                cmd.op, (ops.Interferometer, ops.BSgate, ops.Rgate, ops.Sgate))

            # build up the symplectic transform
            modes = [i.ind for i in cmd.reg]

            if isinstance(cmd.op, ops.Sgate):
                S = _squeezing(cmd.op.p[0].x, cmd.op.p[1].x, modes, n) @ S

            if isinstance(cmd.op, ops.Rgate):
                S = _rotation(cmd.op.p[0].x, modes, n) @ S

            if isinstance(cmd.op, ops.BSgate):
                S = _beamsplitter(cmd.op.p[0].x, cmd.op.p[1].x, modes, n) @ S

            if isinstance(cmd.op, ops.Interferometer):
                U1 = cmd.op.p[0].x
                S_U = np.vstack([
                    np.hstack([U1.real, -U1.imag]),
                    np.hstack([U1.imag, U1.real])
                ])
                S = S_U @ S

        # the resulting covariance state
        cov = S @ S.T

        # calculate Hamilton's A matrix: A = X.(I-Q^{-1})*
        I = np.identity(n)
        O = np.zeros_like(I)
        X = np.block([[O, I], [I, O]])

        x = cov[:n, :n]
        xp = cov[:n, n:]
        p = cov[n:, n:]

        aidaj = (x + p + 1j * (xp - xp.T) - 2 * I) / 4
        aiaj = (x - p + 1j * (xp + xp.T)) / 4

        Q = np.block([[aidaj, aiaj.conj()], [aiaj, aidaj.conj()]
                      ]) + np.identity(2 * n)

        A_res = X @ (np.identity(2 * n) - np.linalg.inv(Q)).conj()

        # The bottom right corner of A_res should be identical to A,
        # up to some constant scaling factor. Check if the ratio
        # of all elements is one
        ratio = np.real_if_close(A_res[n:, n:] / A)
        ratio /= ratio[0, 0]

        assert np.allclose(ratio, np.ones([n, n]), atol=tol, rtol=0)
Example #23
0
def sample_coherent(
    alpha: list,
    t: float,
    Ul: np.ndarray,
    w: np.ndarray,
    n_samples: int,
    loss: float = 0.0,
) -> list:
    r"""Generate samples for simulating vibrational quantum dynamics with an input coherent state.

    **Example usage:**

    >>> alpha = [[0.3, 0.5], [1.4, 0.1]]
    >>> t = 10.0
    >>> Ul = np.array([[0.707106781, -0.707106781],
    ...                [0.707106781, 0.707106781]])
    >>> w = np.array([3914.92, 3787.59])
    >>> n_samples = 5
    >>> sample_coherent(alpha, t, Ul, w, n_samples)
    [[0, 2], [0, 1], [0, 3], [0, 2], [0, 1]]

    Args:
        alpha (list[list[float]]): list of displacement parameters given as ``[magnitudes, angles]``
            for all modes
        t (float): time in femtoseconds
        Ul (array): normal-to-local transformation matrix
        w (array): normal mode frequencies :math:`\omega` in units of :math:`\mbox{cm}^{-1}`
        n_samples (int): number of samples to be generated
        loss (float): loss parameter denoting the fraction of lost photons

    Returns:
        list[list[int]]: a list of samples
    """
    if np.any(np.iscomplex(Ul)):
        raise ValueError(
            "The normal mode to local mode transformation matrix must be real")

    if n_samples < 1:
        raise ValueError("Number of samples must be at least one")

    if not len(alpha) == len(Ul):
        raise ValueError(
            "Number of displacement parameters and the number of modes in the normal-to-local"
            " transformation matrix must be equal")

    modes = len(Ul)

    eng = sf.LocalEngine(backend="gaussian")

    prog = sf.Program(modes)

    # pylint: disable=expression-not-assigned
    with prog.context as q:

        for i in range(modes):
            sf.ops.Dgate(alpha[i][0], alpha[i][1]) | q[i]

        sf.ops.Interferometer(Ul.T) | q

        TimeEvolution(w, t) | q

        sf.ops.Interferometer(Ul) | q

        if loss:
            for _q in q:
                sf.ops.LossChannel(1 - loss) | _q

        sf.ops.MeasureFock() | q

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=UserWarning,
                                message="Cannot simulate non-")

        s = eng.run(prog, shots=n_samples).samples

    return s.tolist()
Example #24
0
    def test_no_unitary(self, chip, tol):
        """Test compilation works with no unitary provided"""
        prog = sf.Program(12)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[6])
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[7])
            ops.S2gate(SQ_AMPLITUDE) | (q[2], q[8])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[9])
            ops.S2gate(SQ_AMPLITUDE) | (q[4], q[10])
            ops.S2gate(SQ_AMPLITUDE) | (q[5], q[11])
            ops.MeasureFock() | q

        res = prog.compile(chip.short_name)
        expected = sf.Program(12)

        with expected.context as q:
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[6])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[7])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[8])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[9])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[4], q[10])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[5], q[11])

            # corresponds to an identity on modes [0, 1, 2, 3, 4, 5]
            # This can be easily seen from below by noting that:
            # MZ(pi, pi) = R(0) = I
            # MZ(pi, 0) @ MZ(pi, 0) = I
            # [R(pi) \otimes I] @ MZ(pi, 0) = I
            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, 0) | (q[4], q[5])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.MZgate(np.pi, np.pi) | (q[3], q[4])

            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, 0) | (q[4], q[5])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.MZgate(np.pi, np.pi) | (q[3], q[4])

            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, np.pi) | (q[2], q[3])
            ops.MZgate(np.pi, np.pi) | (q[4], q[5])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.MZgate(np.pi, np.pi) | (q[3], q[4])

            ops.Rgate(np.pi) | (q[0])
            ops.Rgate(0) | (q[1])
            ops.Rgate(0) | (q[2])
            ops.Rgate(0) | (q[3])
            ops.Rgate(0) | (q[4])
            ops.Rgate(0) | (q[5])

            # corresponds to an identity on modes [6, 7, 8, 9, 10, 11]
            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, 0) | (q[8], q[9])
            ops.MZgate(np.pi, 0) | (q[10], q[11])
            ops.MZgate(np.pi, np.pi) | (q[7], q[8])
            ops.MZgate(np.pi, np.pi) | (q[9], q[10])

            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, 0) | (q[8], q[9])
            ops.MZgate(np.pi, 0) | (q[10], q[11])
            ops.MZgate(np.pi, np.pi) | (q[7], q[8])
            ops.MZgate(np.pi, np.pi) | (q[9], q[10])

            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, np.pi) | (q[8], q[9])
            ops.MZgate(np.pi, np.pi) | (q[10], q[11])
            ops.MZgate(np.pi, np.pi) | (q[7], q[8])
            ops.MZgate(np.pi, np.pi) | (q[9], q[10])

            ops.Rgate(np.pi) | (q[6])
            ops.Rgate(0) | (q[7])
            ops.Rgate(0) | (q[8])
            ops.Rgate(0) | (q[9])
            ops.Rgate(0) | (q[10])
            ops.Rgate(0) | (q[11])

            ops.MeasureFock() | q

        # Check that the applied symplectic is correct

        # remove the Fock measurements
        res.circuit = res.circuit[:-1]

        # extract the Gaussian symplectic matrix
        O = res.compile("gaussian_unitary").circuit[0].op.p[0]

        # construct the expected symplectic matrix corresponding
        # to just the initial two mode squeeze gates
        S = TMS(SQ_AMPLITUDE, 0)

        expected = np.zeros([2*12, 2*12])
        l = 12 // 2
        ch = np.cosh(SQ_AMPLITUDE) * np.identity(l)
        sh = np.sinh(SQ_AMPLITUDE) * np.identity(l)
        zh = np.zeros([l, l])
        expected = np.block([[ch, sh, zh, zh], [sh, ch, zh, zh], [zh, zh, ch, -sh], [zh, zh, -sh, ch]])

        assert np.allclose(O, expected, atol=tol)
Example #25
0
 def execution_context(self):
     """Initialize the engine"""
     self.reset()
     self.prog = sf.Program(self.num_wires)
     self.q = self.prog.register
     return self.prog
#!/usr/bin/env python3
import strawberryfields as sf
from strawberryfields.ops import *
from strawberryfields.utils import scale
from numpy import pi, sqrt
import numpy as np

# initialize engine and program objects
eng = sf.Engine(backend="gaussian")
gaussian_cloning = sf.Program(4)

with gaussian_cloning.context as q:
    # state to be cloned
    Coherent(0.7+1.2j) | q[0]

    # 50-50 beamsplitter
    BS = BSgate(pi/4, 0)

    # symmetric Gaussian cloning scheme
    BS | (q[0], q[1])
    BS | (q[1], q[2])
    MeasureX | q[1]
    MeasureP | q[2]
    Xgate(scale(q[1], sqrt(2))) | q[0]
    Zgate(scale(q[2], sqrt(2))) | q[0]

    # after the final beamsplitter, modes q[0] and q[3]
    # will contain identical approximate clones of the
    # initial state Coherent(0.1+0j)
    BS | (q[0], q[3])
    # end circuit
Example #27
0
params = [r1, sq_r, sq_phi, r2, d_r, d_phi, kappa]

# layer architecture
def layer(i, q):
    Rgate(r1[i]) | q
    Sgate(sq_r[i], sq_phi[i]) | q
    Rgate(r2[i]) | q
    Dgate(d_r[i], d_phi[i]) | q
    Dgate(d_r[i]) | q
    Kgate(kappa[i]) | q

    return q




# Create program and engine
prog = sf.Program(1)
eng = sf.Engine('tf', backend_options={"cutoff_dim": cutoff})

# Apply circuit of layers with corresponding depth
with prog.context as q:
    for k in range(depth):
        layer(k, q[0])

# Run engine
state = eng.run(prog, run_options={"eval": False}).state
ket = state.ket()


#!/usr/bin/env python3
import strawberryfields as sf
from strawberryfields.ops import *

# initialize engine and program objects
eng = sf.Engine(backend="fock", backend_options={"cutoff_dim": 7})
boson_sampling = sf.Program(4)

with boson_sampling.context as q:
    # prepare the input fock states
    Fock(1) | q[0]
    Fock(1) | q[1]
    Vac     | q[2]
    Fock(1) | q[3]

    # rotation gates
    Rgate(0.5719) | q[0]
    Rgate(-1.9782) | q[1]
    Rgate(2.0603) | q[2]
    Rgate(0.0644) | q[3]

    # beamsplitter array
    BSgate(0.7804, 0.8578)  | (q[0], q[1])
    BSgate(0.06406, 0.5165) | (q[2], q[3])
    BSgate(0.473, 0.1176)   | (q[1], q[2])
    BSgate(0.563, 0.1517)   | (q[0], q[1])
    BSgate(0.1323, 0.9946)  | (q[2], q[3])
    BSgate(0.311, 0.3231)   | (q[1], q[2])
    BSgate(0.4348, 0.0798)  | (q[0], q[1])
    BSgate(0.4368, 0.6157)  | (q[2], q[3])
    # end circuit
Example #29
0
to the output of a GKP qubit phase gate applied to a finite energy |+> state.
The qubit phase gate is applied using measurement-based squeezing."""

import strawberryfields as sf
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors, colorbar


sf.hbar = 1
x = np.linspace(-4, 4, 400) * np.sqrt(np.pi)
p = np.linspace(-4, 4, 400) * np.sqrt(np.pi)
wigners = []

# Finite energy GKP |+i> state
prog_y = sf.Program(1)
with prog_y.context as q:
    sf.ops.GKP([np.pi / 2, -np.pi / 2], epsilon=0.1) | q[0]
eng = sf.Engine("bosonic")
result = eng.run(prog_y)
wigners.append(result.state.wigner(0, x, p))

# Squeezing values for measurement-based squeezing ancillae
rdB = np.array([14, 10, 6])
r_anc = np.log(10) * rdB / 20

# Detection efficiencies
eta_anc = np.array([1, 0.98, 0.95])

# Target squeezing and rotation angles to apply a GKP phase gate
r = np.arccosh(np.sqrt(5 / 4))
Example #30
0
    def test_no_unitary(self, tol):
        """Test compilation works with no unitary provided"""
        prog = sf.Program(8)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[7])
            ops.MeasureFock() | q

        res = prog.compile("X8_01")
        expected = sf.Program(8)

        with expected.context as q:
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[7])

            # corresponds to an identity on modes [0, 1, 2, 3]
            # This can be easily seen from below by noting that:
            # MZ(pi, pi) = R(0) = I
            # MZ(pi, 0) @ MZ(pi, 0) = I
            # [R(pi) \otimes I] @ MZ(pi, 0) = I
            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.MZgate(np.pi, 0) | (q[0], q[1])
            ops.MZgate(np.pi, 0) | (q[2], q[3])
            ops.MZgate(np.pi, np.pi) | (q[1], q[2])
            ops.Rgate(0) | (q[0])
            ops.Rgate(0) | (q[1])
            ops.Rgate(0) | (q[2])
            ops.Rgate(0) | (q[3])

            # corresponds to an identity on modes [4, 5, 6, 7]
            ops.MZgate(np.pi, 0) | (q[4], q[5])
            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, np.pi) | (q[5], q[6])
            ops.MZgate(np.pi, 0) | (q[4], q[5])
            ops.MZgate(np.pi, 0) | (q[6], q[7])
            ops.MZgate(np.pi, np.pi) | (q[5], q[6])
            ops.Rgate(0) | (q[4])
            ops.Rgate(0) | (q[5])
            ops.Rgate(0) | (q[6])
            ops.Rgate(0) | (q[7])

            ops.MeasureFock() | q

        assert program_equivalence(res, expected, atol=tol)

        # double check that the applied symplectic is correct

        # remove the Fock measurements
        res.circuit = res.circuit[:-1]

        # extract the Gaussian symplectic matrix
        O = res.compile("gaussian_unitary").circuit[0].op.p[0]

        # construct the expected symplectic matrix corresponding
        # to just the initial two mode squeeze gates
        S = TMS(SQ_AMPLITUDE, 0)

        expected = np.zeros([2 * 8, 2 * 8])
        idx = np.arange(2 * 8).reshape(4, 4).T
        for i in idx:
            expected[i.reshape(-1, 1), i.reshape(1, -1)] = S

        assert np.allclose(O, expected, atol=tol)