U
    JcT'                     @   s  d dl Z d dlmZ d dlmZ d dlmZmZmZ d dl	m
Z
 d dlmZ d dlmZ d dlmZ dd	d
dgZeddeedddZeddeeddd	Zeeegef ee ddddZee ee ee dddZeddeeeddd
ZeddG dd dZdS )    N)Queuewraps)CallableDictList)GraphModule)compatibility
PassResultinplace_wrapperpass_result_wrapper this_before_that_pass_constraintPassManagerF)Zis_backward_compatible)fnreturnc                    s8    dkrdS t   fdd}|jdkr4t |_|S )a+  
    Convenience wrapper for passes which modify an object inplace. This
    wrapper makes them return a PassResult containing the modified object and
    True for the "modified" flag.

    Args:
        fn (Callable[Module, Any])

    Returns:
        wrapped_fn (Callable[Module, PassResult])
    Nc                    s    | pt | dS NTr
   Zgmr    F/tmp/pip-unpacked-wheel-gikjz4vx/torch/fx/passes/infra/pass_manager.py
wrapped_fn   s    z#inplace_wrapper.<locals>.wrapped_fnr   )r   __name__strr   r   r   r   r   r      s    

c                    s$    dkrdS t   fdd}|S )a+  
    Wrapper for passes which currently do not return a PassResult.
    This wrapper makes them return a PassResult containing the modified object
    and True for the "modified" flag.

    Args:
        fn (Callable[Module, Any])

    Returns:
        wrapped_fn (Callable[Module, PassResult])
    Nc                    s    | } t | dS r   r
   r   r   r   r   r   5   s    z'pass_result_wrapper.<locals>.wrapped_fnr   r   r   r   r   r   %   s
    )
constraintpassesr   c                 C   sn   t |D ]`\}}t ||d d  D ]B\}}| ||r8q$td| d| d| d| d| d| dq$qd S )	N   z,pass schedule constraint violated. Expected z before z but found z
 at index z and z	 at indexz in pass list.)	enumerateRuntimeError)r   r   iajbr   r   r   "_validate_pass_schedule_constraint<   s    
(r$   )r   constraintsr   c                    sJ  t |dkr| S dd | D }dd | D  t }| D ]`}| D ]@}||krLq>|D ],}|||sP|| |  |  d7  < qPq> | dkr6|| q6dd | D }g }| s| }	||	 d||	< ||	 D ]2}
||
 sֈ |
  d8  <  |
 dkr||
 qqtt fdd	  }t |dkrFd
| }t	||S )z
    Args
        passes: Passes that we are ordering
        constraints: Constraints applied on these passes

    Returns
        A sorted list of callables and a boolean of if a circular dependency
        existed
    r   c                 S   s   i | ]
}|g qS r   r   .0pr   r   r   
<dictcomp>Y   s      z,_topological_sort_passes.<locals>.<dictcomp>c                 S   s   i | ]
}|d qS )r   r   r&   r   r   r   r)   Z   s      r   c                 S   s   i | ]
}|d qS )Fr   r&   r   r   r   r)   i   s      Tc                    s    |  dkS )Nr   r   )r(   Zindegree_mapr   r   <lambda>x       z*_topological_sort_passes.<locals>.<lambda>z:Circular dependency detected within the following passes: )
lenr   appendputemptygetlistfilterkeysr   )r   r%   graph
candidatesr!   r#   r   visitedZsorted_passesr(   nZcycle_passeserrorr   r*   r   _topological_sort_passesI   s>    



r:   )thisthatr   c                    s   t t d fdd}|S )a  
    Defines a partial order ('depends on' function) where `this` must occur
    before `that`.

    For example, the following pass list and constraint list would be invalid.
    ```
    passes = [pass_b, pass_a]

    constraints = [
        this_before_that_pass_constraint(pass_a, pass_b)
    ]
    ```

    Args:
        this (Callable): pass which should occur first
        that (Callable): pass which should occur later

    Returns:
        depends_on (Callable[[Object, Object], bool]
    r!   r#   c                    s   |  kr|krdS dS )NFTr   r=   r<   r;   r   r   
depends_on   s    z4this_before_that_pass_constraint.<locals>.depends_on)r   )r;   r<   r?   r   r>   r   r      s    c                   @   s   e Zd ZU dZg Zeeejge	f  e
d< g Zeeeegef  e
d< dZee
d< dZee
d< deeed	d
dZedddZedddZdd Zdd ZeddddZejddddZeje	dddZdS )r   ad  
    Construct a PassManager.

    Collects passes and constraints. This defines the pass schedule, manages
    pass constraints and pass execution.

    Args:
        passes (Optional[List[Callable]]): List of passes. A pass is a
            callable which modifies an object and returns a PassResult
        constraint (Optional[List[Callable]]): List of constraints. A
            constraint is a callable which takes two passes (A, B) and returns
            True if A depends on B and False otherwise. See implementation of
            `this_before_that_pass_constraint` for example.
        steps (int): Max number of times we run the passes (default = 1).
        run_checks_after_each_pass (bool): Whether to run checks and linting
            after each pass
        suppress_check_failures (bool): Whether to raise errors when running
            checks
    r   r%   F
_validatedr   stepsN)run_checks_after_each_passsuppress_check_failuresdebugc                 C   s4   |r
|| _ |r|| _|r|| _|| _|| _|| _d S N)r   r%   rA   rB   rC   rD   )selfr   r%   rA   rB   rC   rD   r   r   r   __init__   s    	zPassManager.__init__)_passc                 C   s   | j | d| _dS )z>
        Adds a pass into the current list of passes.
        FN)r   r.   r@   )rF   rH   r   r   r   add_pass   s    zPassManager.add_pass)r   c                 C   s   | j | d| _dS )zI
        Adds a constraint into the current list of constraints.
        FN)r%   r.   r@   rF   r   r   r   r   add_constraint   s    zPassManager.add_constraintc                 C   s,   | j r
dS | jD ]}t|| j qd| _ dS )z
        Validates that current pass schedule defined by `self.passes` is valid
        according to all constraints in `self.constraints`
        NT)r@   r%   r$   r   rJ   r   r   r   validate_constraints   s
    
z PassManager.validate_constraintsc                 C   s   t | j| j| _d| _dS )ab  
        Finds a valid traversal order based on the given constraints and orders
        the passes based on this order.

        If a circular dependency exists between the constraints and steps = 1,
        then we will raise an error because if steps != 1 this means that we
        will re-run the passes, allowing for circular dependencies.
        TN)r:   r   r%   r@   )rF   r   r   r   solve_constraints   s    	zPassManager.solve_constraints)checkr   c                 C   s8   t |}tt|j dkr(tdt| d| dS )z
        Adds a function which takes runs various checks on a given graph module.
        This function is run before and after each pass if the
        `run_checks_after_each_pass` flag is enabled.
        r   zEPassManager check function should only take in one variable, a modulerN   N)inspect	signaturer-   r2   
parametersvalues	TypeErrorsetattr)rF   rN   sigr   r   r   
add_checks   s    
zPassManager.add_checks)moduler   c                 C   s   d S rE   r   )rF   rW   r   r   r   rN      s    zPassManager.checkc                 C   s  | j s|   | | d}t| jD ]}d}t| jD ]\}}| jrXtd|j	 d z||}W nZ t
k
r } z<dd | jd| D }	d|j	 d|	 }
t||
|W 5 d}~X Y nX |j}|p|j}t|tr|  | jr8| | q8|p|}|s& qq&t||S )	a}  
        Runs a list of passes in the order based on `self.passes` on the given
        graph module. Each time a pass is run, checks and linting will be run on
        the graph module if `run_checks_after_each_pass` is set.

        If the module is a graph module, we will run the list of passes until
        the graph stops changing, or until `steps` number of times.
        FzRunning pass ''c                 S   s   g | ]
}|j qS r   )r   r&   r   r   r   
<listcomp>   s     z(PassManager.__call__.<locals>.<listcomp>Nz$An error occurred when running the 'z#' pass after the following passes: )r@   rM   rN   rangerA   r   r   rD   printr   	ExceptiontypeZgraph_modulemodified
isinstancer   Z	recompilerB   r   )rF   rW   Zoverall_modified_r^   r    r   reseZprev_pass_namesmsgr   r   r   __call__  s2    

 

zPassManager.__call__)NNNFFF)r   
__module____qualname____doc__r   r   r   nnModuler   __annotations__r%   boolr@   rA   intrG   rI   rK   rL   rM   rV   rN   rd   r   r   r   r   r      s.   
      )rO   queuer   	functoolsr   typingr   r   r   Ztorch.nnrh   Ztorch.fx.graph_moduler   Ztorch.fx._compatibilityr	   Ztorch.fx.passes.infra.pass_baser   __all__r   r   rk   r$   r:   r   r   r   r   r   r   <module>   s0     6