o
    r::j6                     @  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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   d/home/nk/hobo-godmode/plappi-mvp/.venv/lib/python3.10/site-packages/onnx_ir/passes/common/inliner.py_make_unique_name   s   
r   op_idir.OperatorIdentifierc                 C  s,   | \}}}| d| |rd|  S d S )z9Format an operator identifier as a human-readable string.z::: r   )r   domainr   overloadr   r   r   _format_function_id3   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.idr   r   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r   r   r   r   r   	<genexpr>B   s   2 z7_abbreviate.<locals>.id_abbreviation.<locals>.<genexpr>r   r   )any)r$   short_domainr!   r(   r   id_abbreviation>   s   

z$_abbreviate.<locals>.id_abbreviationc                   s   i | ]}| |qS r   r   )r&   r$   )r-   r   r   
<dictcomp>J       z_abbreviate.<locals>.<dictcomp>N)r$   r   r   r   r   r,   r   )r!   r-   r   _abbreviate9   s   r0   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)	r1   dependenciesfunc_idfunctionnoder   sorterecycler   r   r   _detect_function_cyclesM   s"   



rF   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   f   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__rL   
_functions_function_id_abbreviations_opset_importsr9   _used_value_names_used_node_names_node_context_inlined_functions)selfrL   	__class__r   r   rQ   }   s   
zInlinePass.__init__r1   r2   c                 C  sB   |j | _t| j | _|j| _t | _t | _	i | _
t | _d S rO   )r4   rR   r0   keysrS   opset_importsrT   r9   rU   rV   rW   rX   )rY   r1   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  s    | ]}t |V  qd S rO   )r    )r&   r@   r   r   r   r)      s    z&InlinePass.requires.<locals>.<genexpr>z.Cyclic dependency detected between functions: )r^   rF   joinirpassesPreconditionError)rY   r1   rE   	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   )modifiedrG   )	r^   _inline_calls_ingraphr5   getr4   rX   r   bool)rY   r1   rG   main_id_counttotal_inlinedkvr@   rA   inner_id_countinlinedr   r   r   call   s    



zInlinePass.callrB   ir.Nodecall_site_id
CallSiteIdNodeReplacementc                   s  |  }j| }|j D ],\}}|jvr|j|< qj| |kr:tdt| d| dj|  d| d	q|j  fdd|j D }|rRi  | t	dd	   D rgtd
t| dt
|jt
|jkrtdt| dt
|j dt
|j di t|jD ]\}}	|	|j| < qt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 when inlining function 'z': domain 'z' has version z in the model but version z in the functionc                   s(   i | ]}|j  vr|jd ur|j |qS rO   )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 rO   )typer`   AttributeTypeGRAPHGRAPHSrv   r   r   r   r)      s
    
z/InlinePass._instantiate_call.<locals>.<genexpr>zLInliner does not support graph attribute parameters to functions. Function 'z' has graph attributesz'Input mismatch when inlining function 'z': call site has z% inputs but function defines at most z inputsrB   rq   r   rN   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.rB   Nval)r   r   rV   outputsrU   rW   )rB   	node_nameoutputoutput_name)new_call_stackrY   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&   rB   )clonerr   r   
<listcomp>   r/   z0InlinePass._instantiate_call.<locals>.<listcomp>c                   s   g | ]} | qS r   r   )r&   r   )r   r   r   r     s    )rB   rq   r   rN   )r7   rR   r]   r5   rT   
ValueErrorr    rx   valuesr*   leninputs	enumeraterangerW   rh   r
   Clonerr   r~   )rY   rB   rr   r   rA   keyru   default_attr_valuesr   input
call_stackr   nodesoutput_valuesr   )rx   r   r   rY   r   r   _instantiate_call   sp   


zInlinePass._instantiate_callrg   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   rU   r   initializersr   intrV   r7   rR   r~   rL   rX   rS   r   r`   conveniencereplace_nodes_and_valuesrx   r   ry   rz   r{   rf   as_graphr|   	as_graphs)rY   rg   r   initializerrG   rB   r   r   next_idinlined_countcall_site_prefix	call_siter   r   rw   r   sub_inlinedgr   r   r   rf     sl   









zInlinePass._inline_calls_inrO   )rL   rM   r   rN   )r1   r2   r   rN   )r1   r2   r   r   )rB   rq   rr   rs   r   rt   )rg   r   r   r   )rH   rI   rJ   __doc__rQ   r^   rd   rp   r   rf   __classcell__r   r   rZ   r   r   k   s    

	


M)r   r   r   r   r   r   r   r   )r   r   r   r   )r!   r"   r   r#   )r1   r2   r   r3   )"r   
__future__r   dataclassesr:   collections.abcr   __all__collectionsr   r   r   r	   onnx_irr`   r
   tupleNodeValuert   r   rs   listr   r   r    r0   rF   	dataclassra   
PassResultr   InPlacePassr   r   r   r   r   <module>   s(   



