U
    JcI                     @   s*  d dl Z d dlm  mZ d dlmZmZmZm	Z	m
Z
mZ d dlZd dlZd dlZd dlmZmZ d dlZd dlZd dlZd dlmZ zd dlZdZW n ek
r   dZY nX e jjZddd	d
ddddgZdaG dd dZdd Zdd Zdd Z dd Z!G dd	 d	e"Z#G dd dZ$erFG dd dej%Z&G dd dej%Z'dd dd dd dd dd d d d!Z(e(d"d d#d d$d d%d d&d d'd d(Z)d)hZ*d*d+d,d-d)hZ+d.d/ Z,e)- D ]\Z.Z/e,e.e/e# qe)- D ]$\Z.Z/e.e+krqe,e.e/e$ q[.[/d3d0d1Z0G d2d
 d
e"Z1dS )4    N)SetDictListTypeOptionalcast)	lru_cachepartial)MetaConverterTFhas_symbolic_sizes_stridescreate_contiguousPySymIntShapeEnvSymDispatchMode
PySymFloat	sym_floatFloorDivc                   @   s$   e Zd Zdd Zdd Zdd ZdS )r   c                 C   s
   t  d S N)NotImplementedError)selffunctypesargskwargs r   I/tmp/pip-unpacked-wheel-gikjz4vx/torch/fx/experimental/symbolic_shapes.py__sym_dispatch__3   s    z SymDispatchMode.__sym_dispatch__c                 C   s,   t }t| drt|  dn|| _| a | S )Ninnerz< has already been used as a mode. Please use a fresh version)SYM_FUNCTION_MODEhasattrRuntimeErrorr   )r   oldr   r   r   	__enter__6   s    
zSymDispatchMode.__enter__c                 C   s
   | j ad S r   )r   r   )r   exc_typeexc_valexc_tbr   r   r   __exit__@   s    zSymDispatchMode.__exit__N)__name__
__module____qualname__r   r"   r&   r   r   r   r   r   2   s   
c                 C   s   | j S r   )Z_has_symbolic_sizes_strides)elemr   r   r   r   D   s    c                 C   s:   dg}t | d d D ]}|||d   qtt |S )N   )reversedappendlist)shapestridesZdimr   r   r   r   G   s    c                 C   s6   t }|st|ja zg }|| |||W S |a X d S r   )r   AssertionErrorr   r   )r   r   r   moder   r   r   r   _handle_sym_dispatchM   s    r4   c                 C   s,   t | dr|  S t| tjjr$| S t| S )N__sym_float__)r   r5   
isinstancetorchZ_CZSymFloatNodefloatar   r   r   r   Y   s
    
c                   @   sZ   e Zd ZdZd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 )r   z
    PySymInt objects are the primary "symbolic shape" objects that flow through
    our program. They're what sit under FakeTensor, and contains our primary
    implementation of symbolic shapes.
    Nc                 C   s   || _ || _|| _d S r   expr	shape_envconstantr   r<   r=   r>   r   r   r   __init__i   s    zPySymInt.__init__c                 C   s   t t|| j|dS N)r>   )r   sympyIntegerr=   r   numr   r   r   wrapn   s    zPySymInt.wrapc                 C   s   t | j| j| jdS rA   )r   r<   r=   r>   r   r   r   r   cloneq   s    zPySymInt.clonec                 C   s   | j  S r   r<   rG   r   r   r   __str__t   s    zPySymInt.__str__c                 C   s   | j  S r   rI   rG   r   r   r   __repr__w   s    zPySymInt.__repr__c                 C   s   t dd S )Nz6Trying to extract a concrete int out of a symbolic int)r    rG   r   r   r   __int__{   s    zPySymInt.__int__c                 C   s   t | j| jS r   )intr=   evaluate_exprr<   )r   fileliner   r   r   	guard_int   s    zPySymInt.guard_intc                 C   s    t rtt| fi S t| j| jS r   )r   r4   r   r   r<   r=   rG   r   r   r   r5      s    zPySymInt.__sym_float__c                 C   s   t | j| j| jS r   )boolr=   rN   replacer<   rG   r   r   r   __bool__   s    zPySymInt.__bool__)N)r'   r(   r)   __doc__r@   rF   rH   rJ   rK   rL   rQ   r5   rT   r   r   r   r   r   c   s   
c                   @   s&   e Zd ZdddZdd Zdd ZdS )	r   Nc                 C   s   || _ || _|| _d S r   r;   r?   r   r   r   r@      s    zPySymFloat.__init__c                 C   s   t t|| j|dS rA   )r   rB   ZFloatr=   rD   r   r   r   rF      s    zPySymFloat.wrapc                 C   s   | j  S r   rI   rG   r   r   r   rJ      s    zPySymFloat.__str__)N)r'   r(   r)   r@   rF   rJ   r   r   r   r   r      s   
c                   @   s    e Zd ZdZdZedd ZdS )r   z
        We maintain this so that:
        1. We can use divisibility guards to simplify FloorDiv(a, b) to a / b.
        2. Printing out the expression is nicer (compared to say, representing a//b as (a - a % b) / b)
        )   c                 C   s   |dkrt dS |dkr|S t|t jr>t|t jr>|| S t|trbt|jd |jd | S t ||}|dkrtt || t || S d S )Nr   r+   )rB   rC   r6   r   r   gcdsimplify)clsbasedivisorrW   r   r   r   eval   s    

 zFloorDiv.evalNr'   r(   r)   rU   nargsclassmethodr\   r   r   r   r   r      s   c                   @   s    e Zd ZdZdZedd ZdS )Ceilz
        sympy doesn't have its own ceil(), so rolling one here.
        We maintain this so that we can simplify a sympy.Rational into a sympy.Float.
        sympy.Float isn't supported.
        )r+   c                 C   sZ   t |tjr|S t |tjjjr*|jr*|S t |tjrB| d S t	dt
t| d S )Nr+   z$math.ceil() not supported for type: )r6   rB   rC   coresymbolSymbolZ	is_scalarRationalfloorr   strtype)rY   r:   r   r   r   r\      s    z	Ceil.evalNr]   r   r   r   r   r`      s   r`   c                 C   s   | | S r   r   r:   br   r   r   <lambda>       rj   c                 C   s   | | S r   r   rh   r   r   r   rj      rk   c                 C   s   | | S r   r   rh   r   r   r   rj      rk   c                 C   s   | | S r   r   rh   r   r   r   rj      rk   c                 C   s   | | S r   r   rh   r   r   r   rj      rk   c                 C   s
   t | |S r   )r   rh   r   r   r   rj      rk   )addsubmulmodtruedivfloordivc                 C   s   t | |S r   )rB   Eqrh   r   r   r   rj      rk   c                 C   s   t | |S r   )rB   ZGtrh   r   r   r   rj      rk   c                 C   s   t | |S r   )rB   ZLtrh   r   r   r   rj      rk   c                 C   s   t | |S r   )rB   ZLerh   r   r   r   rj      rk   c                 C   s   t | |S r   )rB   ZGerh   r   r   r   rj      rk   c                 C   s   t | S r   )r`   r9   r   r   r   rj      rk   )eqgtltlegeceilrx   rl   rm   rn   rp   c                    s   t d   fdd} fdd}tkrVt| td d| n<t| td d| tkrtd d| d S )N   c                    s|   t rttt| |fi S t|r*|j}| j| j}| j|} ||}t	|}dkrlt
|| jS || jS d S )N)rp   )r   r4   getattroperatorr6   r<   r=   rS   rB   expandr   )r   otherr<   outr   methodpy_typer   r   
magic_impl   s    


z_make_magic.<locals>.magic_implc                    st   t r0dkrtt}n
tt}t|| fi S | j| j} |}t	|}dkrdt
|| jS || jS d S )N)rx   )r   rz   mathr{   r4   r=   rS   r<   rB   r|   r   )r   opr<   r~   r   r   r   unary_magic_impl   s    

z%_make_magic.<locals>.unary_magic_impl__Z__r)r   unary_magic_methodssetattrreflectable_magic_methods)r   r   r   r   r   r   r   r   _make_magic   s    r   c                    s4   t ||  dt|  fdd} j|_|S )z
    Wrapper around lru_cache that clears when new info about shapes has been
    updated.

    Use lru_cache if the output is always the same, regardless of the
    constraints we know now (i.e. evaluate_expr)

    Use _lru_cache otherwise.
    Nc                    s,   |   kr|       | f||S r   )_get_keycache_clear)r   r   r   Zfn_cacheZ	prior_keyr   r   wrapper)  s    z_lru_cache.<locals>.wrapper)r   	functoolswraps
cache_info)fnmaxsizer   r   r   r   
_lru_cache  s    
r   c                   @   s   e Zd Zdd Zdd Zdd Zdd Zd	d
 Zd)ddZdd Z	e
dddddZe
dddddZe
dd Ze
dddddZedddddZe
ddd d!d"Zedd#d$dd%d&Zedddd'd(Zd$S )*r   c                 C   s$   g | _ i | _i | _t | _i | _d S r   )guards
var_to_valreplacementsset	divisibleval_to_symintrG   r   r   r   r@   7  s
    zShapeEnv.__init__c                 C   s   t | jt | jfS )z
        Defines the current "state" of the guards we've accumulated in this ShapeEnv.
        Determines when we need to invalidate our cache
        )lenr   r   rG   r   r   r   r   E  s    zShapeEnv._get_keyc                 C   s   |dkst tstd|dks(|dkr,|S || jkr@| j| S tj|ddd}t|| }tj	|}t
|| j|< || j|< |S )Nr   z.Need sympy installed to create symbolic shapesr+   TZpositiveinteger)r2   	HAS_SYMPYr    r   rB   rc   r   r7   Z
SymIntNodeZ
new_symintrC   r   )r   namevalZ
sympy_exprZ
py_sym_intZcpp_sym_intr   r   r   create_symintN  s    



zShapeEnv.create_symintc                    s<   t   t }ttjt| d| t fdd| jD S )N)r=   c                 3   s$   | ]\}}}|  j|kV  qd S r   )xreplacer   ).0guardvalue_Znew_envr   r   	<genexpr>i  s     z4ShapeEnv.evaluate_guards_for_args.<locals>.<genexpr>)	r   r
   pytreeZtree_map_onlyr7   ZTensorr	   allr   )r   r   Zmeta_converterr   r   r   evaluate_guards_for_argsc  s    z!ShapeEnv.evaluate_guards_for_argsc                    s    fdd j D S )Nc                    s.   g | ]&\}}}  |d kr ||fqS r   )_maybe_evaluate_staticrX   )r   r   r   r   rG   r   r   
<listcomp>l  s      z2ShapeEnv.get_nontrivial_guards.<locals>.<listcomp>)r   rG   r   rG   r   get_nontrivial_guardsk  s    zShapeEnv.get_nontrivial_guardsFc                    s0   dd fdd d  fdd| jD S )Nc                 S   s>   |t jkrt| S |t jkr(d|  dS d|  d| dS d S )NzNot()zEq(z, )rB   truerf   false)r   r   r   r   r   
format_valo  s
    

z*ShapeEnv.format_guards.<locals>.format_valc                    s    sdS dt | d S )N z
   Guarded at:
z   )textwrapindent)tb)verboser   r   	format_tbw  s    z)ShapeEnv.format_guards.<locals>.format_tb
c                 3   s,   | ]$\}}}d ||  | V  qdS )z - Nr   )r   r   r   r   )r   r   r   r   r   |  s     z)ShapeEnv.format_guards.<locals>.<genexpr>)joinr   )r   r   r   )r   r   r   r   format_guardsn  s    zShapeEnv.format_guardsc                 C   s0   t t}| j D ]\}}|| | q|S r   )collectionsdefaultdictr/   r   itemsr.   )r   Zshape_groupskvr   r   r   get_shape_groups~  s    
zShapeEnv.get_shape_groupsz
sympy.ExprzOptional[sympy.Expr])r<   returnc                 C   s   |  |}t|j}dd t|D }||}i }|tD ]"}t|j	d |j	d  ||< q>t
||}tt|jdkr|S dS )zC
        Tries to evaluate expr without introducing guards
        c                 S   s,   i | ]$\}}|t jd | dddd qS )Zshape_Tr   r+   )rB   rc   )r   idxr   r   r   r   
<dictcomp>  s    z3ShapeEnv._maybe_evaluate_static.<locals>.<dictcomp>r   r+   N)rX   r/   free_symbols	enumerater   atomsr   rB   re   r   r|   r   )r   r<   symbolsZnew_shape_envZnew_exprZfloor_div_replaceatomr   r   r   r     s    


 zShapeEnv._maybe_evaluate_staticc                    s$    fdd|j D }t||S )Nc                    s    i | ]}|  ttj|qS r   )_findr   rB   rc   r   srG   r   r   r     s      z$ShapeEnv.replace.<locals>.<dictcomp>)r   rB   r|   r   )r   r<   r   r   rG   r   rS     s    zShapeEnv.replacec                 C   s>   t  }| jD ]&}| |}t|jdkr|| q|| _d S )Nr   )r   r   rS   r   r   rl   )r   Znew_divisibler   resr   r   r   _update_divisible  s    

zShapeEnv._update_divisiblec                 C   sr   |  |}|trn|   i }|tD ].}|j\}}|  || | jkr*|| ||< q*||}t	|}|S r   )
rS   hasr   r   r   r   r   r   rB   r|   )r   r<   Zdiv_replacementsr   rZ   r[   r   r   r   rX     s    




zShapeEnv.simplifyry   rI   c                 C   s,   t || j}t|jdks(td|S )a  
        Gets a size hint for a given expression from the underlying shapes we had.
        Does not introduce a guard, so only use this when you can guarantee that
        your code is still valid for arbitrary shapes (such as optimization decisions)
        r   z;Size hint has variables we don't have underlying values for)rB   r|   r   r   r   r   r2   )r   r<   Zresult_exprr   r   r   	size_hint  s    zShapeEnv.size_hintzsympy.Symbol)r:   r   c                    sL   | j kr|S  j | } fdd|jD } j | | j |<  j | S )z
        Implements a DSU-like algorithm to find the variable that represents a
        Also handles transitive non-identity replacements.

        a: b + c
        c: d
        c                    s   i | ]}|  |qS r   )r   r   rG   r   r   r     s      z"ShapeEnv._find.<locals>.<dictcomp>)r   r   r   )r   r:   r   Zcur_replacer   rG   r   r     s    	

zShapeEnv._findzsympy.EqNc           
         sx  t  |}|sdS t|j}t|dks4tdt|dkrDdS t| fdddd}|j}|j}zvt	j
|| |d dd	}t|d
krW dS |d |d  }tdd t	|D rڈ |}| jtt	j|d < W n tk
rr   |t	jrlt|t	jd }	zDt	j
|| |	dd	}t|d
krR|d |	 dkrR j|	 W n tk
rj   Y nX Y dS X dS )z
        Evaluates the result of an eq call. If true, uses information to
        simplify shapes (i.e. a == b or a % 5 == 0)
        Nr   z1The expression should not be static by this point   c                    s     | | jfS r   )r   r   )xrG   r   r   rj     rk   z*ShapeEnv._maybe_guard_eq.<locals>.<lambda>T)keyreverse)dictr+   c                 s   s   | ]}|j V  qd S r   )
is_integer)r   tr   r   r   r     s     z+ShapeEnv._maybe_guard_eq.<locals>.<genexpr>)rR   r   r/   r   r   r2   sortedlhsrhsrB   Zsolver   Zpreorder_traversalr   r   r   rc   r   r   ZModtupler   r   rl   )
r   r<   Zconcrete_boolfreer   r   Z	solutionsZsolutionZnew_varZmod_exprr   rG   r   _maybe_guard_eq  s8    

 zShapeEnv._maybe_guard_eqc                 C   s   t |jdkr|S | |}| |}|dk	r2|S t|tjrH| | | |}d	t
t
 dd }| j|||f |S )zO
        Given an expression, evaluates it, adding guards if necessary
        r   Nr   )r   r   rX   r   r6   rB   rr   r   r   r   	tracebackformat_listextract_stackr   r.   )r   r<   Zstatic_exprZconcrete_valstackr   r   r   rN     s    



zShapeEnv.evaluate_expr)F)r'   r(   r)   r@   r   r   r   r   r   r   r   r   rS   r   rX   r   r   r   r   rN   r   r   r   r   r   6  s.   	

	
$)N)2r7   Ztorch.utils._pytreeutilsZ_pytreer   typingr   r   r   r   r   r   r{   r   r   r   r	   r   r   r   Ztorch._subclasses.meta_utilsr
   rB   r   ImportErroropsZaten__all__r   r   r   r   r4   r   objectr   r   ZFunctionr   r`   r   Zmagic_methodsr   Zfloat_magic_methodsr   r   r   r   r   r   r   r   r   r   <module>   s    
      
,
.

