o
    Xi                     @  s  U d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	 ddl
Zddlm  mZ ddlm  mZ ddlm  mZ ddlm  mZ ddlm  mZ ddlmZ ddlmZmZ e	dZejZdZej G d	d
 d
Z!d3ddZ"G dd dZ#G dd dZ$d4ddZ%G dd de#Z&G dd dej'Z(G dd de(Z)d5d$d%Z*d6d+d,Z+e,eej-iZ.d-e/d.< d/Z0de/d0< G d1d2 d2Z1dS )7zRewrite rules for ONNX models.    )annotationsN)CallableSequenceTypeVar)ir)_tapeconvenienceTz!pkg.onnxscript.rewriter.rule_namec                   @  s:   e Zd ZU dZded< ded< ded< ded< d	ed
< dS )ReplacementSubgraphz1A subgraph that will replace the matched pattern._basics.MatchResultmatchSequence[ir.Value]new_outputsSequence[ir.Node]	new_nodesnew_initializersz_tape.UsedOpsetsused_opsetsN)__name__
__module____qualname____doc____annotations__ r   r   U/home/ubuntu/.local/lib/python3.10/site-packages/onnxscript/rewriter/_rewrite_rule.pyr
   #   s   
 r
   returnboolc                  O     dS )zA condition function that always returns True.

    This is used when no condition function is provided for a rewrite rule.
    Tr   )argskwargsr   r   r   always_true.      r   c                   @  sB   e Zd ZdZ				d%d&ddZd'ddZddddd(d#d$ZdS ))PatternzA pattern that can be matched against nodes in an ONNX graph.

    This class encapsulates pattern matching functionality, providing the ability to
    match patterns against nodes without requiring replacement functionality.
    Nr   target_pattern#_pattern_ir.GraphPattern | Callablecondition_functionCallable | Nonematcher^_matcher.PatternMatcher | Callable[[_pattern_ir.GraphPattern], _matcher.PatternMatcher] | Noneverboseintname
str | Noner   Nonec                 C  sn   t |tjst|}|| _|pt| _t |tjr|| _n|du r)t	| j| _n|| j| _|| _
|| _dS )a  Create a pattern matcher.

        Args:
            target_pattern: The _pattern_ir.GraphPattern that will be matched against the IR.
                If a callable is provided, it will be converted to a _pattern_ir.GraphPattern.
            condition_function: The condition function that will be used to check if
                the pattern match found should be rewritten.
            matcher: The pattern matcher that will be used to match the pattern.
                If not provided, a default matcher will be used.
            verbose: The verbosity level of the rule.
            name: An optional name for the pattern that will show up in verbose logging.
        N)
isinstance_pattern_irGraphPattern_to_graph_pattern_target_patternr   _condition_function_matcherPatternMatcherSimplePatternMatcher_verboser*   )selfr"   r$   r&   r(   r*   r   r   r   __init__=   s   


zPattern.__init__strc                 C     | j r| j S dS )NzAnonymous Patternr*   r7   r   r   r   __str__a      zPattern.__str__Tr(   check_nodes_are_removabletracermodelir.Modelgraph_or_functionir.Graph | ir.Functionnodeir.Node
int | Noner@   r   rA   _basics.MatchingTracer | None_basics.MatchResult | Nonec                  s  |r|dkrt d  |dur|nj}jj| ||drt| }jjD ]}|jdurC|jj	vrC
|jd q/d fdd	}	dd }
j D ]\}}|jdurv|
|j||}|sv|	|d	| |  S qXj D ]\}}|jdur|
|j||}|s|	|d
| |  S q||
j|fi j	}|s|	|dS r tjj S rʈ tjj S )t  Check if the node matches the pattern and return the match result.

        Args:
            model: The model containing the graph or function.
            graph_or_function: The graph or function to match against.
            node: The node to try to match the pattern against.
            verbose: The verbosity level of messages.
            check_nodes_are_removable: If True, validate that matched nodes can be safely removed.
            tracer: The tracer for debugging.

        Returns:
            MatchResult if the pattern matches successfully and passes the condition function,
            None otherwise.
           z[match] N)r(   remove_nodesc                   sH   t | tjr| j| j n|| r" tjj dS )z4Local utility to handle check failures consistently.N)	r-   _basicsMatchResultfailreasonfailure_nodes_and_valueslogMatchStatusCONDITION_FAILED)check_resultdefault_messagefailure_objectrD   r   rF   r7   rA   r   r   rP      s   zPattern.match.<locals>.failc                   s    fdd}|S )z4Encapsulates try-except pattern for check functions.c               
     sX   z | i |W S  t jy+ } zt  }||jt|j |W  Y d }~S d }~ww N)rN   MatchFailureErrorrO   rP   rQ   listfailure_sources)r   r   eresultfr   r   wrapped   s   z0Pattern.match.<locals>.wrap_try.<locals>.wrappedr   )ra   rb   r   r`   r   wrap_try   s   zPattern.match.<locals>.wrap_tryz)Node-level check failed for pattern node z+Value-level check failed for pattern value zCondition function check failedrZ   )printr6   r3   r   rN   MatchContextr1   inputsr*   bindingsbindnode_bindingsitemscheck_methodvalue_bindingsr2   rS   rT   SUCCESSNO_MATCH)r7   rB   rD   rF   r(   r@   rA   contextvarrP   rc   pattern_nodeir_noderV   pattern_valueir_valuecheck_match_resultr   rY   r   r   d   s`   



zPattern.match)NNr   N)r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r   r,   r   r9   rB   rC   rD   rE   rF   rG   r(   rH   r@   r   rA   rI   r   rJ   )r   r   r   r   r8   r=   r   r   r   r   r   r!   6   s    	
$	r!   c                   @  s$   e Zd ZdZdddZdd	d
ZdS )ReplacementPatternFunctionzThe replacement pattern that will replace the targeted pattern.

    Attributes:
        function (Callable): The replacement function that will be used to replace the matched pattern.
    r   r,   c                 C  s
   || _ d S rZ   )	_function)r7   functionr   r   r   r8      s   
z#ReplacementPatternFunction.__init__r   r   ReplacementSubgraph | Nonec                 C  sL   t  }| j|fi |j}|d u rd S t|ts|g}t|||j|j|jS rZ   )	RewriterContextry   rg   r-   r   r
   nodesinitializersr   )r7   r   ro   r   r   r   r   get_replacement   s   
z*ReplacementPatternFunction.get_replacementN)r   r,   )r   r   r   r{   )r   r   r   r   r8   r   r   r   r   r   rx      s    
rx   rD   rE   deltac              	   C  sl   | j }|jD ]-\}}||vr|d ur|nd||< q|d ur3||| kr3td| d||  d| dqd S )N   zMultiple versions of opset z used. Expected version 
, but got .)opset_importsr   
ValueError)rD   r   importsdomainversionr   r   r   _update_opset_imports   s   r   c                      sj   e Zd Z								d2d3 fddZd4ddZdddd5d)d*Zdddd+d6d-d.Zd7d0d1Z  ZS )8RewriteRuleNr   TFr"   r#   replacement_pattern%ReplacementPatternFunction | Callabler$   r%   r&   r'   r(   r)   r*   r+   rM   r   graph_pre_visitorCallable[[], None] | Nonegraph_post_visitoras_functionr   r,   c                   sX   |
r|st dt ||||| t|tst|}|| _|| _|| _|	| _|
| _	dS )a  Create a rewrite rule.

        Args:
            target_pattern: The _pattern_ir.GraphPattern that will be matched against the IR.
                If a callable is provided, it will be converted to a _pattern_ir.GraphPattern.
            replacement_pattern: The ReplacementPatternFunction that will be used to
                replace the matched pattern. If a callable is provided, it will be
                converted to a ReplacementPatternFunction.
            condition_function: The condition function that will be used to check if
                the pattern match found should be rewritten.
            matcher: The pattern matcher that will be used to match the pattern.
                If not provided, a default matcher will be used.
            verbose: The verbosity level of the rule.
            name: An optional name for the pattern that will show up in verbose logging.
            remove_nodes: If True, the matched nodes will be removed from the graph.
            graph_pre_visitor: A function that will be called before applying the
                rewriting to the top-level graph or a function.
            graph_post_visitor: A function that will be called after the rewriting
                is complete for a graph or function.
            as_function: If True, the matched nodes will be extracted into a model
                local function. This is only supported when remove_nodes=True and
                when the replacement subgraph has a single node, representing the
                function call.
        z:as_function=True is only supported when remove_nodes=True.N)
r   superr8   r-   rx   _replacement_patternrM   r   r   r   )r7   r"   r   r$   r&   r(   r*   rM   r   r   r   	__class__r   r   r8      s   '

zRewriteRule.__init__r9   c                 C  r:   )NzAnonymous Ruler;   r<   r   r   r   r=   +  r>   zRewriteRule.__str__r(   rA   rB   rC   rD   rE   rF   rG   rH   rA   rI   r{   c                C  s   | j ||||| j|d}|sdS | j|}|du r)|r'|| |||tjj dS t|j	| j
jkrBtd| j
j dt|j	 dt|| t|j| |S )zTIf the node matches the pattern, then replace the node with the replacement pattern.r?   NzsNumber of outputs from replacement function does not match the number of outputs from the target pattern. Expected r   r   )r   rM   r   r   rS   rN   rT   REPLACEMENT_FAILEDlenr   r1   num_outputsr   r   graph)r7   rB   rD   rF   r(   rA   r   replacement_subgraphr   r   r   try_rewrite.  s@   
zRewriteRule.try_rewrite)commuter(   rA   r   c                C  s   t | g|dj|||dS )Nr   r   )RewriteRuleSetapply_to_model)r7   rB   r   r(   rA   r   r   r   r   Y  s   
zRewriteRule.apply_to_modelSequence[RewriteRule]c                   s$   fdd  fddj  D S )Nc                   s8   t  j}t|  j j||  j j j j j	 j

S )zHReturn a shallow copy of self with node_pattern replaced by new_pattern.)typer3   r   r   r2   r6   r*   rM   r   r   r   )new_patternmatcher_classr<   r   r   replace_patternh  s   
z,RewriteRule.commute.<locals>.replace_patternc                      g | ]} |qS r   r   ).0p)r   r   r   
<listcomp>y      z'RewriteRule.commute.<locals>.<listcomp>)r1   r   r<   r   )r   r7   r   r   g  s   zRewriteRule.commute)NNr   NTNNF)r"   r#   r   r   r$   r%   r&   r'   r(   r)   r*   r+   rM   r   r   r   r   r   r   r   r   r,   rv   )rB   rC   rD   rE   rF   rG   r(   rH   rA   rI   r   r{   )rB   rC   r   r   r(   rH   rA   rI   )r   r   )	r   r   r   r8   r=   r   r   r   __classcell__r   r   r   r   r      s&    
5	/r   c                   @  sH   e Zd ZdZdd ddZejd	d
 Zd!ddZddddd"ddZ	dS )#PatternBasea  Base class for implementing pattern matching as a class.

    This class encapsulates the pattern definition and condition checking
    without the replacement functionality.

    Example::

        class TransposePattern(PatternBase):
            def pattern(cls, op, x, perm):
                return op.Transpose(x, perm=perm)

            def check(cls, context, x: ir.Value, perm: ir.Attr) -> bool:
                if perm.is_ref():
                    return False
                if perm.type == ir.AttributeType.INTS:
                    if list(perm.as_ints()) == list(range(len(perm.as_ints()))):
                        return True
                return False
    Nr*   r+   r   r,   c                 K  s   |p| j j| _d | _|| _d S rZ   )r   r   r*   _compiled_pattern_pattern_kwargs)r7   r*   r   r   r   r   r8     s   
zPatternBase.__init__c                 O     t d)Nz6Method 'pattern' must be implemented by derived class.NotImplementedErrorr7   opr   r   r   r   r   pattern     zPatternBase.patternr   c                 O  s   t  S )zaDefault check function that returns a _basics.MatchResult object with success always set to True.)rN   rO   r   r   r   r   check  r   zPatternBase.checkTr?   rB   rC   rD   rE   rF   rG   r(   rH   r@   r   rA   rI   rJ   c                C  sB   | j du rt| j| jfd| ji| j| _ | j j||||||dS )rK   Nr*   r?   )r   r!   r   r   r*   r   r   )r7   rB   rD   rF   r(   r@   rA   r   r   r   r     s    
zPatternBase.matchrZ   )r*   r+   r   r,   )r   r   rw   )
r   r   r   r   r8   abcabstractmethodr   r   r   r   r   r   r   r   |  s    


r   c                      sP   e Zd ZdZedd Z	dd fddZejdd Z	dd Z
dd Z  ZS )RewriteRuleClassBasea	  Base class for implementing rewrite rules as a class.

    Example::

        class TransposeIdentity(RewriteRuleClassBase):
            def pattern(cls, op, x, perm):
                return op.Transpose(x, perm=perm)

            def check(cls, context, x: ir.Value, perm: ir.Attr) -> bool:
                if perm.is_ref():
                    return False
                if perm.type == ir.AttributeType.INTS:
                    if list(perm.as_ints()) == list(range(len(perm.as_ints()))):
                        return True
                return False

            def rewrite(cls, op, x: ir.Value, perm: ir.Attr | None = None):
                return op.Identity(x)

        # Then use
        # TransposeIdentity.rule()
        # to create a RewriteRule object.

    c              
   O  s6   | |i |}t |j|j|j|j|j|j|j|jdS )N)r*   rM   r   r   r   )	r   r   rewriter   r*   rM   setupcleanupr   )clsr   r   instancer   r   r   rule  s   zRewriteRuleClassBase.ruleNTFr*   r+   rM   r   r   r   r,   c                   s   t  | || _|| _d S rZ   )r   r8   rM   r   )r7   r*   rM   r   r   r   r   r8     s   
zRewriteRuleClassBase.__init__c                 O  r   )Nz6Method 'rewrite' must be implemented by derived class.r   r   r   r   r   r     r   zRewriteRuleClassBase.rewritec                 C  r   )zOptional setup function that can be overridden by derived classes.

        Used to do per model/function initialization.
        Nr   r<   r   r   r   r     r    zRewriteRuleClassBase.setupc                 C  r   )z}Optional cleanup function that can be overridden by derived classes.

        Used to do per model/function cleanup.
        Nr   r<   r   r   r   r      r    zRewriteRuleClassBase.cleanup)NTF)r*   r+   rM   r   r   r   r   r,   )r   r   r   r   classmethodr   r8   r   r   r   r   r   r   r   r   r   r   r     s    

r   rf   Sequence[ir.Value | None]r}   r   outputsr   c                   s   i g }g  | D ]"}|rt j|j|j|j|jdnt  }|dur%||< || qd fdddd
ddfddfdd|D }fdd|D }| | |fS )z9Utility function to extract a subgraph out as a function.)r*   shaper   
doc_stringNvalueir.Value | Noner   c                   sx   | d u rd S | vr8| j }|d ur0td|}tddg |g} | |jd  | < }|S td|  d|  S )Nr    Constantr   zValue z not found in value_map.)const_valuer   
AttrTensorNodeappendr   r   )r   r   
value_attr
const_noder_   )constant_nodes	value_mapr   r   
copy_value  s   
z&_copy_for_function.<locals>.copy_valueattrir.Attrc                 S  s2   |   rtd| jtjjtjjhv rtd| S )NzRefAttr not supported.zGraph attributes not supported.)is_refr   r   r   AttributeTypeGRAPHGRAPHS)r   r   r   r   copy_attr_value.  s
   z+_copy_for_function.<locals>.copy_attr_valuerF   rG   c                   s   fdd| j D } fdd| j D }tj| j| j||| jt| j	d | j
| j| j d
}|j	}t| j	D ]\}}|| |< |j
d urM|j
|| _
q8|S )Nc                   r   r   r   r   vr   r   r   r   ;  r   z9_copy_for_function.<locals>.copy_node.<locals>.<listcomp>c                   r   r   r   r   )r   r   r   r   <  r   )overloadr   r   r*   r   metadata_props)rf   
attributesvaluesr   r   r   op_typer   r   r   r*   r   r   copy	enumerate)rF   
new_inputsnew_attributesnew_noder   ioutput)r   r   r   r   r   	copy_node:  s*   
z%_copy_for_function.<locals>.copy_nodec                   r   r   r   r   rF   )r   r   r   r   P  r   z&_copy_for_function.<locals>.<listcomp>c                   r   r   r   r   r   r   r   r   Q  r   )r   r   r   r   )r   r   r   r   )rF   rG   r   rG   )r   Valuer*   r   r   r   r   )rf   r}   r   function_inputsinput	new_valuefunction_nodesfunction_outputsr   )r   r   r   r   r   r   _copy_for_function  s,   	

r   rB   rC   r   r9   r*   c                 C  s0   | j }d}	 t|}|||f|vr|S |d7 }q)a  Get a new overload for the given domain and name.

    Args:
        model: The model to which the new overload will be added.
        domain: The domain of the new overload.
        name: The opname of the new overload.

    Returns:
        The new overload name.
    r   )	functionsr9   )rB   r   r*   existing_functionsr   overload_namer   r   r   _get_new_overloadU  s   r   zmetadata_merger.MetadataMerger_default_metadata_mergerTmerge_metadatac                   @  sP   e Zd Zddd d	d
Zd!ddZddd"ddZdddd#ddZdd ZdS )$r   Fr   rulesr   r   r   r   r,   c                C  sH   |st d|rttjdd |D }|| _tdd |D | _d S )Nz$rules must contain at least one rulec                 S  s   g | ]}|  qS r   r   r   r   r   r   r   r   x  r   z+RewriteRuleSet.__init__.<locals>.<listcomp>c                 s  s    | ]}|j  V  qd S rZ   )rM   r   r   r   r   	<genexpr>|  s    z*RewriteRuleSet.__init__.<locals>.<genexpr>)r   r\   	itertoolschainfrom_iterabler   anyremove_unused_nodes)r7   r   r   r   r   r   r8   t  s   zRewriteRuleSet.__init__r9   c                 C  s   | j j d| j dS )N())r   r   r   r<   r   r   r   __repr__~  s   zRewriteRuleSet.__repr__N)rA   rB   rC   rD   rE   r(   rH   rA   rI   r)   c             
     s  d}| j D ]	}|jr|  q|D ]i}| j D ])}|j|||||d}|du s,|dur-qt|ts4J |jrlt|tjrG|rFtd|  q|j	}	|jD ]}
|
j
|	v r`|r_td|
j
 d qMqM|jD ]}
|
|	|
j
< qdtj|j |jrt|jdkrtd|jd }|j}|j}t|||}||_t|jj  fd	d
|D }t|j||jj\}}}dd |D |j}fdd| D }tj||||d}tj||||dd}||j|  < |r
|j
r|j
 dnd}td| d t!"|jj td t!"|j td |j
r|jD ]	}|j
|j#t$< qt%&|||j'r'|jjng |j|jj|j( t)r=t*+|jj|j |d7 } |j,- D ]3}|j.tj/j0kr_|| j1||j2||d7 }qG|j.tj/j3kry|j2D ]}|| j1||||d7 }qjqGq| j D ]}|j4r|4  q|S )a  
        Apply the rewrite rules to the given graph or function.

        Args:
            model: The model to which the rewrite rules are applied.
            graph_or_function: The graph or function to which the rewrite rules are applied.
            verbose: The verbosity level. Defaults to None.
            tracer: The tracer for debugging. Defaults to None.

        Returns:
            The number of rewrite rules applied.
        r   r   Nz:Rewrites adding initializers not supported for functions: zInitializer z already exists.r   zOas_function=True is only supported for patterns with a single replacement node.c                   s   g | ]}| v r|qS r   r   )r   n)unsorted_nodesr   r   r     s    z>RewriteRuleSet._apply_to_graph_or_function.<locals>.<listcomp>c                 S  s   h | ]}|j qS r   )r   r   r   r   r   	<setcomp>  s    z=RewriteRuleSet._apply_to_graph_or_function.<locals>.<setcomp>c                   s   i | ]\}}| v r||qS r   r   )r   kr   )used_domainsr   r   
<dictcomp>  s    z>RewriteRuleSet._apply_to_graph_or_function.<locals>.<dictcomp>)r}   r   r   )r   r   z: r   z----zMatched Nodes----z++++Replacement Nodes++++z++++End Replacement Nodes++++)5r   r   r   r-   r
   r   r   Functionrd   r~   r*   
onnxscript	optimizerbasic_constant_propagationr   r   r   r   r   r   r   r   setr   r}   r   rf   r   r   rj   Graphr   
identifier	_ir_utilsdisplay_nodesr   RULE_NAME_TAGr   replace_nodes_and_valuesrM   r   r   r   copy_merged_metadatar   r   r   r   r   _apply_to_graph_or_functionr   r   r   )r7   rB   rD   r(   rA   countr   rF   r   r~   initializer	call_noder   r*   r   original_nodesrf   r}   r   parent_opset_importsused_opset_importsr   ra   r  r   r   )r  r  r   r    s   









	



z*RewriteRuleSet._apply_to_graph_or_functionr   c                C  s   t |tjsJ tj|j t|j	 }| j
||j||d}|D ]}tj| || j
||||d7 }q"| jr?tj| |S )a  Apply the rewrite rules in the set to the model.

        Args:
            model: The model to which the rewrite rules are applied.
            verbose: The verbosity level of messages. Defaults to None.
            tracer: if specified, no changes are made to the model, only
                information about the best matches found is computed.

        Returns:
            The number of applications of rewrite rules.
        r   )r-   r   Modelr  r	  r
  r   r\   r   r   r  r   )r7   rB   r(   rA   original_functionsr  rz   r   r   r   r     s   

zRewriteRuleSet.apply_to_modelc                 c  s    | j E d H  d S rZ   )r   r<   r   r   r   __iter__.  s   zRewriteRuleSet.__iter__)r   r   r   r   r   r,   rv   )
rB   rC   rD   rE   r(   rH   rA   rI   r   r)   )rB   rC   r(   rH   rA   rI   r   r)   )r   r   r   r8   r   r  r   r  r   r   r   r   r   s  s    

	 #r   )r   r   )rD   rE   r   r
   )rf   r   r}   r   r   r   )rB   rC   r   r9   r*   r9   r   r9   )2r   
__future__r   r   dataclassesr   typingr   r   r   onnxscript.optimizerr  onnxscript.rewriter._basicsrewriterrN   onnxscript.rewriter._ir_utilsr  onnxscript.rewriter._matcherr3   onnxscript.rewriter._pattern_irr.    onnxscript.utils.metadata_mergerutilsmetadata_mergerr   onnxscript.irr   r   r	   Builderr|   r  	dataclassr
   r   r!   rx   r   r   ABCr   r   r   r   MetadataMergercomma_separator_mergerr   r   r   r   r   r   r   r   <module>   sF   

 
 J
B
M