a
    SicΉ                     @   s  d Z ddlmZ ddlmZ ddlZddlmZm	Z	m
Z
mZmZmZ g dZG dd	 d	eZd
d Zh dZdd Zejdd Zdd Zejd,ddZejd-ddZdd Zdd Zdd Zd.d d!Zd"d# ZG d$d% d%Z ed&d'gZ!d(d) Z"d*d+ Z#dS )/z=
Contains the primary optimization and contraction routines.
    )
namedtuple)DecimalN   )backendsblashelpersparserpathssharing)contract_pathcontractformat_const_einsum_strContractExpression
shape_onlyc                   @   s    e Zd ZdZdd Zdd ZdS )PathInfoa  A printable object to contain information about a contraction path.

    Attributes
    ----------
    naive_cost : int
        The estimate FLOP cost of a naive einsum contraction.
    opt_cost : int
        The estimate FLOP cost of this optimized contraction path.
    largest_intermediate : int
        The number of elements in the largest intermediate array that will be
        produced during the contraction.
    c                    s   || _ || _|| _|| _|| _|| _t|| _t|| _| j| j | _	|	| _
 | _ fdd|dD | _d||| _tt|	| _d S )Nc                    s"   g | ]}t  fd d|D qS )c                 3   s   | ]} | V  qd S N ).0k	size_dictr   O/var/www/html/django/DPS/env/lib/python3.9/site-packages/opt_einsum/contract.py	<genexpr>*       z/PathInfo.__init__.<locals>.<listcomp>.<genexpr>)tuple)r   ksr   r   r   
<listcomp>*   r   z%PathInfo.__init__.<locals>.<listcomp>,z{}->{})contraction_listinput_subscriptsoutput_subscriptpathindices
scale_listr   
naive_costopt_costspeedup	size_listr   splitshapesformateqmaxlargest_intermediate)selfr   r   r    r"   r!   r#   r$   r%   r'   r   r   r   r   __init__   s    

zPathInfo.__init__c              
   C   s   d}d | jd t| jd t| jd | jd | jd | jd | j	d	d
j | dg
}t
| jD ]t\}}|\}}}}}	|d urd|d | j }
nd}
tddtdt| }| j| |	||
|f}|dj |  qld|S )N)scalingZBLAScurrent	remainingz  Complete contraction:  {}
z         Naive scaling:  {}
z     Optimized scaling:  {}
z       Naive FLOP count:  {:.3e}
z   Optimized FLOP count:  {:.3e}
z    Theoretical speedup:  {:.3e}
z)  Largest intermediate:  {:.3e} elements
zQ--------------------------------------------------------------------------------
z{:>6} {:>11} {:>22} {:>37}
zP--------------------------------------------------------------------------------r   ->z...r   8      z
{:>4} {:>14} {:>22}    {:>{}} )r*   r+   lenr"   r,   r#   r$   r%   r&   r-   	enumerater   joinr    append)r.   header
path_printncontractionindsidx_rm
einsum_strr2   do_blasremaining_strZsize_remainingpath_runr   r   r   __repr__.   s&    


	zPathInfo.__repr__N)__name__
__module____qualname____doc__r/   rE   r   r   r   r   r      s   r   c                 C   s@   | dkrt |S | d u rd S | dk r8| dkr0d S tdt| S )N	max_inputr   z)Memory limit must be larger than 0, or -1)r,   
ValueErrorint)memory_limitr'   r   r   r   _choose_memory_argJ   s    rO   >   rN   use_blasr!   optimizer)   einsum_callc            .         s  t |t }t|r"td||dd}|dd}|dd}|dd}|d	d
}t| \}}	} |d dd  D }
|r| ndd | D t |	}t |	dd}i t
 D ]\}}| }t|t|krtd | |t
|D ]j\}}t|| }|v r`| dkr4||< n*|d| fvrhtd||| |q||< qqfdd |	g D }t||}t }tdd |
D t| dk}t|||}t|ttjfs|}nP|dkrtt|g}n6t|tjr||
||}nt|}||
||}g }g }g }g }t
|D ]T\}}ttt|d
d}t||
|}|\}}
} }!t|!| t|}"||" |t|! |t|  fdd|D }#fdd|D }$|rt|#|| |$}%nd}%|t| dkr|	}&nd|#}'dt||'j d}&t!|#|$|&}( |& |( d|#d |& })t dkr|t }*nd}*|| |)|*|%f}+||+ qDt|},|r| |fS t"|||	|||||,|
}-||-fS )a  
    Find a contraction order 'path', without performing the contraction.

    Parameters
    ----------
    subscripts : str
        Specifies the subscripts for summation.
    *operands : list of array_like
        These are the arrays for the operation.
    optimize : str, list or bool, optional (default: ``auto``)
        Choose the type of path.

        - if a list is given uses this as the path.
        - ``'optimal'`` An algorithm that explores all possible ways of
          contracting the listed tensors. Scales factorially with the number of
          terms in the contraction.
        - ``'branch-all'`` An algorithm like optimal but that restricts itself
          to searching 'likely' paths. Still scales factorially.
        - ``'branch-2'`` An even more restricted version of 'branch-all' that
          only searches the best two options at each step. Scales exponentially
          with the number of terms in the contraction.
        - ``'greedy'`` An algorithm that heuristically chooses the best pair
          contraction at each step.
        - ``'auto'`` Choose the best of the above algorithms whilst aiming to
          keep the path finding time below 1ms.

    use_blas : bool
        Use BLAS functions or not
    memory_limit : int, optional (default: None)
        Maximum number of elements allowed in intermediate arrays.
    shapes : bool, optional
        Whether ``contract_path`` should assume arrays (the default) or array
        shapes have been supplied.

    Returns
    -------
    path : list of tuples
        The einsum path
    PathInfo : str
        A printable object containing various information about the path found.

    Notes
    -----
    The resulting path indicates which terms of the input contraction should be
    contracted first, the result of this contraction is then appended to the end of
    the contraction list.

    Examples
    --------

    We can begin with a chain dot example. In this case, it is optimal to
    contract the b and c tensors represented by the first element of the path (1,
    2). The resulting tensor is added to the end of the contraction and the
    remaining contraction, ``(0, 1)``, is then executed.

    >>> a = np.random.rand(2, 2)
    >>> b = np.random.rand(2, 5)
    >>> c = np.random.rand(5, 2)
    >>> path_info = opt_einsum.contract_path('ij,jk,kl->il', a, b, c)
    >>> print(path_info[0])
    [(1, 2), (0, 1)]
    >>> print(path_info[1])
      Complete contraction:  ij,jk,kl->il
             Naive scaling:  4
         Optimized scaling:  3
          Naive FLOP count:  1.600e+02
      Optimized FLOP count:  5.600e+01
       Theoretical speedup:  2.857
      Largest intermediate:  4.000e+00 elements
    -------------------------------------------------------------------------
    scaling                  current                                remaining
    -------------------------------------------------------------------------
       3                   kl,jk->jl                                ij,jl->il
       3                   jl,ij->il                                   il->il


    A more complex index transformation example.

    >>> I = np.random.rand(10, 10, 10, 10)
    >>> C = np.random.rand(10, 10)
    >>> path_info = oe.contract_path('ea,fb,abcd,gc,hd->efgh', C, C, I, C, C)

    >>> print(path_info[0])
    [(0, 2), (0, 3), (0, 2), (0, 1)]
    >>> print(path_info[1])
      Complete contraction:  ea,fb,abcd,gc,hd->efgh
             Naive scaling:  8
         Optimized scaling:  5
          Naive FLOP count:  8.000e+08
      Optimized FLOP count:  8.000e+05
       Theoretical speedup:  1000.000
      Largest intermediate:  1.000e+04 elements
    --------------------------------------------------------------------------
    scaling                  current                                remaining
    --------------------------------------------------------------------------
       5               abcd,ea->bcde                      fb,gc,hd,bcde->efgh
       5               bcde,fb->cdef                         gc,hd,cdef->efgh
       5               cdef,gc->defg                            hd,defg->efgh
       5               defg,hd->efgh                               efgh->efgh
    z8einsum_path: Did not understand the following kwargs: {}rQ   autorN   Nr)   FrR   rP   Tr   c                 S   s   g | ]}t |qS r   )setr   xr   r   r   r      r   z!contract_path.<locals>.<listcomp>c                 S   s   g | ]
}|j qS r   shaperU   r   r   r   r      r   r6   zZEinstein sum subscript '{}' does not contain the correct number of indices for operand {}.r   zJSize of label '{}' for operand {} ({}) does not match previous terms ({}).c                    s   g | ]}t | qS r   )r   compute_size_by_dict)r   termr   r   r   r      r   c                 s   s   | ]}t |V  qd S r   )r7   rU   r   r   r   r      r   z contract_path.<locals>.<genexpr>r      )reversec                    s   g | ]}  |qS r   poprU   )
input_listr   r   r   !  r   c                    s   g | ]}  |qS r   r]   rU   )
input_shpsr   r   r   "  r   rK   )keyr3      )#rT   _VALID_CONTRACT_KWARGSr7   	TypeErrorr*   r^   r   parse_einsum_inputr(   replacer8   rL   rM   rO   sumr   
flop_count
isinstancestrr	   PathOptimizerr   rangeget_path_fnsortedlistfind_contractionr:   rY   r   can_blasr9   findfind_output_shaper   ).operandskwargsunknown_kwargs	path_typerN   r)   einsum_call_argrP   r   r    
input_sets
output_setr"   tnumrZ   shcnumchardimr'   
memory_argZnum_opsinner_productr$   r!   Zpath_optimizer	cost_listr#   r   contract_indsZcontract_tupleout_indsidx_removedidx_contractcost
tmp_inputsZ
tmp_shapesrB   
idx_resultZall_input_indsZ
shp_resultrA   r2   r>   r%   r<   r   )r_   r`   r   r   r   ]   s    g










r   c                  O   s   t d|dd}t| d ts0|| i |S | d | dd  }} t|std|vrj|dt| 7 }t|}||g| R i |S )zOBase einsum, but with pre-parse for valid characters if a string is given.
    einsumbackendnumpyr   r   Nr3   )	r   get_funcr^   ri   rj   r   has_valid_einsum_chars_onlyfind_output_strconvert_to_valid_einsum_chars)rt   ru   fnrA   r   r   r   _einsumM  s    

r   c                 C   s
   |  |S r   )	transpose)rV   axesr   r   r   _default_transposed  s    r   r   c                 C   s   t d|t}|| |S )zBase transpose.
    r   )r   r   r   )rV   r   r   r   r   r   r   
_transposei  s    r   c                 C   s   t d|}|| ||dS )zBase tensordot.
    	tensordot)r   )r   r   )rV   yr   r   r   r   r   r   
_tensordotq  s    r   c                     s  | dd}|du rd}g d  fdd| D }|du rLt| i |S | dd}| d	d
}| dd}| dd}| di } fdd| D }	t|	rtd|	|r| d }
t| ||d|d\} }|rt|
||fi |S t| |fd|i|S )a@  
    contract(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', use_blas=True, optimize=True, memory_limit=None, backend='numpy')

    Evaluates the Einstein summation convention on the operands. A drop in
    replacement for NumPy's einsum function that optimizes the order of contraction
    to reduce overall scaling at the cost of several intermediate arrays.

    Parameters
    ----------
    subscripts : str
        Specifies the subscripts for summation.
    *operands : list of array_like
        These are the arrays for the operation.
    out : array_like
        A output array in which set the resulting output.
    dtype : str
        The dtype of the given contraction, see np.einsum.
    order : str
        The order of the resulting contraction, see np.einsum.
    casting : str
        The casting procedure for operations of different dtype, see np.einsum.
    use_blas : bool
        Do you use BLAS for valid operations, may use extra memory for more intermediates.
    optimize : str, list or bool, optional (default: ``auto``)
        Choose the type of path.

        - if a list is given uses this as the path.
        - ``'optimal'`` An algorithm that explores all possible ways of
          contracting the listed tensors. Scales factorially with the number of
          terms in the contraction.
        - ``'dp'`` A faster (but essentially optimal) algorithm that uses
          dynamic programming to exhaustively search all contraction paths
          without outer-products.
        - ``'greedy'`` An cheap algorithm that heuristically chooses the best
          pairwise contraction at each step. Scales linearly in the number of
          terms in the contraction.
        - ``'random-greedy'`` Run a randomized version of the greedy algorithm
          32 times and pick the best path.
        - ``'random-greedy-128'`` Run a randomized version of the greedy
          algorithm 128 times and pick the best path.
        - ``'branch-all'`` An algorithm like optimal but that restricts itself
          to searching 'likely' paths. Still scales factorially.
        - ``'branch-2'`` An even more restricted version of 'branch-all' that
          only searches the best two options at each step. Scales exponentially
          with the number of terms in the contraction.
        - ``'auto'`` Choose the best of the above algorithms whilst aiming to
          keep the path finding time below 1ms.
        - ``'auto-hq'`` Aim for a high quality contraction, choosing the best
          of the above algorithms whilst aiming to keep the path finding time
          below 1sec.

    memory_limit : {None, int, 'max_input'} (default: None)
        Give the upper bound of the largest intermediate tensor contract will build.

        - None or -1 means there is no limit
        - 'max_input' means the limit is set as largest input tensor
        - a positive integer is taken as an explicit limit on the number of elements

        The default is None. Note that imposing a limit can make contractions
        exponentially slower to perform.
    backend : str, optional (default: ``auto``)
        Which library to use to perform the required ``tensordot``, ``transpose``
        and ``einsum`` calls. Should match the types of arrays supplied, See
        :func:`contract_expression` for generating expressions which convert
        numpy arrays to and from the backend library automatically.

    Returns
    -------
    out : array_like
        The result of the einsum expression.

    Notes
    -----
    This function should produce a result identical to that of NumPy's einsum
    function. The primary difference is ``contract`` will attempt to form
    intermediates which reduce the overall scaling of the given einsum contraction.
    By default the worst intermediate formed will be equal to that of the largest
    input array. For large einsum expressions with many input arrays this can
    provide arbitrarily large (1000 fold+) speed improvements.

    For contractions with just two tensors this function will attempt to use
    NumPy's built-in BLAS functionality to ensure that the given operation is
    preformed optimally. When NumPy is linked to a threaded BLAS, potential
    speedups are on the order of 20-100 for a six core machine.

    Examples
    --------

    See :func:`opt_einsum.contract_path` or :func:`numpy.einsum`

    rQ   TrS   )outdtypeordercastingc                    s   i | ]\}}| v r||qS r   r   r   r   vvalid_einsum_kwargsr   r   
<dictcomp>  r   zcontract.<locals>.<dictcomp>FrP   rN   Nr   _gen_expression_constants_dictc                    s   g | ]\}}| vr|qS r   r   r   r   r   r   r     r   zcontract.<locals>.<listcomp>z+Did not understand the following kwargs: {}r   )rQ   rN   rR   rP   )	r^   itemsr   r7   rd   r*   r   r   _core_contract)rt   ru   Zoptimize_argeinsum_kwargsrP   rN   r   Zgen_expressionconstants_dictrv   Zfull_strr   r   r   r   r   z  s4    \r   c                 C   s   | j jdd S )N.r   )	__class__rG   r(   )rV   r   r   r   infer_backend  s    r   c                 C   s*   |dkr|S t | d }t|s&dS |S )zuFind out what backend we should use, dipatching based on the first
    array if ``backend='auto'`` is specified.
    rS   r   r   )r   r   Zhas_tensordot)arraysr   r   r   r   parse_backend  s    
r   rS   Fc                    s  | dd}|du}t|}t| }t|D ]~\}}	|	\}
 }}}|rxtfdd|
D rx||d f  S fdd|
D }|o|d t|k}|r|d|vs|r||d	\}}|d
\}}d fdd|| D }g g  }} D ]$}|	|
| |	|
| qt|t|t|f|d}||ksJ|rtt|j|}t|||d}|r||dd< n(|r||d< t|g|R d|i|}	| ~~q2|r|S d S dS )zInner loop used to perform an actual contraction given the output
    from a ``contract_path(..., einsum_call=True)`` call.
    r   Nc                 3   s   | ]} | d u V  qd S r   r   rU   rt   r   r   r   %  r   z!_core_contract.<locals>.<genexpr>c                    s   g | ]}  |qS r   r]   rU   r   r   r   r   (  r   z"_core_contract.<locals>.<listcomp>r   ZEINSUMr3   r   r6   c                 3   s   | ]}| vr|V  qd S r   r   )r   s)r@   r   r   r   4  r   )r   r   r   r   )r^   r   r   Z
has_einsumr8   anyr7   r(   r9   r:   rr   r   r   mapindexr   r   )rt   r   r   evaluate_constantsr   Z	out_arrayspecified_outZ	no_einsumnumr>   r?   rA   _Z	blas_flagtmp_operands
handle_out	input_strresults_index
input_leftinput_righttensor_resultleft_pos	right_posr   new_viewr   r   )r@   rt   r   r     s@    


r   c                    st    s| S d| v r$|  d\}}d}n| dd  }}} fddt| dD }dd|||}|dd}|S )zAdd brackets to the constant terms in ``einsum_str``. For example:

        >>> format_const_einsum_str('ab,bc,cd->ad', [0, 2])
        'bc,[ab,cd]->ad'

    No-op if there are no constants.
    r3   r6   c                    s&   g | ]\}}| v rd  |n|qS )z[{}])r*   )r   it	constantsr   r   r   l  r   z+format_const_einsum_str.<locals>.<listcomp>r   z{}{}{}z],[)r(   r8   r*   r9   rf   )rA   r   lhsrhsarrowZwrapped_termsZformatted_einsum_strr   r   r   r   [  s    r   c                   @   s^   e Zd ZdZdd ZdddZdd Zd	d
 ZdddZdddZ	dd Z
dd Zdd ZdS )r   zHelper class for storing an explicit ``contraction_list`` which can
    then be repeatedly called solely with the array arguments.
    c                 K   sX   || _ || _t|| | _|dd | _| jt| | _|| _	|| _
i | _i | _d S )Nr   r   )r   r   r   keysr>   count_full_num_argsr7   num_args_full_contraction_listr   _evaluated_constants_backend_expressions)r.   r>   r   r   r   r   r   r   r/   y  s    zContractExpression.__init__rS   c                    st    fddt  jD }t||}zt|| \}}W n& ty^    ||dd\}}Y n0 | j|< | _dS )aF  Convert any constant operands to the correct backend form, and
        perform as many contractions as possible to create a new list of
        operands, stored in ``self._evaluated_constants[backend]``. This also
        makes sure ``self.contraction_list`` only contains the remaining,
        non-const operations.
        c                    s   g | ]} j |d qS r   )r   getr   r   r.   r   r   r     r   z9ContractExpression.evaluate_constants.<locals>.<listcomp>T)r   r   N)rl   r   r   r   r   KeyErrorr   r   )r.   r   Ztmp_const_opsnew_opsZnew_contraction_listr   r   r   r     s    

z%ContractExpression.evaluate_constantsc                 C   s8   z| j | W S  ty2   | | | j |  Y S 0 dS )zRetrieve or generate the cached list of constant operators (mixed
        in with None representing non-consts) and the remaining contraction
        list.
        N)r   r   r   )r.   r   r   r   r   _get_evaluated_constants  s
    
z+ContractExpression._get_evaluated_constantsc                 C   s@   z| j | W S  ty:   t||| }|| j |< | Y S 0 d S r   )r   r   r   Zbuild_expression)r.   r   r   r   r   r   r   _get_backend_expression  s    
z*ContractExpression._get_backend_expressionNFc                 C   s0   |r
| j n| j}tt||f|||d| jS )z&The normal, core contraction.
        )r   r   r   )r   r   r   ro   r   )r.   r   r   r   r   r   r   r   r   	_contract  s    zContractExpression._contractc                 C   s:   |rt ||| S | ||| }|dur6||d< |S |S )a  Special contraction, i.e., contraction with a different backend
        but converting to and from that backend. Retrieves or generates a
        cached expression using ``arrays`` as templates, then calls it
        with ``arrays``.

        If ``evaluate_constants=True``, perform a partial contraction that
        prepares the constant tensors and operations with the right backend.
        Nr   )r   r   r   )r.   r   r   r   r   resultr   r   r   _contract_with_conversion  s    
z,ContractExpression._contract_with_conversionc              
      sB  | dd}| dd}t||}| dd}|r@td||rJ| jn| j}t||krrtd| jt|| jr|st|| 	|  } fd	d
|D }n|}zDt
|rtdd |D r| j||||dW S | j||||dW S  ty< }	 z6|	jrt|	jnd}
d|
f}||	_ W Y d}	~	n
d}	~	0 0 dS )a  Evaluate this expression with a set of arrays.

        Parameters
        ----------
        arrays : seq of array
            The arrays to supply as input to the expression.
        out : array, optional (default: ``None``)
            If specified, output the result into this array.
        backend : str, optional  (default: ``numpy``)
            Perform the contraction with this backend library. If numpy arrays
            are supplied then try to convert them to and from the correct
            backend array type.
        r   Nr   rS   r   FzbThe only valid keyword arguments to a `ContractExpression` call are `out=` or `backend=`. Got: {}.zKThis `ContractExpression` takes exactly {} array arguments but received {}.c                    s    g | ]}|d u rt  n|qS r   )next)r   opZops_varr   r   r     r   z/ContractExpression.__call__.<locals>.<listcomp>c                 s   s   | ]}t |tjV  qd S r   )ri   npndarrayrU   r   r   r   r     r   z.ContractExpression.__call__.<locals>.<genexpr>)r   r6   zInternal error while evaluating `ContractExpression`. Note that few checks are performed - the number and rank of the array arguments must match the original expression. The internal error was: '{}')r^   r   rL   r*   r   r   r7   r   iterr   r   Zhas_backendallr   r   argsrj   )r.   r   ru   r   r   r   Zcorrect_num_argsZ	ops_constopserrZoriginal_msgmsgr   r   r   __call__  s8    


zContractExpression.__call__c                 C   s*   | j rdt| j }nd}d| j|S )Nz, constants={}r6   z<ContractExpression('{}'{})>)r   r*   rn   r>   )r.   Zconstants_reprr   r   r   rE     s    zContractExpression.__repr__c                 C   s   |   g}t| jD ]J\}}|d|d  |d|d |d rVd|d nd  q| jrx|d| j d|S )	Nz
  {}.  r   z'{}'r[   rK   z [{}]r6   z
einsum_kwargs={})rE   r8   r   r:   r*   r   r9   )r.   r   r   cr   r   r   __str__  s    
0zContractExpression.__str__)rS   )NrS   F)F)rF   rG   rH   rI   r/   r   r   r   r   r   r   rE   r   r   r   r   r   r   u  s   


4r   ShapedrX   c                 C   s   t | S )z^Dummy ``numpy.ndarray`` which has a shape only - for generating
    contract expressions.
    )r   rW   r   r   r   r     s    r   c                    s   | ddstddD ]"}| |ddurtd|qt| tsZt| f \} d|d< |dd	 fd
d D }||d<  fddtD }t	| g|R i |S )a  Generate a reusable expression for a given contraction with
    specific shapes, which can, for example, be cached.

    Parameters
    ----------
    subscripts : str
        Specifies the subscripts for summation.
    shapes : sequence of integer tuples
        Shapes of the arrays to optimize the contraction for.
    constants : sequence of int, optional
        The indices of any constant arguments in ``shapes``, in which case the
        actual array should be supplied at that position rather than just a
        shape. If these are specified, then constant parts of the contraction
        between calls will be reused. Additionally, if a GPU-enabled backend is
        used for example, then the constant tensors will be kept on the GPU,
        minimizing transfers.
    kwargs :
        Passed on to ``contract_path`` or ``einsum``. See ``contract``.

    Returns
    -------
    expr : ContractExpression
        Callable with signature ``expr(*arrays, out=None, backend='numpy')``
        where the array's shapes should match ``shapes``.

    Notes
    -----
    - The `out` keyword argument should be supplied to the generated expression
      rather than this function.
    - The `backend` keyword argument should also be supplied to the generated
      expression. If numpy arrays are supplied, if possible they will be
      converted to and back from the correct backend array type.
    - The generated expression will work with any arrays which have
      the same rank (number of dimensions) as the original shapes, however, if
      the actual sizes are different, the expression may no longer be optimal.
    - Constant operations will be computed upon the first call with a particular
      backend, then subsequently reused.

    Examples
    --------

    Basic usage:

        >>> expr = contract_expression("ab,bc->ac", (3, 4), (4, 5))
        >>> a, b = np.random.rand(3, 4), np.random.rand(4, 5)
        >>> c = expr(a, b)
        >>> np.allclose(c, a @ b)
        True

    Supply ``a`` as a constant:

        >>> expr = contract_expression("ab,bc->ac", a, (4, 5), constants=[0])
        >>> expr
        <ContractExpression('[ab],bc->ac', constants=[0])>

        >>> c = expr(b)
        >>> np.allclose(c, a @ b)
        True

    rQ   Tz9Can only generate expressions for optimized contractions.)r   r   NzX'{}' should only be specified when calling a `ContractExpression`, not when building it.r   r   r   c                    s   i | ]}| | qS r   r   r   )r)   r   r   r   l  r   z'contract_expression.<locals>.<dictcomp>r   c                    s$   g | ]\}}| v r|nt |qS r   )r   )r   r   r   r   r   r   r   p  r   z'contract_expression.<locals>.<listcomp>)
r   rL   r*   ri   rj   r   convert_interleaved_inputr^   r8   r   )
subscriptsr)   ru   argr   Zdummy_arraysr   )r   r)   r   contract_expression   s    =
r   )r   )r   )rS   F)$rI   collectionsr   decimalr   r   r   r6   r   r   r   r   r	   r
   __all__objectr   rO   rc   r   Zeinsum_cache_wrapr   r   Ztranspose_cache_wrapr   Ztensordot_cache_wrapr   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s8    ; q
 
I "