a
    Sic  ã                   @   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 ]Z}| |¡| |¡ }}|dksZ|dksZ|| dkr` dS || d t||v ƒkr$ dS q$|dur¼|D ].}|d | |¡ |d | |¡ krŒ dS qŒ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rFdS |d|… || d… krfd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   úK/var/www/html/django/DPS/env/lib/python3.9/site-packages/opt_einsum/blas.pyÚ
<listcomp>U   ó    zcan_blas.<locals>.<listcomp>ÚDOTz
DOT/EINSUMZGEMMzGEMV/EINSUMZTDOT)Úlenr   ÚcountÚintÚfind)ÚinputsÚresultÚidx_removedÚshapesÚ
input_leftÚinput_rightÚcÚnlÚnrÚsetsÚ	keep_leftÚ
keep_rightÚrsr   r   r   r      sD    +$c                    sf  t |ƒ}t |ƒ| }t |ƒ| }i ‰ t|| jƒD ]\}}	|	ˆ |< q0t||jƒD ]\}}	|	ˆ |< qNt|ƒ}
t |ˆ ¡}t |ˆ ¡}t |ˆ ¡}|| }|D ]}	| |	d¡}q˜||krÊt |  	¡ | 	¡ ¡}n8||
 d… |d|
… krt |  
||¡| 
||¡¡}nþ|d|
… ||
 d… krBt |  
||¡j| 
||¡j¡}nÀ||
 d… ||
 d… kr€t |  
||¡| 
||¡j¡}n‚|d|
… |d|
… krºt |  
||¡j| 
||¡¡}nHd\}}|D ]&}	|| |	¡f7 }|| |	¡f7 }qÆtj| |||fd}t‡ fdd„|D ƒƒ}|j|krDt|ƒdkr:||_n
t |¡}||krbt |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   )Úaxesc                 3   s   | ]}ˆ | V  qd S )Nr   r	   ©Údimension_dictr   r   Ú	<genexpr>é   r   ztensor_blas.<locals>.<genexpr>r   z->)r   ÚzipÚshaper   r   Zcompute_size_by_dictÚreplaceÚnpÚdotÚravelÚreshapeÚTr   Ú	tensordotÚtupleÚsqueezeÚeinsum)Z	view_leftr   Z
view_rightr   Zindex_resultr   r   r   ÚiÚsr    Zdim_leftZ	dim_rightZdim_removedÚtensor_resultÚnew_viewÚleft_posÚ	right_posÚtensor_shaper   r#   r   r   {   sL    -

"  

)N)Ú__doc__Únumpyr)   r!   r   Ú__all__r   r   r   r   r   r   Ú<module>   s
   
o