U
    yh=                     @   s~  d dl Z d dlmZmZ d dlmZmZ d dl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dlmZmZmZ d dlmZ d d	lmZ d d
lmZmZmZm Z m!Z!m"Z"m#Z#m$Z$ eee#e% dddZ&eeee%e%e%e%e%e%e'e'e#e% edddZ(eeeee%e%f f eeee%e%f f ee%edddZ)eeeee"e j*e+f e"e j*e'f e%edddZ,eee"ee e f eeee%eee%e$e f e"ee e f d
ddZ-eeeeedddZ.eee'e/dddZ0e"ee e f e#e"ee e f  eeee%ed d!d"Z1e"ee e f e#e"ee e f  eeee%ed#d$d%Z2d)e%ee%eee%eeef f ee/e#ee%e$e f  ed&	d'd(Z3dS )*    N)GraphModulemap_arg)GraphNode)get_new_attr_name_with_prefix   )
$get_node_first_input_and_output_typegetattr_from_fqnNodeInputOrOutputTypereturn_first_non_observer_nodeget_number_of_non_param_argsget_target_type_str get_arg_indices_of_inputs_to_logget_node_input_qparamsop_type_supports_shadowingget_normalized_nth_input)NSSingleResultValuesType
NSSubgraphNSNodeTargetType)get_node_type_to_io_type_map)_is_activation_post_process)DictTupleCallableListAnyUnionOptionalSet)nodegmreturnc                 C   s`   d }t |dr\| }| jdkrLt| jts,tt|| j}t|rLt| |d}|j	|j
 d }|S )N_node_name_to_scopecall_moduler   )hasattrop
isinstancetargetstrAssertionErrorr	   r   r   r"   name)r   r    fqnZnode_to_use_for_fqnmodule r-   M/var/www/html/venv/lib/python3.8/site-packages/torch/ao/ns/fx/graph_passes.py_maybe_get_fqn    s    

r/   )r   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+   r!   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    r0   r1   r2   r3   r4   r5   r6   r7   r8   r+   Zlogger_node_nameZtarget_typeZ
logger_objZlogger_noder-   r-   r.   _insert_logger_after_node/   s.    
          r<   )r    *node_to_instrument_inputs_to_ref_node_name+node_to_instrument_outputs_to_ref_node_namer0   r3   r!   c                    s  t  }i  t|  } fdd}| jjD ]}|jdkrT|tt|| d| q*||ksf||krt	|| }	||krF|| \}
}t
|}|D ]}t|| |}t|tkr |j }t|| |d|j||
|tjjd||	d |j< qt|tjjjkrt|D ]@\}} |j }t|| |d|j||
|tjj|||	d |j< q qq||| |j< ||k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                    s   t |  fddS )Nc                    s
    | j  S Nr*   r   envr-   r.   <lambda>h       z8add_loggers_to_model.<locals>.load_arg.<locals>.<lambda>r   arB   r-   r.   load_argg   s    z&add_loggers_to_model.<locals>.load_argoutputr   Z_ns_logger_r7   r8   r+   )r   dictnamed_modulesr:   nodesr%   rJ   r   r   r/   r   typer   r*   r<   r   
NODE_INPUTvaluetorchZfxZimmutable_collectionsZimmutable_list	enumerate	node_copyNODE_OUTPUTr   )r    r=   r>   r0   r3   Z	new_graphmodulesrI   r   r+   r4   Zref_node_typeZarg_indices_to_logZnode_arg_idxZnode_argZ	prev_nodearg_idxargZnew_gmr-   rB   r.   add_loggers_to_modelV   s    



       
       
        
rY   )prev_node_cnode_agm_bgraph_cscale
zero_pointdtype_cast_namer!   c                 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*   r9   r;   rR   quantize_per_tensorZquint8)rZ   r[   r\   r]   r^   r_   r`   Zscale_node_nameZ
scale_nodeZzero_point_node_nameZzero_point_noder-   r-   r.    _insert_quantize_per_tensor_node   sF              rd   )
r[   node_crZ   gm_ar\   r]   node_name_prefixr0   node_type_to_io_type_mapr!   c	                 C   sp  d}	d}
d}d}d}d}t | |||\}}t ||||\}}|tjkrP|tjksx|tjkrd|tjksx|tjkr|tjkrtj}	n||kr|tjkrtj	j
}
n|tjkr|tjkrt| ||}|dk	rtj}	|\}}nN|tjkr|tjkrd}tj}n.td| d|  d| d|   d t|trt||}|	r~|dk	rh|dk	rht|| |||||S |d|	|fi |S nL|r|d|||fi |S |
st|
 }t||| |d	||fi |S nt|trXg }|D ]r}t||}|	r|d|	|fi |}|| n:|
s t|
 }t||| |d	||fi |}|| q|S td
t| ddS )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 implementedrb   call_methodr#   ztype fz is not handled)r   r
   FP32INT8ZFP16ZFP32_OR_INT8rR   
dequantizeUNKNOWNnnZIdentityr   rc   Zfloat16r)   format_noder&   r   r   rd   r;   r9   listappendrO   )r[   re   rZ   rf   r\   r]   rg   r0   rh   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          	
  

           
   
   
   rw   )r[   rf   r\   r]   r!   c              	   C   sD  | j dkr^t| jd |}t|| j}t|r:| }t||| |	| j |di |}|S | j dkr$| jdkst
d| j d| jdkrtt| |d	|||}t| jd |}|	| j | j|fi |}|S tt| |d	|||}t| jd |}|	| j | j|t| |d
fi |}|S nt
d|   d| j  ddS )z+
    Simple copy of node_a to graph_c.
    ra   _shadow_copy_r-   rk   rn   ri   ztarget  is not implementedrn   r   r   zhandling of node z	 with op N)r%   r   r*   r	   r'   rR   Z	is_tensordetachr9   r;   r)   _copy_node_from_a_to_cr   rq   )r[   rf   r\   r]   Znode_a_copy_nameZ
node_a_objZnode_a_copyZarg_copyr-   r-   r.   r|   B  sn    	

    

      
     r|   )
subgraph_arf   num_non_param_args_node_ar!   c                 C   s*  g }| j }|| jkr,|| t||d}q
|| |  dd }|D ]}||d kr^|nd}|j|dd}|dk	r|\}	}
n|j|j }	}
d}|t|	k r|dkrn&|dkr|dkrn||	| |s d	S |d7 }q|
	 D ]@}|dkrn*|dkr|dkrn|||s  d	S |d7 }qqJd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   sh   t | tr<t| |}|jdkr(|jdkS |jdkr6dS dS n(t | ttfrd| D ]}t |tsN dS qNdS )Nrk   ry   ra   TF)r&   r   r   r%   r'   rr   tuple)Z
node_a_argrf   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_noders   r   reversenormalized_argumentsargskwargslenvalues)r}   rf   r~   rN   cur_noder   r[   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_ap  sL    



 

r   )input_node_cinput_node_c_2r}   rf   r\   rg   r!   c                 C   s   t | tr| j}nt | ts t| d j}|jg}|j}||jkr\t||d}|d| q8|d }	t	| ||	|||}
t
dt|D ]"}|| }	|
}t	|d|	|||}
q|
S )z*
    TODO(before land): real docblock
    r   r   N)r&   r   r:   rr   r)   r   r   r   insert)_insert_copy_of_node_a_after_input_node_cranger   )r   r   r}   rf   r\   rg   r]   Z
nodes_of_ar   Z
cur_node_aZ
cur_node_cZ	cur_idx_arZ   r-   r-   r.   -_insert_copy_of_subgraph_a_after_input_node_c  s<    


	r   )r   r   r[   rf   r\   rg   r!   c                    s  t | tr| jnt | ts t| d j|j dd}|dk	rJ|\}}n|j|j }}g }	i }
 fdd}d}|t|k r|dkr| }n"|dkr|dk	r|}n||| }|		| |d7 }qv|
 D ]L\}|dkr| |
|< n*|dkr|dk	r||
|< n||
|< |d7 }qt|	}	t|}|jdkrt|}t |jtsZtt |j}t|| |j||	|
|}|S |jd	kst|j|j|	|
|}|S d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rjD ]}t |trNt
dqN| S t
dt dd S )Nz/handling of Node inside list is not implementedzhandling for kwarg of type rz   )r&   r   r   r|   intfloatrR   Zdtyperr   r   r)   rO   )rX   r   rf   r\   r]   r   r-   r.   	_copy_arg.  s    

z<_insert_copy_of_node_a_after_input_node_c.<locals>._copy_argr   r#   )rb   rk   )r&   r   r:   rr   r)   r   r   r   r   rs   itemsr   r   r%   r'   r(   r	   r9   r;   )r   r   r[   rf   r\   rg   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     sp    3

 







      r   )	name_arf   name_br\   matched_subgraph_pairsr0   should_log_inputsrh   r!   c           4         s2  |dkrt  }t }i  t }	 fdd}
i }i }| D ]L\}}|\}}t|j|}t|j}||||f||j< ||||f||j< q@j	j
D ]}|jdkr|t|jd |
 q||k}||k}|s|r|r|| \}}}}n|st|| \}}}}t|jot|}|s^tdt| dt|j|  d  |||
 |j< qt|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< q|tjkr`|tjkr`t|j||}|s`tdt| dt|j|  d
  |||
 |j< qt|j|}t|||stdt| dt|j|  d  |||
 |j< qt|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< qRntdt&|! d|s|r|||
 |j<  |j }&|rt|&d}"|r t|"trt|&d}"nt|"t$r fdd|"D }"t'|j|&|"|||jd ||	}'|rd}(t|'trt |'|d|(| ||t!j"j#dd|d}'|'})n^t|'t$stg }*t%|'D ]8\}+},t |,|d|(| ||t!j"j#|+d|d}-|*(|- q|*}'|'})d}.t|j|}|dkrt|&d}.t)|'|.|||&jd }/|/ |/j< |r|/}0t|0d|)krRt|0d}0q0t|)trtt*|)j}1|0j|1_+n0t|)t$st|)D ]}2t*|2j}1|0j|1_+qt  |/j |d|/j| ||t!j,j#dd|d |/j< |r"t  |j |d|j|||t!j,j#dd| d |j< q|||
 |j< qt-|}3|3S )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                    s   t |  fddS )Nc                    s
    | j  S r?   r@   rA   env_cr-   r.   rD     rE   z6create_a_shadows_b.<locals>.load_arg.<locals>.<lambda>rF   rG   r   r-   r.   rI     s    z$create_a_shadows_b.<locals>.load_argrJ   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_rK   c                    s   g | ]} |j  qS r-   r@   .0rX   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   )r\   r-   r.   r   ;  s     Z_dtype_cast_ Z_ns_logger_a_inp_r   r   rx   Z_ns_logger_a_Z_ns_logger_b_).r   r   rL   rM   r   r   Zbase_op_noder   r   r:   rN   r%   rJ   r   r   r)   r   printrT   r*   r   r
   ro   rm   rl   r   r   r   r/   r   r&   r   r<   r   rP   rQ   rr   rS   rO   rw   rs   r   getattrr2   rU   r   )4r   rf   r   r\   r   r0   r   rh   r]   rV   rI   Z+start_node_b_to_matched_subgraph_a_and_nameZ)end_node_b_to_matched_subgraph_a_and_nameZ
match_namematchr}   Z
subgraph_bZref_node_type_aZref_node_type_bZnode_bZnode_b_is_start_nodeZnode_b_is_end_noder4   Zall_op_types_support_shadowingrt   Znode_output_type_aZnode_input_type_bZnode_output_type_bZnode_io_types_known_a_and_bru   r~   Z
fqn_base_aZ
fqn_base_bZprev_node_brZ   Zprev_node_c_listrW   rX   re   Zdtype_cast_noder2   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   r\   r.   create_a_shadows_bm  s   $





      

              
                   
    


              
r   )N)4rR   Ztorch.fxr   r   Ztorch.fx.graphr   r   Ztorch.ao.quantization.fx.utilsr   utilsr   r	   r
   r   r   r   r   r   r   r   Zns_typesr   r   r   Ztorch.ao.ns.fx.mappingsr   Ztorch.ao.quantization.observerr   typingr   r   r   r   r   r   r   r   r(   r/   r   r<   rY   ZTensorr   rd   rw   r|   boolr   r   r   r   r-   r-   r-   r.   <module>   s   0((R/J3  