B
    d)              
   @   s   d Z ddlZddlmZ ddlZdddddd	d
dddg
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 kp| dk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   C/var/www/html/venv/lib/python3.7/site-packages/opt_einsum/parser.pyr      s    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    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    c             c   sB   d }}x4||k r<t |}|d7 }|| kr,q
|V  |d7 }q
W 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    
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   s   i | ]\}}t ||qS r   )r   ).0r   r   r   r   r   
<dictcomp>b   s    z1convert_to_valid_einsum_chars.<locals>.<dictcomp> c             3   s   | ]}  ||V  qd S )N)get)r   r   )replacerr   r   	<genexpr>c   s    z0convert_to_valid_einsum_chars.<locals>.<genexpr>)sortedset	enumeratejoin)r   symbolsr   )r!   r   r   W   s    
c                sN   t   x.| D ]&}|dkrq| krtt  |< qW 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   s   | ]}  ||V  qd S )N)r    )r   r   )renamer   r   r"   w   s    z%alpha_canonicalize.<locals>.<genexpr>)r   r   lenr&   )Zequationnamer   )r(   r   r   f   s    
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   )tmp_subscriptsr   r   r"      s    z"find_output_str.<locals>.<genexpr>)replacer&   r#   r$   )
subscriptsr   )r-   r   r	   z   s    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   s4   | ], 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   )find)r   r   )cr   r   
<listcomp>   s    z/find_output_shape.<locals>.<genexpr>.<listcomp>N)maxzip)r   )inputsshapes)r3   r   r"      s    z$find_output_shape.<locals>.<genexpr>)tuple)r7   r8   outputr   )r7   r8   r   r
      s    c             C   s   t | dst| S | S d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>
    r0   N)hasattrnpZ
asanyarray)r   r   r   r   r      s    

c             C   s4   d}x*| D ]"}|t kr |d7 }q
||| 7 }q
W |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   z...)Ellipsis)Zold_sub
symbol_mapZnew_subr   r   r   r   convert_subscripts   s    


r?   c                s   t | }g }g }x8tt| d D ]$}||d ||d q"W t|rZ|d nd}dd |D } y4ttj|}|	t
 dd tt|D  W n tk
r   td	Y nX d
 fdd|D }|dk	r|d7 }|t| 7 }|| fS )z:Convert 'interleaved' input to standard einsum input.
       r   Nc             S   s   g | ]}t |qS r   )r   )r   r   r   r   r   r4      s    z-convert_interleaved_input.<locals>.<listcomp>c             S   s   i | ]\}}t ||qS r   )r   )r   idxsymbolr   r   r   r      s    z-convert_interleaved_input.<locals>.<dictcomp>ziFor this input type lists must contain either Ellipsis or hashable and comparable object (e.g. int, str).r+   c             3   s   | ]}t | V  qd S )N)r?   )r   sub)r>   r   r   r"      s    z,convert_interleaved_input.<locals>.<genexpr>z->)listranger)   appendpopr$   	itertoolschainfrom_iterablediscardr=   r%   r#   	TypeErrorr&   r?   )operandsZtmp_operandsZoperand_listZsubscript_listpZoutput_listZ
symbol_setr/   r   )r>   r   convert_interleaved_input   s&    
rP   c             C   s  t | dkrtdt| d trJ| d dd}dd | dd D } nt| \}} d	|ksfd
|kr|d	dkp|d
dk}|s|ddkrtdd|krh|dddddd}dt|t	dd | D }d}d|kr|
d\}}|
d}d}	n|
d}d}	xt|D ]\}
}d|kr$|ddksV|ddkr^td| |
 jdkrtd}n t	t | |
 jdt |d  }||kr|}|dk rtdn6|dkr|dd||
< n|d|| d ||
< q$W d|}|dkrd}n|| d }|	r6|d|d| 7 }n2t|}dtt|t| }|d| | 7 }d|kr|
d\}}n|t| }}x&|D ]}||krtd|qW t |
dt | krt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   s   g | ]}t |qS r   )r   )r   r   r   r   r   r4     s    z&parse_einsum_input.<locals>.<listcomp>r   N->z->z%Subscripts can only contain one '->'..r+   c             s   s   | ]}t |jV  qd S )N)r)   r0   )r   r   r   r   r   r"   #  s    z%parse_einsum_input.<locals>.<genexpr>TF   z...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.)r)   
ValueError
isinstancestrr.   rP   r,   r&   r   r5   splitr%   r0   r	   r#   r$   format)rN   r/   invalidr   Zellipse_indslongestZ	input_tmpZ
output_subZsplit_subscriptsZout_subnumrD   Zellipse_countZout_ellipseZoutput_subscriptZnormal_indsZinput_subscriptscharr   r   r   r      sh    




  



 




)__doc__rI   collectionsr   numpyr<   __all__r   r   r   r   r   r   r   r	   r
   r   r?   rP   r   r   r   r   r   <module>   s&   %