o
    Xi3                     @  s   d Z ddlmZ ddlZddlZddlmZ ddgZddlm	Z	 ddlm
Z
mZmZ ddlZdd	lmZ eeej eej f ZeZee ZdddZd ddZd!ddZejG dd dejjZG dd dejjZ dS )"z)Implementation of an inliner for onnx_ir.    )annotationsN)Callable
InlinePassInlinePassResult)defaultdict)IterableMappingSequence)_clonernamestr	callstack	CallStack
used_namesset[str]returnc                 C  s<   | }d}||v r|d7 }|  d| }||v s| | |S )a(  Generate a unique name from a name, calling-context, and set of used names.

    If there is a name clash, we add a numeric suffix to the name to make
    it unique. We use the same strategy to make node names unique.

    TODO: We can use the callstack in generating a name for a value X in a function
    that is inlined into a graph. This is not yet implemented. Using the full callstack
    leads to very long and hard to read names. Some investigation is needed to find
    a good naming strategy that will produce useful names for debugging.
       _)add)r   r   r   	candidatei r   Q/home/ubuntu/.local/lib/python3.10/site-packages/onnx_ir/passes/common/inliner.py_make_unique_name   s   
r   function_idsIterable[ir.OperatorIdentifier] dict[ir.OperatorIdentifier, str]c                   s    d
 fddfdd D S )z=Create a short unambiguous abbreviation for all function ids.idir.OperatorIdentifierr   r   c                   sR   | \ t  fddD r d }nd}dkr%| d  S | S )z:Create a short unambiguous abbreviation for a function id.c                 3  s4    | ]}|d   ko|d ko|d kV  qdS )r   r      Nr   ).0xdomainr   overloadr   r   	<genexpr><   s   2 z7_abbreviate.<locals>.id_abbreviation.<locals>.<genexpr>r    )any)r   short_domainr   r"   r   id_abbreviation8   s   

z$_abbreviate.<locals>.id_abbreviationc                   s   i | ]}| |qS r   r   )r    r   )r*   r   r   
<dictcomp>D       z_abbreviate.<locals>.<dictcomp>N)r   r   r   r   r   r)   r   )r   r*   r   _abbreviate3   s   r-   modelir.Model"list[ir.OperatorIdentifier] | Nonec           	   
   C  s   i }| j  D ]\}}| D ]}| }|| j v r$||t | qqt|}z|	  W dS  tj
yL } z|jd }|W  Y d}~S d}~ww )zDetect cyclic dependencies between functions in the model.

    Returns:
        A list of function ids forming a cycle if a cycle is detected, otherwise None.
    r   N)	functionsitems	all_nodesop_identifier
setdefaultsetr   graphlibTopologicalSorterprepare
CycleErrorargs)	r.   dependenciesfunc_idfunctionnodeop_idsorterecycler   r   r   _detect_function_cyclesG   s"   



rD   c                   @  s   e Zd ZU ded< dS )r   z dict[ir.OperatorIdentifier, int]id_countN)__name__
__module____qualname____annotations__r   r   r   r   r   `   s   
 c                      sV   e Zd ZdZdd fddZd ddZd ddZd!ddZd"ddZd#ddZ	  Z
S )$r   az  Inline model local functions to the main graph and functions and remove unused functions.

    When a node calls a function defined in the model and when ``criteria`` is None or
    ``criteria(function)`` returns True, the function body is inlined into the graph in place
    of the call node.

    .. versionadded:: 0.1.16
        The ``criteria`` parameter.

    Requires:
        No cyclic dependencies between functions in the model.

    Attributes:
        criteria: Optional function that takes an :class:`onnx_ir.Function` and
            returns True if the it should be inlined. If None, all function calls are inlined.
    Ncriteria$Callable[[ir.Function], bool] | Noner   Nonec                   sD   t    || _i | _i | _i | _t | _t | _i | _	t | _
d S N)super__init__rJ   
_functions_function_id_abbreviations_opset_importsr6   _used_value_names_used_node_names_node_context_inlined_functions)selfrJ   	__class__r   r   rO   w   s   
zInlinePass.__init__r.   r/   c                 C  sB   |j | _t| j | _|j| _t | _t | _	i | _
t | _d S rM   )r1   rP   r-   keysrQ   opset_importsrR   r6   rS   rT   rU   rV   )rW   r.   r   r   r   _reset   s   zInlinePass._resetc                 C  sD   |  | t|}|d ur ddd |D }tjd| d S )Nz -> c                 s  s6    | ]\}}}| d | |rd | nd V  qdS ):r&   Nr   )r    r#   r   r$   r   r   r   r%      s
    
z&InlinePass.requires.<locals>.<genexpr>z.Cyclic dependency detected between functions: )r\   rD   joinirpassesPreconditionError)rW   r.   rC   	cycle_strr   r   r   requires   s   

zInlinePass.requiresr   c                 C  s   |  | i }| |j\}}| D ]\}}||d| ||< q|j D ])\}}|| jv r1q'| |j\}	}
||
7 }|	 D ]\}}||d| ||< qAq'| jD ]}|j|= qTt|t||dS )Nr   )modifiedrE   )	r\   _inline_calls_ingraphr2   getr1   rV   r   bool)rW   r.   rE   main_id_counttotal_inlinedkvr=   r>   inner_id_countinlinedr   r   r   call   s    



zInlinePass.callr?   ir.Nodecall_site_id
CallSiteIdNodeReplacementc                   s  |  }j| }|j D ]&\}}|jvr|j|< qj| |kr4td| dj|  d| q|j  fdd|j D }|rLi  | tdd   D r[tdt	|j
t	|j
krutd	t	|j
 d
t	|j
 i t|j
D ]\}}	|	|j
| < q|tt	|j
t	|j
D ]	}d |j
| < qj|g }
g |
|dfdd}tj |j|ddfdd|D }fdd|jD }||fS )NzOpset mismatch:  z != c                   s(   i | ]}|j  vr|jd ur|j |qS rM   )r   valuer    attr)
attributesr   r   r+      s
    z0InlinePass._instantiate_call.<locals>.<dictcomp>c                 s  s&    | ]}|j tjjtjjhv V  qd S rM   )typer_   AttributeTypeGRAPHGRAPHSrv   r   r   r   r%      s
    
z/InlinePass._instantiate_call.<locals>.<genexpr>z@Inliner does not support graph attribute parameters to functionszInput mismatch: z > r?   rp   r   rL   c                   sV   | j pd}t| j| _ | jD ]}|dur#|j pd}t| j|_ q j| < dS )zORename node/values in inlined node to ensure uniqueness in the inlined context.r?   Nval)r   r   rT   outputsrS   rU   )r?   	node_nameoutputoutput_name)new_call_stackrW   r   r   rename   s   


z,InlinePass._instantiate_call.<locals>.renameT)attr_map	value_mapmetadata_propspost_processresolve_ref_attrsc                   s   g | ]}  |qS r   )
clone_node)r    r?   )clonerr   r   
<listcomp>   r,   z0InlinePass._instantiate_call.<locals>.<listcomp>c                   s   g | ]} | qS r   r   )r    r   )r   r   r   r      s    )r?   rp   r   rL   )r4   rP   r[   r2   rR   
ValueErrorrx   valuesr'   leninputs	enumeraterangerU   rg   r
   Clonerr   r~   )rW   r?   rq   r   r>   keyru   default_attr_valuesr   input
call_stackr   nodesoutput_valuesr   )rx   r   r   rW   r   r   _instantiate_call   sT   


 zInlinePass._instantiate_callrf   ir.Graph,tuple[dict[ir.OperatorIdentifier, int], int]c              	   C  s  |j D ]}|jdur| j|j q|jD ]}| j| qtt}|D ]/}|jr0| j|j | }|| j	v rA||  d7  < |j
D ]}|jdurR| j|j qDq$tt}d}	|D ]}| }|| j	v r| jduru| | j	| suq\| j| || dkrd||  }
||  d7  < nd}
|jp| j| |
 }| ||\}}tjj|||g||j
|d |	d7 }	q\|j D ]0}|jtjjkr| | \}}|	|7 }	q|jtjjkr| D ]}| |\}}|	|7 }	qqq\||	fS )aI  Inline function calls in a graph.

        Returns:
            A tuple of (id_count, inlined_count) where:
            - id_count: A dict mapping function ids to the number of calls in the graph
              (used for naming disambiguation).
            - inlined_count: The number of nodes that were actually inlined.
        Nr   r   r   r&   )insertion_point	old_nodes	new_nodes
old_values
new_values)r   r   rS   r   initializersr   intrT   r4   rP   r~   rJ   rV   rQ   r   r_   conveniencereplace_nodes_and_valuesrx   r   ry   rz   r{   re   as_graphr|   	as_graphs)rW   rf   r   initializerrE   r?   r@   r   next_idinlined_countcall_site_prefix	call_siter   r   rw   r   sub_inlinedgr   r   r   re      sl   









zInlinePass._inline_calls_inrM   )rJ   rK   r   rL   )r.   r/   r   rL   )r.   r/   r   r   )r?   rp   rq   rr   r   rs   )rf   r   r   r   )rF   rG   rH   __doc__rO   r\   rc   ro   r   re   __classcell__r   r   rX   r   r   e   s    

	

G)r   r   r   r   r   r   r   r   )r   r   r   r   )r.   r/   r   r0   )!r   
__future__r   dataclassesr7   collections.abcr   __all__collectionsr   r   r   r	   onnx_irr_   r
   tupleNodeValuers   r   rr   listr   r   r-   rD   	dataclassr`   
PassResultr   InPlacePassr   r   r   r   r   <module>   s&   


