B
    [j&                 @   s   d dl mZmZ d dlmZ d dlmZmZmZm	Z	m
Z
mZmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZmZmZ d d
lmZmZ G dd deZdd Zdd Z dS )    )print_functiondivision)product)TupleAddMulMatrixlogexpandRational)Tr)
prettyForm)Dagger)HermitianOperator)	represent)numpy_ndarrayscipy_sparse_matrixto_numpy)TensorProducttensor_product_simpc                   s   e Zd ZdZe fddZdd Zdd Zdd	 Zd
d Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Z  ZS )Densitya  Density operator for representing mixed states.

    TODO: Density operator support for Qubits

    Parameters
    ==========

    values : tuples/lists
    Each tuple/list should be of form (state, prob) or [state,prob]

    Examples
    ========

    Create a density operator with 2 states represented by Kets.

    >>> from sympy.physics.quantum.state import Ket
    >>> from sympy.physics.quantum.density import Density
    >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
    >>> d
    'Density'((|0>, 0.5),(|1>, 0.5))

    c                s@   t t| |}x*|D ]"}t|tr0t|dkstdqW |S )N   z?Each argument should be of form [state,prob] or ( state, prob ))superr   
_eval_args
isinstancer   len
ValueError)clsargsarg)	__class__ <lib/python3.7/site-packages/sympy/physics/quantum/density.pyr   '   s    

zDensity._eval_argsc             C   s   t dd | jD  S )a  Return list of all states.

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.states()
        (|0>, |1>)

        c             S   s   g | ]}|d  qS )r   r!   ).0r   r!   r!   r"   
<listcomp>B   s    z"Density.states.<locals>.<listcomp>)r   r   )selfr!   r!   r"   states5   s    zDensity.statesc             C   s   t dd | jD  S )a#  Return list of all probabilities.

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.probs()
        (0.5, 0.5)

        c             S   s   g | ]}|d  qS )   r!   )r#   r   r!   r!   r"   r$   Q   s    z!Density.probs.<locals>.<listcomp>)r   r   )r%   r!   r!   r"   probsD   s    zDensity.probsc             C   s   | j | d }|S )at  Return specific state by index.

        Parameters
        ==========

        index : index of state to be returned

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.states()[1]
        |1>

        r   )r   )r%   indexstater!   r!   r"   	get_stateS   s    zDensity.get_statec             C   s   | j | d }|S )a  Return probability of specific state by index.

        Parameters
        ===========

        index : index of states whose probability is returned.

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.probs()[1]
        0.500000000000000

        r'   )r   )r%   r)   probr!   r!   r"   get_probh   s    zDensity.get_probc                s    fdd| j D }t| S )a  op will operate on each individual state.

        Parameters
        ==========

        op : Operator

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> from sympy.physics.quantum.operator import Operator
        >>> A = Operator('A')
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.apply_op(A)
        'Density'((A*|0>, 0.5),(A*|1>, 0.5))

        c                s   g | ]\}} | |fqS r!   r!   )r#   r*   r,   )opr!   r"   r$      s    z$Density.apply_op.<locals>.<listcomp>)r   r   )r%   r.   Znew_argsr!   )r.   r"   apply_op}   s    zDensity.apply_opc          
   K   s   g }xr| j D ]h\}}| }t|tr^xLt|j ddD ]"}||| |d |d   q6W q||| ||  qW t| S )a  Expand the density operator into an outer product format.

        Examples
        ========

        >>> from sympy.physics.quantum.state import Ket
        >>> from sympy.physics.quantum.density import Density
        >>> from sympy.physics.quantum.operator import Operator
        >>> A = Operator('A')
        >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
        >>> d.doit()
        0.5*|0><0| + 0.5*|1><1|

        r   )repeatr   r'   )r   r
   r   r   r   append_generate_outer_prod)r%   ZhintsZtermsr*   r,   r   r!   r!   r"   doit   s    
zDensity.doitc             C   s   |  \}}|  \}}t|dks0t|dkr8tdt|d trxt|dkrxt|dkrxt|d t|d  }nt| tt|  }t| t|  | S )Nr   zHAtleast one-pair of Non-commutative instance required for outer product.r'   )Zargs_cncr   r   r   r   r   r   r   )r%   Zarg1Zarg2Zc_part1Znc_part1Zc_part2Znc_part2r.   r!   r!   r"   r2      s    zDensity._generate_outer_prodc             K   s   t |  f|S )N)r   r3   )r%   Zoptionsr!   r!   r"   
_represent   s    zDensity._representc             G   s   |j d| S )N\rho)r5   )Z_print)r%   printerr   r!   r!   r"   _print_operator_name_latex   s    z"Density._print_operator_name_latexc             G   s   t tdS )Nu   ρ)r   Zunichr)r%   r6   r   r!   r!   r"   _print_operator_name_pretty   s    z#Density._print_operator_name_prettyc             K   s   | dg }t|  | S )Nindices)getr   r3   )r%   kwargsr9   r!   r!   r"   _eval_trace   s    zDensity._eval_tracec             C   s   t | S )zl Compute the entropy of a density matrix.

        Refer to density.entropy() method  for examples.
        )entropy)r%   r!   r!   r"   r=      s    zDensity.entropy)__name__
__module____qualname____doc__classmethodr   r&   r(   r+   r-   r/   r3   r2   r4   r7   r8   r<   r=   __classcell__r!   r!   )r    r"   r      s   r   c             C   s   t | trt| } t | tr$t| } t | trR|   }tt	dd |D  S t | t
rddl}|j| }|	|||  S tddS )aN  Compute the entropy of a matrix/density object.

    This computes -Tr(density*ln(density)) using the eigenvalue decomposition
    of density, which is given as either a Density instance or a matrix
    (numpy.ndarray, sympy.Matrix or scipy.sparse).

    Parameters
    ==========

    density : density matrix of type Density, sympy matrix,
    scipy.sparse or numpy.ndarray

    Examples
    ========

    >>> from sympy.physics.quantum.density import Density, entropy
    >>> from sympy.physics.quantum.represent import represent
    >>> from sympy.physics.quantum.matrixutils import scipy_sparse_matrix
    >>> from sympy.physics.quantum.spin import JzKet, Jz
    >>> from sympy import S, log
    >>> up = JzKet(S(1)/2,S(1)/2)
    >>> down = JzKet(S(1)/2,-S(1)/2)
    >>> d = Density((up,0.5),(down,0.5))
    >>> entropy(d)
    log(2)/2

    c             s   s   | ]}|t | V  qd S )N)r	   )r#   er!   r!   r"   	<genexpr>   s    zentropy.<locals>.<genexpr>r   Nz4numpy.ndarray, scipy.sparse or sympy matrix expected)r   r   r   r   r   r   Z	eigenvalskeysr
   sumr   ZnumpyZlinalgeigvalsr	   r   )ZdensityrH   Znpr!   r!   r"   r=      s    



r=   c             C   s   t | trt| n| } t |tr(t|n|}t | tr@t |tsXtdt| t|f | j|jkrr| jrrtd| tdd }t	|| | tdd 
 S )a   Computes the fidelity [1]_ between two quantum states

    The arguments provided to this function should be a square matrix or a
    Density object. If it is a square matrix, it is assumed to be diagonalizable.

    Parameters
    ==========

    state1, state2 : a density matrix or Matrix


    Examples
    ========

    >>> from sympy import S, sqrt
    >>> from sympy.physics.quantum.dagger import Dagger
    >>> from sympy.physics.quantum.spin import JzKet
    >>> from sympy.physics.quantum.density import Density, fidelity
    >>> from sympy.physics.quantum.represent import represent
    >>>
    >>> up = JzKet(S(1)/2,S(1)/2)
    >>> down = JzKet(S(1)/2,-S(1)/2)
    >>> amp = 1/sqrt(2)
    >>> updown = (amp * up) + (amp * down)
    >>>
    >>> # represent turns Kets into matrices
    >>> up_dm = represent(up * Dagger(up))
    >>> down_dm = represent(down * Dagger(down))
    >>> updown_dm = represent(updown * Dagger(updown))
    >>>
    >>> fidelity(up_dm, up_dm)
    1
    >>> fidelity(up_dm, down_dm) #orthogonal states
    0
    >>> fidelity(up_dm, updown_dm).evalf().round(3)
    0.707

    References
    ==========

    .. [1] http://en.wikipedia.org/wiki/Fidelity_of_quantum_states

    zfstate1 and state2 must be of type Density or Matrix received type=%s for state1 and type=%s for state2z]The dimensions of both args should be equal and the matrix obtained should be a square matrixr'   r   )r   r   r   r   r   typeshapeZ	is_squarer   r   r3   )Zstate1Zstate2Zsqrt_state1r!   r!   r"   fidelity  s    ,

rK   N)!Z
__future__r   r   	itertoolsr   Zsympyr   r   r   r   r	   r
   r   Zsympy.core.tracer   Z sympy.printing.pretty.stringpictr   Zsympy.physics.quantum.daggerr   Zsympy.physics.quantum.operatorr   Zsympy.physics.quantum.representr   Z!sympy.physics.quantum.matrixutilsr   r   r   Z#sympy.physics.quantum.tensorproductr   r   r   r=   rK   r!   r!   r!   r"   <module>   s   $ L.