Exemplo n.º 1
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
Exemplo n.º 2
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 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
Exemplo n.º 4
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)
Exemplo n.º 5
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)
Exemplo n.º 6
0
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
Exemplo n.º 7
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
Exemplo n.º 8
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
Exemplo n.º 9
0
def Werner_state(p, d, alt_param=False):
    '''
    Generates the Werner state with parameter p on two d-dimensional systems.
    The state is defined as

        rho_W=p*singlet+(1-p)*singlet_perp,
        
    where singlet is the state defined as (1/(d^2-d))*(eye(d^2)-SWAP) and
    singlet_perp is the state defined as (1/(d^2+d))*(eye(d^2)+SWAP),
    where SWAP is the swap operator between two d-dimensional systems. The parameter
    p is between 0 and 1.
    
    Werner states are invariant under U ⊗ U for every unitary U.

    If alt_param=True, then the function returns a different parameterization of 
    the Werner state in which the parameter p is between -1 and 1, and

        rho_W=(1/(d^2-d*p))*(eye(d^2)-p*SWAP)

    '''

    if alt_param:
        F = SWAP([1, 2], [d, d])
        return (1 / (d**2 - d * p)) * (eye(d**2) - p * F)
    else:
        singlet, singlet_perp = singlet_state(d, perp=True)
        return p * singlet + (1 - p) * singlet_perp
Exemplo n.º 10
0
def check_kext(rhoAB, dimA, dimB, k, display=False):
    '''
    Checks if the bipartite state rhoAB is k-extendible.
    '''

    all_sys = list(range(1, k + 2))
    dim = [dimA] + [dimB] * k

    t = cvx.Variable()
    R = cvx.Variable((dimA * dimB**k, dimA * dimB**k), hermitian=True)

    obj = cvx.Maximize(t)

    c = [R - t * eye(dimA * dimB**k) >> 0]

    for j in range(2, k + 2):

        sys = list(np.setdiff1d(all_sys, [1, j]))

        R_ABj = partial_trace(R, sys, dim)

        c.append(R_ABj == rhoAB)

    prob = cvx.Problem(obj, constraints=c)

    prob.solve(verbose=display)

    return prob.value, R.value
Exemplo n.º 11
0
def compose_channels(C):

    '''
    Takes a composition of channels. The variable C should be a list of lists,
    with each list consisting of the Kraus operators of the channels to be composed.

    If C=[K1,K2,...,Kn], then this function returns the composition such that
    the channel corresponding to K1 is applied first, then K2, etc.
    '''

    d=C[0][0].shape[0]

    lengths=[]
    for c in C:
        lengths.append(len(c))
    
    combs=list(itertools.product(*[range(length) for length in lengths]))

    K_n=[]

    for comb in combs:
        #tmp=1
        tmp=eye(d)
        for i in range(len(comb)):
            tmp=C[i][comb[i]]@tmp
        K_n.append(tmp)

    return K_n
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
Exemplo n.º 13
0
def diamond_norm(J, dimA, dimB, display=False):
    '''
    Computes the diamond norm of a superoperator with Choi representation J.
    dimA is the dimension of the input space of the channel, and dimB is the
    dimension of the output space.

    The form of the SDP used comes from Theorem 3.1 of:
        
        'Simpler semidefinite programs for completely bounded norms',
            Chicago Journal of Theoretical Computer Science 2013,
            by John Watrous
    '''
    '''
    The Choi representation J in the above paper is defined using a different
    convention:
        J=(N\otimes I)(|Phi^+><Phi^+|).
    In other words, the channel N acts on the first half of the maximally-
    entangled state, while the convention used throughout this code stack
    is
        J=(I\otimes N)(|Phi^+><Phi^+|).
    We thus use syspermute to convert to the form used in the aforementioned
    paper.
    '''

    J = syspermute(J, [2, 1], [dimA, dimB])

    X = cvx.Variable((dimA * dimB, dimA * dimB), hermitian=False)
    rho0 = cvx.Variable((dimA, dimA), PSD=True)
    rho1 = cvx.Variable((dimA, dimA), PSD=True)

    M = cvx.bmat([[cvx.kron(eye(dimB), rho0), X],
                  [X.H, cvx.kron(eye(dimB), rho1)]])

    c = []
    c += [M >> 0, cvx.trace(rho0) == 1, cvx.trace(rho1) == 1]

    obj = cvx.Maximize((1 / 2) * cvx.real(cvx.trace(dag(J) @ X)) +
                       (1 / 2) * cvx.real(cvx.trace(J @ X.H)))

    prob = cvx.Problem(obj, constraints=c)

    prob.solve(verbose=display, eps=1e-7)

    return prob.value
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
Exemplo n.º 15
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
Exemplo n.º 16
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))
Exemplo n.º 17
0
def norm_trace_dist(rho, sigma, sdp=False, dual=False, display=False):
    '''
    Calculates the normalized trace distance (1/2)*||rho-sigma||_1 using an SDP,
    where rho and sigma are quantum states. More generally, they can be Hermitian
    operators.
    '''

    if sdp:
        if not dual:

            dim = rho.shape[0]

            L1 = cvx.Variable((dim, dim), hermitian=True)
            L2 = cvx.Variable((dim, dim), hermitian=True)

            c = [L1 >> 0, L2 >> 0, eye(dim) - L1 >> 0, eye(dim) - L2 >> 0]

            obj = cvx.Maximize(cvx.real(cvx.trace((L1 - L2) @ (rho - sigma))))
            prob = cvx.Problem(obj, constraints=c)

            prob.solve(verbose=display, eps=1e-7)

            return (1 / 2) * prob.value

        elif dual:

            dim = rho.shape[0]

            Y1 = cvx.Variable((dim, dim), hermitian=True)
            Y2 = cvx.Variable((dim, dim), hermitian=True)

            c = [Y1 >> 0, Y2 >> 0, Y1 >> rho - sigma, Y2 >> -(rho - sigma)]

            obj = cvx.Minimize(cvx.real(cvx.trace(Y1 + Y2)))

            prob = cvx.Problem(obj, c)
            prob.solve(verbose=display, eps=1e-7)

            return (1 / 2) * prob.value
    else:
        return (1 / 2) * trace_norm(rho - sigma)
Exemplo n.º 18
0
def isotropic_state(p, d, fidelity=False):
    '''
    Generates the isotropic state with parameter p on two d-dimensional systems.
    The state is defined as

        rho_Iso = p*|Bell><Bell|+(1-p)*eye(d^2)/d^2,

    where -1/(d^2-1)<=p<=1. Isotropic states are invariant under U ⊗ conj(U)
    for any unitary U, where conj(U) is the complex conjugate of U.

    If fidelity=True, then the function returns a different parameterization of
    the isotropic state in which the parameter p is the fidelity of the state
    with respect to the maximally entangled state.
    '''

    Bell = MaxEnt_state(d)

    if fidelity:
        return p * Bell + ((1 - p) / (d**2 - 1)) * (eye(d**2) - Bell)
    else:
        return p * Bell + (1 - p) * eye(d**2) / d**2
def Clifford_twirl_channel_one_qubit(K, rho, sys=1, dim=[2]):
    '''
    Twirls the given channel with Kraus operators in K by the one-qubit 
    Clifford group on the given subsystem (specified by sys).
    '''

    n = int(np.log2(np.sum([d for d in dim])))

    C1 = eye(2**n)
    C2 = Rx_i(sys, np.pi, n)
    C3 = Rx_i(sys, np.pi / 2., n)
    C4 = Rx_i(sys, -np.pi / 2., n)
    C5 = Rz_i(sys, np.pi, n)
    C6 = Rx_i(sys, np.pi, n) * Rz_i(sys, np.pi, n)
    C7 = Rx_i(sys, np.pi / 2., n) * Rz_i(sys, np.pi, n)
    C6 = Rx_i(sys, np.pi, n) * Rz_i(sys, np.pi, n)
    C8 = Rx_i(sys, -np.pi / 2., n) * Rz_i(sys, np.pi, n)
    C9 = Rz_i(sys, np.pi / 2., n)
    C10 = Ry_i(sys, np.pi, n) * Rz_i(sys, np.pi / 2., n)
    C11 = Ry_i(sys, -np.pi / 2., n) * Rz_i(sys, np.pi / 2., n)
    C12 = Ry_i(sys, np.pi / 2., n) * Rz_i(sys, np.pi / 2., n)
    C13 = Rz_i(sys, -np.pi / 2., n)
    C14 = Ry_i(sys, np.pi, n) * Rz_i(sys, -np.pi / 2., n)
    C15 = Ry_i(sys, -np.pi / 2., n) * Rz_i(sys, -np.pi / 2., n)
    C16 = Ry_i(sys, np.pi / 2., n) * Rz_i(sys, -np.pi / 2., n)
    C17 = Rz_i(sys, -np.pi / 2., n) * Rx_i(sys, np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)
    C18 = Rz_i(sys, np.pi / 2., n) * Rx_i(sys, np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)
    C19 = Rz_i(sys, np.pi, n) * Rx_i(sys, np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)
    C20 = Rx_i(sys, np.pi / 2., n) * Rz_i(sys, np.pi / 2., n)
    C21 = Rz_i(sys, np.pi / 2., n) * Rx_i(sys, -np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)
    C22 = Rz_i(sys, -np.pi / 2., n) * Rx_i(sys, -np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)
    C23 = Rx_i(sys, -np.pi / 2., n) * Rz_i(sys, np.pi / 2., n)
    C24 = Rx_i(sys, np.pi, n) * Rx_i(sys, -np.pi / 2., n) * Rz_i(
        sys, np.pi / 2., n)

    C = [
        C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16,
        C17, C18, C19, C20, C21, C22, C23, C24
    ]

    rho_twirl = 0

    for i in range(len(C)):
        rho_twirl += (1. / 24.) * C[i] @ apply_channel(K,
                                                       dag(C[i]) @ rho @ C[i],
                                                       sys, dim) @ dag(C[i])

    return rho_twirl, C
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
Exemplo n.º 21
0
def singlet_state(d, perp=False):
    '''
    Generates the singlet state acting on two d-dimensional systems, which is defined
    as

        (1/(d^2-d))(eye(d^2)-F),

    where F is the swap operator given by SWAP([1,2],[d,d]) (see below).

    If perp=True, then the function also returns the state orthogonal to the singlet
    state, given by 

        (1/(d^2+d))(eye(d^2)+F).
    '''

    F = SWAP([1, 2], [d, d])

    singlet = (1 / (d**2 - d)) * (eye(d**2) - F)

    if perp:
        singlet_perp = (1 / (d**2 + d)) * (eye(d**2) + F)
        return singlet, singlet_perp
    else:
        return singlet
Exemplo n.º 22
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
Exemplo n.º 23
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
Exemplo n.º 24
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
Exemplo n.º 25
0
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
Exemplo n.º 26
0
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
Exemplo n.º 27
0
def Pauli_channel(px, py, pz):
    '''
    Generates the Kraus operators, an isometric extension, and a unitary
    extension of the one-qubit Pauli channel specified by the parameters px, py, pz.
    '''

    pI = 1 - px - py - pz

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

    K = [
        np.sqrt(pI) * eye(2),
        np.sqrt(px) * Sx,
        np.sqrt(py) * Sy,
        np.sqrt(pz) * Sz
    ]

    V, U = generate_channel_isometry(K, 2, 2)

    return K, V, U
Exemplo n.º 28
0
def isotropic_twirl_state(X,d):

    '''
    Applies the twirling channel

        X -> ∫ (U ⊗ conj(U))*X*(U ⊗ conj(U)).H dU

    to the input operator X acting on two d-dimensional systems.

    For d=2, this is equivalent to

        X -> (1/24)*sum_i (c_i ⊗ conj(c_i))*X*(c_i ⊗ conj(c_i)).H

    where the unitaries c_i form the one-qubit Clifford group (because the Clifford
    unitaries constitute a unitary 2-design).

    This channel takes any state rho and converts it to an isotropic state with
    the same fidelity to the maximally entangled state as rho.
    '''

    G=MaxEnt_state(d,normalized=False,density_matrix=True)

    return (Tr(X)/(d**2-1)-Tr(G@X)/(d*(d**2-1)))*eye(d**2)+(Tr(G@X)/(d**2-1)-Tr(X)/(d*(d**2-1)))@G
Exemplo n.º 29
0
def Rz_i(i, t, n):
    '''
	Rotation about the Z axis on qubit i by angle t. The total number of
	qubits is n.
	'''

    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)
    Sz = np.array([[1, 0], [0, -1]])
    Rz = expm(-1j * t * Sz / 2)
    out_temp = tensor(Rz, [eye(2), n - 1])
    out = syspermute(out_temp, perm_rearrange, dims)

    return out
Exemplo n.º 30
0
def su_generators(d):

    '''
    Generates the basis (aka generators) of the Lie algebra su(d)
    corresponding to the Lie group SU(d). The basis has d^2-1 elements.

    All of the generators are traceless and Hermitian. After adding the
    identity matrix, they form an orthogonal basis for all dxd matrices.

    The orthogonality condition is

        Tr[S_i*S_j]=d*delta_{i,j}

    (This is a particular convention we use here; there are other conventions.)

    For d=2, we get the Pauli matrices.
    '''

    S=[]

    S.append(eye(d))

    for l in range(d):
        for k in range(l):
            S.append(np.sqrt(d/2)*(ket(d,k)@dag(ket(d,l))+ket(d,l)@dag(ket(d,k))))
            S.append(np.sqrt(d/2)*(-1j*ket(d,k)@dag(ket(d,l))+1j*ket(d,l)@dag(ket(d,k))))

    for k in range(1,d):
        X=0
        for j in range(k):
            X+=ket(d,j)@dag(ket(d,j))
        
        S.append(np.sqrt(d/(k*(k+1)))*(X-k*ket(d,k)@dag(ket(d,k))))

    
    return S