B
    «»ˆd  ã               @   s6   d Z ddlZddlmZ ddgZd	dd„Zdd„ ZdS )
z1
Determines if a contraction can use BLAS or not
é    Né   )ÚhelpersÚcan_blasÚtensor_blasc             C   sÐ  t | ƒdkrdS | \}}xft|| ƒD ]V}| |¡| |¡ }}|dks\|dks\|| dkr`dS || d t||kƒkr&dS q&W |dk	r¾x4|D ],}|d | |¡ |d | |¡ krŽdS qŽW t |ƒdkrÎdS dd„ | D ƒ}	|	d | }
|	d | }t |ƒ}| d | d krd	S |	d |	d kr(d
S || d… |d|… krHdS |d|… || d… krhdS || d… || d… krŠdS |d|… |d|… kr¨dS t |
ƒdksÄt |ƒdkrÈdS dS dS )aŠ  
    Checks if we can use a BLAS call.

    Parameters
    ----------
    inputs : list of str
        Specifies the subscripts for summation.
    result : str
        Resulting summation.
    idx_removed : set
        Indices that are removed in the summation
    shapes : sequence of tuple[int], optional
        If given, check also that none of the indices are broadcast dimensions.

    Returns
    -------
    type : str or bool
        The type of BLAS call to be used or False if none.

    Notes
    -----
    We assume several operations are not efficient such as a transposed
    DDOT, therefore 'ijk,jki->' should prefer einsum. These return the blas
    type appended with "/EINSUM" to differentiate when they can still be done
    with tensordot if required, e.g. when a backend has no einsum.

    Examples
    --------
    >>> can_blas(['ij', 'jk'], 'ik', set('j'))
    'GEMM'

    >>> can_blas(['ijj', 'jk'], 'ik', set('j'))
    False

    >>> can_blas(['ab', 'cd'], 'abcd', set())
    'OUTER/EINSUM'

    >>> # looks like GEMM but actually 'j' is broadcast:
    >>> can_blas(['ij', 'jk'], 'ik', set('j'), shapes=[(4, 1), (5, 6)])
    False
    é   Fr   Nr   zOUTER/EINSUMc             S   s   g | ]}t |ƒ‘qS © )Úset)Ú.0Úxr   r   úA/var/www/html/venv/lib/python3.7/site-packages/opt_einsum/blas.pyú
<listcomp>U   s    zcan_blas.<locals>.<listcomp>ÚDOTz
DOT/EINSUMZGEMMzGEMV/EINSUMZTDOT)Úlenr   ÚcountÚintÚfind)ÚinputsÚresultÚidx_removedZshapesÚ
input_leftÚinput_rightÚcÚnlÚnrZsetsÚ	keep_leftÚ
keep_rightÚrsr   r   r   r      sD    +
$c                sx  t |ƒ}t |ƒ| }t |ƒ| }i ‰ x t|| jƒD ]\}}	|	ˆ |< q2W x t||jƒD ]\}}	|	ˆ |< qTW t|ƒ}
t |ˆ ¡}t |ˆ ¡}t |ˆ ¡}|| }x|D ]}	| |	d¡}q¢W ||krÖt |  	¡ | 	¡ ¡}n>||
 d… |d|
… krt |  
||¡| 
||¡¡}n|d|
… ||
 d… krPt |  
||¡j| 
||¡j¡}nÄ||
 d… ||
 d… krŽt |  
||¡| 
||¡j¡}n†|d|
… |d|
… krÈt |  
||¡j| 
||¡¡}nLd\}}x.|D ]&}	|| |	¡f7 }|| |	¡f7 }qÖW tj| |||fd}t‡ fdd„|D ƒƒ}|j|krVt|ƒdkrL||_n
t |¡}||krtt |d | |¡}|S )	a  
    Computes the dot product between two tensors, attempts to use np.dot and
    then tensordot if that fails.

    Parameters
    ----------
    view_left : array_like
        The left hand view
    input_left : str
        Indices of the left view
    view_right : array_like
        The right hand view
    input_right : str
        Indices of the right view
    index_result : str
        The resulting indices
    idx_removed : set
        Indices removed in the contraction

    Returns
    -------
    type : array
        The resulting BLAS operation.

    Notes
    -----
    Interior function for tensor BLAS.

    This function will attempt to use `np.dot` by the iterating through the
    four possible transpose cases. If this fails all inner and matrix-vector
    operations will be handed off to einsum while all matrix-matrix operations will
    first copy the data, perform the DGEMM, and then copy the data to the required
    order.

    Examples
    --------

    >>> a = np.random.rand(4, 4)
    >>> b = np.random.rand(4, 4)
    >>> tmp = tensor_blas(a, 'ij', b, 'jk', 'ik', set('j'))
    >>> np.allclose(tmp, np.dot(a, b))

    Ú N)r   r   )Zaxesc             3   s   | ]}ˆ | V  qd S )Nr   )r	   r
   )Údimension_dictr   r   ú	<genexpr>é   s    ztensor_blas.<locals>.<genexpr>r   z->)r   ÚzipÚshaper   r   Zcompute_size_by_dictÚreplaceÚnpÚdotZravelZreshapeÚTr   Z	tensordotÚtupleZsqueezeZeinsum)Z	view_leftr   Z
view_rightr   Zindex_resultr   r   r   ÚiÚsr   Zdim_leftZ	dim_rightZdim_removedZtensor_resultZnew_viewZleft_posZ	right_posZtensor_shaper   )r   r   r   {   sL    -
 "  


)N)Ú__doc__Únumpyr#   r   r   Ú__all__r   r   r   r   r   r   Ú<module>   s
   
o