o
    Zh                     @   s  d dl mZmZmZmZ d dlZd dlm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 dd	lmZmZmZ dd
lmZmZmZmZmZmZmZmZmZm Z  dededee! fddZ"dededede!de!de!de!de!de!de#de#dee! defddZ$dede%ee&e!e!f f de%ee&e!e!f f dede!defddZ'd ed!ed"ed#ed$eej(e)f d%eej(e#f d&e!defd'd(Z*d!ed)ed eee+e f d*ed"ed#ed+e!ded,e%e!e,e f deee+e f fd-d.Z-d!ed*ed"ed#edef
d/d0Z.d1ed*ed2e#de/fd3d4Z0d5eee+e f d6eeee+e f  d1ed*ed"ed+e!defd7d8Z1d5eee+e f d6eeee+e f  d!ed*ed"ed+e!defd9d:Z2	dAd;e!d*ed<e!d"ed=e%e!e&eef f ded>e/d,ee%e!e,e f  defd?d@Z3dS )B    )AnyCallableOptionalUnionN)get_node_type_to_io_type_map)get_new_attr_name_with_prefix)_is_activation_post_process)GraphModulemap_arg)GraphNode   )NSNodeTargetTypeNSSingleResultValuesType
NSSubgraph)
 get_arg_indices_of_inputs_to_log$get_node_first_input_and_output_typeget_node_input_qparamsget_normalized_nth_inputget_number_of_non_param_argsget_target_type_strgetattr_from_fqnNodeInputOrOutputTypeop_type_supports_shadowingreturn_first_non_observer_nodenodegmreturnc                 C   s`   d }t |dr.| }| jdkr&t| jtsJ t|| j}t|r&t| |d}|j|j	 d }|S )N_node_name_to_scopecall_moduler   )
hasattrop
isinstancetargetstrr   r   r   r   name)r   r   fqnZnode_to_use_for_fqnmodule r(   J/var/www/auris/lib/python3.10/site-packages/torch/ao/ns/fx/graph_passes.py_maybe_get_fqn   s   

r*   
logger_clslogger_node_name_suffixref_node_name
model_nameref_nameref_node_target_typeresults_typeindex_within_argindex_of_argr&   c                 C   s\   t | j| |}t| |}||| j||||||	|
|
}t||| | jd|| fi }|S )z
    Given a starting graph of

    prev_node -> node -> next_node

    This function creates a new logger_cls obj and adds it
    after node, resulting in

    prev_node -> node -> logger_obj -> next_node
    r   )r   r%   r   setattrgraphcreate_node)r   r   r+   r,   r-   r.   r/   r0   r1   r2   r3   r&   Zlogger_node_nameZtarget_typeZ
logger_objZlogger_noder(   r(   r)   _insert_logger_after_node*   s*   
r7   *node_to_instrument_inputs_to_ref_node_name+node_to_instrument_outputs_to_ref_node_namec                    s  t  }i   fdd}| jjD ]}|jdkr#|tt|| d| q||v s+||v rt|| }||v r|| \}	}
t|}|D ]Y}t|| |}t	|t
krj |j }t|| |d|j||	|
tjjd||d |j< q@t	|tjjjkrt|D ]\}} |j }t|| |d|j||	|
tjj|||d |j< qwq@	 q@||| |j< ||v r|| \}	}
t |j | |d|j||	|
tjjdd|d |j< q||| |j< qt| |}|S )z
    Takes the graph of gm, adds loggers to the output
    of each node in nodes_to_instrument. Returns a GraphModule with the new
    graph.
    c                       t |  fddS )Nc                    
    | j  S Nr%   r   envr(   r)   <lambda>k      
 z8add_loggers_to_model.<locals>.load_arg.<locals>.<lambda>r
   ar?   r(   r)   load_argj      z&add_loggers_to_model.<locals>.load_argoutputr   Z_ns_logger_r2   r3   r&   )r   r5   nodesr!   rH   r
   r   r*   r   typer   r%   r7   r   
NODE_INPUTvaluetorchZfxZimmutable_collectionsZimmutable_list	enumerate	node_copyNODE_OUTPUTr	   )r   r8   r9   r+   r.   Z	new_graphrF   r   r&   r/   Zref_node_typeZarg_indices_to_logZnode_arg_idxZnode_argZ	prev_nodearg_idxargZnew_gmr(   r?   r)   add_loggers_to_modelZ   s   




rT   prev_node_cnode_agm_bgraph_cscale
zero_pointdtype_cast_namec                 C   s~   t |jd |}t||| |d|di |}t |jd |}	t||	| |d|	di |	}
|dtj| ||
tjfi |S )NZ_input_scale_get_attrr(   Z_input_zero_point_call_function)r   r%   r4   r6   rN   quantize_per_tensorZquint8)rU   rV   rW   rX   rY   rZ   r[   Zscale_node_nameZ
scale_nodeZzero_point_node_nameZzero_point_noder(   r(   r)    _insert_quantize_per_tensor_node   s*   


r_   node_cgm_anode_name_prefixnode_type_to_io_type_mapc	                 C   sZ  d}	d}
d}d}d}d}t | |||\}}t ||||\}}|tjkr(|tjks<|tjkr2|tjks<|tjkr@|tjkr@tj}	nQ||krN|tjkrNtj	j
}
nC|tjkrj|tjkrjt| ||}|duritj}	|\}}n'|tjkrz|tjkrzd}tj}ntd| d|  d| d|   d t|trt||}|	r|dur|durt|| |||||S |d|	|fi |S |r|d|||fi |S |
sJ |
 }t||| |d	||fi |S t|tr#g }|D ]8}t||}|	r|d|	|fi |}|| q|
sJ |
 }t||| |d	||fi |}|| q|S td
t| d)a  
    Given a starting graph C (derived from graph B) of

    ... -> prev_node_c -> node_c -> ...

    And a corresponding related node_a, inserts the correct dtype
    cast node after prev_node_c to cast into the dtype expected
    by node_a, resulting in:

                          dtype_cast
                        /
    ... -> prev_node_c -> node_c -> ...

    For example, if node_c is an int8 op and node_a is an fp32 op, this function
    will insert a dequant.
    Ntozdtype cast from  z to z needs to be implementedr]   call_methodr   ztype fz is not handled)r   r   FP32INT8ZFP16ZFP32_OR_INT8rN   
dequantizeUNKNOWNnnZIdentityr   r^   Zfloat16AssertionErrorformat_noder"   r   r   r_   r6   r4   listappendrK   )rV   r`   rU   ra   rW   rX   rb   r+   rc   Zdtype_cast_opZdtype_cast_mod_clsZdtype_cast_methodZdtype_cast_method_dtypeZdtype_cast_scaleZdtype_cast_zero_pointnode_input_type_aZ_node_output_type_aZnode_input_type_cZ_node_output_type_cnode_a_input_qparamsZnew_dtype_cast_nameZdtype_cast_modresultsZprev_node_c_innerZnew_dtype_cast_noder(   r(   r)   _insert_dtype_cast_after_node   s   














rs   c              	   C   s<  | j dkr/t| jd |}t|| j}t|r| }t||| |	| j |di |}|S | j dkr| jdv sBJ d| j d| jdkrit
t| |d	|||}t| jd |}|	| j | j|fi |}|S t
t| |d	|||}t| jd |}|	| j | j|t| |d
fi |}|S td|   d| j  d)z+
    Simple copy of node_a to graph_c.
    r\   _shadow_copy_r(   rf   ri   rd   ztarget  is not implementedri   r   r   zhandling of node z	 with op )r!   r   r%   r   r#   rN   Z	is_tensordetachr4   r6   _copy_node_from_a_to_cr   rl   rm   )rV   ra   rW   rX   Znode_a_copy_nameZ
node_a_objZnode_a_copyZarg_copyr(   r(   r)   rx     s`   
	


rx   
subgraph_anum_non_param_args_node_ac                 C   s6  g }| j }|| jkr|| t||d}|| jks
|| |  dd }|D ]o}||d u r3|nd}|j|dd}|durE|\}	}
n|j|j}	}
d}|t|	k rv|dkrYn|dkrb|dkrbn
||	| |sl d	S |d7 }|t|	k sT|
	 D ]}|dkrn|dkr|dkrn	|||s  d	S |d7 }qzq)dS )
z
    This function returns `False` if the input subgraph cannot be copied by
    `_insert_copy_of_subgraph_a_after_input_node_c`. This usually means
    that there is a corner case logic for which copy is not yet implemented.
    r   c                 S   sf   t | trt| |}|jdkr|jdv S |jdkrdS dS t | ttfr1| D ]
}t |ts0 dS q&dS )Nrf   ru   r\   TF)r"   r   r   r!   r#   rn   tuple)Z
node_a_argra   Zarg_aelr(   r(   r)   _can_insert  s   





z3_can_insert_copy_of_subgraph_a.<locals>._can_insertr   TZnormalize_to_only_use_kwargsN   F)
end_node
start_nodero   r   reversenormalized_argumentsargskwargslenvalues)ry   ra   rz   rJ   cur_noder}   rV   Zlocal_num_non_param_args_node_anorm_args_kwargs	norm_argsnorm_kwargscur_idx	kwarg_valr(   r(   r)   _can_insert_copy_of_subgraph_a  sL   







r   input_node_cinput_node_c_2c                 C   s   t | ttfs	J |jg}|j}||jkr&t||d}|d| ||jks|d }t| |||||}	tdt	|D ]}
||
 }|	}t|d||||}	q:|	S )z*
    TODO(before land): real docblock
    r   r   N)
r"   r   rn   r   r   r   insert)_insert_copy_of_node_a_after_input_node_cranger   )r   r   ry   ra   rW   rb   Z
nodes_of_ar   Z
cur_node_aZ
cur_node_cZ	cur_idx_arU   r(   r(   r)   -_insert_copy_of_subgraph_a_after_input_node_c  s.   


r   c                    s  t | tr	| jnt | tsJ | d j|j dd}|dur%|\}}n|j|j}}g }	i }
 fdd}d}|t|k rh|dkrH| }n|dkrS|durS|}n||| }|	| |d7 }|t|k sA|	 D ]$\}|dkry| |
|< n|dkr|dur||
|< n||
|< |d7 }qlt
|	}	t|}|jdkrt|}t |jtsJ t |j}t|| |j||	|
|}|S |jd	v sJ |j|j|	|
|}|S )
a  
    Assume that node_a from graph_a has
      args (input, (input2)?, arg1, ...), and
      kwargs {kw0: kwarg0, ...}

    Note: input2 is optional. If it equals to None, we assume that the op
    has a single non-param input.  If it is specified, we assume that the op
    has two non-param inputs.

    Copies the underlying values of arg1..argn and kwarg0..kwargn into gm_b,
    and creates the corresponding nodes in graph_c. Note: observers are ignored,
    so if an arg is an observer we navigate up until we find a non-observer parent.

    If node_a is a call_module, points the module pointed to by node_a to gm_b.

    Creates the copy of node_a in graph_c, with input as the first arg,
    and all other args and kwargs pointing to the copies of the objects
    in gm_b created above.

    An example in pictures:

    graph A:
    ========

    input -------------> node_a
                         / / /
    (input_2)?----------/ / /
                         / /
    weight -> weight_obs  /
                         /
    bias ----------------

    graph C (derived from B):
    =========================

    input_node_c --> node_a_copy
                     / / /
    (input_node_c_2)? / /
                     / /
    weight_copy ----/ /
                     /
    bias_copy ------/
    r   Tr~   Nc                    s~   t | trt|  } t|  } | S t | tttjfr| S t tt	fr5D ]}t |tr2J dq'| S t
dt d)Nz/handling of Node inside list is not implementedzhandling for kwarg of type rv   )r"   r   r   rx   intfloatrN   Zdtypern   r{   rl   rK   )rS   r|   ra   rW   rX   r   r(   r)   	_copy_arg  s"   

z<_insert_copy_of_node_a_after_input_node_c.<locals>._copy_argr   r   )r]   rf   )r"   r   r5   rn   r   r   r   r   ro   itemsr{   r   r!   r#   r$   r   r4   r6   )r   r   rV   ra   rW   rb   r   r   r   new_argsZ
new_kwargsr   r   Znew_argZ
kwarg_nameZnode_a_shadows_c_nameZnew_mod_copy_nameZmod_anode_a_shadows_cr(   r   r)   r   ;  s\   
3







r   name_aname_bmatched_subgraph_pairsshould_log_inputsc           3         s(  |du rt  }t }i   fdd}	i }
i }| D ]&\}}|\}}t|j|}t|j}||||f|
|j< ||||f||j< qjjD ]}|j	dkrY|
t|jd |	 qE||
v }||v }|sf|r|rq|
| \}}}}n|suJ || \}}}}t|jot|}|stdt| dt|j|  d  |||	 |j< qEt|j|||\}}t|||\}}|tjko|tjko|tjko|tjk}|stdt| dt|j|  d	  |||	 |j< qE|tjkr$|tjkr$t|j||}|s$tdt| dt|j|  d
  |||	 |j< qEt|j|}t|||sPtdt| dt|j|  d  |||	 |j< qEt|j|}t|j}|r|rt|d} t| tr | j }!t|!|d|j|||tjj dd|d |!j< n>t| t!r fdd| D }"t"| D ]\}#}$|"|# }!t|!|d|j|||tjj |#d|d |!j< qn
t#dt$|  d|s|r|||	 |j<  |j }%|rt|%d}!|rt|!trt|%d}!nt|!t!rfdd|!D }!t%|j|%|!|||jd ||	}&|rcd}'t|&tr4t|&|d|'| ||tjj dd|d}&|&}(n/t|&t!s<J g })t"|&D ]\}*}+t|+|d|'| ||tjj |*d|d},|)&|, qB|)}&|&}(d}-t|j|}|dkrvt|%d}-t'|&|-|||%jd }.|. |.j< |r|.}/t|/d|(krt|/d}/t|/d|(kst|(trt(|(j}0|/j|0_)nt|(t!sJ |(D ]}1t(|1j}0|/j|0_)qt |.j |d|.j| ||tj*j dd|d |.j< |rt |j |d|j|||tj*j dd|d |j< qE|||	 |j< qEt+|}2|2S )a  
    Creates a new GraphModule consisting of the graph of C, with the meaningful
    nodes of A shadowing the corresponding nodes of B.  For example,

    Graph A:
    a0 -> op0_fp32 -> a1 -> op1_fp32 -> a2

    Graph B:
    b0 -> op0_int8 -> b1 -> op1_int8 -> b2

    matched_node_pairs: {'op0': (op0_fp32, op0_int8), 'op1': (op1_fp32, op1_int8)}

    Graph C (A shadows B):

        / dequant0 -> op0_fp32 -> logger_a_0  / dequant_1 -> op1_fp32 -> logger_a_1
       /                                     /
    b0 -------------> op0_int8 -> logger_b_0 --------------> op1_int8 -> logger_b_1

    In a nutshell, this function does the following for each node pair:
    * copies the necessary attributes and modules from gm_a to gm_b,
      keeping names unique
    * adds a dtype cast op (dequant, quant, etc)
    * adds a copy of node_a in gm_b's graph
    * adds loggers to the outputs of node_a and node_b
    Nc                    r:   )Nc                    r;   r<   r=   r>   env_cr(   r)   rA     rB   z6create_a_shadows_b.<locals>.load_arg.<locals>.<lambda>rC   rD   r   r(   r)   rF     rG   z$create_a_shadows_b.<locals>.load_argrH   r   z$skipping shadow loggers for node_b: z, start_node_a: z, unsupportedz, unknown dtype castz, unknown input qparamsz", unhandled logic in subgraph copyZ_ns_logger_b_inp_rI   c                    s   g | ]} |j  qS r(   r=   .0rS   r   r(   r)   
<listcomp>  s    z&create_a_shadows_b.<locals>.<listcomp>ztype z is not handled yetc                    s   g | ]}t | d qS )r   )r   r   )rW   r(   r)   r     s    
Z_dtype_cast_ Z_ns_logger_a_inp_r   r   rt   Z_ns_logger_a_Z_ns_logger_b_),r   r   r   r   Zbase_op_noder   r   r5   rJ   r!   rH   r
   r   r   printrP   r%   r   r   rj   rh   rg   r   r   r   r*   r   r"   r   r7   r   rL   rM   rn   rO   rl   rK   rs   ro   r   getattrr-   rQ   r	   )3r   ra   r   rW   r   r+   r   rc   rX   rF   Z+start_node_b_to_matched_subgraph_a_and_nameZ)end_node_b_to_matched_subgraph_a_and_nameZ
match_namematchry   Z
subgraph_bZref_node_type_aZref_node_type_bZnode_bZnode_b_is_start_nodeZnode_b_is_end_noder/   Zall_op_types_support_shadowingrp   Znode_output_type_aZnode_input_type_bZnode_output_type_bZnode_io_types_known_a_and_brq   rz   Z
fqn_base_aZ
fqn_base_bZprev_node_brU   Zprev_node_c_listrR   rS   r`   Zdtype_cast_noder-   Zinput_loggerZnew_loggersZdtype_cast_idxZdtype_cast_node_innerZdtype_cast_loggerZnode_c_second_non_param_argr   r   Zinput_logger_modZinput_logger_innerZgm_cr(   )r   rW   r)   create_a_shadows_b  s8  $





	






r   r<   )4typingr   r   r   r   rN   Ztorch.ao.ns.fx.mappingsr   Ztorch.ao.quantization.fx.utilsr   Ztorch.ao.quantization.observerr   Ztorch.fxr	   r
   Ztorch.fx.graphr   r   Zns_typesr   r   r   utilsr   r   r   r   r   r   r   r   r   r   r$   r*   r   r7   dictr{   rT   ZTensorr   r_   rn   setrs   rx   boolr   r   r   r   r(   r(   r(   r)   <module>   s@  0	

0
o
!	

 !
;
K
+
 	