o
    Üe  ã                   @   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 | \}}t|| ƒD ]-}| |¡| |¡}}|dks-|dks-|| dkr0 dS || d t||v ƒkr? dS q|dur^|D ]}|d | |¡ |d | |¡ kr] dS qFt |ƒdkrfdS dd„ | D ƒ}	|	d | }
|	d | }t |ƒ}| d | d kr‡d	S |	d |	d kr‘d
S || d… |d|… kr dS |d|… || d… kr¯d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 )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   ú?D:\Projects\ConvertPro\env\Lib\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)ZinputsÚresultÚidx_removedZshapesÚ
input_leftÚinput_rightÚcÚnlÚnrZsetsÚ	keep_leftÚ
keep_rightÚrsr   r   r   r      sH   +ÿ$ÿc                    sZ  t |ƒ}t |ƒ| }t |ƒ| }i ‰ t|| jƒD ]\}}	|	ˆ |< qt||jƒD ]\}}	|	ˆ |< q't|ƒ}
t |ˆ ¡}t |ˆ ¡}t |ˆ ¡}|| }|D ]}	| |	d¡}qL||krdt |  	¡ | 	¡ ¡}n—||
 d… |d|
… kr€t |  
||¡| 
||¡¡}n{|d|
… ||
 d… kržt |  
||¡j| 
||¡j¡}n]||
 d… ||
 d… kr¼t |  
||¡| 
||¡j¡}n?|d|
… |d|
… krØt |  
||¡j| 
||¡¡}n#d\}}|D ]}	|| |	¡f7 }|| |	¡f7 }qÞtj| |||fd}t‡ fdd„|D ƒƒ}|j|krt|ƒdkr||_nt |¡}||kr+t |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	   ©Z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   -

"  

r   )Ú__doc__Únumpyr$   r   r   Ú__all__r   r   r   r   r   r   Ú<module>   s    
o