o
    ei!O                     @   s4  d dl Z d dlmZmZ d dlmZmZ d dlZd dl	m
Z
 d dlmZ d dlmZmZ ddlmZmZmZ g d	Ze
d
ddd Ze
d
ddd Ze
d
deG dd dZe
d
dd
d
ejjfdejjdee dededeejj deejjeejjeeef f f fddZ e
d
ddddZ!dS )    N)	dataclassfield)OptionalUnion)compatibility)map_arg)HolderModulelift_subgraph_as_module   )CALLABLE_NODE_OPSis_node_output_tensorNodeList)getattr_recursivesetattr_recursive	Componentsplit_by_tags!move_non_tensor_nodes_on_boundaryF)is_backward_compatiblec                 C   sb   | dD ])}t| tjjr!t| dr|| jv r| j| } q d S t| |r,t| |} q d S | S )N._modules)split
isinstancetorchnn
ModuleListhasattrr   getattr)objnamelayer r    e/var/www/addictedbytheproject.nl/epg/venv/lib/python3.10/site-packages/torch/fx/passes/split_utils.pyr      s   
r   c                 C   sJ   d|vrt | || d S |d}tt| |d d|dd  | d S )Nr   r   r
   )setattrr   r   r   join)r   attrvaluer   r    r    r!   r   &   s   
(r   c                   @   s   e Zd ZU dZejjed< eed< e	ed< e
edZeed< e
edZeed< e
edZeed< e
edZeejjejjf ed	< e
edZee	 ed
< dZeejj ed< dS )r   zX
    A component serves as a container for a subgraph we want to create afterwards.
    graphorderr   )default_factoryinput_placeholdersorig_inputsorig_outputsgetattr_mapsconstructor_argsNgm)__name__
__module____qualname____doc__r   fxGraph__annotations__intstrr   listr)   r*   r+   dictr,   Noder-   r.   r   GraphModuler    r    r    r!   r   /   s   
 "r   r.   tagsreturn_fqn_mappingreturn_tupleGraphModuleClsreturnc                    s  dt jjjdtfdd}i i i }g }i t j }i }	d}
|D ]}tt j t||  |   ||< q!| j	j
D ]}|jdkrO|
durLtd|}
q=|jdkri|j|j|jd	|	|< t|j|	| _q=|jd
kroq=t|ds}td|  fdd||j||j D }||j   |< tdd |D dd} j|k rtd j d j d|  fdd} j	||}|j|_||<  |< q=|
du rtd||
jd D ]}|jd
kr|j|j|jd	|	|< qd|< qD ]}|jdkr| j| qi }|D ]q ttj j}|r& j	 | n j	 t|dkr4|d n| t!|  j	 jd\ _"}|#| |j$ jtt|	j j%dd}t|dkrk|sk||	 jd < qt& jD ]\}}t j'|| j|	|< qpq| t(|
jd |	j t)dd |D }| j	j*|_*||
jd D ]}|jd
krt+||jt,| |j- q|||}|r||fS |S )a9  
    Splits a GraphModule using tags on its graph nodes. We honor the order of
    tags. For example, we have tags = ["a", "b", "c"], the function will create
    the initial submodules in the order of "a", "b", "c".

    To set a tag:
    gm.graph.nodes[idx].tag = "mytag"

    This will result in all nodes with the same tag being extracted and placed in their
    own submodule. For placeholder, output and get_attr node, the tag is ignored. placeholder
    and output nodes are created when needed while get_attr nodes get copied to submodules
    where they are used.

    Given the following module def:

    class SimpleModule(torch.nn.Module):
        def __init__(self) -> None:
            super().__init__()
            self.linear1 = torch.nn.Linear(...)
            self.linear2 = torch.nn.Linear(...)
            self.linear3 = torch.nn.Linear(...)

        def forward(self, in1, in2):
            r1 = self.linear1(in1)
            r2 = self.linear2(in2)
            r3 = torch.cat([r1, r2])
            return self.linear3(r3)

    Marking the node corresponding to in1 with the tag sc.REQUEST_ONLY.lower() results in the following split:

    ro:
    def forward(self, in1):
        self = self.root
        linear1 = self.linear1(in1)
        return linear1

    main:
    def forward(self, in2, linear1):
        self = self.root
        linear2 = self.linear2(in2)
        cat_1 = torch.cat([linear1, linear2])
        linear3 = self.linear3(cat_1)
        return linear3

    main:
    def forward(self, in1, in2):
        self = self.root
        ro_0 = self.ro_0(in1)
        main_1 = self.main_1(in2, ro_0)
        return main_1

    Returns:
        split_gm: torch fx graph after split
        orig_to_split_fqn_mapping: a map between the original fqn and the fqn
            after split for call_module and get_attr.
    xr@   c                 S   s   g }t | |j |S )zC
        Stores nodes in x to a list and returns the list.
        )r   append)rA   rr    r    r!   flatten   s   zsplit_by_tags.<locals>.flattenNoutputzMultiple output nodes in graph!placeholder	type_exprget_attrtagzNode does not have tag: c                    s   g | ]}|j d vr | qS )>   rI   rF   )op).0rA   )node_to_componentr    r!   
<listcomp>   s
    
z!split_by_tags.<locals>.<listcomp>c                 s   s    | ]}|j V  qd S N)r'   )rL   cr    r    r!   	<genexpr>   s    z split_by_tags.<locals>.<genexpr>r   )defaultz
Component z8 order must be >= max of its upstream components, order=z	 and max=c                    s   | j dkr&|  jvr! jj| j| jd j| < t| j j|  _ j|  S | j dkr5|   kr5|  S |  jvr[ j	|   jj
| j| jd}t| j|_ j	| d | <  j j|  S )NrI   rG   rF   )rK   r,   r&   rI   targettypecopymetar*   rB   rF   r   r)   index)rA   rF   compnode_remappingrM   used_in_mainr    r!   
remap_func   s    



z!split_by_tags.<locals>.remap_funczGraph had no output node!r
   )subgraph	comp_name)argskwargsc                 S   s   i | ]}|j |jqS r    )r   r.   )rL   rY   r    r    r!   
<dictcomp>4  s    z!split_by_tags.<locals>.<dictcomp>).r   r3   nodeArgumentr   r4   r   lenrB   r&   nodesrK   RuntimeErrorrF   r   rT   rU   rV   r   AssertionErrorformat_noder_   r`   rJ   maxr'   	node_copyrI   r+   tuplemap__getitem__rE   r	   r.   updatecall_moduler*   	enumerateProxyr   r   _codegenr"   r   rS   )r.   r<   r=   r>   r?   rD   tag_to_componentall_componentsmain_gmain_remappingoutput_noderJ   rb   upstream_componentsmxr\   nrA   orig_to_split_fqn_mappingoutscomp_orig_to_split_fqn_mapping	main_nodeio	main_root	result_gmr    rX   r!   r   I   s   A	













"



r   c                    s  i t | D ]\}}|jD ]}||< qqdtjjdttjj ffdd dtjjdttjj ffdd}dtjjdtdtf fdd	}dtjjd
tdtdttt	tjj f f fdd}t | D ]\}}|j
smqeg }t	 }	|jD ]}t|r|qu|||r|| qu|rY|d}
|
|	v rq|	|
 |
vs|
 |krq |
}t|dkrtdt	 }|D ]}| }||kr|| qt|dkrtd|
j d q| }||
||\}}|rW|D ]:}|j| | | j| ||< td|
j d|j d|j
rdnd d| d| | j
rdnd d|  q||
}|D ]-}t|sU||	vrU|v rU| |krU|||sPtd|j d|| q)|sqedS )a  
    Move non-tensor nodes on the boundary between subgraphs.

    For each subgraph:

    1. Find nodes whose type is not tensor and any of its children is in another
       subgraph, put them in a queue for next step

    2. Do a BFS on those nodes in the queue,  and run a DFS for each node, let's say node X and it is in subgraph A:

       a. if it is in to_subgraph, return (continue DFS)
       b. if it is in from_subgraph, collect the nodes to nodes_to_move, and continue DFS
       c. otherwise, this means it cannot be moved
       d. also check if node X's parent should be put into the queue. (The queue may
          have duplicated nodes, just process the node once)

    Args:
        subgraphs: List of subgraphs containing nodes to be processed
    rb   r@   c                        fdd| j D S )z@Get children nodes that are in callable ops and in some subgraphc                    "   g | ]}|j tv r| v r|qS r    rK   r   )rL   usernode_to_subgraphr    r!   rN   a  
    zTmove_non_tensor_nodes_on_boundary.<locals>.get_children_in_graph.<locals>.<listcomp>)usersrb   r   r    r!   get_children_in_graph_     
z@move_non_tensor_nodes_on_boundary.<locals>.get_children_in_graphc                    r   )z>Get parent nodes that are in callable ops and in some subgraphc                    r   r    r   )rL   argr   r    r!   rN   i  r   zSmove_non_tensor_nodes_on_boundary.<locals>.get_parents_in_graph.<locals>.<listcomp>)all_input_nodesr   r   r    r!   get_parents_in_graphg  r   z?move_non_tensor_nodes_on_boundary.<locals>.get_parents_in_graphcurrent_subgraph_idxc                    s    | }t  fdd|D S )z
        Check if the node has any children in a subgraph different from current_subgraph_idx.
        This is the requirement used in both step 1 and step d.
        c                 3   s    | ]	}|  kV  qd S rO   r    )rL   child)r   r   r    r!   rQ   w  s    
z\move_non_tensor_nodes_on_boundary.<locals>.has_children_in_other_subgraph.<locals>.<genexpr>)any)rb   r   childrenr   r   )r   r!   has_children_in_other_subgrapho  s   zImove_non_tensor_nodes_on_boundary.<locals>.has_children_in_other_subgraphfrom_subgraphto_subgraphc                    s:   t  t  d  fdd|   fS )a  
        Check if node and its dependencies can be moved from from_subgraph to to_subgraph.
        Returns (can_move, nodes_to_move)

        For node X, do a DFS on its descendants, for each node:
        - if it is in to_subgraph, return (continue DFS)
        - if it is in from_subgraph, collect the nodes to nodes_to_move, and continue DFS
        - otherwise, this means it cannot be moved
        Tc                    st   | v rd S  |  | vrd S |  }|krd S |kr% |  nd d S | }|D ]} r7| q/d S )NF)add)current_nodecurrent_subgraphr   r   )can_movedfsr   r   r   nodes_to_mover   visitedr    r!   r     s$   
zVmove_non_tensor_nodes_on_boundary.<locals>.can_move_node_and_dependencies.<locals>.dfs)set)rb   r   r   r   )r   r   r   r   r   r   r!   can_move_node_and_dependencies{  s   zImove_non_tensor_nodes_on_boundary.<locals>.can_move_node_and_dependenciesr   z:Only node that has children in other subgraph can be movedr
   zCannot move non-tensor node z: on boundary because it has children in multiple subgraphsz"In order move the non-tensor node z on boundary, moved node z from accgpu_z to zParent z) should have children in another subgraphN)rp   re   r   r3   r:   r8   r6   boolrk   r   is_accr   rB   popr   rd   rg   printr   remove)	subgraphsr   r]   rb   r   r   r   subgraph_idxqueue	processedr   r   target_subgraph_candidatesr   child_subgraphtarget_subgraphr   r   node_to_moveparentsparentr    r   r!   r   D  s   

""4









r   )r@   N)"rU   dataclassesr   r   typingr   r   torch.fxr   torch.fx._compatibilityr   torch.fx.graphr   torch.fx.passes.utilsr   r	   tools_commonr   r   r   __all__r   r   r   r3   r;   r8   r7   r   rT   rk   r9   r   r   r    r    r    r!   <module>   sH   	


" {