o
    i5                     @   sD   d dl Z dd Zdd Zdd Zdd	 Zd
d Zdd Zdd ZdS )    Nc                 C   s,   t | |}td|d }|r|d7 }|| S )a  Copied from _flop_count in numpy/core/einsumfunc.py

    Computes the number of FLOPS in the contraction.

    Parameters
    ----------
    idx_contraction : iterable
        The indices involved in the contraction
    inner : bool
        Does this contraction require an inner product?
    num_terms : int
        The number of terms in a contraction
    size_dictionary : dict
        The size of each of the indices in idx_contraction

    Returns
    -------
    flop_count : int
        The total number of FLOPS required for the contraction.

    Examples
    --------

    >>> _flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5})
    90

    >>> _flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5})
    270

       )_compute_size_by_dictmax)idx_contractioninner	num_termssize_dictionaryoverall_size	op_factor r   T/home/ubuntu/veenaModal/venv/lib/python3.10/site-packages/cupy/linalg/_einsum_opt.py_flop_count   s
   
 r   c                 C   s   d}| D ]}||| 9 }q|S )a  Copied from _compute_size_by_dict in numpy/core/einsumfunc.py

    Computes the product of the elements in indices based on the dictionary
    idx_dict.

    Parameters
    ----------
    indices : iterable
        Indices to base the product on.
    idx_dict : dictionary
        Dictionary of index sizes

    Returns
    -------
    ret : int
        The resulting product.

    Examples
    --------
    >>> _compute_size_by_dict('abbc', {'a': 2, 'b':3, 'c':5})
    90

    r   r   )indicesidx_dictretir   r   r   r   ,   s   r   c           
      C   sn   t  }| }g }t|D ]\}}|| v r||O }q|| ||O }q||@ }|| }	|| |||	|fS )aD  Copied from _find_contraction in numpy/core/einsumfunc.py

    Finds the contraction for a given set of input and output sets.

    Parameters
    ----------
    positions : iterable
        Integer positions of terms used in the contraction.
    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

    Returns
    -------
    new_result : set
        The indices of the resulting contraction
    remaining : list
        List of sets that have not been contracted, the new set is appended to
        the end of this list
    idx_removed : set
        Indices removed from the entire contraction
    idx_contraction : set
        The indices used in the current contraction

    Examples
    --------

    # A simple dot product test case
    >>> pos = (0, 1)
    >>> isets = [set('ab'), set('bc')]
    >>> oset = set('ac')
    >>> _find_contraction(pos, isets, oset)
    ({'a', 'c'}, [{'a', 'c'}], {'b'}, {'a', 'b', 'c'})

    # A more complex case with additional terms in the contraction
    >>> pos = (0, 2)
    >>> isets = [set('abd'), set('ac'), set('bdc')]
    >>> oset = set('ac')
    >>> _find_contraction(pos, isets, oset)
    ({'a', 'c'}, [{'a', 'c'}, {'a', 'c'}], {'b', 'd'}, {'a', 'b', 'c', 'd'})
    )setcopy	enumerateappend)
	positions
input_sets
output_setidx_contract
idx_remain	remainingindvalue
new_resultidx_removedr   r   r   _find_contractionJ   s   ,



r    c              	   C   s(  dg | fg}t t| d D ]j}g }|D ]D}|\}}	}
tt t| | dD ]0}t||
|}|\}}}}t||}||kr?q'|t||t|| }|	|g }||||f q'q|r^|}qt|dd dd }|t	t t| | g7 }|  S t|dkrt	t t| gS t|dd dd }|S )a  Copied from _optimal_path in numpy/core/einsumfunc.py

    Computes all possible pair contractions, sieves the results based
    on ``memory_limit`` and returns the lowest cost path. This algorithm
    scales factorial with respect to the elements in the list ``input_sets``.

    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 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_path(isets, oset, idx_sizes, 5000)
    [(0, 2), (0, 1)]
    r   r      c                 S      | d S Nr   r   xr   r   r   <lambda>       z_optimal_path.<locals>.<lambda>keyc                 S   r"   r#   r   r$   r   r   r   r&      r'   )
rangelen	itertoolscombinationsr    r   r   r   mintuple)r   r   r   memory_limitfull_results	iterationiter_resultscurrcostr   r   concontr   new_input_setsr   r   new_size
total_costnew_pospathr   r   r   _optimal_path   s4    


r=   c                    s   t | |}|\}}	}
}t| }||krdS  fdd| D }t|| }t||
t|  }| |f}|| |kr=dS || |	gS )a*  Copied from _parse_possible_contraction in numpy/core/einsumfunc.py

    Compute the cost (removed size + flops) and resultant indices for
    performing the contraction specified by ``positions``.

    Parameters
    ----------
    positions : tuple of int
        The locations of the proposed tensors to contract.
    input_sets : list of sets
        The indices found on each tensors.
    output_set : set
        The output indices of the expression.
    idx_dict : dict
        Mapping of each index to its size.
    memory_limit : int
        The total allowed size for an intermediary tensor.
    path_cost : int
        The contraction cost so far.
    naive_cost : int
        The cost of the unoptimized expression.

    Returns
    -------
    cost : (int, int)
        A tuple containing the size of any indices removed, and the flop cost.
    positions : tuple of int
        The locations of the proposed tensors to contract.
    new_input_sets : list of sets
        The resulting new list of indices if this proposed contraction is performed.

    Nc                 3   s    | ]
}t |  V  qd S N)r   ).0pr   r   r   r   	<genexpr>   s    z._parse_possible_contraction.<locals>.<genexpr>)r    r   sumr   r+   )r   r   r   r   r0   	path_cost
naive_costcontract
idx_resultr8   r   r   r9   	old_sizesremoved_sizer5   sortr   rA   r   _parse_possible_contraction   s   #


rK   c                 C   s   |d }|\}}g }| D ]\\}\}}}	||v s||v rq|	|t ||k t ||k = |	|t ||k t ||k = |	d|d d  |t ||k t ||k |t ||k t ||k f}
|||
|	f q|S )aX  Copied from _update_other_results in numpy/core/einsumfunc.py

    Update the positions and provisional input_sets of ``results`` based on
    performing the contraction result ``best``. Remove any involving the tensors
    contracted.

    Parameters
    ----------
    results : list
        List of contraction results produced by ``_parse_possible_contraction``.
    best : list
        The best contraction of ``results`` i.e. the one that will be performed.

    Returns
    -------
    mod_results : list
        The list of modified results, updated with outcome of ``best`` contraction.
    r   r!   )intinsertr   )resultsbestbest_conbxbymod_resultsr5   r%   ycon_setsmod_conr   r   r   _update_other_results  s   8rX   c              
      s  t | dkr	dgS t | dkrdgS ttt | | |}|\}}}}t||t | |}	ttt | d}
g }d}g }tt | d D ]}|
D ]#}| |d  | |d  rWqGt|| |||||	}|durj|| qGt |dkrttt | dD ]}t|| |||||	}|dur|| q{t |dkr|t	tt |   |S t
|dd d	}t||}|d } t | d   fd
dt D }
||d  ||d d 7 }qC|S )a  Copied from _greedy_path in numpy/core/einsumfunc.py

    Finds the path by contracting the best pair until the input list is
    exhausted. The best pair is found by minimizing the tuple
    ``(-prod(indices_removed), cost)``.  What this amounts to is prioritizing
    matrix multiplication or inner product operations, then Hadamard like
    operations, and finally outer operations. Outer products are limited by
    ``memory_limit``. This algorithm scales cubically with respect to the
    number of elements in the list ``input_sets``.

    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_limit : int
        The maximum number of elements in a temporary array

    Returns
    -------
    path : list
        The greedy 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}
    >>> _greedy_path(isets, oset, idx_sizes, 5000)
    [(0, 2), (0, 1)]
    r   )r   r!   )r   r   r   Nc                 S   r"   r#   r   r$   r   r   r   r&     r'   z_greedy_path.<locals>.<lambda>r(   c                 3   s    | ]}| fV  qd S r>   r   )r?   r   new_tensor_posr   r   rB     s    z_greedy_path.<locals>.<genexpr>)r+   r    r*   r   r,   r-   
isdisjointrK   r   r/   r.   rX   )r   r   r   r0   rF   rG   r8   r   r   rE   	comb_iterknown_contractionsrD   r<   r2   r   resultrP   r   rY   r   _greedy_path7  sZ   %


r_   )r,   r   r   r    r=   rK   rX   r_   r   r   r   r   <module>   s    (=I=*