def test_adjoint_of_complementary_channel_kraus_operators():
    r"""Test the adjoint channel of the complementary channel based on kraus operators."""
    # Test Adjoint complementary channel is unital
    rho = np.eye(4)
    krauss_ops = initialize_dephrasure_examples(0.1, 0.2)
    chan = DenseKraus(krauss_ops, [1, 1], 2, 3, orthogonal_kraus=[2])
    chan_s = SparseKraus(krauss_ops, [1, 1], 2, 3)

    desired = np.eye(2)
    # Test Dense Kraus
    actual = chan.entropy_exchange(rho, 1, adjoint=True)
    assert np.all(np.abs(actual - desired) < 1e-4)
    # Test Sparse Kraus
    actual = chan_s.entropy_exchange(rho, 1, adjoint=True).todense()
    assert np.all(np.abs(actual - desired) < 1e-4)

    # Test based on random density matrix.
    rho = rand_dm_ginibre(4, 4).data.toarray()
    desired = sum([
        rho[i, j] * krauss_ops[i].T.dot(krauss_ops[j]) for i in range(0, 4)
        for j in range(0, 4)
    ])
    actual = chan.entropy_exchange(rho, 1, adjoint=True)
    assert np.all(np.abs(actual - desired) < 1e-4)
    actual = chan_s.entropy_exchange(rho, 1, adjoint=True).todense()
    assert np.all(np.abs(actual - desired) < 1e-4)
def test_adjoint_of_a_channel():
    r"""Test the adjoint of the channel on DenseKraus and SparseKraus."""
    p1, p2, p3 = 0.1, 0.01, 0.4
    krauss_ops = initialize_pauli_examples(p1, p2, p3)
    # Dense Krauss
    dense_krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 2)
    sparse_krauss_obj = SparseKraus(krauss_ops, [1, 1], 2, 2)
    for n in range(1, 3):
        # Update Krauss Operators
        k_ops = krauss_ops.copy()
        if n != 1:
            k_ops = np.kron(np.array(k_ops), krauss_ops)
        dense_krauss_obj.update_kraus_operators(n)
        sparse_krauss_obj.update_kraus_operators(n)

        # Test on random density matrices
        for _ in range(0, 10):
            rho = np.array(rand_dm_ginibre(2**n).data.todense())

            desired = np.zeros((2**n, 2**n), dtype=np.complex128)
            for k in k_ops:
                desired += np.conj(k.T).dot(rho.dot(k))

            dense_chan = dense_krauss_obj.channel(rho, True)
            sparse_chan = sparse_krauss_obj.channel(rho, True)
            assert_array_almost_equal(desired, dense_chan)
            assert_array_almost_equal(desired, sparse_chan)
def test_initialization_of_krauss_operators_sparse():
    r"""Test sparse krauss operators and exchange array have the right shape."""
    # Test it on dephrasure channel.
    p, q = 0.25, 0.1
    k_ops = initialize_dephrasure_examples(p, q)
    numb_krauss = len(k_ops)
    desired_krauss, desired_exchange = init_of_krauss_and_exchange_array(k_ops)

    krauss = SparseKraus(k_ops, [1, 1], 2, 3)

    # Test Krauss operators are in the right format.
    actual = krauss.nth_kraus_ops
    assert issubclass(type(actual), SparseArray)
    actual = actual.todense()
    assert actual.shape == (numb_krauss, 3, 2)
    assert_array_almost_equal(actual, desired_krauss)

    # Test Array for entropy exchange is in the right format.
    actual = krauss.nth_kraus_exch
    assert issubclass(type(actual), SparseArray)
    actual = actual.todense()
    assert actual.shape == (numb_krauss, numb_krauss, 2, 2)
    assert_array_almost_equal(actual, desired_exchange)

    # Test wrong dimensions
    assert_raises(AssertionError, SparseKraus, k_ops, [1, 1], 2, 2)
    assert_raises(AssertionError, SparseKraus, k_ops, [1, 2], 2, 3)
    assert_raises(AssertionError, SparseKraus, k_ops, [1, 1], 3, 2)
    k_ops = [np.array([[1., 0.], [0., 1.]])]
    assert_raises(AssertionError, SparseKraus, k_ops, [1, 1], 2, 3)
Exemple #4
0
    def __mul__(self, other):
        r"""
        Parallel Concatenate two channels A \otimes B.

        Parameters
        ----------
        other : AnalyticQChan
            The channel object B.

        Returns
        -------
        AnalyticQChan :
            Returns a new channel that models A tensored with B.

        Notes
        -----
        - The number of particles set to one particle and one particle for output.

        """
        assert self._type == "kraus", "Only works for kraus operators."
        issparse = self.sparse or other.sparse
        if issparse:
            new_kraus_ops = SparseKraus.parallel_concatenate(
                self.kraus, other.kraus)
        else:
            new_kraus_ops = DenseKraus.parallel_concatenate(
                self.kraus, other.kraus)
        numb_qubits = [1, 1]
        dim_in = self.dim_in * other.dim_in
        dim_out = self.dim_out * other.dim_out
        return AnalyticQChan(new_kraus_ops,
                             numb_qubits,
                             dim_in,
                             dim_out,
                             sparse=issparse)
def test_serial_concatenation():
    r"""Test serial concantenation of two krauss operators."""
    p, q = 0.1, 0.2
    k1 = initialize_dephrasure_examples(p, q)
    # Second kraus operators of dephasing channel.
    k2 = [
        np.sqrt(q) * np.array([[
            0.,
            1.,
        ], [1., 0.]]),
        np.sqrt(1 - q) * np.array([[1., 0.], [0., -1.]])
    ]

    # Serial Concatenation of "k1 \circ k2"
    desired = [x.dot(y) for x in k1 for y in k2]
    actual = DenseKraus.serial_concatenate(k1, k2)
    assert_array_almost_equal(np.array(desired), actual)

    # Test sparse
    actual = SparseKraus.serial_concatenate(k1, k2)
    assert_array_almost_equal(desired, actual.todense())

    # Test __add__ method
    dkraus1 = DenseKraus(k1, [1, 1], 2, 3)
    dkraus2 = DenseKraus(k2, [1, 1], 2, 2)
    dkraus3 = dkraus1 + dkraus2
    assert_array_almost_equal(np.array(desired), dkraus3.kraus_ops)

    # Test __add__ on sparse matrices.
    dkraus1 = SparseKraus(k1, [1, 1], 2, 3)
    dkraus2 = SparseKraus(k2, [1, 1], 2, 2)
    dkraus3 = dkraus1 + dkraus2
    assert_array_almost_equal(np.array(desired), dkraus3.kraus_ops.todense())

    # Test Incompatible dimensions.
    assert_raises(TypeError, DenseKraus.__add__, dkraus2, dkraus1)
    assert_raises(TypeError, DenseKraus.serial_concatenate, k2, k1)
    assert_raises(AssertionError, DenseKraus.serial_concatenate, dkraus1,
                  dkraus2)
    assert_raises(AssertionError, DenseKraus.serial_concatenate, dkraus2,
                  dkraus1)
    assert_raises(TypeError, SparseKraus.__add__, dkraus2, dkraus1)
    assert_raises(TypeError, SparseKraus.serial_concatenate, k2, k1)
    assert_raises(AssertionError, SparseKraus.serial_concatenate, dkraus1,
                  dkraus2)
    assert_raises(AssertionError, SparseKraus.serial_concatenate, dkraus2,
                  dkraus1)
def test_entanglement_fidelity():
    r"""Test the average_entanglement_fidelity method of both Sparse and Dense Kraus Operators."""
    # Get krauss operators from dephrasure channel
    krauss_ops = initialize_dephrasure_examples(0.1, 0.2)
    chan = DenseKraus(krauss_ops, [1, 1], 2, 3)
    chan_sp = SparseKraus(krauss_ops, [1, 1], 2, 3)
    desired_fid = 0.
    probs = [0.25, 0.75]
    states = [
        np.array(rand_dm_ginibre(2).data.todense()),
        np.array(rand_dm_ginibre(2).data.todense())
    ]
    for i, p in enumerate(probs):
        for k in krauss_ops:
            desired_fid += p * np.abs(np.trace(k.dot(states[i])))**2

    assert np.abs(desired_fid -
                  chan.average_entanglement_fidelity(probs, states))
    assert np.abs(desired_fid -
                  chan_sp.average_entanglement_fidelity(probs, states))
def test_sparse_krauss_versus_dense_krauss():
    r"""Test the sparse krauss operators versus dense krauss operators."""
    p, q = 0.2, 0.4

    k_ops = initialize_dephrasure_examples(p, q)
    spkrauss = SparseKraus(k_ops, [1, 1], 2, 3)
    dKrauss = DenseKraus(k_ops, [1, 1], 2, 3)

    for n in range(1, 4):
        # Update channel to correpond to n.
        spkrauss.update_kraus_operators(n)
        dKrauss.update_kraus_operators(n)

        # Test nth krauss operators
        assert issubclass(type(spkrauss.nth_kraus_ops), SparseArray)
        spkrauss_nth_krauss = spkrauss.nth_kraus_ops.todense()
        dkrauss_nth_krauss = dKrauss.nth_kraus_ops
        assert_array_almost_equal(spkrauss_nth_krauss, dkrauss_nth_krauss)

        # Test exchange array
        assert issubclass(type(spkrauss.nth_kraus_exch), SparseArray)
        spkrauss_exchange = spkrauss.nth_kraus_exch.todense()
        dkrauss_exchange = dKrauss.nth_kraus_exch
        assert_array_almost_equal(dkrauss_exchange, spkrauss_exchange)

        # Test channel output
        rho = np.array(rand_dm_ginibre(2**n).data.todense())
        assert_array_almost_equal(spkrauss.channel(rho), dKrauss.channel(rho))

        # Test entropy exchange
        assert_array_almost_equal(
            spkrauss.entropy_exchange(rho, n)[0],
            dKrauss.entropy_exchange(rho, n)[0])
def test_trace_perserving():
    r"""Test trace-perserving method of kraus operators."""
    p1, p2, p3 = 0.1, 0.01, 0.4
    # Test pauli channel
    krauss_ops = initialize_pauli_examples(p1, p2, p3)
    # Dense Krauss
    krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 2)
    assert krauss_obj.is_trace_perserving()
    # Sparse Krauss Operators
    krauss_obj = SparseKraus(krauss_ops, [1, 1], 2, 2)
    assert krauss_obj.is_trace_perserving()

    # Dephrasure
    krauss_ops = initialize_dephrasure_examples(0.1, 0.2)
    krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 3)
    assert krauss_obj.is_trace_perserving()
    # Sparse Krauss Operators
    krauss_obj = SparseKraus(krauss_ops, [1, 1], 2, 3)
    assert krauss_obj.is_trace_perserving()

    # Erasure Channel
    krauss_ops = [
        np.sqrt(1. - p1) * np.array([[1., 0.], [0., 1.], [0., 0.]]),
        np.sqrt(p1) * np.array([[0., 0.], [0., 0.], [1., 0.]]),
        np.sqrt(p1) * np.array([[0., 0.], [0., 0.], [0., 1.]])
    ]
    krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 3)
    assert krauss_obj.is_trace_perserving()

    # Test non-trace-perserving map.
    krauss_ops = [
        np.sqrt(1. - p1) * np.array([[1., 0.], [0., 1.], [0., 0.]]),
        np.array([[0., 0.], [0., 0.], [1., 0.]]),
        np.array([[0., 0.], [0., 0.], [0., 1.]])
    ]
    krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 3)
    assert not krauss_obj.is_trace_perserving()
def test_channel_method_using_dephrasure_sparse():
    r"""Test the channel from 'krauss.SparseKraus.channel' using dephrasure channel."""
    p, q = 0.2, 0.4
    k_ops = initialize_dephrasure_examples(p, q)
    krauss_ops = np.array(k_ops).copy()
    spkrauss = SparseKraus(k_ops, [1, 1], 2, 3)

    for n in range(1, 4):
        if n != 1:
            krauss_ops = np.kron(np.array(k_ops), krauss_ops)

        # Get random density state.
        rho = np.array(rand_dm_ginibre(2**n).data.todense())
        assert np.all(krauss_ops.shape[2] == rho.shape[0])

        # Multiply each krauss operators individually
        desired = np.zeros((3**n, 3**n), dtype=np.complex128)
        for krauss in krauss_ops:
            temp = krauss.dot(rho).dot(krauss.T.conj())
            desired += temp

        spkrauss.update_kraus_operators(n)
        actual = spkrauss.channel(rho)
        assert_array_almost_equal(actual, desired)
def test_update_krauss_operator_function_sparse():
    r"""Test the function 'SparseKraus._update_nth_krauss_ops'."""
    # Test on dephrasure channel.
    p, q = 0.2, 0.4
    single_krauss_ops = initialize_dephrasure_examples(p, q)
    krauss = SparseKraus(single_krauss_ops, [1, 1], 2, 3)

    # Test increasing to n = 4
    desired = single_krauss_ops.copy()
    for i in range(1, 4):
        desired = np.kron(single_krauss_ops, desired)
    krauss.update_kraus_operators(4)
    assert_array_almost_equal(krauss.nth_kraus_ops.todense(), desired)

    # Test decreasing to n = 3
    krauss_ops = np.kron(single_krauss_ops, single_krauss_ops)
    krauss.update_kraus_operators(2)
    assert_array_almost_equal(krauss.nth_kraus_ops.todense(), krauss_ops)
Exemple #11
0
    def __add__(self, other):
        r"""
        Return channel A + B, where B is applied first then A is applied next.

        Parameters
        ----------
        other : AnalyticQChan
            Quantum channel object.

        Returns
        -------
        AnalyticQChan :
            Returns the channel A + B, where B is applied first then A is applied next.

        Raises
        ------
        TypeError :
            Returns error if the dimension of A does not match the dimension B.

        """
        assert self._type == "kraus", "Only works for kraus operators."
        issparse = self.sparse or other.sparse
        if issparse:
            new_kraus_ops = SparseKraus.serial_concatenate(
                self.kraus, other.kraus)
        else:
            new_kraus_ops = DenseKraus.serial_concatenate(
                self.kraus, other.kraus)
        numb_qubits = [other.numb_qubits[0], self.numb_qubits[1]]
        dim_in = other.dim_in
        dim_out = self.dim_out
        return AnalyticQChan(new_kraus_ops,
                             numb_qubits,
                             dim_in,
                             dim_out,
                             sparse=issparse)
def test_parallel_concatenation():
    r"""Test parallel concatenation of two kraus operators."""
    p, q = 0.1, 0.2
    k1 = initialize_dephrasure_examples(p, q)
    # Second kraus operators of dephasing channel.
    k2 = [
        np.sqrt(q) * np.array([[
            0.,
            1.,
        ], [1., 0.]]),
        np.sqrt(1 - q) * np.array([[1., 0.], [0., -1.]])
    ]

    # Test parallel concatenation method.
    desired = [np.kron(x, y) for x in k1 for y in k2]
    actual = DenseKraus.parallel_concatenate(k1, k2)
    assert_array_almost_equal(desired, actual)

    # Test parallel concatenation method across various inputs.
    actual = SparseKraus.parallel_concatenate(k1, k2)
    assert_array_almost_equal(desired, actual.todense())
    actual = SparseKraus.parallel_concatenate(np.array(k1), k2)
    assert_array_almost_equal(desired, actual.todense())
    actual = SparseKraus.parallel_concatenate(k1, np.array(k2))
    assert_array_almost_equal(desired, actual.todense())
    actual = SparseKraus.parallel_concatenate(np.array(k1), np.array(k2))
    assert_array_almost_equal(desired, actual.todense())

    # Test multiplication operator
    dkraus1 = DenseKraus(k1, [1, 1], 2, 3)
    dkraus2 = DenseKraus(k2, [1, 1], 2, 2)
    dkraus3 = dkraus1 * dkraus2
    assert_array_almost_equal(desired, dkraus3.kraus_ops)

    dkraus1 = SparseKraus(k1, [1, 1], 2, 3)
    dkraus2 = SparseKraus(k2, [1, 1], 2, 2)
    dkraus3 = dkraus1 * dkraus2
    assert_array_almost_equal(desired, dkraus3.kraus_ops.todense())
def test_adjoint_of_entropy_exchange():
    r"""Test the adjoint of the entropy exchange on pauli channel example."""
    p1, p2, p3 = 0.1, 0.01, 0.4
    krauss_ops = initialize_pauli_examples(p1, p2, p3)
    dense_krauss_obj = DenseKraus(krauss_ops, [1, 1], 2, 2)
    sparse_krauss_obj = SparseKraus(krauss_ops, [1, 1], 2, 2)

    # Test on random density matrices
    for n in [1, 2]:
        if n != 1:
            krauss_ops = np.kron(krauss_ops, krauss_ops)
        for _ in range(0, 50):
            dense_krauss_obj.update_kraus_operators(n)
            sparse_krauss_obj.update_kraus_operators(n)

            rho = np.array(rand_dm_ginibre(4**n).data.todense())

            desired = np.zeros((2**n, 2**n), dtype=np.complex128)
            for i, k1 in enumerate(krauss_ops):
                for j, k2 in enumerate(krauss_ops):
                    desired += np.conj(k2.T).dot(k1) * rho[j, i]

            dense_chan = dense_krauss_obj.entropy_exchange(rho, n, True)
            sparse_chan = sparse_krauss_obj.entropy_exchange(rho, n,
                                                             True).todense()

            assert_array_almost_equal(dense_chan, sparse_chan)
            assert_array_almost_equal(desired, dense_chan)
            assert_array_almost_equal(desired, sparse_chan)

    # Test that adjoint maps of compl. positive maps are always unital
    rho = np.eye(4**n)
    desired = np.eye(2**n)
    assert_array_almost_equal(
        desired,
        sparse_krauss_obj.entropy_exchange(rho, n, True).todense())
    assert_array_almost_equal(desired,
                              dense_krauss_obj.entropy_exchange(rho, n, True))
def test_entropy_exchange_dephrasure_channel_sparse():
    p, q = 0.2, 0.4
    single_krauss_ops = initialize_dephrasure_examples(p, q)
    spskrauss = SparseKraus(single_krauss_ops, [1, 1], 2, 3)
    entropy_exchange_helper_assertion(spskrauss, single_krauss_ops)
Exemple #15
0
    def __init__(self,
                 operator,
                 numb_qubits,
                 dim_in,
                 dim_out,
                 orthogonal_krauss=(),
                 sparse=False):
        r"""
        Construct the AnalyticQChan class.

        Parameters
        ----------
        operator : list or np.array
            If provided as a list, then it is assumed that each element of the list are the kraus
            operators for the channel. If "np.array", then it is assumed to be a two-dimensional
            array representing the choi matrix of the channel.
        numb_qubits : tuple
            Tuple (M, N) representing the number of particles/qubits M that are the input of the
            channel and number of particles/qubits N that are the output of the channel.
        dim_in : int
            The dimension of a single hilbert space of the input of the channel.  Normally,
            dealing with qubit input so dimension is two.
        dim_out : int
            The dimension of a single hilbert space of the output of the channel. Normally,
            dealing with qubit output so dimension is two.
        orthogonal_krauss : tuple
            Gives the indices, where the sets of kraus operators are orthogonal with one another
            ie A * B = 0, if A and B are within the same set.
        sparse : bool
            If true, then the kraus operators are modeled as sparse matrices. Cannot be used if
            operator is choi matrix.

        Raises
        ------
        TypeError :
            If operator is not a list or numpy array. The dimensions (dim_in, dim_out,
            numb_qubits) provided must match the shape of the operators.
        AssertionError :
            If operator is numpy array, then it must be a two-dimensional numpy array
            representing a matrix.

        Examples
        --------
        Constructing a simple qubit channel
        >> numb_qubits = [1, 1]  # Accepts one qubits and outputs one qubits.
        >> dim_in = 2  # Dimension of single qubit hilbert space is 2.
        >> dim_out = 2  # Dimension of single qubit hilbert space after the qubit channel is 2.

        >> p = 0.25  # Probability of error
        >> identity = np.sqrt(p) * np.eye(2)
        >> bit_flip = np.sqrt(1 - p) * np.array([[0., 1.], [1., 0.]])
        >> kraus_ops = [identity, bit_flip]
        >> channel = AnalyticQChan(kraus_ops, numb_qubits, dim_in, dim_out, sparse=True)

        Constructing the erasure channel where dimension of output hilbert space is larger.
        >> numb_qubits = [1, 1]
        >> dim_in = 2
        >> dim_out = 3  # Erasure channel adds a extra dimension to output of the channel.

        >> p = 0.25  # Probability of no error
        >> identity = np.sqrt(p) * np.array([[1., 0.], [0., 1.], [0., 0.]])
        >> e1 = np.sqrt(1 - p) * np.array([[0., 0.], [0., 0.], [1., 0.]])
        >> e2 = np.sqrt(1 - p) * np.array([[0., 0.], [0., 0.], [0., 1.]])

        >> kraus_ops = [identity, e1, e2]
        The indices {0} of kraus ops is orthogonal to kraus ops indices to {1, 2}
        >> orthogonal = [1]
        >> channel = AnalyticQChan(kraus_ops, numb_qubits, dim_in, dim_out,
        >>                         orthogonal_krauss=orthogonal)

        """
        if not (isinstance(operator, (list, np.ndarray, SparseArray))):
            # Test if it is kraus operator or choi type.
            raise TypeError(
                "Operator argument should be a list, ndarray or SparseArray.")

        # If Operator is Kraus Type
        self.sparse = sparse
        if (isinstance(operator, (np.ndarray, SparseArray)) and operator.ndim == 3) or \
                isinstance(operator, list):
            self._type = "kraus"
            if sparse:
                self.krauss = SparseKraus(operator, numb_qubits, dim_in,
                                          dim_out)
            else:
                self.krauss = DenseKraus(operator, numb_qubits, dim_in,
                                         dim_out, orthogonal_krauss)
            assert self.krauss.is_trace_perserving(), "Kraus operators provided are not " \
                                                      "trace-perserving."

        # If Operator is Choi Type
        elif isinstance(operator, np.ndarray):
            # Must be completely-positive and trace-perserving.
            assert operator.ndim == 2, "Choi matrix is two-dimensional array. Instead it is %d." \
                                       % operator.ndim
            self._type = "choi"
            self.choi = ChoiQutip(operator, numb_qubits, dim_in, dim_out)