def test_modes_subset(depth):
    """Tests that the compiler recognizes which modes are not being modified and acts accordingly"""

    width = 10
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    indices = (1, 4, 2, 6, 7)
    active_modes = len(indices)
    with circuit.context as q:
        for _ in range(depth):
            U, s, V, _ = random_params(active_modes, 2.0 / depth, 1.0)
            ops.Interferometer(U) | tuple(q[i] for i in indices)
            for i, index in enumerate(indices):
                ops.Sgate(s[i]) | q[index]
            ops.Interferometer(V) | tuple(q[i] for i in indices)
    compiled_circuit = circuit.compile(compiler="gaussian_unitary")
    cv = eng.run(circuit).state.cov()
    mean = eng.run(circuit).state.means()

    cv1 = eng1.run(compiled_circuit).state.cov()
    mean1 = eng1.run(compiled_circuit).state.means()
    assert np.allclose(cv, cv1)
    assert np.allclose(mean, mean1)
    assert len(compiled_circuit.circuit[0].reg) == 5
    indices = [compiled_circuit.circuit[0].reg[i].ind for i in range(5)]
    assert indices == sorted(list(indices))
def test_non_primitive_gates():
    """Tests that the compiler is able to compile a number of non-primitive Gaussian gates"""

    width = 6
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    A = np.random.rand(width, width) + 1j * np.random.rand(width, width)
    A = A + A.T
    valsA = np.linalg.svd(A, compute_uv=False)
    A = A / 2 * np.max(valsA)
    B = np.random.rand(
        width // 2, width // 2) + 1j * np.random.rand(width // 2, width // 2)
    valsB = np.linalg.svd(B, compute_uv=False)
    B = B / 2 * valsB
    B = np.block([[0 * B, B], [B.T, 0 * B]])
    with circuit.context as q:
        ops.GraphEmbed(A) | q
        ops.BipartiteGraphEmbed(B) | q
        ops.Pgate(0.1) | q[1]
        ops.CXgate(0.2) | (q[0], q[1])
        ops.MZgate(0.4, 0.5) | (q[2], q[3])
        ops.Fourier | q[0]
        ops.Xgate(0.4) | q[1]
        ops.Zgate(0.5) | q[3]
    compiled_circuit = circuit.compile(compiler="gaussian_unitary")
    cv = eng.run(circuit).state.cov()
    mean = eng.run(circuit).state.means()

    cv1 = eng1.run(compiled_circuit).state.cov()
    mean1 = eng1.run(compiled_circuit).state.means()
    assert np.allclose(cv, cv1)
    assert np.allclose(mean, mean1)
def test_symplectic_composition(depth, width):
    """Tests that symplectic operations are composed correctly"""
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    Snet = np.identity(2 * width)
    with circuit.context as q:
        for _ in range(depth):
            S = random_symplectic(width, scale=0.2)
            Snet = S @ Snet
            ops.GaussianTransform(S) | q
    compiled_circuit = circuit.compile(compiler="gaussian_unitary")
    assert np.allclose(compiled_circuit.circuit[0].op.p[0], Snet)
Exemplo n.º 4
0
def _sample_sf(p: sf.Program,
               shots: int = 1,
               backend_options: Optional[dict] = None) -> np.ndarray:
    """Generate samples from Strawberry Fields.

    Args:
        p (sf.Program): the program to sample from
        shots (int): the number of samples; defaults to 1
        backend_options (dict[str, Any]): dictionary specifying options used by backends during
        sampling; defaults to :const:`BACKEND_DEFAULTS`

    Returns:
        array: an array of ``len(shots)`` samples, with each sample the result of running a
        :class:`~strawberryfields.program.Program` specified by ``p``
    """
    backend_options = {**BACKEND_DEFAULTS, **(backend_options or {})}

    if not backend_options["backend"] in QUANTUM_BACKENDS:
        raise ValueError("Invalid backend selected")

    if backend_options["remote"]:
        raise ValueError("Remote sampling not yet available")

    eng = sf.LocalEngine(backend=backend_options["backend"])

    return np.array(eng.run(p, run_options={"shots": shots}).samples)
Exemplo n.º 5
0
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

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

    if n_samples == 1:
        return [s]

    return s.tolist()
Exemplo n.º 6
0
    def test_tensorflow_not_installed(self, monkeypatch):
        """Test that an exception is raised if TensorFlow is not installed"""
        with monkeypatch.context() as m:
            # force Python check to pass
            m.setattr("sys.version_info", (3, 6, 3))

            with pytest.raises(ImportError, message="version 1.3 of TensorFlow is required"):
                reload(sf.backends.tfbackend)
                sf.LocalEngine('tf')
Exemplo n.º 7
0
def prob_event_mc(
    graph: nx.Graph,
    photon_number: int,
    max_count_per_mode: int,
    n_mean: float = 5,
    samples: int = 1000,
) -> float:
    """Gives a Monte Carlo estimate of the probability of a given event for a GBS device encoded
    according to the input graph.

    To make this estimate, several samples from the event are drawn uniformly at random. For each
    sample, we calculate the probability of observing that sample from a GBS programmed according to
    the input graph and mean photon number. These probabilities are then rescaled according to the
    cardinality of the event. The estimate is the sample mean of the rescaled probabilities. To make
    this estimate, several samples from the event are drawn uniformly at random using
    :func:event_to_sample.

    **Example usage**:

    >>> graph = nx.complete_graph(8)
    >>> p_event_mc(graph, 4, 2)
    0.1395

    Args:
        graph (nx.Graph): input graph encoded in the GBS device
        photon_number (int): number of photons in the event
        max_count_per_mode (int): maximum number of photons per mode in the event
        n_mean (float): total mean photon number of the GBS device
        samples (int): number of samples used in the Monte Carlo estimation

    Returns:
        float: the estimated probability
    """

    modes = graph.order()
    A = nx.to_numpy_array(graph)
    mean_photon_per_mode = n_mean / float(modes)

    p = sf.Program(modes)

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

    eng = sf.LocalEngine(backend="gaussian")
    result = eng.run(p)

    prob = 0

    for _ in range(samples):
        sample = event_to_sample(photon_number, max_count_per_mode, modes)
        prob += result.state.fock_prob(sample, cutoff=photon_number + 1)

    prob = prob * event_cardinality(photon_number, max_count_per_mode,
                                    modes) / samples

    return prob
Exemplo n.º 8
0
    def test_tensorflow_not_installed(self, monkeypatch):
        """Test that an exception is raised if TensorFlow is not installed"""
        with monkeypatch.context() as m:
            # Force the Python check to pass.
            m.setattr(sys, "version_info", (3, 6, 3))

            with pytest.raises(ImportError,
                               match="version 2.x of TensorFlow is required"):
                sf.LocalEngine("tf")
Exemplo n.º 9
0
def prob_orbit_mc(graph: nx.Graph,
                  orbit: list,
                  n_mean: float = 5,
                  samples: int = 1000) -> float:
    """Gives a Monte Carlo estimate of the probability of a given orbit for a GBS device encoded
    according to the input graph.

    To make this estimate, several samples from the orbit are drawn uniformly at random using
    :func:`orbit_to_sample`.

    For each sample, this function calculates the probability of observing that sample from a GBS
    device programmed according to the input graph and mean photon number. The sum of the
    probabilities is then rescaled according to the cardinality of the orbit and the total number of
    samples. The estimate is the sample mean of the rescaled probabilities. To make
    this estimate, several samples from the orbit are drawn uniformly at random using
    :func:orbit_to_sample.

    **Example usage**:

    >>> graph = nx.complete_graph(8)
    >>> prob_orbit_mc(graph, [2, 1, 1])
    0.03744

    Args:
        graph (nx.Graph): input graph encoded in the GBS device
        orbit (list[int]): orbit for which to estimate the probability
        n_mean (float): total mean photon number of the GBS device
        samples (int): number of samples used in the Monte Carlo estimation

    Returns:
        float: estimated orbit probability
    """

    modes = graph.order()
    photons = sum(orbit)
    A = nx.to_numpy_array(graph)
    mean_photon_per_mode = n_mean / float(modes)

    p = sf.Program(modes)

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

    eng = sf.LocalEngine(backend="gaussian")
    result = eng.run(p)

    prob = 0

    for _ in range(samples):
        sample = orbit_to_sample(orbit, modes)
        prob += result.state.fock_prob(sample, cutoff=photons + 1)

    prob = prob * orbit_cardinality(orbit, modes) / samples

    return prob
def test_displacements_only(depth, width):
    """Tests that a circuit and its compiled version produce
    the same Gaussian state when there are only displacements"""
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    with circuit.context as q:
        for _ in range(depth):
            alphas = np.random.rand(width) + 1j * np.random.rand(width)
            for i in range(width):
                ops.Dgate(np.abs(alphas[i]), np.angle(alphas[i])) | q[i]
    compiled_circuit = circuit.compile(compiler="gaussian_unitary")
    cv = eng.run(circuit).state.cov()
    mean = eng.run(circuit).state.means()

    cv1 = eng1.run(compiled_circuit).state.cov()
    mean1 = eng1.run(compiled_circuit).state.means()
    assert np.allclose(cv, cv1)
    assert np.allclose(mean, mean1)
Exemplo n.º 11
0
    def test_incorrect_python_version(self, monkeypatch):
        """Test that an exception is raised if the version
        of Python installed is > 3.6"""
        with monkeypatch.context() as m:
            m.setattr("sys.version_info", (3, 8, 1))
            m.setattr(tensorflow, "__version__", "1.12.2")

            with pytest.raises(ImportError, message="you will need to install Python 3.6"):
                reload(sf.backends.tfbackend)
                sf.LocalEngine('tf')
Exemplo n.º 12
0
def test_modes_subset(depth):
    """Tests that the compiler recognizes which modes are not being modified and acts accordingly"""

    width = 10
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    indices = (1, 4, 2, 6, 7)
    active_modes = len(indices)
    with circuit.context as q:
        for _ in range(depth):
            U = unitary_group.rvs(len(indices))
            ops.Interferometer(U) | tuple(q[i] for i in indices)

    compiled_circuit = circuit.compile(compiler="passive")

    assert len(compiled_circuit.circuit[0].reg) == 5
    indices = [compiled_circuit.circuit[0].reg[i].ind for i in range(5)]
    assert indices == sorted(list(indices))
Exemplo n.º 13
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_incorrect_tf_version(self, monkeypatch):
        """Test that an exception is raised if the version
        of TensorFlow installed is not version 2.x"""
        with monkeypatch.context() as m:
            # force Python check to pass
            m.setattr("sys.version_info", (3, 6, 3))
            m.setattr(tensorflow, "__version__", "1.12.2")

            with pytest.raises(ImportError,
                               match="version 2.x of TensorFlow is required"):
                reload(sf.backends.tfbackend)
                sf.LocalEngine('tf')
def test_gaussian_program(depth, width):
    """Tests that a circuit and its compiled version produce the same Gaussian state"""
    eng = sf.LocalEngine(backend="gaussian")
    eng1 = sf.LocalEngine(backend="gaussian")
    circuit = sf.Program(width)
    with circuit.context as q:
        for _ in range(depth):
            U, s, V, alphas = random_params(width, 2.0 / depth, 1.0)
            ops.Interferometer(U) | q
            for i in range(width):
                ops.Sgate(s[i]) | q[i]
            ops.Interferometer(V) | q
            for i in range(width):
                ops.Dgate(np.abs(alphas[i]), np.angle(alphas[i])) | q[i]
    compiled_circuit = circuit.compile(compiler="gaussian_unitary")
    cv = eng.run(circuit).state.cov()
    mean = eng.run(circuit).state.means()

    cv1 = eng1.run(compiled_circuit).state.cov()
    mean1 = eng1.run(compiled_circuit).state.means()
    assert np.allclose(cv, cv1)
    assert np.allclose(mean, mean1)
    def test_op_prob(self, params):
        """Test if the function ``strawberryfields.apps.qchem.vibronic.VibronicTransition`` gives
        the correct probabilities of all possible Fock basis states when used in a circuit"""

        U1, r, U2, alpha, prob = params

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

        gbs = sf.Program(len(U1))

        with gbs.context as q:

            vibronic.VibronicTransition(U1, r, U2, alpha) | q

            p = eng.run(gbs).state.all_fock_probs(cutoff=4)

        assert np.allclose(p, prob)
    def test_all_loss(self, monkeypatch, adj, dim):
        """Test if function samples from the vacuum when maximum loss is applied."""
        mock_eng_run = mock.MagicMock()

        with monkeypatch.context() as m:
            m.setattr(sf.LocalEngine, "run", mock_eng_run)
            sample.sample(A=adj, n_mean=1, threshold=False, loss=1)
            p_func = mock_eng_run.call_args[0][0]

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

        state = eng.run(p_func).state
        cov = state.cov()
        disp = state.displacement()

        assert np.allclose(cov, 0.5 * state.hbar * np.eye(2 * dim))
        assert np.allclose(disp, np.zeros(dim))
Exemplo n.º 18
0
    def test_incorrect_tf_version(self, monkeypatch):
        """Test that an exception is raised if the version
        of TensorFlow installed is not version 2.x"""
        with monkeypatch.context() as m:
            # Force the Python check to pass.
            m.setattr(sys, "version_info", (3, 6, 3))

            # Unload the TF backend to ensure sf.LocalEngine() will run __init__.py.
            m.delitem(sys.modules,
                      "strawberryfields.backends.tfbackend",
                      raising=False)
            # Set the TF version in case the existing version is valid.
            m.setitem(sys.modules, "tensorflow",
                      MagicMock(__version__="1.2.3"))

            with pytest.raises(ImportError,
                               match="version 2.x of TensorFlow is required"):
                sf.LocalEngine("tf")
Exemplo n.º 19
0
    def test_zgate_decompose(self, backend, hbar, applied_cmds):
        """Test parameter processing occuring within the Zgate._decompose method."""
        import tensorflow as tf

        mapping = {"p": tf.Variable(0.1)}
        prog = self.create_program(sf.ops.Zgate, mapping)

        # verify bound parameters are correct
        assert prog.free_params["p"].val is mapping["p"]

        # assert executed program is constructed correctly
        eng = sf.LocalEngine(backend)
        result = eng.run(prog, args=mapping)

        assert len(applied_cmds) == 1
        assert isinstance(applied_cmds[0].op, sf.ops.Dgate)
        assert par_evaluate(applied_cmds[0].op.p[0]) == mapping["p"] / np.sqrt(2 * hbar)
        assert applied_cmds[0].op.p[1] == np.pi / 2
Exemplo n.º 20
0
    def test_two_mode_gate_complex_phase(self, backend, gate, applied_cmds):
        """Test non-decomposed two-mode gates with complex phase arguments."""
        import tensorflow as tf
        mapping = {'r': tf.Variable(0.1), 'phi': tf.Variable(0.2)}
        prog = self.create_program(gate, mapping)

        # verify bound parameters are correct
        assert prog.free_params['r'].val is mapping['r']
        assert prog.free_params['phi'].val is mapping['phi']

        # assert executed program is constructed correctly
        eng = sf.LocalEngine(backend)
        result = eng.run(prog, args=mapping)

        assert len(applied_cmds) == 1
        assert isinstance(applied_cmds[0].op, gate)
        assert applied_cmds[0].op.p[0].val == mapping["r"]
        assert applied_cmds[0].op.p[1].val == mapping["phi"]
    def test_all_loss(self, monkeypatch):
        """Test if function samples from the vacuum when maximum loss is applied."""
        dim = 5
        graph = nx.complete_graph(dim)
        mock_eng_run = mock.MagicMock()

        with monkeypatch.context() as m:
            m.setattr(sf.LocalEngine, "run", mock_eng_run)
            similarity.prob_event_mc(graph, 6, 3, samples=1, loss=1)
            p_func = mock_eng_run.call_args[0][0]

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

        state = eng.run(p_func).state
        cov = state.cov()
        disp = state.displacement()

        assert np.allclose(cov, 0.5 * state.hbar * np.eye(2 * dim))
        assert np.allclose(disp, np.zeros(dim))
    def test_all_loss(self, monkeypatch, p):
        """Test if function samples from the vacuum when maximum loss is applied. This test is
        only done for the zero temperature case"""
        if p == 'p0':
            dim = len(alpha)
            mock_eng_run = mock.MagicMock()

            with monkeypatch.context() as m:
                m.setattr(sf.LocalEngine, "run", mock_eng_run)
                sample.vibronic(*p, 1, loss=1)
                p_func = mock_eng_run.call_args[0][0]

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

            state = eng.run(p_func).state
            cov = state.cov()
            disp = state.displacement()

            assert np.allclose(cov, 0.5 * state.hbar * np.eye(2 * dim))
            assert np.allclose(disp, np.zeros(dim))
Exemplo n.º 23
0
def _get_state(graph: nx.Graph,
               n_mean: float = 5,
               loss: float = 0.0) -> BaseGaussianState:
    r"""Embeds the input graph into a GBS device and returns the corresponding Gaussian state."""
    modes = graph.order()
    A = nx.to_numpy_array(graph)
    mean_photon_per_mode = n_mean / float(modes)

    p = sf.Program(modes)

    # pylint: disable=expression-not-assigned
    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

    eng = sf.LocalEngine(backend="gaussian")
    return eng.run(p).state
Exemplo n.º 24
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()
Exemplo n.º 25
0
def prob_event_mc(
    graph: nx.Graph,
    photon_number: int,
    max_count_per_mode: int,
    n_mean: float = 5,
    samples: int = 1000,
    loss: float = 0.0,
) -> float:
    r"""Gives a Monte Carlo estimate of the GBS probability of a given event according to the input
    graph.

    To make this estimate, several samples from the event are drawn uniformly at random using
    :func:`event_to_sample`. The GBS probabilities of these samples are then calculated and the
    sum is used to create an estimate of the event probability.

    **Example usage:**

    >>> graph = nx.complete_graph(8)
    >>> prob_event_mc(graph, 4, 2)
    0.1395

    Args:
        graph (nx.Graph): input graph encoded in the GBS device
        photon_number (int): number of photons in the event
        max_count_per_mode (int): maximum number of photons per mode in the event
        n_mean (float): total mean photon number of the GBS device
        samples (int): number of samples used in the Monte Carlo estimation
        loss (float): fraction of photons lost in GBS

    Returns:
        float: estimated orbit probability
    """
    if 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")
    if photon_number < 0:
        raise ValueError("Photon number must not be below zero")
    if max_count_per_mode < 0:
        raise ValueError(
            "Maximum number of photons per mode must be non-negative")

    modes = graph.order()
    A = nx.to_numpy_array(graph)
    mean_photon_per_mode = n_mean / float(modes)

    p = sf.Program(modes)

    # pylint: disable=expression-not-assigned
    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

    eng = sf.LocalEngine(backend="gaussian")
    result = eng.run(p)

    prob = 0

    for _ in range(samples):
        sample = event_to_sample(photon_number, max_count_per_mode, modes)
        prob += result.state.fock_prob(sample, cutoff=photon_number + 1)

    prob = prob * event_cardinality(photon_number, max_count_per_mode,
                                    modes) / samples

    return prob
Exemplo n.º 26
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,
    engine='local',
) -> 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)

    if engine=='X8': #VGG check you engine using sf.ping()
      print("Atemting to use X8 ... ")
      eng = sf.RemoteEngine("X8")
    else:
      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-")

        if engine=='X8': #VGG check you engine using sf.ping()
          s = eng.run(gbs,shots=n_samples,state=True).samples
        else:
          s = eng.run(gbs, shots=n_samples).samples

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

    if np.any(t == 0):
        s = np.pad(s, ((0, 0), (0, n_modes))).tolist()
    return s
Exemplo n.º 27
0
 def test_load_backend(self):
     """Backend can be correctly loaded via strings"""
     eng = sf.LocalEngine("base")
     assert isinstance(eng.backend, BaseBackend)
Exemplo n.º 28
0
 def test_bad_backend(self):
     """Backend must be a string or a BaseBackend instance."""
     with pytest.raises(TypeError, match='backend must be a string or a BaseBackend instance'):
         eng = sf.LocalEngine(0)
Exemplo n.º 29
0
def eng(backend):
    """Engine fixture."""
    return sf.LocalEngine(backend)
Exemplo n.º 30
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()