B
    [K^                 @   s  d Z ddlmZmZ ddlZddlmZmZmZm	Z	m
Z
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 dd	lmZ dd
lmZmZmZ ddlmZ ddlm Z  ddl!m"Z"m#Z# ddl$m%Z% dddddddddddgZ&G dd deZ'G dd de'eZ(G dd de'eZ)G dd de'Z*G d d de*e(Z+G d!d de*e)Z,d"d Z-d#d Z.d-d%dZ/d.d'dZ0d/d(dZ1d0d)dZ2d*d+ Z3d1d,dZ4dS )2zQubits for quantum computing.

Todo:
* Finish implementing measurement logic. This should include POVM.
* Update docstrings.
* Update tests.
    )print_functiondivisionN)IntegerlogMulAddPow	conjugate)sympify)string_typesrange
SYMPY_INTS)Matrixzeros)
prettyForm)ComplexSpace)KetBraState)QuantumError)	represent)numpy_ndarrayscipy_sparse_matrix)bitcountQubitQubitBraIntQubitIntQubitBraqubit_to_matrixmatrix_to_qubitmatrix_to_densitymeasure_allmeasure_partialmeasure_partial_oneshotmeasure_all_oneshotc               @   sd   e Zd ZdZedd Zedd Zedd Zedd	 Z	ed
d Z
dd Zdd Zdd ZdS )
QubitStatez"Base class for Qubit and QubitBra.c             C   s   t |dkr$t|d tr$|d jS t |dkrJt|d trJt|d }t|}x(|D ] }|dksX|dksXtd| qXW |S )N   r   z$Qubit values must be 0 or 1, got: %r)len
isinstancer%   qubit_valuesr   tupler
   
ValueError)clsargselement r/   :lib/python3.7/site-packages/sympy/physics/quantum/qubit.py
_eval_args7   s    

zQubitState._eval_argsc             C   s   t dt| S )N   )r   r'   )r,   r-   r/   r/   r0   _eval_hilbert_spaceK   s    zQubitState._eval_hilbert_spacec             C   s
   t | jS )z"The number of Qubits in the state.)r'   r)   )selfr/   r/   r0   	dimensionS   s    zQubitState.dimensionc             C   s   | j S )N)r5   )r4   r/   r/   r0   nqubitsX   s    zQubitState.nqubitsc             C   s   | j S )z,Returns the values of the qubits as a tuple.)label)r4   r/   r/   r0   r)   \   s    zQubitState.qubit_valuesc             C   s   | j S )N)r5   )r4   r/   r/   r0   __len__e   s    zQubitState.__len__c             C   s   | j t| j| d  S )Nr&   )r)   intr5   )r4   bitr/   r/   r0   __getitem__h   s    zQubitState.__getitem__c             G   sV   t | j}x<|D ]4}t| j| d }|| dkr<d||< qd||< qW | jt| S )zFlip the bit(s) given.r&   r   )listr)   r9   r5   	__class__r*   )r4   bitsZnewargsir:   r/   r/   r0   flipo   s    


zQubitState.flipN)__name__
__module____qualname____doc__classmethodr1   r3   propertyr5   r6   r)   r8   r;   r@   r/   r/   r/   r0   r%   0   s   	r%   c               @   sD   e Zd ZdZedd Zdd Zdd Zdd	 Zd
d Z	dd Z
dS )r   a  A multi-qubit ket in the computational (z) basis.

    We use the normal convention that the least significant qubit is on the
    right, so ``|00001>`` has a 1 in the least significant qubit.

    Parameters
    ==========

    values : list, str
        The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011').

    Examples
    ========

    Create a qubit in a couple of different ways and look at their attributes:

        >>> from sympy.physics.quantum.qubit import Qubit
        >>> Qubit(0,0,0)
        |000>
        >>> q = Qubit('0101')
        >>> q
        |0101>

        >>> q.nqubits
        4
        >>> len(q)
        4
        >>> q.dimension
        4
        >>> q.qubit_values
        (0, 1, 0, 1)

    We can flip the value of an individual qubit:

        >>> q.flip(1)
        |0111>

    We can take the dagger of a Qubit to get a bra:

        >>> from sympy.physics.quantum.dagger import Dagger
        >>> Dagger(q)
        <0101|
        >>> type(Dagger(q))
        <class 'sympy.physics.quantum.qubit.QubitBra'>

    Inner products work as expected:

        >>> ip = Dagger(q)*q
        >>> ip
        <0101|0101>
        >>> ip.doit()
        1
    c             C   s   t S )N)r   )r4   r/   r/   r0   
dual_class   s    zQubit.dual_classc             K   s    | j |j krtdS tdS d S )Nr&   r   )r7   r   )r4   brahintsr/   r/   r0   _eval_innerproduct_QubitBra   s    z!Qubit._eval_innerproduct_QubitBrac             K   s   | j d|S )N)N)_represent_ZGate)r4   optionsr/   r/   r0   _represent_default_basis   s    zQubit._represent_default_basisc       
      K   s   | dd}d}d}x&t| jD ]}||| 7 }|d }q W dgd| j  }d|t|< |dkrht|S |dkrddl}|j|dd	 S |d
krddl	m
}	 |	j|dd	 S dS )zBRepresent this qubits in the computational basis (ZGate).
        formatsympyr&   r   r2   numpyNcomplex)Zdtypezscipy.sparse)sparse)getreversedr)   r5   r9   r   rP   matrixZ	transposeZscipyrR   Z
csr_matrix)
r4   ZbasisrL   rN   nZdefinite_stateitresultZnprR   r/   r/   r0   rK      s     zQubit._represent_ZGatec             K   s   | dg }t|}t|dkr0ttd| j}|  | | }x0tt|d ddD ]}| |t|| }qVW t|| jkr|d S t|S d S )Nindicesr   r&   )	rS   r<   r'   r   r6   sort_reduced_densityr9   r    )r4   rH   kwargsrY   Z
sorted_idxZnew_matr?   r/   r/   r0   _eval_trace   s    zQubit._eval_tracec          	   K   s   dd }t |f|}|j}|d }t |}xht|D ]\}	xVt|D ]J}
xDtdD ]8}||
||}||	||}||	|
f  |||f 7  < qTW qFW q8W |S )zCompute the reduced density matrix by tracing out one qubit.
           The qubit argument should be of type python int, since it is used
           in bit operations
        c             S   s,   d| d }| |? d| > | |@  ||>  S )Nr2   r&   r/   )jkqubitZbit_maskr/   r/   r0   find_index_that_is_projected   s    z<Qubit._reduced_density.<locals>.find_index_that_is_projectedr2   )r   Zcolsr   r   r   )r4   rU   ra   rL   rb   Z
old_matrixZold_sizeZnew_sizeZ
new_matrixr?   r_   r`   colrowr/   r/   r0   r\      s    (zQubit._reduced_densityN)rA   rB   rC   rD   rE   rG   rJ   rM   rK   r^   r\   r/   r/   r/   r0   r   {   s   5c               @   s   e Zd ZdZedd ZdS )r   a  A multi-qubit bra in the computational (z) basis.

    We use the normal convention that the least significant qubit is on the
    right, so ``|00001>`` has a 1 in the least significant qubit.

    Parameters
    ==========

    values : list, str
        The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011').

    See also
    ========

    Qubit: Examples using qubits

    c             C   s   t S )N)r   )r4   r/   r/   r0   rG     s    zQubitBra.dual_classN)rA   rB   rC   rD   rE   rG   r/   r/   r/   r0   r     s   c               @   s<   e Zd ZdZedd Zdd Zdd Zdd	 ZeZ	eZ
d
S )IntQubitStatez>A base class for qubits that work with binary representations.c                s   t  dkr$t d tr$t S t  dkrp d dkrptttt d } fdd|D }t|S t  dkr d dkrtt d } d |k rtd d  d f  fddtt d D }t|S t S d S )Nr&   r   c                s   g | ]} d  |? d@ qS )r   r&   r/   ).0r?   )r-   r/   r0   
<listcomp>&  s    z,IntQubitState._eval_args.<locals>.<listcomp>r2   z cannot represent %s with %s bitsc                s   g | ]} d  |? d@ qS )r   r&   r/   )rf   r?   )r-   r/   r0   rg   /  s    )	r'   r(   r%   r1   rT   r   r   absr+   )r,   r-   Zrvaluesr)   Zneedr/   )r-   r0   r1     s    


zIntQubitState._eval_argsc             C   s4   d}d}x&t | jD ]}||| 7 }|d> }qW |S )z(Return the numerical value of the qubit.r   r&   )rT   r)   )r4   ZnumberrV   r?   r/   r/   r0   as_int4  s    zIntQubitState.as_intc             G   s   t |  S )N)strri   )r4   printerr-   r/   r/   r0   _print_label=  s    zIntQubitState._print_labelc             G   s   | j |f| }t|S )N)rl   r   )r4   rk   r-   r7   r/   r/   r0   _print_label_pretty@  s    z!IntQubitState._print_label_prettyN)rA   rB   rC   rD   rE   r1   ri   rl   rm   Z_print_label_reprZ_print_label_latexr/   r/   r/   r0   re     s   	re   c               @   s$   e Zd ZdZedd Zdd ZdS )r   a'  A qubit ket that store integers as binary numbers in qubit values.

    The differences between this class and ``Qubit`` are:

    * The form of the constructor.
    * The qubit values are printed as their corresponding integer, rather
      than the raw qubit values. The internal storage format of the qubit
      values in the same as ``Qubit``.

    Parameters
    ==========

    values : int, tuple
        If a single argument, the integer we want to represent in the qubit
        values. This integer will be represented using the fewest possible
        number of qubits. If a pair of integers, the first integer gives the
        integer to represent in binary form and the second integer gives
        the number of qubits to use.

    Examples
    ========

    Create a qubit for the integer 5:

        >>> from sympy.physics.quantum.qubit import IntQubit
        >>> from sympy.physics.quantum.qubit import Qubit
        >>> q = IntQubit(5)
        >>> q
        |5>

    We can also create an ``IntQubit`` by passing a ``Qubit`` instance.

        >>> q = IntQubit(Qubit('101'))
        >>> q
        |5>
        >>> q.as_int()
        5
        >>> q.nqubits
        3
        >>> q.qubit_values
        (1, 0, 1)

    We can go back to the regular qubit form.

        >>> Qubit(q)
        |101>
    c             C   s   t S )N)r   )r4   r/   r/   r0   rG   x  s    zIntQubit.dual_classc             K   s   t | |S )N)r   rJ   )r4   rH   rI   r/   r/   r0   _eval_innerproduct_IntQubitBra|  s    z'IntQubit._eval_innerproduct_IntQubitBraN)rA   rB   rC   rD   rE   rG   rn   r/   r/   r/   r0   r   H  s   /c               @   s   e Zd ZdZedd ZdS )r   zBA qubit bra that store integers as binary numbers in qubit values.c             C   s   t S )N)r   )r4   r/   r/   r0   rG     s    zIntQubitBra.dual_classN)rA   rB   rC   rD   rE   rG   r/   r/   r/   r0   r     s   c       	         s:  d}t | trd}t | tr d}| jd dkrL| jd }t|d}d}t}n8| jd dkrx| jd }t|d}d}t}ntd	|  t |tstd
|  d}x|t	|D ]p |r|  df }n| d f }|dks|dkrt
|}|dkr fddt	|D }|  ||||   }qW t |tttfr6| }|S )a  Convert from the matrix repr. to a sum of Qubit objects.

    Parameters
    ----------
    matrix : Matrix, numpy.matrix, scipy.sparse
        The matrix to build the Qubit representation of. This works with
        sympy matrices, numpy matrices and scipy.sparse sparse matrices.

    Examples
    ========

    Represent a state and then go back to its qubit form:

        >>> from sympy.physics.quantum.qubit import matrix_to_qubit, Qubit
        >>> from sympy.physics.quantum.gate import Z
        >>> from sympy.physics.quantum.represent import represent
        >>> q = Qubit('01')
        >>> matrix_to_qubit(represent(q))
        |01>
    rO   rP   zscipy.sparser   r&   r2   FTz*Matrix must be a row/column vector, got %rz>Matrix must be a row/column vector of size 2**nqubits, got: %rg        c                s    g | ]}t  d |> @ dkqS )r&   r   )r9   )rf   x)r?   r/   r0   rg     s    z#matrix_to_qubit.<locals>.<listcomp>)r(   r   r   shaper   r   r   r   r   r   rQ   reverser   r   r   expand)	rU   rN   Zmlistlenr6   Zketr,   rX   r.   Zqubit_arrayr/   )r?   r0   r     sD    







c             C   s>   ddl m} |  }dd |D }t|dkr2dS || S dS )z
    Works by finding the eigenvectors and eigenvalues of the matrix.
    We know we can decompose rho by doing:
    sum(EigenVal*|Eigenvect><Eigenvect|)
    r   )Densityc             S   s<   g | ]4}|d  D ]&}|d dkrt t|g|d gqqS )r2   r   )r   r   )rf   ro   Zvectorr/   r/   r0   rg     s   z%matrix_to_density.<locals>.<listcomp>N)Zsympy.physics.quantum.densityrs   Z
eigenvectsr'   )Zmatrs   Zeigenr-   r/   r/   r0   r      s    rO   c             C   s   t | |dS )zConverts an Add/Mul of Qubit objects into it's matrix representation

    This function is the inverse of ``matrix_to_qubit`` and is a shorthand
    for ``represent(qubit)``.
    )rN   )r   )ra   rN   r/   r/   r0   r     s    Tc             C   s   t | |}|dkrg }|r"| }t|j}tt|td }xDt|D ]8}|| dkrN|t	t
|||| t||  f qNW |S tddS )a  Perform an ensemble measurement of all qubits.

    Parameters
    ==========

    qubit : Qubit, Add
        The qubit to measure. This can be any Qubit or a linear combination
        of them.
    format : str
        The format of the intermediate matrices to use. Possible values are
        ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
        implemented.

    Returns
    =======

    result : list
        A list that consists of primitive states and their probabilities.

    Examples
    ========

        >>> from sympy.physics.quantum.qubit import Qubit, measure_all
        >>> from sympy.physics.quantum.gate import H, X, Y, Z
        >>> from sympy.physics.quantum.qapply import qapply

        >>> c = H(0)*H(1)*Qubit('00')
        >>> c
        H(0)*H(1)*|00>
        >>> q = qapply(c)
        >>> measure_all(q)
        [(|00>, 1/4), (|01>, 1/4), (|10>, 1/4), (|11>, 1/4)]
    rO   r2   g        z7This function can't handle non-sympy matrix formats yetN)r   
normalizedmaxrp   r9   mathr   r   appendr   r   r	   NotImplementedError)ra   rN   	normalizemZresultssizer6   r?   r/   r/   r0   r!     s    "

(c       
      C   s   t | |}t|ttfr"t|f}|dkr|r6| }t||}g }xR|D ]J}d}||j| d 7 }|dkrJ|r~t| }	nt|}	|	|	|f qJW |S t
ddS )a  Perform a partial ensemble measure on the specified qubits.

    Parameters
    ==========

    qubits : Qubit
        The qubit to measure.  This can be any Qubit or a linear combination
        of them.
    bits : tuple
        The qubits to measure.
    format : str
        The format of the intermediate matrices to use. Possible values are
        ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
        implemented.

    Returns
    =======

    result : list
        A list that consists of primitive states and their probabilities.

    Examples
    ========

        >>> from sympy.physics.quantum.qubit import Qubit, measure_partial
        >>> from sympy.physics.quantum.gate import H, X, Y, Z
        >>> from sympy.physics.quantum.qapply import qapply

        >>> c = H(0)*H(1)*Qubit('00')
        >>> c
        H(0)*H(1)*|00>
        >>> q = qapply(c)
        >>> measure_partial(q, (0,))
        [(sqrt(2)*|00>/2 + sqrt(2)*|10>/2, 1/2), (sqrt(2)*|01>/2 + sqrt(2)*|11>/2, 1/2)]
    rO   r   z7This function can't handle non-sympy matrix formats yetN)r   r(   r   r   r9   rt   _get_possible_outcomesHr   rw   rx   )
ra   r>   rN   ry   rz   possible_outcomesoutputoutcomeZprob_of_outcomeZnext_matrixr/   r/   r0   r"   )  s*    $



c       	      C   sz   ddl }t| |}|dkrn| }t||}|  }d}x<|D ]*}||j| d 7 }||kr>t| S q>W ntddS )a  Perform a partial oneshot measurement on the specified qubits.

    A oneshot measurement is equivalent to performing a measurement on a
    quantum system. This type of measurement does not return the probabilities
    like an ensemble measurement does, but rather returns *one* of the
    possible resulting states. The exact state that is returned is determined
    by picking a state randomly according to the ensemble probabilities.

    Parameters
    ----------
    qubits : Qubit
        The qubit to measure.  This can be any Qubit or a linear combination
        of them.
    bits : tuple
        The qubits to measure.
    format : str
        The format of the intermediate matrices to use. Possible values are
        ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
        implemented.

    Returns
    -------
    result : Qubit
        The qubit that the system collapsed to upon measurement.
    r   NrO   z7This function can't handle non-sympy matrix formats yet)randomr   rt   r|   r}   r   rx   )	ra   r>   rN   r   rz   r~   random_numberZ
total_probr   r/   r/   r0   r#   t  s    


c       
      C   s   t | j}tt|dd }g }x,tdt|> D ]}|td| d q4W g }x|D ]}|d|>  qZW xVtd| D ]F}d}x,tt|D ]}	|||	 @ r||	d 7 }qW | | || |< q~W |S )a  Get the possible states that can be produced in a measurement.

    Parameters
    ----------
    m : Matrix
        The matrix representing the state of the system.
    bits : tuple, list
        Which bits will be measured.

    Returns
    -------
    result : list
        The list of possible states which can occur given this measurement.
        These are un-normalized so we can derive the probability of finding
        this state by taking the inner product with itself
    r2   g?r&   r   )	ru   rp   r9   rv   r   r   r'   rw   r   )
rz   r>   r{   r6   Zoutput_matricesr?   Z	bit_masksr:   Ztruenessr_   r/   r/   r0   r|     s    

r|   c             C   s   ddl }t| }|dkr| }|  }d}d}x.|D ]&}|||  7 }||krTP |d7 }q6W tt|ttt	|j
dd S tddS )ad  Perform a oneshot ensemble measurement on all qubits.

    A oneshot measurement is equivalent to performing a measurement on a
    quantum system. This type of measurement does not return the probabilities
    like an ensemble measurement does, but rather returns *one* of the
    possible resulting states. The exact state that is returned is determined
    by picking a state randomly according to the ensemble probabilities.

    Parameters
    ----------
    qubits : Qubit
        The qubit to measure.  This can be any Qubit or a linear combination
        of them.
    format : str
        The format of the intermediate matrices to use. Possible values are
        ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
        implemented.

    Returns
    -------
    result : Qubit
        The qubit that the system collapsed to upon measurement.
    r   NrO   r&   r2   g?z7This function can't handle non-sympy matrix formats yet)r   r   rt   r	   r   r   r9   rv   r   ru   rp   rx   )ra   rN   r   rz   r   ZtotalrX   r?   r/   r/   r0   r$     s    
$)rO   )rO   T)rO   T)rO   )rO   )5rD   Z
__future__r   r   rv   rO   r   r   r   r   r   r	   Zsympy.core.basicr
   Zsympy.core.compatibilityr   r   r   Zsympy.matricesr   r   Z sympy.printing.pretty.stringpictr   Zsympy.physics.quantum.hilbertr   Zsympy.physics.quantum.stater   r   r   Zsympy.physics.quantum.qexprr   Zsympy.physics.quantum.representr   Z!sympy.physics.quantum.matrixutilsr   r   Zmpmath.libmp.libintmathr   __all__r%   r   r   re   r   r   r   r    r   r!   r"   r#   r|   r$   r/   r/   r/   r0   <module>   sN    K /7G

8
K
03