Exemple #1
0
 def _simple_relative_entropy_implementation(self,
                                             rho,
                                             sigma,
                                             log_base=np.log,
                                             tol=1e-12):
     """ A simplified relative entropy implementation for use in
         double-checking the optimised implementation within
         QuTiP itself.
     """
     # S(rho || sigma) = sum_i(p_i log p_i) - sum_ij(p_i P_ij log q_i)
     rvals, rvecs = sp_eigs(rho.data, rho.isherm, vecs=True)
     svals, svecs = sp_eigs(sigma.data, sigma.isherm, vecs=True)
     # Calculate S
     S = 0
     for i in range(len(rvals)):
         if abs(rvals[i]) >= tol:
             S += rvals[i] * log_base(rvals[i])
         for j in range(len(svals)):
             P_ij = (np.dot(rvecs[i], svecs[j].conjugate()) *
                     np.dot(svecs[j], rvecs[i].conjugate()))
             if abs(svals[j]) < tol and not (abs(rvals[i]) < tol
                                             or abs(P_ij) < tol):
                 # kernel of sigma intersects support of rho
                 return np.inf
             if abs(svals[j]) >= tol:
                 S -= rvals[i] * P_ij * log_base(svals[j])
     return np.real(S)
def test_Transformation8():
    "Check Qobj eigs and direct eig solver reverse transformations match"

    N = 10
    H = rand_herm(N)

    # generate a random basis
    rand = rand_dm(N, density=1)
    
    evals, rand_basis = rand.eigenstates()
    evals2, rand_basis2 = sp_eigs(rand.data, isherm=1)
    
    H1 = H.transform(rand_basis, True)
    H2 = H.transform(rand_basis2, True)
    assert_((H1 - H2).norm() < 1e-6)
    
    ket = rand_ket(N)
    K1 = ket.transform(rand_basis,1)
    K2 = ket.transform(rand_basis2,1)
    assert_((K1 - K2).norm() < 1e-6)
    
    bra = rand_ket(N).dag()
    B1 = bra.transform(rand_basis,1)
    B2 = bra.transform(rand_basis2,1)
    assert_((B1 - B2).norm() < 1e-6)
Exemple #3
0
    def testRandhermEigs(self):
        "Random: Hermitian - Eigs given"

        H = [rand_herm([1, 2, 3, 4, 5], 0.5) for k in range(5)]
        for h in H:
            eigs = sp_eigs(h.data, h.isherm, vecs=False)
            assert_(np.abs(np.sum(eigs) - 15.0) < 1e-12)
Exemple #4
0
def test_Transformation8():
    "Check Qobj eigs and direct eig solver reverse transformations match"

    N = 10
    H = rand_herm(N)

    # generate a random basis
    rand = rand_dm(N, density=1)

    evals, rand_basis = rand.eigenstates()
    evals2, rand_basis2 = sp_eigs(rand.data, isherm=1)

    H1 = H.transform(rand_basis, True)
    H2 = H.transform(rand_basis2, True)
    assert_((H1 - H2).norm() < 1e-6)

    ket = rand_ket(N)
    K1 = ket.transform(rand_basis, 1)
    K2 = ket.transform(rand_basis2, 1)
    assert_((K1 - K2).norm() < 1e-6)

    bra = rand_ket(N).dag()
    B1 = bra.transform(rand_basis, 1)
    B2 = bra.transform(rand_basis2, 1)
    assert_((B1 - B2).norm() < 1e-6)
Exemple #5
0
 def eigenenergies(self,sparse=False,sort='low',eigvals=0,tol=0,maxiter=100000):
     """Finds the Eigenenergies (Eigenvalues) of a quantum object.
        
     Eigenenergies are defined for operators or superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     sort : str 
         Sort eigenvalues 'low' to high, or 'high' to low.
         
     eigvals : int 
         Number of requested eigenvalues. Default is all eigenvalues.
         
     tol : float 
         Tolerance used by sparse Eigensolver (0=machine precision). The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigvals: array
         Array of eigenvalues for operator.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     return sp_eigs(self,vecs=False,sparse=sparse,sort=sort,eigvals=eigvals,tol=tol,maxiter=maxiter)
Exemple #6
0
    def testRandhermEigs(self):
        "Random: Hermitian - Eigs given"

        H = [rand_herm([1,2,3,4,5],0.5) for k in range(5)]
        for h in H:
            eigs = sp_eigs(h.data, h.isherm, vecs=False)
            assert_(np.abs(np.sum(eigs)-15.0) < 1e-12)
Exemple #7
0
def hellinger_dist(A, B, sparse=False, tol=0):
    """
    Calculates the quantum Hellinger distance between two density matrices.

    Formula:
    hellinger_dist(A, B) = sqrt(2-2*Tr(sqrt(A)*sqrt(B)))

    See: D. Spehner, F. Illuminati, M. Orszag, and W. Roga, "Geometric
    measures of quantum correlations with Bures and Hellinger distances"
    arXiv:1611.03449

    Parameters
    ----------
    A : :class:`qutip.Qobj`
        Density matrix or state vector.
    B : :class:`qutip.Qobj`
        Density matrix or state vector with same dimensions as A.
    tol : float
        Tolerance used by sparse eigensolver, if used. (0=Machine precision)
    sparse : {False, True}
        Use sparse eigensolver.

    Returns
    -------
    hellinger_dist : float
        Quantum Hellinger distance between A and B. Ranges from 0 to sqrt(2).

    Examples
    --------
    >>> x=fock_dm(5,3)
    >>> y=coherent_dm(5,1)
    >>> hellinger_dist(x,y)
    1.3725145002591095

    """
    if A.dims != B.dims:
        raise TypeError("A and B do not have same dimensions.")

    if A.isket or A.isbra:
        sqrtmA = ket2dm(A)
    else:
        sqrtmA = A.sqrtm(sparse=sparse, tol=tol)
    if B.isket or B.isbra:
        sqrtmB = ket2dm(B)
    else:
        sqrtmB = B.sqrtm(sparse=sparse, tol=tol)

    product = sqrtmA * sqrtmB

    eigs = sp_eigs(product.data,
                   isherm=product.isherm,
                   vecs=False,
                   sparse=sparse,
                   tol=tol)
    #np.maximum() is to avoid nan appearing sometimes due to numerical
    #instabilities causing np.sum(eigs) slightly (~1e-8) larger than 1
    #when hellinger_dist(A, B) is called for A=B
    return np.sqrt(2.0 * np.maximum(0., (1.0 - np.real(np.sum(eigs)))))
Exemple #8
0
 def eigenstates(self,
                 sparse=False,
                 sort='low',
                 eigvals=0,
                 tol=0,
                 maxiter=100000):
     """Find the eigenstates and eigenenergies. 
        
     Eigenstates and Eigenvalues are defined for operators and 
     superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     sort : str 
         Sort eigenvalues (and vectors) 'low' to high, or 'high' to low.
         
     eigvals : int 
         Number of requested eigenvalues. Default is all eigenvalues.
         
     tol : float 
         Tolerance used by sparse Eigensolver (0 = machine precision).The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigvals : array
         Array of eigenvalues for operator.
     
     eigvecs : array
         Array of quantum operators representing the oprator eigenkets.
         Order of eigenkets is determined by order of eigenvalues.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     evals, evecs = sp_eigs(self,
                            sparse=sparse,
                            sort=sort,
                            eigvals=eigvals,
                            tol=tol,
                            maxiter=maxiter)
     new_dims = [self.dims[0], [1] * len(self.dims[0])]
     new_shape = [self.shape[0], 1]
     ekets = np.array(
         [Qobj(vec, dims=new_dims, shape=new_shape) for vec in evecs])
     norms = np.array([ket.norm() for ket in ekets])
     return evals, ekets / norms
Exemple #9
0
    def norm(self, oper_norm='tr', sparse=False, tol=0, maxiter=100000):
        """Returns the norm of a quantum object.

        Norm is L2-norm for kets and trace-norm (by default) for operators.
        Other operator norms may be specified using the `oper_norm` argument.

        Parameters
        ----------
        oper_norm : str
            Which norm to use for operators: trace 'tr', Frobius 'fro',
            one 'one', or max 'max'. This parameter does not affect the norm
            of a state vector.

        sparse : bool
            Use sparse eigenvalue solver for trace norm.  Other norms are not
            affected by this parameter.

        tol : float
            Tolerance for sparse solver (if used) for trace norm. The sparse
            solver may not converge if the tolerance is set too low.

        maxiter : int
            Maximum number of iterations performed by sparse solver (if used)
            for trace norm.

        Returns
        -------
        norm : float
            The requested norm of the operator or state quantum object.


        Notes
        -----
        The sparse eigensolver is much slower than the dense version.
        Use sparse only if memory requirements demand it.

        """
        if self.type == 'oper' or self.type == 'super':
            if oper_norm == 'tr':
                vals = sp_eigs(self,
                               vecs=False,
                               sparse=sparse,
                               tol=tol,
                               maxiter=maxiter)
                return sum(sqrt(abs(vals)**2))
            elif oper_norm == 'fro':
                return _sp_fro_norm(self)
            elif oper_norm == 'one':
                return _sp_one_norm(self)
            elif oper_norm == 'max':
                return _sp_max_norm(self)
            else:
                raise ValueError(
                    "Operator norm must be 'tr', 'fro', 'one', or 'max'.")
        else:
            return _sp_L2_norm(self)
Exemple #10
0
    def testRanddm(self):
        "random density matrix"

        R = [rand_dm(5) for k in range(5)]
        for r in R:
            assert_(r.tr() - 1.0 < 1e-15)
            # verify all eigvals are >=0
            assert_(not any(sp_eigs(r.data, r.isherm, vecs=False)) < 0)
            # verify hermitian
            assert_(r.isherm)
Exemple #11
0
    def testRanddm(self):
        "random density matrix"

        R = [rand_dm(5) for k in range(5)]
        for r in R:
            assert_(r.tr() - 1.0 < 1e-15)
            # verify all eigvals are >=0
            assert_(not any(sp_eigs(r.data, r.isherm, vecs=False)) < 0)
            # verify hermitian
            assert_(r.isherm)
Exemple #12
0
 def sqrtm(self, sparse=False, tol=0, maxiter=100000):
     """The sqrt of a quantum operator.
        
     Operator must be square.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse eigenvalue/vector solver.
         
     tol : float 
         Tolerance used by sparse solver (0 = machine precision).
         
     maxiter : int 
         Maximum number of iterations used by sparse solver.
         
     Returns
     -------
     oper: qobj
         Matrix square root of operator.
         
     Raises
     ------
     TypeError
         Quantum object is not square.
         
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     if self.dims[0][0] == self.dims[1][0]:
         evals, evecs = sp_eigs(self,
                                sparse=sparse,
                                tol=tol,
                                maxiter=maxiter)
         numevals = len(evals)
         dV = sp.spdiags(np.sqrt(np.abs(evals)),
                         0,
                         numevals,
                         numevals,
                         format='csr')
         evecs = sp.hstack(evecs, format='csr')
         spDv = dV.dot(evecs.conj().T)
         out = Qobj(evecs.dot(spDv), dims=self.dims, shape=self.shape)
         if qset.auto_tidyup:
             return out.tidyup()
         else:
             return out
     else:
         raise TypeError('Invalid operand for matrix square root')
Exemple #13
0
 def testRanddmEigs(self):
     "Random: Density matrix - Eigs given"
     R = []
     for k in range(5):
         eigs = np.random.random(5)
         eigs /= np.sum(eigs)
         R += [rand_dm(eigs)]
     for r in R:
         assert_(r.tr() - 1.0 < 1e-15)
         # verify all eigvals are >=0
         assert_(not any(sp_eigs(r.data, r.isherm, vecs=False)) < 0)
         # verify hermitian
         assert_(r.isherm)
Exemple #14
0
def _entropy_relative(rho, sigma, base=e, sparse=False):
    """
    ****NEEDS TO BE WORKED ON****

    Calculates the relative entropy S(rho||sigma) between two density
    matrices.

    Parameters
    ----------
    rho : qobj
        First density matrix.
    sigma : qobj
        Second density matrix.
    base : {e,2}
        Base of logarithm.

    Returns
    -------
    rel_ent : float
        Value of relative entropy.

    """
    if rho.type != 'oper' or sigma.type != 'oper':
        raise TypeError("Inputs must be density matrices..")
    # sigma terms
    svals = sp_eigs(sigma.data, sigma.isherm, vecs=False, sparse=sparse)
    snzvals = svals[svals != 0]
    if base == 2:
        slogvals = log2(snzvals)
    elif base == e:
        slogvals = log(snzvals)
    else:
        raise ValueError("Base must be 2 or e.")
    # rho terms
    rvals = sp_eigs(rho.data, rho.isherm, vecs=False, sparse=sparse)
    rnzvals = rvals[rvals != 0]
    # calculate tr(rho*log sigma)
    rel_trace = float(real(sum(rnzvals * slogvals)))
    return -entropy_vn(rho, base, sparse) - rel_trace
Exemple #15
0
def _entropy_relative(rho, sigma, base=e, sparse=False):
    """
    ****NEEDS TO BE WORKED ON**** (after 2.0 release)

    Calculates the relative entropy S(rho||sigma) between two density
    matrices..

    Parameters
    ----------
    rho : qobj
        First density matrix.
    sigma : qobj
        Second density matrix.
    base : {e,2}
        Base of logarithm.

    Returns
    -------
    rel_ent : float
        Value of relative entropy.

    """
    if rho.type != 'oper' or sigma.type != 'oper':
        raise TypeError("Inputs must be density matrices..")
    # sigma terms
    svals = sp_eigs(sigma, vecs=False, sparse=sparse)
    snzvals = svals[svals != 0]
    if base == 2:
        slogvals = log2(snzvals)
    elif base == e:
        slogvals = log(snzvals)
    else:
        raise ValueError("Base must be 2 or e.")
    # rho terms
    rvals = sp_eigs(rho, vecs=False, sparse=sparse)
    rnzvals = rvals[rvals != 0]
    # calculate tr(rho*log sigma)
    rel_trace = float(real(sum(rnzvals * slogvals)))
    return -entropy_vn(rho, base, sparse) - rel_trace
Exemple #16
0
 def testRanddmEigs(self):
     "Random: Density matrix - Eigs given"
     R = []
     for k in range(5):
         eigs = np.random.random(5)
         eigs /= np.sum(eigs)
         R += [rand_dm(eigs)]
     for r in R:
         assert_(r.tr() - 1.0 < 1e-15)
         # verify all eigvals are >=0
         assert_(not any(sp_eigs(r.data, r.isherm, vecs=False)) < 0)
         # verify hermitian
         assert_(r.isherm)
Exemple #17
0
 def norm(self,oper_norm='tr',sparse=False,tol=0,maxiter=100000):
     """Returns the norm of a quantum object. 
     
     Norm is L2-norm for kets and trace-norm (by default) for operators.  
     Other operator norms may be specified using the `oper_norm` argument.
     
     Parameters
     ----------
     oper_norm : str 
         Which norm to use for operators: trace 'tr', Frobius 'fro',one 'one', 
         or max 'max'. This parameter does not affect the norm of a state vector. 
         
     sparse : bool 
         Use sparse eigenvalue solver for trace norm.  Other norms are not
         affected by this parameter.
         
     tol : float 
         Tolerance for sparse solver (if used) for trace norm.  The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used) for
         trace norm.
     
     Returns
     -------
     norm : float
         The requested norm of the operator or state quantum object.
     
     
     Notes
     ----- 
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     if self.type=='oper' or self.type=='super':
         if oper_norm=='tr':
             vals=sp_eigs(self,vecs=False,sparse=sparse,tol=tol,maxiter=maxiter)
             return sum(sqrt(abs(vals)**2))
         elif oper_norm=='fro':
             return _sp_fro_norm(self)
         elif oper_norm=='one':
             return _sp_one_norm(self)
         elif oper_norm=='max':
             return _sp_max_norm(self)
         else:
             raise ValueError("Operator norm must be 'tr', 'fro', 'one', or 'max'.")
     else:
         return _sp_L2_norm(self)
Exemple #18
0
def power(qstate, power, sparse=False, tol=0, maxiter=100000):
    """power of a quantum operator.
    Operator must be square.
    Parameters
    ----------
    qstate: qutip.Qobj()
        quantum state
    power: float
        power
    sparse : bool
        Use sparse eigenvalue/vector solver.
    tol : float
        Tolerance used by sparse solver (0 = machine precision).
    maxiter : int
        Maximum number of iterations used by sparse solver.
    Returns
    -------
    oper : qobj
        Matrix square root of operator.
    Raises
    ------
    TypeError
        Quantum object is not square.
    Notes
    -----
    The sparse eigensolver is much slower than the dense version.
    Use sparse only if memory requirements demand it.
    """
    if qstate.dims[0][0] == qstate.dims[1][0]:
        evals, evecs = sp_eigs(qstate.data,
                               qstate.isherm,
                               sparse=sparse,
                               tol=tol,
                               maxiter=maxiter)
        numevals = len(evals)
        dV = spdiags(np.power(evals, power, dtype=complex),
                     0,
                     numevals,
                     numevals,
                     format='csr')
        if qstate.isherm:
            spDv = dV.dot(evecs.T.conj().T)
        else:
            spDv = dV.dot(np.linalg.inv(evecs.T))

        out = qu.Qobj(evecs.T.dot(spDv), dims=qstate.dims)
        return out.tidyup() if qu.settings.auto_tidyup else out

    else:
        raise TypeError('Invalid operand for matrix square root')
Exemple #19
0
 def eigenenergies(self,
                   sparse=False,
                   sort='low',
                   eigvals=0,
                   tol=0,
                   maxiter=100000):
     """Finds the Eigenenergies (Eigenvalues) of a quantum object.
        
     Eigenenergies are defined for operators or superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     sort : str 
         Sort eigenvalues 'low' to high, or 'high' to low.
         
     eigvals : int 
         Number of requested eigenvalues. Default is all eigenvalues.
         
     tol : float 
         Tolerance used by sparse Eigensolver (0=machine precision). The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigvals: array
         Array of eigenvalues for operator.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     return sp_eigs(self,
                    vecs=False,
                    sparse=sparse,
                    sort=sort,
                    eigvals=eigvals,
                    tol=tol,
                    maxiter=maxiter)
Exemple #20
0
 def sqrtm(self,sparse=False,tol=0,maxiter=100000):
     """The sqrt of a quantum operator.
        
     Operator must be square.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse eigenvalue/vector solver.
         
     tol : float 
         Tolerance used by sparse solver (0 = machine precision).
         
     maxiter : int 
         Maximum number of iterations used by sparse solver.
         
     Returns
     -------
     oper: qobj
         Matrix square root of operator.
         
     Raises
     ------
     TypeError
         Quantum object is not square.
         
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     if self.dims[0][0]==self.dims[1][0]:
         evals,evecs=sp_eigs(self,sparse=sparse,tol=tol,maxiter=maxiter)
         numevals=len(evals)
         dV=sp.spdiags(np.sqrt(np.abs(evals)),0,numevals,numevals,format='csr')
         evecs=sp.hstack(evecs,format='csr')
         spDv=dV.dot(evecs.conj().T)
         out=Qobj(evecs.dot(spDv),dims=self.dims,shape=self.shape)
         if qset.auto_tidyup:
             return out.tidyup()
         else:
             return out
     else:
         raise TypeError('Invalid operand for matrix square root')
Exemple #21
0
 def eigenstates(self,sparse=False,sort='low',eigvals=0,tol=0,maxiter=100000):
     """Find the eigenstates and eigenenergies. 
        
     Eigenstates and Eigenvalues are defined for operators and 
     superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     sort : str 
         Sort eigenvalues (and vectors) 'low' to high, or 'high' to low.
         
     eigvals : int 
         Number of requested eigenvalues. Default is all eigenvalues.
         
     tol : float 
         Tolerance used by sparse Eigensolver (0 = machine precision).The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigvals : array
         Array of eigenvalues for operator.
     
     eigvecs : array
         Array of quantum operators representing the oprator eigenkets.
         Order of eigenkets is determined by order of eigenvalues.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     evals,evecs = sp_eigs(self,sparse=sparse,sort=sort,eigvals=eigvals,tol=tol,maxiter=maxiter)
     new_dims  = [self.dims[0], [1] * len(self.dims[0])]
     new_shape = [self.shape[0], 1]
     ekets = np.array([Qobj(vec, dims=new_dims, shape=new_shape) for vec in evecs])
     norms=np.array([ket.norm() for ket in ekets])
     return evals, ekets/norms
Exemple #22
0
 def groundstate(self, sparse=False, tol=0, maxiter=100000):
     """Finds the ground state Eigenvalue and Eigenvector.
     
     Defined for quantum operators or superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     tol : float
         Tolerance used by sparse Eigensolver (0 = machine precision). The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigval : float
         Eigenvalue for the ground state of quantum operator.
     
     eigvec : qobj
         Eigenket for the ground state of quantum operator.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     grndval, grndvec = sp_eigs(self,
                                sparse=sparse,
                                eigvals=1,
                                tol=tol,
                                maxiter=maxiter)
     new_dims = [self.dims[0], [1] * len(self.dims[0])]
     new_shape = [self.shape[0], 1]
     grndvec = Qobj(grndvec[0], dims=new_dims, shape=new_shape)
     grndvec = grndvec / grndvec.norm()
     return grndval[0], grndvec
Exemple #23
0
def tracedist(A, B, sparse=False, tol=0):
    """
    Calculates the trace distance between two density matrices..
    See: Nielsen & Chuang, "Quantum Computation and Quantum Information"

    Parameters
    ----------!=
    A : qobj
        Density matrix or state vector.
    B : qobj
        Density matrix or state vector with same dimensions as A.
    tol : float
        Tolerance used by sparse eigensolver, if used. (0=Machine precision)
    sparse : {False, True}
        Use sparse eigensolver.

    Returns
    -------
    tracedist : float
        Trace distance between A and B.

    Examples
    --------
    >>> x=fock_dm(5,3)
    >>> y=coherent_dm(5,1)
    >>> tracedist(x,y)
    0.9705143161472971

    """
    if A.isket or A.isbra:
        A = ket2dm(A)
    if B.isket or B.isbra:
        B = ket2dm(B)

    if A.dims != B.dims:
        raise TypeError("A and B do not have same dimensions.")

    diff = A - B
    diff = diff.dag() * diff
    vals = sp_eigs(diff.data, diff.isherm, vecs=False, sparse=sparse, tol=tol)
    return float(np.real(0.5 * np.sum(np.sqrt(np.abs(vals)))))
def tracedist(A, B, sparse=False, tol=0):
    """
    Calculates the trace distance between two density matrices..
    See: Nielsen & Chuang, "Quantum Computation and Quantum Information"

    Parameters
    ----------!=
    A : qobj
        Density matrix or state vector.
    B : qobj
        Density matrix or state vector with same dimensions as A.
    tol : float
        Tolerance used by sparse eigensolver, if used. (0=Machine precision)
    sparse : {False, True}
        Use sparse eigensolver.

    Returns
    -------
    tracedist : float
        Trace distance between A and B.

    Examples
    --------
    >>> x=fock_dm(5,3)
    >>> y=coherent_dm(5,1)
    >>> tracedist(x,y)
    0.9705143161472971

    """
    if A.isket or A.isbra:
        A = ket2dm(A)
    if B.isket or B.isbra:
        B = ket2dm(B)

    if A.dims != B.dims:
        raise TypeError("A and B do not have same dimensions.")

    diff = A - B
    diff = diff.dag() * diff
    vals = sp_eigs(diff.data, diff.isherm, vecs=False, sparse=sparse, tol=tol)
    return float(np.real(0.5 * np.sum(np.sqrt(np.abs(vals)))))
Exemple #25
0
def entropy_vn(rho, base=e, sparse=False):
    """
    Von-Neumann entropy of density matrix
    
    Parameters
    ----------
    rho : qobj
        Density matrix.
    base : {e,2} 
        Base of logarithm.
    
    Other Parameters
    ----------------
    sparse : {False,True}
        Use sparse eigensolver.
    
    Returns
    ------- 
    entropy : float
        Von-Neumann entropy of `rho`.
    
    Examples
    --------
    >>> rho=0.5*fock_dm(2,0)+0.5*fock_dm(2,1)
    >>> entropy_vn(rho,2)
    1.0
    
    """
    if rho.type == 'ket' or rho.type == 'bra':
        rho = ket2dm(rho)
    vals = sp_eigs(rho, vecs=False, sparse=sparse)
    nzvals = vals[vals != 0]
    if base == 2:
        logvals = log2(nzvals)
    elif base == e:
        logvals = log(nzvals)
    else:
        raise ValueError("Base must be 2 or e.")
    return float(real(-sum(nzvals * logvals)))
Exemple #26
0
def tracedist(A,B,sparse=False,tol=0):
    """
    Calculates the trace distance between two density matricies.
    See: Nielsen & Chuang, "Quantum Computation and Quantum Information"
    
    Parameters
    ----------
    A : qobj 
        Density matrix.
    B : qobj:
        Density matrix with same dimensions as A.
        
    Other Parameters
    ----------------
    tol : float 
        Tolerance used by sparse eigensolver. (0=Machine precision)
    sparse : {False, True}
        Use sparse eigensolver.
    
    Returns
    -------
    tracedist : float
        Trace distance between A and B.
    
    Examples
    --------    
    >>> x=fock_dm(5,3)
    >>> y=coherent_dm(5,1)
    >>> tracedist(x,y)
    0.9705143161472971
    
    """
    if A.dims!=B.dims:
        raise TypeError('Density matricies do not have same dimensions.')
    else:
        diff=A-B
        diff=diff.dag()*diff
        vals=sp_eigs(diff,vecs=False,sparse=sparse,tol=tol)
        return float(real(0.5*sum(sqrt(vals))))
Exemple #27
0
def tracedist(A, B, sparse=False, tol=0):
    """
    Calculates the trace distance between two density matricies.
    See: Nielsen & Chuang, "Quantum Computation and Quantum Information"
    
    Parameters
    ----------
    A : qobj 
        Density matrix.
    B : qobj:
        Density matrix with same dimensions as A.
        
    Other Parameters
    ----------------
    tol : float 
        Tolerance used by sparse eigensolver. (0=Machine precision)
    sparse : {False, True}
        Use sparse eigensolver.
    
    Returns
    -------
    tracedist : float
        Trace distance between A and B.
    
    Examples
    --------    
    >>> x=fock_dm(5,3)
    >>> y=coherent_dm(5,1)
    >>> tracedist(x,y)
    0.9705143161472971
    
    """
    if A.dims != B.dims:
        raise TypeError('Density matricies do not have same dimensions.')
    else:
        diff = A - B
        diff = diff.dag() * diff
        vals = sp_eigs(diff, vecs=False, sparse=sparse, tol=tol)
        return float(real(0.5 * sum(sqrt(vals))))
Exemple #28
0
def entropy_vn(rho,base=e,sparse=False):
    """
    Von-Neumann entropy of density matrix
    
    Parameters
    ----------
    rho : qobj
        Density matrix.
    base : {e,2} 
        Base of logarithm.
    
    Other Parameters
    ----------------
    sparse : {False,True}
        Use sparse eigensolver.
    
    Returns
    ------- 
    entropy : float
        Von-Neumann entropy of `rho`.
    
    Examples
    --------
    >>> rho=0.5*fock_dm(2,0)+0.5*fock_dm(2,1)
    >>> entropy_vn(rho,2)
    1.0
    
    """
    if rho.type=='ket' or rho.type=='bra':
        rho=ket2dm(rho)
    vals=sp_eigs(rho,vecs=False,sparse=sparse)
    nzvals=vals[vals!=0]
    if base==2:
        logvals=log2(nzvals)
    elif base==e:
        logvals=log(nzvals)
    else:
        raise ValueError("Base must be 2 or e.")
    return float(real(-sum(nzvals*logvals)))
Exemple #29
0
 def groundstate(self,sparse=False,tol=0,maxiter=100000):
     """Finds the ground state Eigenvalue and Eigenvector.
     
     Defined for quantum operators or superoperators only.
     
     Parameters
     ----------
     sparse : bool 
         Use sparse Eigensolver
         
     tol : float
         Tolerance used by sparse Eigensolver (0 = machine precision). The sparse solver
         may not converge if the tolerance is set too low.
         
     maxiter : int 
         Maximum number of iterations performed by sparse solver (if used).
     
     Returns
     -------
     eigval : float
         Eigenvalue for the ground state of quantum operator.
     
     eigvec : qobj
         Eigenket for the ground state of quantum operator.
     
     Notes
     -----
     The sparse eigensolver is much slower than the dense version.  
     Use sparse only if memory requirements demand it.
     
     """
     grndval,grndvec=sp_eigs(self,sparse=sparse,eigvals=1,tol=tol,maxiter=maxiter)
     new_dims  = [self.dims[0], [1] * len(self.dims[0])]
     new_shape = [self.shape[0], 1]
     grndvec=Qobj(grndvec[0],dims=new_dims,shape=new_shape)
     grndvec=grndvec/grndvec.norm()
     return grndval[0],grndvec
Exemple #30
0
    def _spectral_decomp(self, k):
        """
        Calculates the diagonalization of the dynamics generator
        generating lists of eigenvectors, propagators in the diagonalised
        basis, and the 'factormatrix' used in calculating the propagator
        gradient
        """
        if self.memory_optimization >= 2:
            sparse = True
        else:
            sparse = False

        if self.oper_dtype == Qobj:
            H = self._dyn_gen[k]
            # Returns eigenvalues as array (row)
            # and eigenvectors as rows of an array
            eig_val, eig_vec = sp_eigs(H.data, H.isherm, sparse=sparse)
            eig_vec = eig_vec.T

        elif self.oper_dtype == np.ndarray:
            H = self._dyn_gen[k]
            # returns row vector of eigenvals, columns with the eigenvecs
            eig_val, eig_vec = np.linalg.eig(H)
        else:
            if sparse:
                H = self._dyn_gen[k].toarray()
            else:
                H = self._dyn_gen[k]
            # returns row vector of eigenvals, columns with the eigenvecs
            eig_val, eig_vec = la.eigh(H)

        # assuming H is an nxn matrix, find n
        n = self.get_drift_dim()

        # Calculate the propagator in the diagonalised basis
        eig_val_tau = -1j*eig_val*self.tau[k]
        prop_eig = np.exp(eig_val_tau)

        # Generate the factor matrix through the differences
        # between each of the eigenvectors and the exponentiations
        # create nxn matrix where each eigen val is repeated n times
        # down the columns
        o = np.ones([n, n])
        eig_val_cols = eig_val_tau*o
        # calculate all the differences by subtracting it from its transpose
        eig_val_diffs = eig_val_cols - eig_val_cols.T
        # repeat for the propagator
        prop_eig_cols = prop_eig*o
        prop_eig_diffs = prop_eig_cols - prop_eig_cols.T
        # the factor matrix is the elementwise quotient of the
        # differeneces between the exponentiated eigen vals and the
        # differences between the eigen vals
        # need to avoid division by zero that would arise due to denegerate
        # eigenvalues and the diagonals
        degen_mask = np.abs(eig_val_diffs) < self.fact_mat_round_prec
        eig_val_diffs[degen_mask] = 1
        factors = prop_eig_diffs / eig_val_diffs
        # for degenerate eigenvalues the factor is just the exponent
        factors[degen_mask] = prop_eig_cols[degen_mask]

        # Store eigenvectors, propagator and factor matric
        # for use in propagator computations
        self._decomp_curr[k] = True
        if isinstance(factors, np.ndarray):
            self._dyn_gen_factormatrix[k] = factors
        else:
            self._dyn_gen_factormatrix[k] = np.array(factors)

        if self.oper_dtype == Qobj:
            self._prop_eigen[k] = Qobj(np.diagflat(prop_eig),
                                                    dims=self.dyn_dims)
            self._dyn_gen_eigenvectors[k] = Qobj(eig_vec,
                                                dims=self.dyn_dims)
            if self._dyn_gen_eigenvectors_adj is not None:
                self._dyn_gen_eigenvectors_adj[k] = \
                            self._dyn_gen_eigenvectors[k].dag()
        else:
            self._prop_eigen[k] = np.diagflat(prop_eig)
            self._dyn_gen_eigenvectors[k] = eig_vec
            if self._dyn_gen_eigenvectors_adj is not None:
                self._dyn_gen_eigenvectors_adj[k] = \
                            self._dyn_gen_eigenvectors[k].conj().T
Exemple #31
0
    def testRandhermPosDef(self):
        "Random: Hermitian - Positive semi-def"

        H = [rand_herm(5, pos_def=1) for k in range(5)]
        for h in H:
            assert_(not any(sp_eigs(h.data, h.isherm, vecs=False)) < 0)
Exemple #32
0
    def _spectral_decomp(self, k):
        """
        Calculates the diagonalization of the dynamics generator
        generating lists of eigenvectors, propagators in the diagonalised
        basis, and the 'factormatrix' used in calculating the propagator
        gradient
        """
        if self.memory_optimization >= 2:
            sparse = True
        else:
            sparse = False

        if self.oper_dtype == Qobj:
            H = self._dyn_gen[k]
            # Returns eigenvalues as array (row)
            # and eigenvectors as rows of an array
            eig_val, eig_vec = sp_eigs(H.data, H.isherm, sparse=sparse)
            eig_vec = eig_vec.T

        elif self.oper_dtype == np.ndarray:
            H = self._dyn_gen[k]
            # returns row vector of eigenvals, columns with the eigenvecs
            eig_val, eig_vec = np.linalg.eig(H)
        else:
            if sparse:
                H = self._dyn_gen[k].toarray()
            else:
                H = self._dyn_gen[k]
            # returns row vector of eigenvals, columns with the eigenvecs
            eig_val, eig_vec = la.eigh(H)

        # assuming H is an nxn matrix, find n
        n = self.get_drift_dim()

        # Calculate the propagator in the diagonalised basis
        eig_val_tau = -1j * eig_val * self.tau[k]
        prop_eig = np.exp(eig_val_tau)

        # Generate the factor matrix through the differences
        # between each of the eigenvectors and the exponentiations
        # create nxn matrix where each eigen val is repeated n times
        # down the columns
        o = np.ones([n, n])
        eig_val_cols = eig_val_tau * o
        # calculate all the differences by subtracting it from its transpose
        eig_val_diffs = eig_val_cols - eig_val_cols.T
        # repeat for the propagator
        prop_eig_cols = prop_eig * o
        prop_eig_diffs = prop_eig_cols - prop_eig_cols.T
        # the factor matrix is the elementwise quotient of the
        # differeneces between the exponentiated eigen vals and the
        # differences between the eigen vals
        # need to avoid division by zero that would arise due to denegerate
        # eigenvalues and the diagonals
        degen_mask = np.abs(eig_val_diffs) < self.fact_mat_round_prec
        eig_val_diffs[degen_mask] = 1
        factors = prop_eig_diffs / eig_val_diffs
        # for degenerate eigenvalues the factor is just the exponent
        factors[degen_mask] = prop_eig_cols[degen_mask]

        # Store eigenvectors, propagator and factor matric
        # for use in propagator computations
        self._decomp_curr[k] = True
        if isinstance(factors, np.ndarray):
            self._dyn_gen_factormatrix[k] = factors
        else:
            self._dyn_gen_factormatrix[k] = np.array(factors)

        if self.oper_dtype == Qobj:
            self._prop_eigen[k] = Qobj(np.diagflat(prop_eig),
                                       dims=self.dyn_dims)
            self._dyn_gen_eigenvectors[k] = Qobj(eig_vec, dims=self.dyn_dims)
            if self._dyn_gen_eigenvectors_adj is not None:
                self._dyn_gen_eigenvectors_adj[k] = \
                            self._dyn_gen_eigenvectors[k].dag()
        else:
            self._prop_eigen[k] = np.diagflat(prop_eig)
            self._dyn_gen_eigenvectors[k] = eig_vec
            if self._dyn_gen_eigenvectors_adj is not None:
                self._dyn_gen_eigenvectors_adj[k] = \
                            self._dyn_gen_eigenvectors[k].conj().T
Exemple #33
0
    def testRandhermPosDef(self):
        "Random: Hermitian - Positive semi-def"

        H = [rand_herm(5,pos_def=1) for k in range(5)]
        for h in H:
            assert_(not any(sp_eigs(h.data, h.isherm, vecs=False)) < 0)
Exemple #34
0
def entropy_relative(rho, sigma, base=e, sparse=False, tol=1e-12):
    """
    Calculates the relative entropy S(rho||sigma) between two density
    matrices.

    Parameters
    ----------
    rho : :class:`qutip.Qobj`
        First density matrix (or ket which will be converted to a density
        matrix).
    sigma : :class:`qutip.Qobj`
        Second density matrix (or ket which will be converted to a density
        matrix).
    base : {e,2}
        Base of logarithm. Defaults to e.
    sparse : bool
        Flag to use sparse solver when determining the eigenvectors
        of the density matrices. Defaults to False.
    tol : float
        Tolerance to use to detect 0 eigenvalues or dot producted between
        eigenvectors. Defaults to 1e-12.

    Returns
    -------
    rel_ent : float
        Value of relative entropy. Guaranteed to be greater than zero
        and should equal zero only when rho and sigma are identical.

    Examples
    --------

    First we define two density matrices:

    >>> rho = qutip.ket2dm(qutip.ket("00"))
    >>> sigma = rho + qutip.ket2dm(qutip.ket("01"))
    >>> sigma = sigma.unit()

    Then we calculate their relative entropy using base 2 (i.e. ``log2``)
    and base e (i.e. ``log``).

    >>> qutip.entropy_relative(rho, sigma, base=2)
    1.0
    >>> qutip.entropy_relative(rho, sigma)
    0.6931471805599453

    References
    ----------

    See Nielsen & Chuang, "Quantum Computation and Quantum Information",
    Section 11.3.1, pg. 511 for a detailed explanation of quantum relative
    entropy.
    """
    if rho.isket:
        rho = ket2dm(rho)
    if sigma.isket:
        sigma = ket2dm(sigma)
    if not rho.isoper or not sigma.isoper:
        raise TypeError("Inputs must be density matrices.")
    if rho.dims != sigma.dims:
        raise ValueError("Inputs must have the same shape and dims.")
    if base == 2:
        log_base = log2
    elif base == e:
        log_base = log
    else:
        raise ValueError("Base must be 2 or e.")
    # S(rho || sigma) = sum_i(p_i log p_i) - sum_ij(p_i P_ij log q_i)
    #
    # S is +inf if the kernel of sigma (i.e. svecs[svals == 0]) has non-trivial
    # intersection with the support of rho (i.e. rvecs[rvals != 0]).
    rvals, rvecs = sp_eigs(rho.data, rho.isherm, vecs=True, sparse=sparse)
    if any(abs(imag(rvals)) >= tol):
        raise ValueError("Input rho has non-real eigenvalues.")
    rvals = real(rvals)
    svals, svecs = sp_eigs(sigma.data, sigma.isherm, vecs=True, sparse=sparse)
    if any(abs(imag(svals)) >= tol):
        raise ValueError("Input sigma has non-real eigenvalues.")
    svals = real(svals)
    # Calculate inner products of eigenvectors and return +inf if kernel
    # of sigma overlaps with support of rho.
    P = abs(inner(rvecs, conj(svecs)))**2
    if (rvals >= tol) @ (P >= tol) @ (svals < tol):
        return inf
    # Avoid -inf from log(0) -- these terms will be multiplied by zero later
    # anyway
    svals[abs(svals) < tol] = 1
    nzrvals = rvals[abs(rvals) >= tol]
    # Calculate S
    S = nzrvals @ log_base(nzrvals) - rvals @ P @ log_base(svals)
    # the relative entropy is guaranteed to be >= 0, so we clamp the
    # calculated value to 0 to avoid small violations of the lower bound.
    return max(0, S)