U
    Kc]h                     @   s  d Z ddlZddlZddlmZ ddlm  m  m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 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 ddlm Z m!Z!m"Z"m#Z#m$Z$m%Z% e!ej&e!ej&ej&f f Z'G dd dej(Z)G dd dej*Z+d6e,e
e#e!ee,f  ee$e e,e e"e"f f  ddddZ-d7e,e
e,e
e$e e,e%e f  e$e e,e%e f  e$e e,e e"e"f f  edddZ.d8e,ej(e,ej(e$e e,e%e f  e$e e,e%e f  e$e e,e e"e"f f  edddZ/e,e
e#e!ee,e,f  e#e!ee,e,f  e"ej(dddZ0d9e,e
e,e
e"e1e$e e,e%e f  e$e e,e%e f  e!ej(ej(f d	ddZ2d:e,ej(e,ej(e"e1e$e e,e%e f  e$e e,e%e f  e!ej(ej(f d!	d"d#Z3ej(ee"dd$d%d&Z4ej(ej(e"e,ed'd(d)Z5d;e,e
e,e
e"e1e$e e,e%e f  e$e e,e%e f  e$e e,e%e f  ej(d*
d+d,Z6d<e,ej(e,ej(e"e1e$e e,e%e f  e$e e,e%e f  e$e e,e%e f  ej(d-
d.d/Z7ej(e"e,ed0d1d2Z8ee,e,e"ej&ej&gej&f e,dd3d4d5Z9dS )=a.
  
This module contains tooling to compare weights and activations
across models. Example usage::

    import copy
    import torch
    import torch.quantization.quantize_fx as quantize_fx
    import torch.ao.ns._numeric_suite_fx as ns

    m = torch.nn.Sequential(torch.nn.Conv2d(1, 1, 1)).eval()
    mp = quantize_fx.prepare_fx(m, {'': torch.quantization.default_qconfig})
    # We convert a copy because we need the original prepared model
    # to be available for comparisons, and `quantize_fx.convert_fx` is inplace.
    mq = quantize_fx.convert_fx(copy.deepcopy(mp))

    #
    # Comparing weights
    #

    # extract weight pairs
    weight_comparison = ns.extract_weights('a', mp, 'b', mq)

    # add SQNR for each comparison, inplace
    ns.extend_logger_results_with_comparison(
        weight_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

    # weight_comparison contains the weights from `mp` and `mq` stored
    # in pairs, and can be used for further analysis.


    #
    # Comparing activations, with error propagation
    #

    # add loggers
    mp_ns, mq_ns = ns.add_loggers(
        'a', copy.deepcopy(mp),
        'b', copy.deepcopy(mq),
        ns.OutputLogger)

    # send an example datum to capture intermediate activations
    datum = torch.randn(1, 1, 1, 1)
    mp_ns(datum)
    mq_ns(datum)

    # extract intermediate activations
    act_comparison = ns.extract_logger_info(
        mp_ns, mq_ns, ns.OutputLogger, 'b')

    # add SQNR for each comparison, inplace
    ns.extend_logger_results_with_comparison(
        act_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

    # act_comparison contains the activations from `mp_ns` and `mq_ns` stored
    # in pairs, and can be used for further analysis.

    #
    # Comparing activations, without error propagation
    #

    # create shadow model
    mp_shadows_mq = ns.add_shadow_loggers(
        'a', copy.deepcopy(mp),
        'b', copy.deepcopy(mq),
        ns.OutputLogger)

    # send an example datum to capture intermediate activations
    datum = torch.randn(1, 1, 1, 1)
    mp_shadows_mq(datum)

    # extract intermediate activations
    shadow_act_comparison = ns.extract_shadow_logger_info(
        mp_shadows_mq, ns.OutputLogger, 'b')

    # add SQNR for each comparison, inplace
    ns.extend_logger_results_with_comparison(
        shadow_act_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

    # shadow_act_comparison contains the activations from `mp_ns` and `mq_ns` stored
    # in pairs, and can be used for further analysis.

    N)GraphModule)Node)$get_base_name_to_sets_of_related_ops)get_matching_subgraph_pairsget_type_a_related_to_b   )extract_weight_from_node)add_loggers_to_modelcreate_a_shadows_b)'rekey_logger_info_on_node_name_of_modelmaybe_add_missing_fqnsget_target_type_str)NSSingleResultValuesTypeNSResultsTypeNSNodeTargetType)DictTupleCallableListOptionalSetc                       sl   e Zd ZU dZeej ed< ee ed< dZ	e
e
e
e
e
e
e
eeee
 d
 fddZdd	 Zd
d Z  ZS )OutputLoggerz7
    Base class for capturing intermediate values.
    stats	stats_rnnT)
ref_node_nameprev_node_name
model_nameref_nameprev_node_target_typeref_node_target_typeresults_typeindex_within_argindex_of_argfqnc                    sV   t    g | _g | _|| _|| _|| _|| _|| _|| _	|| _
|| _|	| _|
| _d S )N)super__init__r   r   r   r   r   r   r   r   r    r!   r"   r#   )selfr   r   r   r   r   r   r    r!   r"   r#   	__class__ A/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/ns/_numeric_suite_fx.pyr%      s    
zOutputLogger.__init__c                 C   s   t |tjr| j|  n^t |tr|t|dkr|t|d dkr||d  |d d  |d d  ff}| j| |S )	
           r   r   )	
isinstancetorchTensorr   appenddetachtuplelenr   )r&   xZnew_resr)   r)   r*   forward   s    &,zOutputLogger.forwardc                 C   sN   d| j  d| j d| j d| j d| j d| j d| j d| j d	| j d
S )NzOutputLogger(ref_name=z, model_name=z,
prev_node_name=z, ref_node_name=z,
ref_node_target_type=z
results_type=z, index_within_arg=z,
index_of_arg=z, fqn=))	r   r   r   r   r   r    r!   r"   r#   )r&   r)   r)   r*   __repr__   s    zOutputLogger.__repr__)__name__
__module____qualname____doc__r   r.   r/   __annotations__RNNReturnTypeZ
_is_impurestrintr   r%   r5   r7   __classcell__r)   r)   r'   r*   r      s"   
:
r   c                       s.   e Zd ZdZejjeed fddZ	  Z
S )NSTracerzy
    Just like a regular FX quantization tracer, but treats observers and fake_quantize
    modules as leaf modules.
    )mmodule_qualified_namereturnc                    s6   t |tjjjrdS t |tjjjr(dS t ||S )r+   T)r-   r.   aoquantizationZObserverBaseZFakeQuantizeBaser$   is_leaf_module)r&   rB   rC   r'   r)   r*   rG      s
    zNSTracer.is_leaf_module)r8   r9   r:   r;   r.   nnModuler>   boolrG   r@   r)   r)   r'   r*   rA      s   rA   )r   modelnodes_and_names_to_instrumentresults"op_to_type_to_weight_extraction_fnrD   c           	      C   s\   t jd |D ]F\}}tjj}t|||}|r||krD|i i||< |g|| | | < qd S )Nz=quantization_api._numeric_suite_fx._extract_weights_one_model)r.   _C_log_api_usage_oncer   ZWEIGHTvaluer   )	r   rK   rL   rM   rN   noder   Zres_typeZextracted_weightr)   r)   r*   _extract_weights_one_model   s      rS   )model_name_agm_amodel_name_bgm_b base_name_to_sets_of_related_opsunmatchable_types_maprN   rD   c                 C   s   t jd t||||}g }g }	| D ]0\}
}|\}}||j|
f |	|j|
f q*i }t| |||| t|||	|| t| t	||}|S )Nz8quantization_api._numeric_suite_fx._extract_weights_impl)
r.   rO   rP   r   itemsr0   base_op_noderS   r   r   )rT   rU   rV   rW   rX   rY   rN   matched_subgraph_pairsZnodes_and_names_to_instrument_aZnodes_and_names_to_instrument_b
match_namematch
subgraph_a
subgraph_brM   r)   r)   r*   _extract_weights_impl   s>    	        
ra   )rT   model_arV   model_brX   rY   rN   rD   c                 C   s   t jd |dkrt }t|}g }g }	t||	}
t||	}t||
|}t|dr`|j	|_	t|||}t|dr|j	|_	t
| ||||||S )a  
    Extract weights from model A and model B, and return a comparison.

    Args:
        model_name_a: string name of model A to use in results
        model_a: model A
        model_name_b: string name of model B to use in results
        model_b: model B
        base_name_to_sets_of_related_ops: optional override of subgraph base nodes, subject to change
        unmatchable_types_map: optional override of unmatchable types, subject to change
        op_to_type_to_weight_extraction_fn: optional override of function which extracts weight
            from a type, subject to change

    Return:
        NSResultsType, containing the weight comparisons
    z2quantization_api._numeric_suite_fx.extract_weightsN_node_name_to_scope)r.   rO   rP   r   r   rA   r   tracehasattrrd   ra   )rT   rb   rV   rc   rX   rY   rN   Ztype_a_related_to_bskipped_module_namesskipped_module_classestracer_atracer_brU   rW   r)   r)   r*   extract_weights  s2    



     rk   )r   rK   $nodes_and_names_to_instrument_inputs%nodes_and_names_to_instrument_outputs
logger_clsrD   c           
      C   s`   t jd i }i }|D ]\}}}	||	f||< q|D ]\}}}	||	f||< q4t||||| }|S )Nz9quantization_api._numeric_suite_fx._add_loggers_one_model)r.   rO   rP   r	   )
r   rK   rl   rm   rn   Z%node_to_instrument_inputs_to_ref_nameZ&node_to_instrument_outputs_to_ref_namerR   r   Zref_node_typer)   r)   r*   _add_loggers_one_modelO  s       ro   )	name_arU   name_brW   rn   should_log_inputsrX   rY   rD   c                 C   s   t jd t||||}g }	g }
g }g }| D ]p\}\}}t|j|}t|j|}|r~|	|j||f |
|j||f ||j	||f ||j	||f q2t
| ||	||}t
|||
||}||fS )Nz4quantization_api._numeric_suite_fx._add_loggers_impl)r.   rO   rP   r   rZ   r   r[   r0   Z
start_nodeZend_nodero   )rp   rU   rq   rW   rn   rr   rX   rY   r\   Z&nodes_and_names_to_instrument_inputs_aZ&nodes_and_names_to_instrument_inputs_bZ'nodes_and_names_to_instrument_outputs_aZ'nodes_and_names_to_instrument_outputs_br]   r_   r`   Zref_node_type_aZref_node_type_bZnew_model_aZnew_model_br)   r)   r*   _add_loggers_implg  sT    
  



      rs   F)	rp   rb   rq   rc   rn   rr   rX   rY   rD   c              
   C   s   t jd g }g }	t||	}
t||	}t||
|}t|drJ|j|_t|||}t|drl|j|_t| |||||||dS )aP  
    Instrument model A and model B with loggers.

    Args:
        model_name_a: string name of model A to use in results
        model_a: model A
        model_name_b: string name of model B to use in results
        model_b: model B
        logger_cls: class of Logger to use
        base_name_to_sets_of_related_ops: optional override of subgraph base nodes, subject to change
        unmatchable_types_map: optional override of unmatchable types, subject to change

    Return:
        Returns a tuple of (model_a_with_loggers, model_b_with_loggers).  Modifies both models inplace.
    z.quantization_api._numeric_suite_fx.add_loggersrd   )rr   rX   rY   )	r.   rO   rP   rA   r   re   rf   rd   rs   )rp   rb   rq   rc   rn   rr   rX   rY   rg   rh   ri   rj   rU   rW   r)   r)   r*   add_loggers  s*    



    rt   )rK   rM   rn   rD   c                 C   s:  t jd |  D ]\}}t||p>t|t jjo>|jdk}|r|j}||krZi ||< |j	|| ksxt
|j	 d|j|| kri || |j< |j	|| |j krg || |j |j	< |j}t|jdkr|j}|| |j |j	 |j||j|j|j|j|j|j|jd	 || |j |j	 jdd d qd S )	NzAquantization_api._numeric_suite_fx._extract_logger_info_one_modelr   z is already present in resultsr   )	typevaluesr   r   r   r   r!   r"   r#   c                 S   s   | d  d| d  S )Nr"   :r!   r)   )resr)   r)   r*   <lambda>  s    z0_extract_logger_info_one_model.<locals>.<lambda>)key)r.   rO   rP   Znamed_modulesr-   ZjitZRecursiveScriptModuleoriginal_namer   r   AssertionErrorr    r   r3   r   r0   r   r   r   r   r!   r"   r#   sort)rK   rM   rn   Zgm_namemodZ	is_loggerrz   Zstats_to_user)   r)   r*   _extract_logger_info_one_model  sD    

r   )rb   rc   rn   !model_name_to_use_for_layer_namesrD   c                 C   s@   t jd i }| |fD ]}t||| qt| t||}|S )a  
    Traverse all loggers in `model_a` and `model_b`, and extract the logged
    information.

    Args:
        model_a: model A
        model_b: model B
        logger_cls: class of Logger to use
        model_name_to_use_for_layer_names: string name of model to use for
          layer names in the output

    Return:
        NSResultsType, containing the logged comparisons
    z6quantization_api._numeric_suite_fx.extract_logger_info)r.   rO   rP   r   r   r   )rb   rc   rn   r   rM   rK   r)   r)   r*   extract_logger_info  s     r   )
rp   rU   rq   rW   rn   rr   rX   node_type_to_io_type_maprY   rD   c	              
   C   s6   t jd t||||}	t| ||||	|||d}
|
S )Nz;quantization_api._numeric_suite_fx._add_shadow_loggers_impl)rr   r   )r.   rO   rP   r   r
   )rp   rU   rq   rW   rn   rr   rX   r   rY   r\   Zgm_a_shadows_br)   r)   r*   _add_shadow_loggers_impl  s$           r   )
rp   rb   rq   rc   rn   rr   rX   r   rY   rD   c	                 C   s   t jd g }	g }
t|	|
}t|	|
}t|||}t|drJ|j|_t|||}t|drl|j|_t| ||||||||d	S )a  
    Instrument model A and model B with shadow loggers.

    Args:
        model_name_a: string name of model A to use in results
        model_a: model A
        model_name_b: string name of model B to use in results
        model_b: model B
        logger_cls: class of Logger to use
        should_log_inputs: whether to log inputs
        base_name_to_sets_of_related_ops: optional override of subgraph base nodes, subject to change
        unmatchable_types_map: optional override of unmatchable types, subject to change
    z5quantization_api._numeric_suite_fx.add_shadow_loggersrd   )rr   rX   r   rY   )	r.   rO   rP   rA   r   re   rf   rd   r   )rp   rb   rq   rc   rn   rr   rX   r   rY   rg   rh   ri   rj   rU   rW   r)   r)   r*   add_shadow_loggers&  s,    



    r   )model_a_shadows_brn   r   rD   c                 C   s<   t jd tt}t| || t| t||}t|S )al  
    Traverse all loggers in a shadow model, and extract the logged
    information.

    Args:
        model_a_shadows_b: shadow model
        logger_cls: class of Logger to use
        model_name_to_use_for_layer_names: string name of model to use for
          layer names in the output

    Return:
        NSResultsType, containing the logged comparisons
    z=quantization_api._numeric_suite_fx.extract_shadow_logger_info)	r.   rO   rP   collectionsdefaultdictdictr   r   r   )r   rn   r   rM   r)   r)   r*   extract_shadow_logger_infoR  s    
 r   )rM   model_name_1model_name_2comparison_fncomparison_namerD   c                 C   s  |   D ]\}}|  D ]\}}||ks6t| d||ksLt| d|| }|| }	|	D ]}
|
d }|
d }d}|D ],}|d }|d }||kr|||kr||} qq||dk	st|d }|
d }g |
|< t||D ] \}}|||}|
| | qq`qqdS )ak  
    Compares the logged values from `model_name_2` against the corresponding
    values in `model_name_1`, using `comparison_fn`. Records the result
    in `model_name_2`'s results under `comparison_name`. Modifies `results` inplace.

    Args:
        results: the result data structure from `extract_logger_info` or
          `extract_shadow_logger_info`.
        model_name_1: string name of model 1
        model_name_2: string name of model 2
        comparison_fn: function to compare two Tensors
        model_name_to_use_for_layer_names: string name of model to use for
          layer names in the output
    z not found in resultsr!   r"   Nrv   )rZ   r|   zipr0   )rM   r   r   r   r   _Zresults_type_to_resultsZmodel_name_to_resultsZ	results_1Z	results_2Zresult_2Zindex_within_arg_2Zindex_of_arg_2Zresult_1Zcur_result_1Zindex_within_arg_1Zindex_of_arg_1Zvalues_1Zvalues_2Zvalue_1Zvalue_2Zcomparison_resultr)   r)   r*   %extend_logger_results_with_comparisono  s<    


r   )N)NNN)NNN)NN)FNN)NNN)FNNN):r;   r   r.   Ztorch.nnrH   Z!torch.ao.quantization.quantize_fxrE   rF   Zquantize_fxZtorch.fxr   Ztorch.fx.graphr   Ztorch.ao.ns.fx.mappingsr   Ztorch.ao.ns.fx.graph_matcherr   r   Zfx.weight_utilsr   Zfx.graph_passesr	   r
   Zfx.utilsr   r   r   Zfx.ns_typesr   r   r   typingr   r   r   r   r   r   r/   r=   rI   r   ZQuantizationTracerrA   r>   rS   ra   rk   ro   rJ   rs   rt   r   r   r   r   r   r   r)   r)   r)   r*   <module>   s  V V    -   2  2   .1'       -