def apply_teleportation_channel(rho,dA=2,dR1=2,dR2=2,dB=2):

    '''
    Applies the d-dimensional teleportation channel to the four-qudit state rho_{AR1R2B}.
    The channel measures R1 and R2 in the d-dimensional Bell basis and, based on the
    outcome, applies a 'correction operation' to B. So the output of the channel consists
    only of the systems A and B.

    We obtain quantum teleportation by letting

        rho_{AR1R2B} = psi_{R1} ⊗ Phi_{R2B}^+,

    so that dA=1. This simulates teleportation of the state psi in the system R1 to
    the system B. 

    We obtain entanglement swapping by letting

        rho_{AR1R2B} = Phi_{AR1}^+ ⊗ Phi_{R2B}^+.
    
    The result of the channel is then Phi_{AB}^+
    '''

    X=[matrix_power(discrete_Weyl_X(dB),x) for x in range(dB)]
    Z=[matrix_power(discrete_Weyl_Z(dB),z) for z in range(dB)]
    
    rho_out=np.sum([tensor(eye(dA),dag(Bell_state(dR1,z,x)),Z[z]@X[x])@rho@tensor(eye(dA),Bell_state(dR1,z,x),dag(X[x])@dag(Z[z])) for z in range(dB) for x in range(dB)],0)

    return rho_out
def generate_channel_isometry(K, dimA, dimB):
    '''
    Generates an isometric extension of the
    channel specified by the Kraus operators K. dimA is the dimension of the
    input space of the channel, and dimB is the dimension of the output space
    of the channel. If dimA=dimB, then the function also outputs a unitary
    extension of the channel given by a particular construction.
    '''

    dimE = len(K)

    V = np.sum([tensor(K[i], ket(dimE, i)) for i in range(dimE)], 0)

    if dimA == dimB:
        # In this case, the unitary we generate has dimensions dimA*dimE x dimA*dimE
        U = tensor(V, dag(ket(dimE, 0)))
        states = [V @ ket(dimA, i) for i in range(dimA)]
        for i in range(dimA * dimE - dimA):
            states.append(RandomStateVector(dimA * dimE))

        states_new = gram_schmidt(states, dimA * dimE)

        count = dimA
        for i in range(dimA):
            for j in range(1, dimE):
                U = U + tensor(states_new[count], dag(ket(dimA, i)),
                               dag(ket(dimE, j)))
                count += 1

        return V, np.array(U)
    else:
        return V
예제 #3
0
def generate_nQubit_Pauli(indices):
    '''
    Generates a tensor product of Pauli operators for n qubits. indices is a list
    of indices i specifying the Pauli operator for each tensor factor. i=0 is the identity, i=1 is sigma_x,
    i=2 is sigma_y, and i=3 is sigma_z.
    '''

    Id = eye(2)
    Sx = np.array([[0, 1], [1, 0]])
    Sy = np.array([[0, -1j], [1j, 0]])
    Sz = np.array([[1, 0], [0, -1]])

    out = 1

    for index in indices:
        if index == 0:
            out = tensor(out, Id)
        elif index == 1:
            out = tensor(out, Sx)
        elif index == 2:
            out = tensor(out, Sy)
        elif index == 3:
            out = tensor(out, Sz)

    return out
예제 #4
0
def CZ_ij(i, j, n):
    '''
    CZ gate on qubits i and j, i being the control and j being the target.
    The total number of qubits is n. (Note that for the CZ gate it does matter
    which qubit is the control and which qubit is the target.)
    '''

    dims = 2 * np.ones(n)
    dims = dims.astype(int)

    indices = np.linspace(1, n, n)
    indices_diff = np.setdiff1d(indices, [i, j])

    perm_arrange = np.append(np.array([i, j]), indices_diff)
    perm_rearrange = np.zeros(n)

    for i in range(n):
        perm_rearrange[i] = np.argwhere(perm_arrange == i + 1)[0][0] + 1

    perm_rearrange = perm_rearrange.astype(int)

    Sz = np.array([[1, 0], [0, -1]])
    CZ = tensor(ket(2, 0) @ dag(ket(2, 0)), eye(2)) + tensor(
        ket(2, 1) @ dag(ket(2, 1)), Sz)

    out_temp = tensor(CZ, [eye(2), n - 2])

    out = syspermute(out_temp, perm_rearrange, dims)

    return out
예제 #5
0
def RandomStateVector(dim, rank=None):
    '''
    Generates a random pure state.

    For multipartite states, dim should be a list of dimensions for each
    subsystem. In this case, the rank variable is for the Schmidt rank. To specify
    the Schmidt rank, there has to be a bipartition of the systems, so that dim
    has only two elements.
    '''

    if rank == None:
        if type(dim) == list:
            dim = np.prod(dim)

        # Generate the real and imaginary parts of the components using numbers
        # sampled from the standard normal distribution (normal distribution with
        # mean zero and variance 1).
        psi = dag(
            np.array([np.random.randn(dim)]) +
            1j * np.array([np.random.randn(dim)]))

        psi = psi / norm(psi)

        return psi
    else:
        dimA = dim[0]
        dimB = dim[1]

        if rank == None:
            rank = max([dimA, dimB])
        else:
            k = rank

        psi_k = MaxEnt_state(k, density_matrix=False, normalized=False)
        a = dag(
            np.array([np.random.rand(dimA * k)]) +
            1j * np.array([np.random.rand(dimA * k)]))
        b = dag(
            np.array([np.random.rand(dimB * k)]) +
            1j * np.array([np.random.rand(dimB * k)]))

        psi_init = syspermute(tensor(a, b), [1, 3, 2, 4], [k, dimA, k, dimB])

        psi = tensor(dag(psi_k), eye(dimA * dimB)) @ psi_init

        psi = psi / norm(psi)

        return psi
예제 #6
0
def Kraus_representation(P, dimA, dimB):
    '''
    Takes a Choi representation P of a channel and returns its Kraus representation.
    
    The Choi representation is defined with the channel acting on the second half of
    the maximally entangled vector.
    '''

    D, U = eig(P)

    U_cols = U.shape[1]

    # Need to check if the matrix U generated by eig is unitary (up to numerical precision)
    check1 = np.allclose(eye(dimA * dimB), U @ dag(U))
    check2 = np.allclose(eye(dimA * dimB), dag(U) @ U)

    if check1 and check2:
        U = np.array(U)

    # If U is not unitary, use Gram-Schmidt to make it unitary (i.e., make the columns of U orthonormal)
    else:
        C = gram_schmidt([U[:, i] for i in range(U_cols)], dimA * dimB)
        U = np.sum([tensor(dag(ket(U_cols, i)), C[i]) for i in range(U_cols)],
                   0)
        #print(U)
    K = []

    for i in range(U_cols):
        Col = U[:, i]
        K_tmp = np.array(np.sqrt(D[i]) * Col.reshape([dimA, dimB]))
        K.append(K_tmp.transpose())

    return K
def post_teleportation_chain_fidelity(rho, n, dA=2):
    '''
    Calculates the fidelity of the output state of the teleportation chain channel with
    respect to the maximally entangled state on AB. The input state rho is of the
    form

        rho_{A R11 R12 R21 R22 ... Rn1 Rn2 B}.

    We assume that A, B, and all R systems have the same dimension.
    '''

    f = 0

    indices = list(itertools.product(*[range(dA)] * n))

    for z_indices in indices:
        for x_indices in indices:

            z_sum = np.mod(sum(z_indices), dA)
            x_sum = np.mod(sum(x_indices), dA)

            Bell_tot = Bell_state(dA, z_sum, x_sum, density_matrix=True)

            for j in range(n):
                Bell_tot = tensor(
                    Bell_tot,
                    Bell_state(dA,
                               z_indices[j],
                               x_indices[j],
                               density_matrix=True))

            f += fidelity(rho, Bell_tot)

    return f
예제 #8
0
def jordan_wigner(n):
    '''
    Generates the Jordan-Wigner representation of the fermionic creation, annihilation,
    and Majorana operators for an n-mode system.

    The convention for the Majorana operators is as follows:

        c_j=aj^{dag}+aj
        c_{n+j}=i(aj^{dag}-aj)

    '''

    s = ket(2, 0) @ dag(ket(2, 1))

    S = su_generators(2)

    a = {}  # Dictionary for the annihilation operators
    c = {}  # Dictionary for the Majorana operators

    for j in range(1, n + 1):
        a[j] = tensor([S[3], j - 1], s, [S[0], n - j])
        c[j] = dag(a[j]) + a[j]
        c[n + j] = 1j * (dag(a[j]) - a[j])

    return a, c
예제 #9
0
def graph_state(A_G,n,density_matrix=False,return_CZ=False,alt=True):

    '''
    Generates the graph state corresponding to the undirected graph G with n vertices.
    A_G denotes the adjacency matrix of G, which for an undirected graph is a binary
    symmetric matrix indicating which vertices are connected.

    See the following book chapter for a review:

        ``Cluster States'' in Compedium of Quantum Physics, pp. 96-105, by H. J. Briegel.

    '''

    plus=(1/np.sqrt(2))*(ket(2,0)+ket(2,1))

    plus_n=tensor([plus,n])

    G=eye(2**n)

    for i in range(n):
        for j in range(i,n):
            if A_G[i,j]==1:
                G=G@CZ_ij(i+1,j+1,n)

    if density_matrix:
        plus_n=plus_n@dag(plus_n)
        if return_CZ:
            return G@plus_n@dag(G),G
        else:
            return G@plus_n@dag(G)
    else:
        if return_CZ:
            return G@plus_n,G
        else:
            return G@plus_n
def post_graph_state_dist_fidelity(A_G,n,rho):

    '''
    Finds the fidelity of the output state of the graph state distribution channel
    with respect to the graph state |G>, where A_G is the adjacency matrix of the 
    graph G and n is the number of vertices of G.
    '''

    X_n=list(itertools.product(*[range(2)]*n))

    f=0

    for x_n in X_n:

        x_n=np.array([x_n]).T  # Turn x_n into a column vector matrix

        z_n=A_G*x_n
        z_n=np.mod(z_n,2)

        Bell=Bell_state(2,z_n[0,0],x_n[0,0],density_matrix=True)
    
        for k in range(1,n):
            Bell=tensor(Bell,Bell_state(2,z_n[k,0],x_n[k,0],density_matrix=True))
        
        Bell=syspermute(Bell,list(range(1,2*n,2))+list(range(2,2*n+1,2)),2*np.ones(2*n,dtype=int))

        f+=fidelity(rho,Bell)

    return f
예제 #11
0
def coherent_state_fermi(A,rep='JW',density_matrix=False):

    '''
    Generates the fermionic coherent state vector for n modes, where A is a complex
    anti-symmetric n x n matrix. The matrix A should be at least 2 x 2 -- for one mode,
    the coherent state is the just the vacuum.

    The definition being used here comes from

        A. Perelomov. Generalized Coherent States and Their Applications (Sec. 9.2)

    '''

    n=np.shape(A)[0]  # Number of modes

    a,_=jordan_wigner(n)

    At=np.zeros((2**n,2**n),dtype=complex)

    N=np.linalg.det(eye(n)+A@dag(A))**(1/4)

    for i in range(1,n+1):
        for j in range(1,n+1):
            At=At+(-1/2)*A[i-1,j-1]*dag(a[j])@dag(a[i])

    vac=tensor([ket(2,0),n])

    if not density_matrix:
        return (1/N)*expm(At)@vac
    else:
        coh=(1/N)*expm(At)@vac
        return coh@dag(coh)
예제 #12
0
파일: S_i.py 프로젝트: sumeetkhatri/QuTIpy
def S_i(i, n):
    '''
    Generates the matrix for the S gate applied to the ith qubit.
    n is the total number of qubits. The S gate is defined as:

        S:=[[1 0],
            [0 1j]]

    It is one of the generators of the Clifford group.
    '''

    dims = 2 * np.ones(n)
    dims = dims.astype(int)
    indices = np.linspace(1, n, n)
    indices_diff = np.setdiff1d(indices, i)
    perm_arrange = np.append(np.array([i]), indices_diff)
    perm_rearrange = np.zeros(n)

    for i in range(n):
        perm_rearrange[i] = np.argwhere(perm_arrange == i + 1)[0][0] + 1

    perm_rearrange = perm_rearrange.astype(int)
    S = np.array([[1, 0], [0, 1j]])
    out_temp = tensor(S, [eye(2), n - 1])
    out = syspermute(out_temp, perm_rearrange, dims)

    return out
def apply_teleportation_chain_channel(rho, n, dA=2, dR=2, dB=2):
    '''
    Applies the teleportation chain channel to the state rho, which is of the form

        rho_{A R11 R12 R21 R22 ... Rn1 Rn2 B}.
    
    The channel is defined by performing a d-dimensional Bell basis measurement
    independently on the system pairs Ri1 and Ri2, for 1 <= i <= n; based on the
    outcome, a 'correction operation' is applied to B. The system pairs Ri1 and Ri2
    can be thought of as 'repeaters'. Note that n>=1. For n=1, we get the same channel
    as in apply_teleportation_channel().

    We obtain teleportation by letting dA=1 and letting

        rho_{A R11 R12 R21 R22 ... Rn1 Rn2 B} = psi_{R11} ⊗ Phi_{R12 R21}^+ ⊗ ... ⊗ Phi_{Rn2 B}^+,
    
    so that we have teleportation of the state psi in the system R11 to the system B. 

    We obtain a chain of entanglement swaps by letting

        rho_{A R11 R12 R21 R22 ... Rn1 Rn2 B} = Phi_{A R11}^+ ⊗ Phi_{R12 R21}^+ ⊗ ... ⊗ Phi_{Rn2 B}^+.
    '''

    indices = list(itertools.product(*[range(dB)] * n))

    rho_out = np.array(np.zeros((dA * dB, dA * dB), dtype=complex))

    for z_indices in indices:
        for x_indices in indices:

            Bell_zx = Bell_state(dB, z_indices[0], x_indices[0])
            for j in range(1, n):
                Bell_zx = tensor(Bell_zx,
                                 Bell_state(dB, z_indices[j], x_indices[j]))

            z_sum = np.mod(sum(z_indices), dB)
            x_sum = np.mod(sum(x_indices), dB)

            W_zx = matrix_power(discrete_Weyl_Z(dB), z_sum) @ matrix_power(
                discrete_Weyl_X(dB), x_sum)

            rho_out = rho_out + tensor(eye(dA), dag(
                Bell_zx), W_zx) @ rho @ tensor(eye(dA), Bell_zx, dag(W_zx))

    return rho_out
def post_ent_swap_GHZ_fidelity(rho):
    '''
    Finds the fidelity of the output state of the apply_ent_swap_GHZ_channel() function
    with respect to the three-party GHZ state.
    '''

    Phi = [Bell_state(2, z, 0, density_matrix=True) for z in range(2)]

    return sum([fidelity(tensor(Phi[z], Phi[z]), rho) for z in range(2)])
def entanglement_distillation(rho1,
                              rho2,
                              outcome=1,
                              twirl_after=False,
                              normalize=False):
    '''
    Applies a particular entanglement distillation channel to the two two-qubit states
    rho1 and rho2. [PRL 76, 722 (1996)]

    The channel is probabilistic. If the variable outcome=1, then the function returns
    the two-qubit state conditioned on the success of the distillation protocol.
    '''

    CNOT = CNOT_ij(1, 2, 2)
    proj0 = ket(2, 0) @ dag(ket(2, 0))
    proj1 = ket(2, 1) @ dag(ket(2, 1))

    P0 = tensor(eye(2), proj0, eye(2), proj0)
    P1 = tensor(eye(2), proj1, eye(2), proj1)
    P2 = eye(16) - P0 - P1
    C = tensor(CNOT, CNOT)
    K0 = P0 * C
    K1 = P1 * C
    K2 = P2 * C

    rho_in = syspermute(tensor(rho1, rho2), [1, 3, 2, 4],
                        [2, 2, 2, 2])  # rho_in==rho_{A1A2B1B2}

    if outcome == 1:
        # rho_out is unnormalized. The trace of rho_out is equal to the success probability.
        rho_out = partial_trace(K0 @ rho_in @ dag(K0) + K1 @ rho_in @ dag(K1),
                                [2, 4], [2, 2, 2, 2])
        if twirl_after:
            rho_out = isotropic_twirl_state(rho_out, 2)
        if normalize:
            rho_out = rho_out / Tr(rho_out)

    elif outcome == 0:
        # rho_out is unnormalized. The trace of rho_out is equal to the failure probability.
        rho_out = partial_trace(K2 @ rho_in @ dag(K2), [2, 4], [2, 2, 2, 2])
        if normalize:
            rho_out = rho_out / Tr(rho_out)

    return rho_out
예제 #16
0
def Bell_state(d, z, x, density_matrix=False):
    '''
    Generates a d-dimensional Bell state with 0 <= z,x <= d-1. These are defined as

    |Phi_{z,x}> = (Z(z)X(x) ⊗ I)|Phi^+>

    '''

    Bell = MaxEnt_state(d, density_matrix=density_matrix)

    W_zx = matrix_power(discrete_Weyl_Z(d), z) @ matrix_power(
        discrete_Weyl_X(d), x)

    if density_matrix:
        out = tensor(W_zx, eye(d)) @ Bell @ tensor(dag(W_zx), eye(d))
        return out
    else:
        out = tensor(W_zx, eye(d)) @ Bell
        return out
예제 #17
0
    def objfunc(x):

        Re = np.array(x[0:dim**3])
        Im = np.array(x[dim**3:])

        psi = np.array([Re + 1j * Im]).T
        psi = psi / norm(psi)

        p = []
        S = []

        for j in range(dim**2):
            R = tensor(dag(ket(dim**2, j)), eye(dim)) @ (
                psi @ dag(psi)) @ tensor(ket(dim**2, j), eye(dim))
            p.append(Tr(R))
            rho = R / Tr(R)
            rho_out = apply_channel(K, rho)
            S.append(rho_out)

        return -np.real(Holevo_inf_ensemble(p, S))
예제 #18
0
def mutual_information(rhoAB, dimA, dimB):
    '''
    Computes the mutual information of the bipartite state rhoAB, defined as

    I(A;B)_rho=D(rhoAB||rhoA\otimes rhoB)
    '''

    rhoA = partial_trace(rhoAB, [2], [dimA, dimB])
    rhoB = partial_trace(rhoAB, [1], [dimA, dimB])

    return relative_entropy(rhoAB, tensor(rhoA, rhoB))
예제 #19
0
def generate_nQubit_Pauli_X(indices):
    '''
    Generates a tensor product of Pauli-X operators for n qubits. indices is
    a list of bits.
    '''

    Id = eye(2)
    Sx = np.array([[0, 1], [1, 0]])

    out = 1

    for index in indices:
        if index == 0:
            out = tensor(out, Id)
        elif index == 1:
            out = tensor(out, Sx)
        else:
            return ('Error: Indices must be bits, either 0 or 1!')

    return out
def apply_ent_swap_GHZ_channel(rho):

    '''
    Applies the channel that takes two copies of a maximally entangled state and outputs
    a three-party GHZ state. The input state rho is of the form

        rho_{A R1 R2 B}.

    A CNOT is applied to R1 and R2, followed by a measurement in the standard basis on
    R2, followed by a correction operation on B based on the outcome of the measurement.

    Currently only works for qubits.
    '''

    C=CNOT_ij(2,3,4)

    X=[matrix_power(discrete_Weyl_X(2),x) for x in range(2)]
    
    rho_out=np.sum([tensor(eye(4),dag(ket(2,x)),eye(2))@C@tensor(eye(8),X[x])@rho@tensor(eye(8),X[x])@dag(C)@tensor(eye(4),ket(2,x),eye(2)) for x in range(2)],0)

    return rho_out
예제 #21
0
def Natural_representation(K):

    '''
    Calculates the natural representation of the channel (in the standard basis)
    given by the Kraus operators in K. In terms of the Kraus operators, the natural
    representation of the channel in the standard basis is given by

    N=sum_i K_i ⊗ conj(K_i),

    where the sum is over the Kraus operators K_i in K.
    '''

    return np.sum([tensor(k,np.conjugate(k)) for k in K],0)
예제 #22
0
    def K(j, x):

        # j is between 1 and n, denoting the pair of R systems. x is either 0 or 1.
        # For each j, the qubit indices are 2*j and 2*j+1 for the pair Rj1 and Rj2

        Mx = tensor(eye(2), eye(2**(2 * j - 2)), eye(2),
                    ket(2, x) @ dag(ket(2, x)), eye(2**(2 * (n - j))), eye(2))

        C = CNOT_ij(2 * j, 2 * j + 1, 2 * n + 2)

        X = 1j * Rx_i(2 * j + 2, np.pi, 2 * n + 2)

        return Mx @ C @ matrix_power(X, x)
예제 #23
0
def post_teleportation_fidelity(rho, dA=2):
    '''
    Calculates the fidelity of the output state of the teleportation channel with
    respect to the maximally entangled state on AB. The input state rho is of the
    form rho_{AR1R2B}. We assume that A, R1, R2, B all have the same dimension.
    '''

    return sum([
        fidelity(
            rho,
            tensor(Bell_state(dA, z, x, density_matrix=True),
                   Bell_state(dA, z, x, density_matrix=True)))
        for z in range(dA) for x in range(dA)
    ])
def apply_graph_state_dist_channel(A_G,n,rho):

    '''
    Applies the graph state distribution channel to the 2n-partite state rho, where
    n is the number of vertices in the graph G with adjacency matrix A_G (binary 
    symmetric matrix).

    rho is a state of the form rho_{A_1...A_n R_1...R_n}

    The local graph state operations and measurements are applied to the qubits
    R_1,...,R_n, and the correction operations are applied to A_1,...,A_n.

    When rho is a state of the form

        Phi_{A_1 R_1}^+ ⊗ Phi_{A_2 R_2}^+ ⊗ ... ⊗ Phi_{A_n R_n}^+,

    then the output state on A_1,...,A_n is the graph state |G>. 
    '''

    indices=list(itertools.product(*[range(2)]*n))

    H=(1/np.sqrt(2))*np.array([[1,1],[1,-1]])
    Hn=tensor([H,n])

    ket_G=graph_state(A_G,n)

    rho_out=np.array(np.zeros((2**n,2**n),dtype=complex))

    for index in indices:
        Zx=generate_nQubit_Pauli_Z(index)

        Gx=Zx*ket_G
        rho_out=rho_out+tensor(Zx,dag(Gx))@rho@tensor(Zx,Gx)

    
    return rho_out
예제 #25
0
def Petz_Renyi_mut_inf_state(rhoAB, dimA, dimB, alpha, opt=True):
    '''
    Computes the Petz-Renyi mutual information of the bipartite state
    rhoAB for 0<=alpha<=1.

    TO DO: Figure out how to do the computation with optimization over sigmaB.
    '''

    rhoA = partial_trace(rhoAB, [2], [dimA, dimB])
    rhoB = partial_trace(rhoAB, [1], [dimA, dimB])

    if opt == False:
        return Petz_Renyi_rel_ent(rhoAB, tensor(rhoA, rhoB), alpha)
    else:
        return None
예제 #26
0
def generate_nQudit_X(d,indices):

    '''
    Generates a tensor product of discrete Weyl-X operators. indices is a 
    list of dits (i.e., each element of the list is a number between 0 and
    d-1).
    '''

    X=discrete_Weyl_X(d)

    out=1

    for index in indices:
        out=tensor(out,matrix_power(X,index))

    return out
예제 #27
0
def depolarizing_channel_n_uses(p,n,rho,m):


    '''
    Generates the output state corresponding to the depolarizing channel
    applied to each one of n systems in the joint state rho. p is the 
    depolarizing probability as defined in the function "depolarizing_channel"
    above.

    If rho contains m>n systems, then the first m-n systems are left alone.
    '''

    dims=2*np.ones(m).astype(int)

    rho_out=np.zeros((2**m,2**m))

    for k in range(n+1):
        indices=list(itertools.combinations(range(1,n+1),k))

        #print k,indices

        for index in indices:
            index=list(index)

            index=np.array(index)+(m-n)
            index=list(index.astype(int))

            index_diff=np.setdiff1d(range(1,m+1),index)

            perm_arrange=np.append(index,index_diff).astype(int)
            perm_rearrange=np.zeros(m)

            for i in range(m):
                perm_rearrange[i]=np.argwhere(perm_arrange==i+1)[0][0]+1

            perm_rearrange=perm_rearrange.astype(int)

            mix=matrix_power(eye(2**k)/2,k)

            rho_part=partial_trace(rho,index,dims)

            rho_out=rho_out+(4*p/3.)**k*(1-(4*p/3.))**(n-k)*syspermute(tensor(mix,rho_part),perm_rearrange,dims)

    return rho_out
예제 #28
0
파일: SWAP.py 프로젝트: sumeetkhatri/QuTIpy
def SWAP(sys, dim):
    '''
    Generates a swap matrix between the pair of systems in sys. dim is a list
    of the dimensions of the subsystems.

    For example, SWAP([1,2],[2,2]) generates the two-qubit swap matrix.
    '''

    dim_total = np.product(dim)

    n = len(dim)
    sys_rest = list(np.setdiff1d(range(1, n + 1), sys))
    perm = sys + sys_rest
    p = {}

    for i in range(1, n + 1):
        p[i] = perm[i - 1]

    p2 = {v: k for k, v in p.items()}

    perm_rearrange = list(p2.values())

    dim1 = dim[sys[0] - 1]  # Dimension of the first subsystem to be swapped
    dim2 = dim[sys[1] - 1]  # Dimension of the second subsystem to be swapped

    dim_rest = int(float(dim_total) / float(dim1 * dim2))

    G1 = np.matrix(np.sum([ket(dim1, [i, i]) for i in range(dim1)], 0))
    G2 = np.matrix(np.sum([ket(dim2, [i, i]) for i in range(dim2)], 0))

    G = G1 @ dag(G2)

    S = partial_transpose(G, [2], [(dim1, dim2), (dim1, dim2)])

    P = tensor(S, eye(dim_rest))

    p_alt = list(np.array(list(p.values())) - 1)

    P = syspermute(P, perm_rearrange, list(np.array(dim)[p_alt]))

    return P
예제 #29
0
파일: H_i.py 프로젝트: sumeetkhatri/QuTIpy
def H_i(i, n):
    '''
    Generates the matrix for the Hadamard gate applied to the ith qubit.
    n is the total number of qubits.
    '''

    dims = 2 * np.ones(n)
    dims = dims.astype(int)
    indices = np.linspace(1, n, n)
    indices_diff = np.setdiff1d(indices, i)
    perm_arrange = np.append(np.array([i]), indices_diff)
    perm_rearrange = np.zeros(n)

    for i in range(n):
        perm_rearrange[i] = np.argwhere(perm_arrange == i + 1)[0][0] + 1

    perm_rearrange = perm_rearrange.astype(int)
    H = (1 / np.sqrt(2)) * np.array([[1, 1], [1, -1]])
    out_temp = tensor(H, [eye(2), n - 1])
    out = syspermute(out_temp, perm_rearrange, dims)

    return out
예제 #30
0
def n_channel_uses(K, n):
    '''
    Given the Kraus operators K of a channel, this function generates the
    Kraus operators corresponding to the n-fold tensor power of the channel.
    dimA is the dimension of the input space, and dimB the dimension of the
    output space.
    '''

    r = len(K)  # Number of Kraus operators

    combs = list(itertools.product(*[range(r)] * n))

    K_n = []

    for comb in combs:
        #print comb
        tmp = 1
        for i in range(n):
            tmp = tensor(tmp, K[comb[i]])
        K_n.append(tmp)

    return K_n