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
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
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
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
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
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
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
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
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
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