a
    Sic                     @   s  d Z ddlZddlZddlZddlZddlmZmZmZ ddl	Z
ddlmZ g dZddedhZG d	d
 d
eZdd Zdd Zdd Zdd ZdWddZdd Zdd ZeedZdd Zdd Zdd Zeed ZG d!d" d"eZdXd#d$Zej edd%Z!ej ed&d%Z"ej edd%Z#d'd( Z$d)d* Z%d+d, Z&d-d. Z'dYd0d1Z(dZd2d3Z)d4d5 Z*d6d7 Z+d8d9 Z,d:d; Z-d<d= Z.d>d? Z/d@dA Z0dBdC Z1G dDdE dEeZ2d[dFdGZ3i Z4e5ddHD ]Z6ee4e6< qe5dHdID ]Z6e!e4e6< qe5dIdJD ]Z6e"e4e6< qe5dJdKD ]Z6e#e4e6< qd\dLdMZ7i Z8e5ddND ]Z6ee8e6< q.e5dNdOD ]Z6e3e8e6< qHd]dPdQZ9e7e9ee!e"e#e)e)e)e3e3dRZ:dSdT Z;dUdV Z<dS )^zT
Contains the path technology behind opt_einsum in addition to several path helpers
    N)CounterOrderedDictdefaultdict   )helpers)	optimalBranchBoundbranchgreedyautoauto_hqget_path_fnDynamicProgrammingdynamic_programminginfc                   @   s"   e Zd ZdZdd ZdddZdS )PathOptimizera  Base class for different path optimizers to inherit from.

    Subclassed optimizers should define a call method with signature::

        def __call__(self, inputs, output, size_dict, memory_limit=None):
            """
            Parameters
            ----------
            inputs : list[set[str]]
                The indices of each input array.
            outputs : set[str]
                The output indices
            size_dict : dict[str, int]
                The size of each index
            memory_limit : int, optional
                If given, the maximum allowed memory.
            """
            # ... compute path here ...
            return path

    where ``path`` is a list of int-tuples specifiying a contraction order.
    c                 C   s2   |||f}t | ds|| _n|| jkr.tddS )zUtility that stateful optimizers can use to ensure they are not
        called with different contractions across separate runs.
        _first_call_argszThe arguments specifiying the contraction that this path optimizer instance was called with have changed - try creating a new instance.N)hasattrr   
ValueError)selfinputsoutput	size_dictargs r   L/var/www/html/django/DPS/env/lib/python3.9/site-packages/opt_einsum/paths.py_check_args_against_first_call/   s
    


z,PathOptimizer._check_args_against_first_callNc                 C   s   t d S N)NotImplementedErrorr   r   r   r   memory_limitr   r   r   __call__;   s    zPathOptimizer.__call__)N)__name__
__module____qualname____doc__r   r"   r   r   r   r   r      s   r   c                    sj   t jdttt|  t jd g }| D ]>}|t fdd|D  |D ]} |d  d8  < qJq&|S )z
    Convert a path with static single assignment ids to a path with recycled
    linear ids. For example::

        >>> ssa_to_linear([(0, 3), (2, 4), (1, 5)])
        [(0, 3), (1, 2), (0, 1)]
    r   )dtypec                 3   s   | ]}t  | V  qd S r   )int).0ssa_ididsr   r   	<genexpr>J       z ssa_to_linear.<locals>.<genexpr>N)nparangemaxmapint32appendtuple)ssa_pathpathssa_idsr*   r   r+   r   ssa_to_linear?   s    r9   c                    s   t tt| t|  d }tt| t|}g }| D ]F}|t fdd|D  t	|ddD ]
} |= qd t
| q8|S )z
    Convert a path with recycled linear ids to a path with static single
    assignment ids. For example::

        >>> linear_to_ssa([(0, 3), (1, 2), (0, 1)])
        [(0, 3), (2, 4), (1, 5)]
    r   c                 3   s   | ]} | V  qd S r   r   )r)   id_linear_to_ssar   r   r-   ]   r.   z linear_to_ssa.<locals>.<genexpr>T)reverse)sumr2   lenlistrange	itertoolscountr4   r5   sortednext)r7   
num_inputsZnew_idsr6   r,   r:   r   r;   r   r<   P   s    
r<   c                 C   sh   | | | |  }}||B }||@ }	t j|gt| j|||h R  }
||
@ }t||	|
 d|}||fS )a  
    Calculate the resulting indices and flops for a potential pairwise
    contraction - used in the recursive (optimal/branch) algorithms.

    Parameters
    ----------
    inputs : tuple[frozenset[str]]
        The indices of each tensor in this contraction, note this includes
        tensors unavaiable to contract as static single assignment is used ->
        contracted tensors are not removed from the list.
    output : frozenset[str]
        The set of output indices for the whole contraction.
    remaining : frozenset[int]
        The set of indices (corresponding to ``inputs``) of tensors still
        available to contract.
    i : int
        Index of potential tensor to contract.
    j : int
        Index of potential tensor to contract.
    size_dict dict[str, int]
        Size mapping of all the indices.

    Returns
    -------
    k12 : frozenset
        The resulting indices of the potential tensor.
    cost : int
        Estimated flop count of operation.
       )	frozensetunionr2   __getitem__r   
flop_count)r   r   	remainingijr   k1k2eithersharedkeepk12costr   r   r   calc_k12_flopsd   s    "rV   c                 C   s2   t jt| j| }|| }t|}t||||S )z
    Compute the flop count for a contraction of all remaining arguments. This
    is used when a memory limit means that no pairwise contractions can be made.
    )rH   rI   r2   rJ   r?   r   rK   )r   rL   r   r   idx_contractioninner	num_termsr   r   r   _compute_oversize_flops   s    rZ   c                    sz   t tt| } ttdt tt| fdi i  fdd  d| ttt| dd td S )	a  
    Computes all possible pair contractions in a depth-first recursive manner,
    sieving results based on ``memory_limit`` and the best path found so far.
    Returns the lowest cost path. This algorithm scales factoriallly with
    respect to the elements in the list ``input_sets``.

    Parameters
    ----------
    inputs : list
        List of sets that represent the lhs side of the einsum subscript.
    output : set
        Set that represents the rhs side of the overall einsum subscript.
    size_dict : dictionary
        Dictionary of index sizes.
    memory_limit : int
        The maximum number of elements in a temporary array.

    Returns
    -------
    path : list
        The optimal contraction order within the memory limit constraint.

    Examples
    --------
    >>> isets = [set('abd'), set('ac'), set('bdc')]
    >>> oset = set('')
    >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
    >>> optimal(isets, oset, idx_sizes, 5000)
    [(0, 2), (0, 1)]
    r   )flopsr6   c                    sj  t |dkr |d< | d< d S t|dD ]6\}}||krH|| }}|| || f}z| \}}W n0 ty   t|||| \}}|< Y n0 || }	|	d krq,tvr2z| }
W n& ty   t| }
|< Y n0 |
kr2|t|| }	|	d k r,|	d< | t	|f d< q, | ||ff ||f |||h t |hB |	d q,d S )Nr   r[   r6   rG   r7   r   rL   r[   )
r?   rB   combinationsKeyErrorrV   _UNLIMITED_MEMr   compute_size_by_dictrZ   r5   )r7   rL   r   r[   rM   rN   keyrT   flops12	new_flopssize12_optimal_iteratebestr!   r   result_cache
size_cacher   r   r   rf      s>    
$

z!optimal.<locals>._optimal_iterater   r   r\   r6   )r5   r2   rH   floatrA   r?   setr9   )r   r   r   r!   r   re   r   r      s    ,r   c                 C   s   | |f||fk S r   r   r[   sizeZ
best_flopsZ	best_sizer   r   r   better_flops_first   s    rn   c                 C   s   || f||fk S r   r   rl   r   r   r   better_size_first   s    ro   r[   rm   c                 C   s   t |  S r   )_BETTER_FNSra   r   r   r   get_better_fn   s    rs   c                 C   s   | | | S )zpThe default heuristic cost, corresponding to the total reduction in
    memory of performing a contraction.
    r   rd   size1size2rT   rO   rP   r   r   r   cost_memory_removed  s    rw   c                 C   s   t dd| | |  S )zyLike memory-removed, but with a slight amount of noise that breaks ties
    and thus jumbles the contractions a bit.
    g      ?g{Gz?)randomgaussrt   r   r   r   cost_memory_removed_jitter  s    rz   )memory-removedzmemory-removed-jitterc                   @   s0   e Zd ZdZdddZedd	 Zdd
dZdS )r   a  
    Explores possible pair contractions in a depth-first recursive manner like
    the ``optimal`` approach, but with extra heuristic early pruning of branches
    as well sieving by ``memory_limit`` and the best path found so far. Returns
    the lowest cost path. This algorithm still scales factorially with respect
    to the elements in the list ``input_sets`` if ``nbranch`` is not set, but it
    scales exponentially like ``nbranch**len(input_sets)`` otherwise.

    Parameters
    ----------
    nbranch : None or int, optional
        How many branches to explore at each contraction step. If None, explore
        all possible branches. If an integer, branch into this many paths at
        each step. Defaults to None.
    cutoff_flops_factor : float, optional
        If at any point, a path is doing this much worse than the best path
        found so far was, terminate it. The larger this is made, the more paths
        will be fully explored and the slower the algorithm. Defaults to 4.
    minimize : {'flops', 'size'}, optional
        Whether to optimize the path with regard primarily to the total
        estimated flop-count, or the size of the largest intermediate. The
        option not chosen will still be used as a secondary criterion.
    cost_fn : callable, optional
        A function that returns a heuristic 'cost' of a potential contraction
        with which to sort candidates. Should have signature
        ``cost_fn(size12, size1, size2, k12, k1, k2)``.
    N   r[   r{   c                 C   sP   || _ || _|| _t||| _t|| _tdtdd| _	t
dd | _d S )Nr   rp   c                   S   s   t dS )Nr   )rj   r   r   r   r   <lambda>?  r.   z&BranchBound.__init__.<locals>.<lambda>)nbranchcutoff_flops_factorminimize	_COST_FNSgetcost_fnrs   betterrj   rg   r   best_progress)r   r~   r   r   r   r   r   r   __init__7  s    
zBranchBound.__init__c                 C   s   t | jd S )Nr6   )r9   rg   )r   r   r   r   r7   A  s    zBranchBound.pathc                    sv    | ttt|}tfdd|D i  fdd  d|ttt|ddd jS )a  

        Parameters
        ----------
        input_sets : list
            List of sets that represent the lhs side of the einsum subscript
        output_set : set
            Set that represents the rhs side of the overall einsum subscript
        idx_dict : dictionary
            Dictionary of index sizes
        memory_limit : int
            The maximum number of elements in a temporary array

        Returns
        -------
        path : list
            The contraction order within the memory limit constraint.

        Examples
        --------
        >>> isets = [set('abd'), set('ac'), set('bdc')]
        >>> oset = set('')
        >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
        >>> optimal(isets, oset, idx_sizes, 5000)
        [(0, 2), (0, 1)]
        c                    s   i | ]}|t | qS r   r   r`   r)   kr   r   r   
<dictcomp>e  r.   z(BranchBound.__call__.<locals>.<dictcomp>c                    s  t dkr.	jd<  	jd< 	jd< d S  	
fdd}g }tdD ]V\}}||krx|| }}| |  }	}
|	|
rq^||	|
||}|r^t|| q^|stdD ]J\}}||kr|| }}| |  }	}
||	|
||}|rt|| qd}	jd u s0|	jk r|rt|\}}}}\}}}||ff |f ||h t hB ||d	 |d7 }qd S )
Nr   rm   r[   r6   c              
      s  z| |f \}}W n4 t yH   t||
 \}}| |f< Y n0 z	| }W n& t y|   t|
 }	|< Y n0  | }t|}||jd jd sd S |jt k r|jt< n|j	jt  krd S t
vrF|krF t
 }|jd k rB|jd< tf jd< d S 	|  	|   }	}
||	|
|| |}||||||f|fS )Nr[   rm   r6   )r^   rV   r   r`   r1   r   rg   r   r?   r   r_   rZ   r5   r   )rO   rP   rM   rN   rT   rb   rd   rc   new_sizeru   rv   rU   )r[   r   r!   r   r7   rL   rh   r   rm   ri   r   r   r   _assess_candidateq  s2    (

zHBranchBound.__call__.<locals>._branch_iterate.<locals>._assess_candidaterG   r   r7   r   rL   r[   rm   )	r?   rg   rB   r]   
isdisjointheapqheappushr~   heappop)r7   r   rL   r[   rm   r   
candidatesrM   rN   rO   rP   	candidatebi_rc   r   rT   _branch_iterater!   r   rh   r   ri   r   )r[   r   r7   rL   rm   r   r   h  sD    


 *


z-BranchBound.__call__.<locals>._branch_iterater   r   r   )r   r5   r2   rH   rk   rA   r?   r7   r    r   r   r   r"   E  s    VzBranchBound.__call__)Nr|   r[   r{   )N)r#   r$   r%   r&   r   propertyr7   r"   r   r   r   r   r     s
   


r   c                 K   s   t f i |}|| |||S r   )r   )r   r   r   r!   Zoptimizer_kwargs	optimizerr   r   r   r	     s    r	   )r~   rG   c                 C   s   ||B }||@ }	||	 }
|| @ |	|d @ B |
|d @ B }|t |||| || |||}|| }|| }||kr||||f\}}}}|||f}||||fS )N   rG   r   )r   sizesrL   
footprintsdim_ref_countsrO   rP   r   rQ   twoonerT   rU   Zid1Zid2r   r   r   _get_candidate  s     "
r   c
                    sN    fdd|D }
|r:|
D ]}t || q&nt |t|
 d S )Nc              
   3   s$   | ]}t | V  qd S r   )r   )r)   rP   r   r   r   rO   r   rL   r   r   r   r-     r.   z"_push_candidate.<locals>.<genexpr>)r   r   min)r   r   rL   r   r   rO   k2squeuepush_allr   r   r   r   r   r   _push_candidate  s
    r   c                 C   s   |D ]x}t | | }|dkr:|d | |d | q|dkr`|d | |d | q|d | |d | qd S )Nr   rG   r   )r?   discardadd)dim_to_keysr   dimsdimrC   r   r   r   _update_ref_counts  s    r   c                 C   s2   t | \}}}}||vs"||vr&dS ||||fS )zKDefault contraction chooser that simply takes the minimum cost option.
    N)r   r   )r   rL   rU   rO   rP   rT   r   r   r   _simple_chooser  s    r   r{   c                    sN  t | dkrdgS t||}|du r0t}d}nd}ttt| } ttj|  B i }t	t | }g }t
| D ]8\}	}
|
|v r|||
 |	f t|||
< qr|	||
< qrtt |D ] }
|
 D ]} | |
 qq fdddD }fd	d|D }g }  D ]^\}}t||jd
}t
|dd D ]4\}}|d| d }t||||||||
 q2q|r|||}|du rql|\}}}}||}||}| D ]} | | q| D ]} | | q|||f ||v r||| t|f n| D ]} | | qt|||< t ||| B  t|||< |}t fdd|D }|| |rlt||||||||
 qlfdd| D }t| t|\}}}|rJt|\}}}|t||t||f ||B @ }t|}t|}t||||f\}}}q|S )z
    This is the core function for :func:`greedy` but produces a path with
    static single assignment ids rather than recycled linear ids.
    SSA ids are cheaper to work with and easier to reason about.
    r   r   NFTc                    s,   i | ]$  t  fd d D  qS )c                 3   s"   | ]\}}t | kr|V  qd S r   r?   )r)   r   keysrC   r   r   r-   -  r.   z1ssa_greedy_optimize.<locals>.<dictcomp>.<genexpr>)rk   items)r)   )r   r   r   r   r   ,  s   z'ssa_greedy_optimize.<locals>.<dictcomp>)rG   r   c                    s   i | ]}|t | qS r   r   )r)   ra   )r   r   r   r   2  r.   rr   r   c                 3   s    | ]} | D ]
}|V  qqd S r   r   )r)   r   rP   )r   r   r   r-   V  r.   z&ssa_greedy_optimize.<locals>.<genexpr>c                    s&   g | ]\}}t | @ ||fqS r   r   )r)   ra   r*   )r   r   r   r   
<listcomp>\  r.   z'ssa_greedy_optimize.<locals>.<listcomp>) r?   r   r   r   r@   r2   rH   intersectionrB   rC   	enumerater4   rE   r   rk   r   r   rD   rJ   r   popremover   r   r`   r   r   heapifyr   r   r1   heappushpop)r   r   r   	choose_fnr   r   rL   r8   r6   r*   ra   r   r   r   r   r   rM   rO   r   conrU   rP   rT   Zssa_id1Zssa_id2r   Zssa_id12r   )r   r   r   r   ssa_greedy_optimize  s    
"






r   c                 C   s6   |t vrt| |||d|dS t| ||||d}t|S )a  
    Finds the path by a three stage algorithm:

    1. Eagerly compute Hadamard products.
    2. Greedily compute contractions to maximize ``removed_size``
    3. Greedily compute outer products.

    This algorithm scales quadratically with respect to the
    maximum number of elements sharing a common dim.

    Parameters
    ----------
    inputs : list
        List of sets that represent the lhs side of the einsum subscript
    output : set
        Set that represents the rhs side of the overall einsum subscript
    size_dict : dictionary
        Dictionary of index sizes
    memory_limit : int
        The maximum number of elements in a temporary array
    choose_fn : callable, optional
        A function that chooses which contraction to perform from the queu
    cost_fn : callable, optional
        A function that assigns a potential contraction a cost.

    Returns
    -------
    path : list
        The contraction order (a list of tuples of ints).

    Examples
    --------
    >>> isets = [set('abd'), set('ac'), set('bdc')]
    >>> oset = set('')
    >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
    >>> greedy(isets, oset, idx_sizes)
    [(0, 2), (0, 1)]
    r   )r~   r   )r   r   )r_   r	   r   r9   )r   r   r   r!   r   r   r6   r   r   r   r
   j  s    'r
   c                    s   t | tkrg S | g} g }g }t| dkr| d}|dt  tdd |D D ]< |d  t fdd|D f7  < ||d d   qTdd |D D ], |d  t|t|  f7  < |   qq|S )a  
    Converts a contraction tree to a contraction path as it has to be
    returned by path optimizers. A contraction tree can either be an int
    (=no contraction) or a tuple containing the terms to be contracted. An
    arbitrary number (>= 1) of terms can be contracted at once. Note that
    contractions are commutative, e.g. (j, k, l) = (k, l, j). Note that in
    general, solutions are not unique.

    Parameters
    ----------
    c : tuple or int
        Contraction tree

    Returns
    -------
    path : list[set[int]]
        Contraction path

    Examples
    --------
    >>> _tree_to_sequence(((1,2),(0,(4,5,3))))
    [(1, 2), (1, 2, 3), (0, 2), (0, 1)]
    r   r   c                 S   s   g | ]}t |tkr|qS r   typer(   r)   rM   r   r   r   r     r.   z%_tree_to_sequence.<locals>.<listcomp>c                 3   s   | ]}| k rd V  qdS r   Nr   )r)   qrM   r   r   r-     r.   z$_tree_to_sequence.<locals>.<genexpr>c                 S   s   g | ]}t |tkr|qS r   r   r   r   r   r   r     r.   )	r   r(   r?   r   insertr5   rD   r>   r4   )ctsrN   r   r   r   _tree_to_sequence  s    $
$r   c           	         s   g }t tt}t j | }t|dkrt  }| g}t|dkr| }|| || @   fdd|D }|| || q>|| q"|S )a  
    Finds disconnected subgraphs in the given list of inputs. Inputs are
    connected if they share summation indices. Note: Disconnected subgraphs
    can be contracted independently before forming outer products.

    Parameters
    ----------
    inputs : list[set]
        List of sets that represent the lhs side of the einsum subscript
    output : set
        Set that represents the rhs side of the overall einsum subscript

    Returns
    -------
    subgraphs : list[set[int]]
        List containing sets of indices for each subgraph

    Examples
    --------
    >>> _find_disconnected_subgraphs([set("ab"), set("c"), set("ad")], set("bd"))
    [{0, 2}, {1}]

    >>> _find_disconnected_subgraphs([set("ab"), set("c"), set("ad")], set("abd"))
    [{0}, {1}, {2}]
    r   c                    s$   h | ]}t  | @ d kr|qS r   r   r   Zi_tmpr   r   r   	<setcomp>  r.   z/_find_disconnected_subgraphs.<locals>.<setcomp>)	rk   rA   r?   rI   r   r   extenddifference_updater4   )	r   r   	subgraphsZunused_inputsZi_sumgr   rN   nr   r   r   _find_disconnected_subgraphs  s    


r   c                 C   s"   dd t |t| ddd D S )zSelect elements of ``seq`` which are marked by the bitmap set ``s``.

    E.g.:

        >>> list(_bitmap_select(0b11010, ['A', 'B', 'C', 'D', 'E']))
        ['B', 'D', 'E']
    c                 s   s   | ]\}}|d kr|V  qdS )1Nr   )r)   xbr   r   r   r-   
  r.   z!_bitmap_select.<locals>.<genexpr>Nr   r   )zipbin)r   seqr   r   r   _bitmap_select  s    r   c           	      C   s8   | ||A @ }|r"t jt|| }nt  }|| }|| S )zoCalculates the effective outer indices of the intermediate tensor
    corresponding to the subgraph ``s``.
    )rk   rI   r   )	r   all_tensorsr   r   i1_cut_i2_wo_outputi1_union_i2rZi_rZ
i_contractr   r   r   _dp_calc_legs  s    r   c                 C   s   | | t || }||kr|||B }||vs<||| d k r|t||	||
||}t ||}|du sj||kr|||||ff||< dS )a  Performs the inner comparison of whether the two subgraphs (the bitmaps
    ``s1`` and ``s2``) should be merged and added to the dynamic programming
    search. Will skip for a number of reasons:

    1. If the number of operations to form ``s = s1 | s2`` including previous
       contractions is above the cost-cap.
    2. If we've already found a better way of making ``s``.
    3. If the intermediate tensor corresponding to ``s`` is going to break the
       memory limit.
    r   N)r   r`   r   )cost1cost2r   r   cost_caps1s2xnr   r   r   r   r!   cntrct1cntrct2rU   r   rM   memr   r   r   _dp_compare_flops  s    r   c                 C   sx   ||B }t ||	||
||}t||}t| ||}||krt||vsR||| d k rt|du sb||krt||||ff||< dS )zLike ``_dp_compare_flops`` but sieves the potential contraction based
    on the size of the intermediate tensor created, rather than the number of
    operations, and so calculates that first.
    r   N)r   r   r`   r1   )r   r   r   r   r   r   r   r   r   r   r   r   r!   r   r   r   rM   r   rU   r   r   r   _dp_compare_size3  s    r   c                 C   s   t dd | S )zMake a simple left to right binary tree out of iterable ``seq``.

        >>> tuple_nest([1, 2, 3, 4])
        (((1, 2), 3), 4)

    c                 S   s   | |fS r   r   r   yr   r   r   r}   J  r.   z#simple_tree_tuple.<locals>.<lambda>)	functoolsreduce)r   r   r   r   simple_tree_tupleC  s    r   c           
         s~    fddt |D }g g g   }}}t | D ]D\}}|| }	|	sP||f q.||	 ||	|krl|fn| q.|||fS )aU  Take ``inputs`` and parse for single term index operations, i.e. where
    an index appears on one tensor and nowhere else.

    If a term is completely reduced to a scalar in this way it can be removed
    to ``inputs_done``. If only some indices can be summed then add a 'single
    term contraction' that will perform this summation.
    c                    s    h | ]\}} | d kr|qS )r   r   )r)   rM   r   
ind_countsr   r   r   U  r.   z0_dp_parse_out_single_term_ops.<locals>.<setcomp>)r   r4   )
r   all_indsr   Zi_singleZinputs_parsedinputs_doneinputs_contractionsrN   rM   Z	i_reducedr   r   r   _dp_parse_out_single_term_opsM  s    
r   c                   @   s$   e Zd ZdZd
ddZddd	ZdS )r   a  
    Finds the optimal path of pairwise contractions without intermediate outer
    products based a dynamic programming approach presented in
    Phys. Rev. E 90, 033315 (2014) (the corresponding preprint is publically
    available at https://arxiv.org/abs/1304.6112). This method is especially
    well-suited in the area of tensor network states, where it usually
    outperforms all the other optimization strategies.

    This algorithm shows exponential scaling with the number of inputs
    in the worst case scenario (see example below). If the graph to be
    contracted consists of disconnected subgraphs, the algorithm scales
    linearly in the number of disconnected subgraphs and only exponentially
    with the number of inputs per subgraph.

    Parameters
    ----------
    minimize : {'flops', 'size'}, optional
        Whether to find the contraction that minimizes the number of
        operations or the size of the largest intermediate tensor.
    cost_cap : {True, False, int}, optional
        How to implement cost-capping:

            * True - iteratively increase the cost-cap
            * False - implement no cost-cap at all
            * int - use explicit cost cap

    search_outer : bool, optional
        In rare circumstances the optimal contraction may involve an outer
        product, this option allows searching such contractions but may well
        slow down the path finding considerably on all but very small graphs.
    r[   TFc                 C   sB   || _ ttd| j  | _|| _dd dd d| j | _|| _d S )Nrp   c                 S   s   | S r   r   r   r   r   r   r}     r.   z-DynamicProgramming.__init__.<locals>.<lambda>c                 S   s   dS )NTr   r   r   r   r   r}     r.   )FT)r   r   r   _check_contractionsearch_outer_check_outerr   )r   r   r   r   r   r   r   r     s    zDynamicProgramming.__init__Nc           !         s(  t tjg  |R  }t|}dd t|D fdd D  tfdd|D }fdd D fddttD t	 ||\ } st
t|S |d	gt| }| jrttt g}	n
t |}	d	t > d	 }
|	D ]}d
gd dd tt|d	 D  }t fdd|D |d	< tdd dd |D }tjt|  }| jdu rt||@ }n| jdu rtd}n| j}tttj|d}t|d dkrtdt|d	 d	 D ]}|| }td	|d d	 D ]}||  D ]\}\}}}|||   D ]x\}\}}}||@ s*||| ksZ||k r*||@ | }| |r*||B }| |||||||||
 |||| q*qqq|| }qt|d  d \}}}| |t| qfddt tt||jdD t} t
| S )a  
        Parameters
        ----------
        inputs : list
            List of sets that represent the lhs side of the einsum subscript
        output : set
            Set that represents the rhs side of the overall einsum subscript
        size_dict : dictionary
            Dictionary of index sizes
        memory_limit : int
            The maximum number of elements in a temporary array

        Returns
        -------
        path : list
            The contraction order (a list of tuples of ints).

        Examples
        --------
        >>> n_in = 3  # exponential scaling
        >>> n_out = 2 # linear scaling
        >>> s = dict()
        >>> i_all = []
        >>> for _ in range(n_out):
        >>>     i = [set() for _ in range(n_in)]
        >>>     for j in range(n_in):
        >>>         for k in range(j+1, n_in):
        >>>             c = oe.get_symbol(len(s))
        >>>             i[j].add(c)
        >>>             i[k].add(c)
        >>>             s[c] = 2
        >>>     i_all.extend(i)
        >>> o = DynamicProgramming()
        >>> o(i_all, set(), s)
        [(1, 2), (0, 4), (1, 2), (0, 2), (0, 1)]
        c                 S   s   i | ]\}}||qS r   r   )r)   rN   r   r   r   r   r     r.   z/DynamicProgramming.__call__.<locals>.<dictcomp>c                    s"   g | ]}t  fd d|D qS )c                 3   s   | ]} | V  qd S r   r   r)   r   
symbol2intr   r   r-     r.   z9DynamicProgramming.__call__.<locals>.<listcomp>.<genexpr>)rk   r   r   r   r   r     r.   z/DynamicProgramming.__call__.<locals>.<listcomp>c                 3   s   | ]} | V  qd S r   r   r   r   r   r   r-     r.   z.DynamicProgramming.__call__.<locals>.<genexpr>c                    s"   i | ]\}}| v r | |qS r   r   )r)   r   vr   r   r   r     r.   c                    s   g | ]} | qS r   r   r)   rN   r   r   r   r     r.   r   NrG   c                 S   s   g | ]
}t  qS r   )dictr   r   r   r   r     r.   c                 3   s(   | ] }d |>  | d| ffV  qdS )r   r   Nr   r   )r   r   r   r   r-     r.   c                 S   s   | |B S r   r   r   r   r   r   r}     r.   z-DynamicProgramming.__call__.<locals>.<lambda>c                 s   s   | ]}d |> V  qdS r   r   r   r   r   r   r-     r.   TFr   r   r   c                    s   g | ]} | qS r   r   r   )subgraph_contractionsr   r   r     s   rr   )!r   rB   chainr5   r   rk   r   rA   r?   r   r   r   r   r   r   r   r   rI   r   r   r   r`   rj   r1   r   r2   rJ   r   r   r@   valuesr4   rD   )!r   r   r   r   r!   r   r   r   Zsubgraph_contractions_sizer   r   r   r   Zsubgraph_indsr   Zcost_incrementr   r   mr   i1r   r   r   i2r   r   r   r   rM   rU   contractiontreer   )r   r   r   r   r   r   r"     sd    %

$
"


zDynamicProgramming.__call__)r[   TF)N)r#   r$   r%   r&   r   r"   r   r   r   r   r   d  s   
r   c                 K   s   t f i |}|| |||S r   )r   )r   r   r   r!   kwargsr   r   r   r   r      s    r         	      c                 C   s   t | }t|t| |||S )zuFinds the contraction path by automatically choosing the method based on
    how many input arguments there are.
    )r?   _AUTO_CHOICESr   r
   )r   r   r   r!   Nr   r   r   r   0  s    r         c                 C   s*   ddl m} t| }t||| |||S )zFinds the contraction path by automatically choosing the method based on
    how many input arguments there are, but targeting a more generous
    amount of search time than ``'auto'``.
    r   )random_greedy_128)path_randomr  r?   _AUTO_HQ_CHOICESr   )r   r   r   r!   r  r  r   r   r   r   ?  s    r   )r   zauto-hqr   z
branch-allzbranch-2zbranch-1r
   eagerZopportunisticdpzdynamic-programmingc                 C   s&   | t v rtd| |t |  < dS )zAAdd path finding function ``fn`` as an option with ``name``.
    z#Path optimizer '{}' already exists.N)_PATH_OPTIONSr^   formatlower)namefnr   r   r   register_path_fnY  s    r  c                 C   s(   | t vr td| tt  t |  S )zBGet the correct path finding function from str ``path_type``.
    z4Path optimizer '{}' not found, valid options are {}.)r  r^   r  rk   r   )	path_typer   r   r   r   b  s
    r   )N)N)Nr{   )NNr{   )N)N)N)=r&   r   r   rB   rx   collectionsr   r   r   numpyr/    r   __all__rj   r_   objectr   r9   r<   rV   rZ   r   rn   ro   rq   rs   rw   rz   r   r   r	   partialZ
branch_allZbranch_2Zbranch_1r   r   r   r   r   r
   r   r   r   r   r   r   r   r   r   r   r
  rA   rM   r   r  r   r  r  r   r   r   r   r   <module>   s   ()
Z )

	
n
.:0
 =


	