o
    e)                     @   s   d Z ddlZddlmZ ddlZg dZdZdd Zdd	 Z	d
d Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd ZdS )zC
A functionally equivalent parser of the numpy.einsum input parser
    N)OrderedDict)
is_valid_einsum_charhas_valid_einsum_chars_only
get_symbolgen_unused_symbolsconvert_to_valid_einsum_charsalpha_canonicalizefind_output_strfind_output_shapepossibly_convert_to_numpyparse_einsum_input4abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZc                 C   s   | t v p| dv S )u   Check if the character ``x`` is valid for numpy einsum.

    Examples
    --------
    >>> is_valid_einsum_char("a")
    True

    >>> is_valid_einsum_char("Ǵ")
    False
    z,->.)_einsum_symbols_basex r   AD:\Projects\ConvertPro\env\Lib\site-packages\opt_einsum/parser.pyr      s   r   c                 C   s   t tt| S )u   Check if ``einsum_str`` contains only valid characters for numpy einsum.

    Examples
    --------
    >>> has_valid_einsum_chars_only("abAZ")
    True

    >>> has_valid_einsum_chars_only("Över")
    False
    )allmapr   )
einsum_strr   r   r   r   #   s   r   c                 C   s   | dk rt |  S t| d S )u  Get the symbol corresponding to int ``i`` - runs through the usual 52
    letters before resorting to unicode characters, starting at ``chr(192)``.

    Examples
    --------
    >>> get_symbol(2)
    'c'

    >>> get_symbol(200)
    'Ŕ'

    >>> get_symbol(20000)
    '京'
    4      )r   chr)ir   r   r   r   1   s   r   c                 c   sJ    d }}||k r#t |}|d7 }|| v rq|V  |d7 }||k s	dS dS )zGenerate ``n`` symbols that are not already in ``used``.

    Examples
    --------
    >>> list(oe.parser.gen_unused_symbols("abd", 2))
    ['c', 'e']
    r      Nr   )usednr   Zcntsr   r   r   r   E   s   r   c                    s>   t t| td }dd t|D  d fdd| D S )u  Convert the str ``einsum_str`` to contain only the alphabetic characters
    valid for numpy einsum. If there are too many symbols, let the backend
    throw an error.

    Examples
    --------
    >>> oe.parser.convert_to_valid_einsum_chars("Ĥěļļö")
    'cbdda'
    z,->c                 S      i | ]	\}}|t |qS r   r   ).0r   r   r   r   r   
<dictcomp>b       z1convert_to_valid_einsum_chars.<locals>.<dictcomp> c                 3       | ]	}  ||V  qd S Ngetr    r   replacerr   r   	<genexpr>c       z0convert_to_valid_einsum_chars.<locals>.<genexpr>)sortedset	enumeratejoin)r   symbolsr   r)   r   r   W   s   
r   c                    sJ   t   | D ]}|dv rq| vrtt  |< qd fdd| D S )u   Alpha convert an equation in an order-independent canonical way.

    Examples
    --------
    >>> oe.parser.alpha_canonicalize("dcba")
    'abcd'

    >>> oe.parser.alpha_canonicalize("Ĥěļļö")
    'abccd'
    z.,->r#   c                 3   r$   r%   r&   r(   renamer   r   r+   w   r,   z%alpha_canonicalize.<locals>.<genexpr>)r   r   lenr0   )Zequationnamer   r2   r   r   f   s   r   c                    s,   |  dd d fddtt D S )aU  
    Find the output string for the inputs ``subscripts`` under canonical einstein summation rules. That is, repeated indices are summed over by default.

    Examples
    --------
    >>> oe.parser.find_output_str("ab,bc")
    'ac'

    >>> oe.parser.find_output_str("a,b")
    'ab'

    >>> oe.parser.find_output_str("a,a,b,b")
    ''
    ,r#   c                 3   s"    | ]}  |d kr|V  qdS )r   N)count)r    r   Ztmp_subscriptsr   r   r+      s     z"find_output_str.<locals>.<genexpr>)replacer0   r-   r.   )
subscriptsr   r8   r   r	   z   s    r	   c                    s   t  fdd|D S )aO  Find the output shape for given inputs, shapes and output string, taking
    into account broadcasting.

    Examples
    --------
    >>> oe.parser.find_output_shape(["ab", "bc"], [(2, 3), (3, 4)], "ac")
    (2, 4)

    # Broadcasting is accounted for
    >>> oe.parser.find_output_shape(["a", "a"], [(4, ), (1, )], "a")
    (4,)
    c                 3   s6    | ] t d d t fddD D V  qdS )c                 s   s$    | ]\}}|d kr|| V  qdS )r   Nr   )r    shapelocr   r   r   r+      s   " z.find_output_shape.<locals>.<genexpr>.<genexpr>c                    s   g | ]}|  qS r   )findr(   cr   r   
<listcomp>   s    z/find_output_shape.<locals>.<genexpr>.<listcomp>N)maxzip)r    inputsshapesr>   r   r+      s    &
z$find_output_shape.<locals>.<genexpr>)tuple)rD   rE   outputr   rC   r   r
      s   r
   c                 C   s   t | ds
t| S | S )aT  Convert things without a 'shape' to ndarrays, but leave everything else.

    Examples
    --------
    >>> oe.parser.possibly_convert_to_numpy(5)
    array(5)

    >>> oe.parser.possibly_convert_to_numpy([5, 3])
    array([5, 3])

    >>> oe.parser.possibly_convert_to_numpy(np.array([5, 3]))
    array([5, 3])

    # Any class with a shape is passed through
    >>> class Shape:
    ...     def __init__(self, shape):
    ...         self.shape = shape
    ...

    >>> myshape = Shape((5, 5))
    >>> oe.parser.possibly_convert_to_numpy(myshape)
    <__main__.Shape object at 0x10f850710>
    r;   )hasattrnpZ
asanyarrayr   r   r   r   r      s   

r   c                 C   s0   d}| D ]}|t u r|d7 }q||| 7 }q|S )a  Convert user custom subscripts list to subscript string according to `symbol_map`.

    Examples
    --------
    >>>  oe.parser.convert_subscripts(['abc', 'def'], {'abc':'a', 'def':'b'})
    'ab'
    >>> oe.parser.convert_subscripts([Ellipsis, object], {object:'a'})
    '...a'
    r#   ...)Ellipsis)Zold_sub
symbol_mapZnew_subr   r   r   r   convert_subscripts   s   

rM   c                    s   t | }g }g }tt| d D ]}||d ||d qt|r+|d nd}dd |D } zttj|}|	t
 dd tt|D  W n tyY   td	w d
 fdd|D }|duru|d7 }|t| 7 }|| fS )z:Convert 'interleaved' input to standard einsum input.
       r   Nc                 S      g | ]}t |qS r   r   r(   r   r   r   r@          z-convert_interleaved_input.<locals>.<listcomp>c                 S   r   r   r   )r    idxsymbolr   r   r   r!      r"   z-convert_interleaved_input.<locals>.<dictcomp>ziFor this input type lists must contain either Ellipsis or hashable and comparable object (e.g. int, str).r6   c                 3   s    | ]}t | V  qd S r%   )rM   )r    subrL   r   r   r+          z,convert_interleaved_input.<locals>.<genexpr>->)listranger4   appendpopr.   	itertoolschainfrom_iterablediscardrK   r/   r-   	TypeErrorr0   rM   )operandsZtmp_operandsZoperand_listZsubscript_listpZoutput_listZ
symbol_setr:   r   rV   r   convert_interleaved_input   s(   
rd   c                 C   s  t | dkr
tdt| d tr%| d dd}dd | dd D } nt| \}} d	|v s3d
|v rN|d	dkp@|d
dk}|sJ|ddkrNtdd|v r'|dddddd}dt|t	dd | D }d}d|v r|
d\}}|
d}d}	n|
d}d}	t|D ]\\}
}d|v r|ddks|ddkrtd| |
 jdkrd}nt	t | |
 jdt |d  }||kr|}|dk rtd|dkr|dd||
< q|d|| d ||
< qd|}|dkrd}n|| d }|	r|d|d| 7 }nt|}dtt|t| }|d| | 7 }d|v r4|
d\}}n|t|}}|D ]}||vrKtd|q=t |
dt | kr]td||| fS )af  
    A reproduction of einsum c side einsum parsing in python.

    Returns
    -------
    input_strings : str
        Parsed input strings
    output_string : str
        Parsed output string
    operands : list of array_like
        The operands to use in the numpy contraction

    Examples
    --------
    The operand list is simplified to reduce printing:

    >>> a = np.random.rand(4, 4)
    >>> b = np.random.rand(4, 4, 4)
    >>> parse_einsum_input(('...a,...a->...', a, b))
    ('za,xza', 'xz', [a, b])

    >>> parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
    ('za,xza', 'xz', [a, b])
    r   zNo input operands r#   c                 S   rP   r   rQ   r(   r   r   r   r@     rR   z&parse_einsum_input.<locals>.<listcomp>r   N->rX   z%Subscripts can only contain one '->'..r6   c                 s   s    | ]}t |jV  qd S r%   )r4   r;   r(   r   r   r   r+   #  rW   z%parse_einsum_input.<locals>.<genexpr>TF   rJ   zInvalid Ellipses.r   zEllipses lengths do not match.z1Output character '{}' did not appear in the inputzDNumber of einsum subscripts must be equal to the number of operands.)r4   
ValueError
isinstancestrr9   rd   r7   r0   r   rA   splitr/   r;   r	   r-   r.   format)rb   r:   invalidr   Zellipse_indslongestZ	input_tmpZ
output_subZsplit_subscriptsZout_subnumrU   Zellipse_countZout_ellipseZoutput_subscriptZnormal_indsZinput_subscriptscharr   r   r   r      sl   


 



r   )__doc__r]   collectionsr   numpyrI   __all__r   r   r   r   r   r   r   r	   r
   r   rM   rd   r   r   r   r   r   <module>   s$   %