示例#1
0
def test_permutation_operator_dim_2_2_perm_2_1():
    """Test permutation operator when dim is [2, 2] and perm is [2, 1]"""
    res = permutation_operator([2, 2], [2, 1])
    expected_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0],
                             [0, 0, 0, 1]])
    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#2
0
    def __init__(self, q_a: np.ndarray, num_reps: int) -> None:
        """
        Initialize the variables for semidefinite program.

        :param q_a: The fixed SDP variable.
        :param num_reps: The number of parallel repetitions.
        """
        self._q_a = q_a
        self._num_reps = num_reps

        self._sys = list(range(1, 2 * self._num_reps, 2))
        if len(self._sys) == 1:
            self._sys = self._sys[0]

        self._dim = 2 * np.ones((1, 2 * self._num_reps)).astype(int).flatten()
        self._dim = self._dim.tolist()

        # For the dual problem, the following unitary operator is used to
        # permute the subsystems of Alice and Bob which is defined by the
        # action:
        #   π(y1 ⊗ y2 ⊗ x1 ⊗ x2) = y1 ⊗ x1 ⊗ y2 ⊗ x2
        # for all y1 ∈ Y1, y2 ∈ Y2, x1 ∈ X1, x2 ∈ X2.).
        l_1 = list(range(1, self._num_reps + 1))
        l_2 = list(range(self._num_reps + 1, self._num_reps**2 + 1))
        if self._num_reps == 1:
            self._pperm = np.array([1])
        else:
            perm = [*sum(zip(l_1, l_2), ())]
            self._pperm = permutation_operator(2, perm)
示例#3
0
def test_permutation_operator_standard_swap():
    """Generates the standard swap operator on two qubits."""
    expected_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0],
                             [0, 0, 0, 1]])

    res = permutation_operator(2, [2, 1])

    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#4
0
def test_permutation_operator_dim_2_perm_1_3_2():
    """Test permutation operator when dim is 2 and perm is [1, 3, 2]."""
    res = permutation_operator(2, [1, 3, 2])
    expected_res = np.array([[
        [1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 0, 0, 0, 0, 0],
        [0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 1, 0],
        [0, 0, 0, 0, 0, 1, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 1],
    ]])
    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#5
0
def test_permutation_operator_sparse_option():
    """Sparse swap operator on two qutrits."""
    res = permutation_operator(3, [2, 1], False, True)

    expected_res = np.array([
        [1, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 1, 0, 0],
        [0, 1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 1, 0],
        [0, 0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 1],
    ])
    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#6
0
def test_permutation_operator_dim_2_2_perm_1_2():
    """Test permutation operator when dim is [2, 2] and perm is [1, 2]"""
    res = permutation_operator([2, 2], [1, 2])
    expected_res = np.identity(4)
    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#7
0
def antisymmetric_projection(
    dim: int, p_param: int = 2, partial: bool = False
) -> sparse.lil_matrix:
    r"""
    Produce the projection onto the antisymmetric subspace [WikAsym]_.

    Produces the orthogonal projection onto the anti-symmetric subspace of :code:`p_param` copies of
    :code:`dim`-dimensional space. If :code:`partial = True`, then the antisymmetric projection (PA)
    isn't the orthogonal projection itself, but rather a matrix whose columns form an orthonormal
    basis for the symmetric subspace (and hence the PA * PA' is the orthogonal projection onto the
    symmetric subspace.)

    Examples
    ==========

    The :math:`2`-dimensional antisymmetric projection with :math:`p=1` is given as
    :math:`2`-by-:math:`2` identity matrix

    .. math::
        A_{2,1} =
        \begin{pmatrix}
            1 & 0 \\
            0 & 1
        \end{pmatrix}.

    Using :code:`toqito`, we can see this gives the proper result.

    >>> from toqito.perms import antisymmetric_projection
    >>> antisymmetric_projection(2, 1).todense()
    [[1., 0.],
     [0., 1.]]

    When the :math:`p` value is greater than the dimension of the antisymmetric projection, this
    just gives the matrix consisting of all zero entries. For instance, when :math:`d = 2` and
    :math:`p = 3` we have that

    .. math::
        A_{2, 3} =
        \begin{pmatrix}
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0
        \end{pmatrix}.

    Using :code:`toqito` we can see this gives the proper result.

    >>> from toqito.perms import antisymmetric_projection
    >>> antisymmetric_projection(2, 3).todense()
    [[0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0.]]

    References
    ==========
    .. [WikAsym] Wikipedia: Anti-symmetric operator
        https://en.wikipedia.org/wiki/Anti-symmetric_operator

    :param dim: The dimension of the local systems.
    :param p_param: Default value of 2.
    :param partial: Default value of 0.
    :return: Projection onto the antisymmetric subspace.
    """
    dimp = dim ** p_param

    if p_param == 1:
        return sparse.eye(dim)
    # The antisymmetric subspace is empty if `dim < p`.
    if dim < p_param:
        return sparse.lil_matrix((dimp, dimp * (1 - partial)))

    p_list = np.array(list(permutations(np.arange(1, p_param + 1))))
    p_fac = p_list.shape[0]

    anti_proj = sparse.lil_matrix((dimp, dimp))
    for j in range(p_fac):
        anti_proj += perm_sign(p_list[j, :]) * permutation_operator(
            dim * np.ones(p_param), p_list[j, :], False, True
        )
    anti_proj = anti_proj / p_fac

    if partial:
        anti_proj = anti_proj.todense()
        anti_proj = sparse.lil_matrix(linalg.orth(anti_proj))
    return anti_proj
示例#8
0
def symmetric_projection(
        dim: int,
        p_val: int = 2,
        partial: bool = False) -> [np.ndarray, sparse.lil_matrix]:
    r"""
    Produce the projection onto the symmetric subspace [CJKLZ14]_.

    For a complex Euclidean space :math:`\mathcal{X}` and a positive integer :math:`n`, the
    projection onto the symmetric subspace is given by

    .. math::
        \frac{1}{n!} \sum_{\pi \in S_n} W_{\pi}

    where :math:`W_{\pi}` is the swap operator and where :math:`S_n` is the symmetric group on
    :math:`n` symbols.

    Produces the orthogonal projection onto the symmetric subspace of :code:`p_val` copies of
    `dim`-dimensional space. If `partial = True`, then the symmetric projection (PS) isn't the
    orthogonal projection itself, but rather a matrix whose columns form an orthonormal basis for
    the symmetric subspace (and hence the PS * PS' is the orthogonal projection onto the symmetric
    subspace).

    This function was adapted from the QETLAB package.

    Examples
    ==========

    The :math:`2`-dimensional symmetric projection with :math:`p=1` is given as
    :math:`2`-by-:math:`2` identity matrix

    .. math::
        \begin{pmatrix}
            1 & 0 \\
            0 & 1
        \end{pmatrix}.

    Using :code:`toqito`, we can see this gives the proper result.

    >>> from toqito.perms import symmetric_projection
    >>> symmetric_projection(2, 1).todense()
    [[1., 0.],
     [0., 1.]]

    When :math:`d = 2` and :math:`p = 2` we have that

    .. math::
        \begin{pmatrix}
            1 & 0 & 0 & 0 \\
            0 & 1/2 & 1/2 & 0 \\
            0 & 1/2 & 1/2 & 0 \\
            0 & 0 & 0 & 1
        \end{pmatrix}.

    Using :code:`toqito` we can see this gives the proper result.

    >>> from toqito.perms import symmetric_projection
    >>> symmetric_projection(dim=2).todense()
    [[1. , 0. , 0. , 0. ],
     [0. , 0.5, 0.5, 0. ],
     [0. , 0.5, 0.5, 0. ],
     [0. , 0. , 0. , 1. ]]

    References
    ==========
     .. [CJKLZ14] J. Chen, Z. Ji, D. Kribs, N. Lütkenhaus, and B. Zeng.
        "Symmetric extension of two-qubit states".
        Physical Review A 90.3 (2014): 032318.
        https://arxiv.org/abs/1310.3530
        E-print: arXiv:1310.3530 [quant-ph]

    :param dim: The dimension of the local systems.
    :param p_val: Default value of 2.
    :param partial: Default value of 0.
    :return: Projection onto the symmetric subspace.
    """
    dimp = dim**p_val

    if p_val == 1:
        return np.eye(dim)

    p_list = np.array(list(permutations(np.arange(1, p_val + 1))))
    p_fac = np.math.factorial(p_val)
    sym_proj = np.zeros((dimp, dimp))

    for j in range(p_fac):
        sym_proj += permutation_operator(dim * np.ones(p_val), p_list[j, :],
                                         False, True)
    sym_proj = sym_proj / p_fac

    if partial:
        sym_proj = linalg.orth(sym_proj)
    return sym_proj
示例#9
0
def optimal_clone(
    states: List[np.ndarray],
    probs: List[float],
    num_reps: int = 1,
    strategy: bool = False,
) -> Union[float, np.ndarray]:
    r"""
    Compute probability of counterfeiting quantum money [MVW12]_.

    The primal problem for the :math:`n`-fold parallel repetition is given as follows:

    .. math::
        \begin{equation}
            \begin{aligned}
                \text{maximize:} \quad &
                \langle W_{\pi} \left(Q^{\otimes n} \right) W_{\pi}^*, X \rangle \\
                \text{subject to:} \quad & \text{Tr}_{\mathcal{Y}^{\otimes n}
                                           \otimes \mathcal{Z}^{\otimes n}}(X)
                                           = \mathbb{I}_{\mathcal{X}^{\otimes
                                           n}},\\
                                           & X \in \text{Pos}(
                                           \mathcal{Y}^{\otimes n}
                                           \otimes \mathcal{Z}^{\otimes n}
                                           \otimes \mathcal{X}^{\otimes n})
            \end{aligned}
        \end{equation}

    The dual problem for the :math:`n`-fold parallel repetition is given as follows:

    .. math::
        \begin{equation}
            \begin{aligned}
                \text{minimize:} \quad & \text{Tr}(Y) \\
                \text{subject to:} \quad & \mathbb{I}_{\mathcal{Y}^{\otimes n}
                \otimes \mathcal{Z}^{\otimes n}} \otimes Y \geq W_{\pi}
                \left( Q^{\otimes n} \right) W_{\pi}^*, \\
                & Y \in \text{Herm} \left(\mathcal{X}^{\otimes n} \right)
            \end{aligned}
        \end{equation}

    Examples
    ==========

    Wiesner's original quantum money scheme [Wies83]_ was shown in [MVW12]_ to have an optimal
    probability of 3/4 for succeeding a counterfeiting attack.

    Specifically, in the single-qubit case, Wiesner's quantum money scheme corresponds to the
    following ensemble:

    .. math::
        \left\{
            \left( \frac{1}{4}, |0\rangle \right),
            \left( \frac{1}{4}, |1\rangle \right),
            \left( \frac{1}{4}, |+\rangle \right),
            \left( \frac{1}{4}, |-\rangle \right)
        \right\},

    which yields the operator

    .. math::
        \begin{equation}
            Q = \frac{1}{4} \left(|000 \rangle \langle 000| + |111 \rangle \langle 111| +
                                  |+++ \rangle + \langle +++| + |--- \rangle \langle ---| \right)
        \end{equation}

    We can see that the optimal value we obtain in solving the SDP is 3/4.

    >>> from toqito.state_opt import optimal_clone
    >>> from toqito.states import basis
    >>> import numpy as np
    >>> e_0, e_1 = basis(2, 0), basis(2, 1)
    >>> e_p = (e_0 + e_1) / np.sqrt(2)
    >>> e_m = (e_0 - e_1) / np.sqrt(2)
    >>>
    >>> states = [e_0, e_1, e_p, e_m]
    >>> probs = [1 / 4, 1 / 4, 1 / 4, 1 / 4]
    >>> wiesner = optimal_clone(states, probs)
    0.749999999967631

    References
    ==========
    .. [MVW12] Abel Molina, Thomas Vidick, and John Watrous.
        "Optimal counterfeiting attacks and generalizations for Wiesner’s
        quantum money."
        Conference on Quantum Computation, Communication, and Cryptography.
        Springer, Berlin, Heidelberg, 2012.
        https://arxiv.org/abs/1202.4010

    .. [Wies83] Stephen Wiesner
        "Conjugate coding."
        ACM Sigact News 15.1 (1983): 78-88.
        https://dl.acm.org/doi/pdf/10.1145/1008908.1008920

    :return: The optimal probability with of counterfeiting quantum money.
    """
    dim = len(states[0])**3

    # Construct the following operator:
    #                                ___               ___
    # Q = ∑_{k=1}^N p_k |ψ_k ⊗ ψ_k ⊗ ψ_k> <ψ_k ⊗ ψ_k ⊗ ψ_k|
    q_a = np.zeros((dim, dim))
    for k, state in enumerate(states):
        q_a += (probs[k] * tensor(state, state, state.conj()) *
                tensor(state, state, state.conj()).conj().T)

    # The system is over:
    # Y_1 ⊗ Z_1 ⊗ X_1, ... , Y_n ⊗ Z_n ⊗ X_n.
    num_spaces = 3

    # In the event of more than a single repetition, one needs to apply a
    # permutation operator to the variables in the SDP to properly align
    # the spaces.
    if num_reps == 1:
        pperm = np.array([1])
    else:
        # The permutation vector `perm` contains elements of the
        # sequence from: https://oeis.org/A023123
        q_a = tensor(q_a, num_reps)
        perm = []
        for i in range(1, num_spaces + 1):
            perm.append(i)
            var = i
            for j in range(1, num_reps):
                perm.append(var + num_spaces * j)
        pperm = permutation_operator(2, perm)

    if strategy:
        return primal_problem(q_a, pperm, num_reps)
    return dual_problem(q_a, pperm, num_reps)
示例#10
0
def symmetric_projection(
    dim: int, p_val: int = 2, partial: bool = False
) -> [np.ndarray, sparse.lil_matrix]:
    r"""
    Produce the projection onto the symmetric subspace.

    For a complex Euclidean space :math:`\mathcal{X}` and a positive integer :math:`n`, the
    projection onto the symmetric subspace is given by

    .. math::
        \frac{1}{n!} \sum_{\pi \in S_n} W_{\pi}

    where :math:`W_{\pi}` is the swap operator and where :math:`` is the .

    Produces the orthogonal projection onto the symmetric subspace of `p`
    copies of `dim`-dimensional space. If `partial = True`, then the symmetric
    projection (PS) isn't the orthogonal projection itself, but rather a matrix
    whose columns form an orthonormal basis for the symmetric subspace (and
    hence the PS * PS' is the orthogonal projection onto the symmetric
    subspace.)

    Examples
    ==========

    The :math:`2`-dimensional symmetric projection with :math:`p=1` is given as
    :math:`2`-by-:math:`2` identity matrix

    .. math::
        \begin{pmatrix}
            1 & 0 \\
            0 & 1
        \end{pmatrix}.

    Using `toqito`, we can see this gives the proper result.

    >>> from toqito.perms import symmetric_projection
    >>> symmetric_projection(2, 1).todense()
    [[1., 0.],
     [0., 1.]]

    When :math:`d = 2` and :math:`p = 2` we have that

    .. math::
        \begin{pmatrix}
            1 & 0 & 0 & 0 \\
            0 & 1/2 & 1/2 & 0 \\
            0 & 1/2 & 1/2 & 0 \\
            0 & 0 & 0 & 1
        \end{pmatrix}.

    Using `toqito` we can see this gives the proper result.

    >>> from toqito.perms import symmetric_projection
    >>> symmetric_projection(dim=2).todense()
    [[1. , 0. , 0. , 0. ],
     [0. , 0.5, 0.5, 0. ],
     [0. , 0.5, 0.5, 0. ],
     [0. , 0. , 0. , 1. ]]

    :param dim: The dimension of the local systems.
    :param p_val: Default value of 2.
    :param partial: Default value of 0.
    :return: Projection onto the symmetric subspace.
    """
    dimp = dim ** p_val

    if p_val == 1:
        return sparse.eye(dim)

    p_list = np.array(list(permutations(np.arange(1, p_val + 1))))
    p_fac = np.math.factorial(p_val)
    sym_proj = sparse.lil_matrix((dimp, dimp))

    for j in range(p_fac):
        sym_proj += permutation_operator(
            dim * np.ones(p_val), p_list[j, :], False, True
        )
    sym_proj = sym_proj / p_fac

    if partial:
        sym_proj = sym_proj.todense()
        sym_proj = sparse.lil_matrix(linalg.orth(sym_proj))
    return sym_proj
示例#11
0
def werner(dim: int, alpha: Union[float, List[float]]) -> np.ndarray:
    r"""
    Produce a Werner state [Wer89]_.

    A Werner state is a state of the following form

    .. math::

        \begin{equation}
            \rho_{\alpha} = \frac{1}{d^2 - d\alpha} \left(\mathbb{I} \otimes
            \mathbb{I} - \alpha S \right) \in \mathbb{C}^d \otimes \mathbb{C}^d
        \end{equation}

    Yields a Werner state with parameter :code:`alpha` acting on :code:`(dim * dim)`- dimensional
    space. More specifically, :math:`\rho` is the density operator defined by
    :math:`(\mathbb{I} - `alpha` S)` (normalized to have trace 1), where :math:`\mathbb{I}` is the
    density operator and :math:`S` is the operator that swaps two copies of :code:`dim`-dimensional
    space (see swap and swap_operator for example).

    If :code:`alpha` is a vector with :math:`p!-1` entries, for some integer :math:`p > 1`, then a
    multipartite Werner state is returned. This multipartite Werner state is the normalization of
    I - `alpha(1)*P(2)` - ... - `alpha(p!-1)*P(p!)`, where P(i) is the operator that permutes p
    subsystems according to the i-th permutation when they are written in lexicographical order
    (for example, the lexicographical ordering when p = 3 is:
    `[1, 2, 3], [1, 3, 2], [2, 1,3], [2, 3, 1], [3, 1, 2], [3, 2, 1],`

    so P(4) in this case equals permutation_operator(dim, [2, 3, 1]).

    Examples
    ==========

    Computing the qutrit Werner state with :math:`\alpha = 1/2` can be done in :code:`toqito` as

    >>> from toqito.states import werner
    >>> werner(3, 1 / 2)
    [[ 0.06666667,  0.        ,  0.        ,  0.        ,  0.        ,
       0.        ,  0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.13333333,  0.        , -0.06666667,  0.        ,
       0.        ,  0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.13333333,  0.        ,  0.        ,
       0.        , -0.06666667,  0.        ,  0.        ],
     [ 0.        , -0.06666667,  0.        ,  0.13333333,  0.        ,
       0.        ,  0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.06666667,
       0.        ,  0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
       0.13333333,  0.        , -0.06666667,  0.        ],
     [ 0.        ,  0.        , -0.06666667,  0.        ,  0.        ,
       0.        ,  0.13333333,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
       -0.06666667,  0.        ,  0.13333333,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
       0.        ,  0.        ,  0.        ,  0.06666667]]

    We may also compute multipartite Werner states in :code:`toqito` as well.

    >>> from toqito.states import werner
    >>> werner(2, [0.01, 0.02, 0.03, 0.04, 0.05])
    [[ 0.12179487,  0.        ,  0.        ,  0.        ,  0.        ,
       0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.12820513,  0.        ,  0.        , -0.00641026,
       0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.12179487,  0.        ,  0.        ,
       0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.12820513,  0.        ,
       0.        , -0.00641026,  0.        ],
     [ 0.        , -0.00641026,  0.        ,  0.        ,  0.12820513,
       0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
       0.12179487,  0.        ,  0.        ],
     [ 0.        ,  0.        ,  0.        , -0.00641026,  0.        ,
       0.        ,  0.12820513,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
       0.        ,  0.        ,  0.12179487]]

    References
    ==========
    .. [Wer89] R. F. Werner.
        Quantum states with Einstein-Podolsky-Rosen correlations admitting a
        hidden-variable model. Phys. Rev. A, 40(8):4277–4281. 1989

    :param dim: The dimension of the Werner state.
    :param alpha: Parameter to specify Werner state.
    :return: A Werner state of dimension :code:`dim`.
    """
    # The total number of permutation operators.
    if isinstance(alpha, float):
        n_fac = 2
    else:
        n_fac = len(alpha) + 1

    # Multipartite Werner state.
    if n_fac > 2:
        # Compute the number of parties from `len(alpha)`.
        n_var = n_fac
        # We won't actually go all the way to `n_fac`.
        for i in range(2, n_fac):
            n_var = n_var // i
            if n_var == i + 1:
                break
            if n_var < i:
                raise ValueError(
                    "InvalidAlpha: The `alpha` vector must contain"
                    " p!-1 entries for some integer p > 1.")

        # Done error checking and computing the number of parties -- now
        # compute the Werner state.
        perms = list(itertools.permutations(np.arange(n_var)))
        sorted_perms = np.argsort(perms, axis=1) + 1

        for i in range(2, n_fac):
            rho = np.identity(dim**
                              n_var) - alpha[i - 1] * permutation_operator(
                                  dim, sorted_perms[i, :], False, True)
        rho = rho / np.trace(rho)
        return rho
    # Bipartite Werner state.
    return (np.identity(dim**2) -
            alpha * swap_operator(dim, True)) / (dim * (dim - alpha))
def test_permutation_operator_sparse_option():
    """Sparse swap operator on two qutrits."""
    res = permutation_operator(3, [2, 1], False, True)

    np.testing.assert_equal(res[0][0], 1)