U
    9%e&                     @   s   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 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 d dlmZmZmZ d dlmZmZ d dl m!Z! G dd deZ"dd Z#dd Z$dS )    )product)Add)Tuple)expand)Mul)Slog)MutableDenseMatrix
prettyForm)Dagger)HermitianOperator)	represent)numpy_ndarrayscipy_sparse_matrixto_numpy)TensorProducttensor_product_simp)Trc                       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                    s8   t  |}|D ]"}t|tr*t|dkstdq|S )N   z?Each argument should be of form [state,prob] or ( state, prob ))super
_eval_args
isinstancer   len
ValueError)clsargsarg	__class__ \/var/www/html/Darija-Ai-API/env/lib/python3.8/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>D   s     z"Density.states.<locals>.<listcomp>r   r   selfr"   r"   r#   states7   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&   S   s     z!Density.probs.<locals>.<listcomp>r'   r(   r"   r"   r#   probsF   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_stateU   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_probj   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/   r1   opr"   r#   r&      s     z$Density.apply_op.<locals>.<listcomp>)r   r   )r)   r4   new_argsr"   r3   r#   apply_op   s    zDensity.apply_opc              
   K   sx   g }| j D ]d\}}| }t|trXt|j ddD ]"}||| |d |d   q2q
||| ||  q
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)   hintsZtermsr/   r1   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_part2r4   r"   r"   r#   r9      s    
zDensity._generate_outer_prodc                 K   s   t |  f|S N)r   r;   )r)   optionsr"   r"   r#   
_represent   s    zDensity._representc                 G   s   dS )Nz\rhor"   r)   printerr   r"   r"   r#   _print_operator_name_latex   s    z"Density._print_operator_name_latexc                 G   s   t dS )Nu   ρr   r?   r"   r"   r#   _print_operator_name_pretty   s    z#Density._print_operator_name_prettyc                 K   s   | dg }t|  | S )Nindices)getr   r;   )r)   kwargsrC   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.
        )entropyr(   r"   r"   r#   rG      s    zDensity.entropy)__name__
__module____qualname____doc__classmethodr   r*   r,   r0   r2   r6   r;   r9   r>   rA   rB   rF   rG   __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 )a  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.spin import JzKet
    >>> from sympy import S
    >>> up = JzKet(S(1)/2,S(1)/2)
    >>> down = JzKet(S(1)/2,-S(1)/2)
    >>> d = Density((up,S(1)/2),(down,S(1)/2))
    >>> entropy(d)
    log(2)/2

    c                 s   s   | ]}|t | V  qd S r<   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   MatrixZ	eigenvalskeysr   sumr   numpyZlinalgeigvalsr	   r   )ZdensityrT   npr"   r"   r#   rG      s    



rG   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j	 }t
|| | tj	  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 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] https://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 matrix)r   r   r   rP   r   typeshapeZ	is_squarer   ZHalfr   r;   )Zstate1Zstate2Zsqrt_state1r"   r"   r#   fidelity  s    ,
rX   N)%	itertoolsr   Zsympy.core.addr   Zsympy.core.containersr   Zsympy.core.functionr   Zsympy.core.mulr   Zsympy.core.singletonr   Z&sympy.functions.elementary.exponentialr	   Zsympy.matrices.denser
   rP   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   Zsympy.physics.quantum.tracer   r   rG   rX   r"   r"   r"   r#   <module>   s$    H,