Пример #1
0
def unitary_to_pauligate_2q(U):
    """
    Get the linear operator on (vectorized) density
    matrices corresponding to a 2-qubit unitary 
    operator on states.

    Parameters
    ----------
    U : numpy array
        A 4x4 array giving the action of the unitary
        on a state in the sigma-z basis.

    Returns
    -------
    numpy array
        The operator on density matrices that have been
        vectorized as length-16 vectors in the Pauli-product basis.
        Array has shape == (16,16).
    """

    assert( U.shape == (4,4) )
    op_mx = _np.empty( (16,16), 'd') #, 'complex' )
    Udag = _np.conjugate(_np.transpose(U))

    sigmaVec_2Q = pp_matrices(4)

    for i in range(16):
        for j in range(16):
            op_mx[i,j] = _np.real(_mt.trace(_np.dot(sigmaVec_2Q[i],_np.dot(U,_np.dot(sigmaVec_2Q[j],Udag)))))
        # in clearer notation: op_mx[i,j] = trace( sigma[i] * U * sigma[j] * Udag )
    return op_mx
Пример #2
0
def stdmx_to_stdvec(m):
    """
    Convert a matrix in the standard basis to
     a vector in the standard basis.

    Parameters
    ----------
    m : numpy array
        The matrix, must be square.

    Returns
    -------
    numpy array
        The vector, length == number of elements in m
    """

    assert(len(m.shape) == 2 and m.shape[0] == m.shape[1])
    dim = m.shape[0]
    stdMxs = std_matrices(dim)

    v = _np.empty((dim**2,1))
    for i,stdMx in enumerate(stdMxs):
        v[i,0] = _np.real(_mt.trace(_np.dot(stdMx,m)))

    return v
Пример #3
0
def unitary_to_pauligate_1q(U):
    """
    Get the linear operator on (vectorized) density
    matrices corresponding to a 1-qubit unitary 
    operator on states.

    Parameters
    ----------
    U : numpy array
        A 2x2 array giving the action of the unitary
        on a state in the sigma-z basis.

    Returns
    -------
    numpy array
        The operator on density matrices that have been
        vectorized as length-4 vectors in the Pauli basis.
        Array has shape == (4,4).
    """
    assert( U.shape == (2,2) )
    op_mx = _np.empty( (4,4) ) #, 'complex' )
    Udag = _np.conjugate(_np.transpose(U))

    sigmaVec = pp_matrices(2)

    for i in (0,1,2,3):
        for j in (0,1,2,3):
            op_mx[i,j] = _np.real(_mt.trace(_np.dot(sigmaVec[i],_np.dot(U,_np.dot(sigmaVec[j],Udag)))))
        # in clearer notation: op_mx[i,j] = _mt.trace( sigma[i] * U * sigma[j] * Udag )
    return op_mx
Пример #4
0
def stdmx_to_stdvec(m):
    """
    Convert a matrix in the standard basis to
     a vector in the standard basis.

    Parameters
    ----------
    m : numpy array
        The matrix, must be square.

    Returns
    -------
    numpy array
        The vector, length == number of elements in m
    """

    assert (len(m.shape) == 2 and m.shape[0] == m.shape[1])
    dim = m.shape[0]
    stdMxs = std_matrices(dim)

    v = _np.empty((dim**2, 1))
    for i, stdMx in enumerate(stdMxs):
        v[i, 0] = _np.real(_mt.trace(_np.dot(stdMx, m)))

    return v
Пример #5
0
def stdmx_to_ppvec(m):
    """
    Convert a matrix in the standard basis to
     a vector in the Pauli basis.

    Parameters
    ----------
    m : numpy array
        The matrix, shape 2x2 (1Q) or 4x4 (2Q)

    Returns
    -------
    numpy array
        The vector, length 4 or 16 respectively.
    """

    assert(len(m.shape) == 2 and m.shape[0] == m.shape[1])
    dim = m.shape[0]
    ppMxs = pp_matrices(dim)

    v = _np.empty((dim**2,1))
    for i,ppMx in enumerate(ppMxs):
        v[i,0] = _np.real(_mt.trace(_np.dot(ppMx,m)))

    return v
Пример #6
0
def stdmx_to_ppvec(m):
    """
    Convert a matrix in the standard basis to
     a vector in the Pauli basis.

    Parameters
    ----------
    m : numpy array
        The matrix, shape 2x2 (1Q) or 4x4 (2Q)

    Returns
    -------
    numpy array
        The vector, length 4 or 16 respectively.
    """

    assert (len(m.shape) == 2 and m.shape[0] == m.shape[1])
    dim = m.shape[0]
    ppMxs = pp_matrices(dim)

    v = _np.empty((dim**2, 1))
    for i, ppMx in enumerate(ppMxs):
        v[i, 0] = _np.real(_mt.trace(_np.dot(ppMx, m)))

    return v
Пример #7
0
def unitary_to_pauligate_2q(U):
    """
    Get the linear operator on (vectorized) density
    matrices corresponding to a 2-qubit unitary 
    operator on states.

    Parameters
    ----------
    U : numpy array
        A 4x4 array giving the action of the unitary
        on a state in the sigma-z basis.

    Returns
    -------
    numpy array
        The operator on density matrices that have been
        vectorized as length-16 vectors in the Pauli-product basis.
        Array has shape == (16,16).
    """

    assert (U.shape == (4, 4))
    op_mx = _np.empty((16, 16), 'd')  #, 'complex' )
    Udag = _np.conjugate(_np.transpose(U))

    sigmaVec_2Q = pp_matrices(4)

    for i in range(16):
        for j in range(16):
            op_mx[i, j] = _np.real(
                _mt.trace(
                    _np.dot(sigmaVec_2Q[i],
                            _np.dot(U, _np.dot(sigmaVec_2Q[j], Udag)))))
        # in clearer notation: op_mx[i,j] = trace( sigma[i] * U * sigma[j] * Udag )
    return op_mx
Пример #8
0
def unitary_to_pauligate_1q(U):
    """
    Get the linear operator on (vectorized) density
    matrices corresponding to a 1-qubit unitary 
    operator on states.

    Parameters
    ----------
    U : numpy array
        A 2x2 array giving the action of the unitary
        on a state in the sigma-z basis.

    Returns
    -------
    numpy array
        The operator on density matrices that have been
        vectorized as length-4 vectors in the Pauli basis.
        Array has shape == (4,4).
    """
    assert (U.shape == (2, 2))
    op_mx = _np.empty((4, 4))  #, 'complex' )
    Udag = _np.conjugate(_np.transpose(U))

    sigmaVec = pp_matrices(2)

    for i in (0, 1, 2, 3):
        for j in (0, 1, 2, 3):
            op_mx[i, j] = _np.real(
                _mt.trace(
                    _np.dot(sigmaVec[i], _np.dot(U, _np.dot(sigmaVec[j],
                                                            Udag)))))
        # in clearer notation: op_mx[i,j] = _mt.trace( sigma[i] * U * sigma[j] * Udag )
    return op_mx
Пример #9
0
def jamiolkowski_iso(gateMx,
                     gateMxBasis="gm",
                     choiMxBasis="gm",
                     dimOrStateSpaceDims=None):
    """
    Given a gate matrix, return the corresponding Choi matrix that is normalized
    to have trace == 1.

    Parameters
    ----------
    gateMx : numpy array
        the gate matrix to compute Choi matrix of.
        
    gateMxBasis : {"std","gm","pp"}, optional
        the basis of gateMx: standard (matrix units), Gell-Mann, or Pauli-product,
        respectively.

    choiMxBasis : {"std","gm","pp"}, optional
        the basis for the returned Choi matrix: standard (matrix units), Gell-Mann,
        or Pauli-product, respectively.

    dimOrStateSpaceDims : int or list of ints, optional
        Structure of the density-matrix space, which further specifies the basis
        of gateMx (see BasisTools).

    Returns
    -------
    numpy array
        the Choi matrix, normalized to have trace == 1, in the desired basis.
    """

    #first, get gate matrix into std basis
    gateMx = _np.asarray(gateMx)
    if gateMxBasis == "std":
        gateMxInStdBasis = gateMx
    elif gateMxBasis == "gm" or gateMxBasis == "pauli":
        gateMxInStdBasis = _bt.gm_to_std(gateMx, dimOrStateSpaceDims)
    elif gateMxBasis == "pp":
        gateMxInStdBasis = _bt.pp_to_std(gateMx, dimOrStateSpaceDims)
    else:
        raise ValueError("Invalid gateMxBasis: %s" % gateMxBasis)

    #expand gate matrix so it acts on entire space of dmDim x dmDim density matrices
    #  so that we can take dot products with the BVec matrices below
    gateMxInStdBasis = _bt.expand_from_std_direct_sum_mx(
        gateMxInStdBasis, dimOrStateSpaceDims)
    N = gateMxInStdBasis.shape[0]  #dimension of the full-basis (expanded) gate
    dmDim = int(round(_np.sqrt(N)))  #density matrix dimension

    #Note: we need to use the *full* basis of Matrix Unit, Gell-Mann, or Pauli-product matrices when
    # generating the Choi matrix, even when the original gate matrix doesn't include the entire basis.
    # This is because even when the original gate matrix doesn't include a certain basis element (B0 say),
    # conjugating with this basis element and tracing, i.e. trace(B0^dag * Gate * B0), is not necessarily zero.

    #get full list of basis matrices (in std basis) -- i.e. we use dmDim not dimOrStateSpaceDims
    if choiMxBasis == "gm":
        BVec = _bt.gm_matrices(dmDim)
    elif choiMxBasis == "std":
        BVec = _bt.std_matrices(dmDim)
    elif choiMxBasis == "pp":
        BVec = _bt.pp_matrices(dmDim)
    else:
        raise ValueError("Invalid choiMxBasis: %s" % choiMxBasis)
    assert (
        len(BVec) == N
    )  #make sure the number of basis matrices matches the dim of the gate given

    choiMx = _np.empty((N, N), 'complex')
    for i in range(N):
        for j in range(N):
            BjBi = _np.kron(_np.conjugate(BVec[j]), BVec[i])
            BjBi_dag = _np.transpose(_np.conjugate(BjBi))
            choiMx[i,j] = _mt.trace( _np.dot(gateMxInStdBasis, BjBi_dag) ) \
                        / _mt.trace( _np.dot( BjBi, BjBi_dag) )

    # This construction results in a Jmx with trace == dim(H) = sqrt(gateMx.shape[0]) (dimension of density matrix)
    #  but we'd like a Jmx with trace == 1, so normalize:
    choiMx_normalized = choiMx / dmDim
    return choiMx_normalized
Пример #10
0
def jamiolkowski_iso(gateMx, gateMxBasis="gm", choiMxBasis="gm", dimOrStateSpaceDims=None):
    """
    Given a gate matrix, return the corresponding Choi matrix that is normalized
    to have trace == 1.

    Parameters
    ----------
    gateMx : numpy array
        the gate matrix to compute Choi matrix of.
        
    gateMxBasis : {"std","gm","pp"}, optional
        the basis of gateMx: standard (matrix units), Gell-Mann, or Pauli-product,
        respectively.

    choiMxBasis : {"std","gm","pp"}, optional
        the basis for the returned Choi matrix: standard (matrix units), Gell-Mann,
        or Pauli-product, respectively.

    dimOrStateSpaceDims : int or list of ints, optional
        Structure of the density-matrix space, which further specifies the basis
        of gateMx (see BasisTools).

    Returns
    -------
    numpy array
        the Choi matrix, normalized to have trace == 1, in the desired basis.
    """
    
    #first, get gate matrix into std basis
    gateMx = _np.asarray(gateMx)
    if gateMxBasis == "std":
        gateMxInStdBasis = gateMx
    elif gateMxBasis == "gm" or gateMxBasis == "pauli":
        gateMxInStdBasis = _bt.gm_to_std(gateMx, dimOrStateSpaceDims)
    elif gateMxBasis == "pp":
        gateMxInStdBasis = _bt.pp_to_std(gateMx, dimOrStateSpaceDims)
    else: raise ValueError("Invalid gateMxBasis: %s" % gateMxBasis)

    #expand gate matrix so it acts on entire space of dmDim x dmDim density matrices
    #  so that we can take dot products with the BVec matrices below
    gateMxInStdBasis = _bt.expand_from_std_direct_sum_mx(gateMxInStdBasis, dimOrStateSpaceDims)
    N = gateMxInStdBasis.shape[0] #dimension of the full-basis (expanded) gate
    dmDim = int(round(_np.sqrt(N))) #density matrix dimension

    #Note: we need to use the *full* basis of Matrix Unit, Gell-Mann, or Pauli-product matrices when
    # generating the Choi matrix, even when the original gate matrix doesn't include the entire basis.
    # This is because even when the original gate matrix doesn't include a certain basis element (B0 say), 
    # conjugating with this basis element and tracing, i.e. trace(B0^dag * Gate * B0), is not necessarily zero.

    #get full list of basis matrices (in std basis) -- i.e. we use dmDim not dimOrStateSpaceDims
    if choiMxBasis == "gm":
        BVec = _bt.gm_matrices(dmDim)
    elif choiMxBasis == "std":
        BVec = _bt.std_matrices(dmDim)
    elif choiMxBasis == "pp":
        BVec = _bt.pp_matrices(dmDim)
    else: raise ValueError("Invalid choiMxBasis: %s" % choiMxBasis)    
    assert(len(BVec) == N) #make sure the number of basis matrices matches the dim of the gate given
                                                  
    choiMx = _np.empty( (N,N), 'complex')
    for i in range(N):
        for j in range(N):
            BjBi = _np.kron( _np.conjugate(BVec[j]), BVec[i] )
            BjBi_dag = _np.transpose(_np.conjugate(BjBi))
            choiMx[i,j] = _mt.trace( _np.dot(gateMxInStdBasis, BjBi_dag) ) \
                        / _mt.trace( _np.dot( BjBi, BjBi_dag) )

    # This construction results in a Jmx with trace == dim(H) = sqrt(gateMx.shape[0]) (dimension of density matrix)
    #  but we'd like a Jmx with trace == 1, so normalize:
    choiMx_normalized = choiMx / dmDim
    return choiMx_normalized