
    sgΓ                        U d dl Z d dlZd dlmZ d dlmZmZmZmZm	Z	 d dl
Z
d dlmc mc mZ d dlmZ d dlmc mZ d dlmZ d dlmZmZmZ d dlmZmZ d dlmZ d dlm Z  d	d
l!m"Z"m#Z#m$Z$ g Z%ee   e&d<   de
jN                  de(de
jN                  de
jN                  fdZ)e
jT                  e
jV                  e
jX                  e
jZ                  iZ. G d dej^                        Z0 G d dej^                        Z1de0de1de
jN                  fdZ2 G d d edddg            Z3e0ji                  e
jj                  e
jX                        Z6e1ji                  e
jn                  e
jZ                        Z8 e3e6e8      Z9de:fdZ;de:fdZ<de:fd Z=d!e de:fd"Z>d#ej^                  de:fd$Z?d%e d&ed'ee@ej^                  f   de	ee    ee1   f   fd(ZAd)e d'ee@ej^                  f   dee    fd*ZBd!e d'ee@ej^                  f   dee0   fd+ZCd!e d'ee@ej^                  f   dee
jN                     fd,ZDd!e d'ee@ej^                  f   ddfd-ZEd!e d'ee@ej^                  f   d.e
jN                  d/ee
jN                     ddf
d0ZFd)e d&ed'ee@ej^                  f   d.e
jN                  d/ee
jN                     ddfd1ZGd)e d'ee@ej^                  f   ddfd2ZHd&ed!e d3e fd4ZId&ed'ee@ej^                  f   dee@e1f   fd5ZJd&ed'ee@ej^                  f   d6ee@e1f   ddfd7ZKd&efd8ZLd9ej^                  d:ej^                  d;e
jN                  dee@eMf   fd<ZNd=ee@eMf   d>e(defd?ZOy)@    N)
namedtuple)AnyDictListOptionalTuple)_get_observed_graph_module_attr)
_with_argsObserverBasePerChannelMinMaxObserver)_parent_namecheck_min_max_valid)GraphModule)Node   )get_new_attr_name_with_prefixmaybe_get_next_modulenode_arg_is_weightCUSTOM_MODULE_SUPP_LISTscaleaxisinputreturnc                 l    dg|j                   z  }|j                  |      ||<   | j                  |      S )zMReshapes the scale so that we can multiply it to the input by the given axis.r   )ndimsizeview)r   r   r   	new_shapes       U/var/www/html/venv/lib/python3.12/site-packages/torch/ao/quantization/fx/_equalize.pyreshape_scaler       s3    ejj Ijj&IdO::i      c                        e Zd ZdZej
                  ej                  dddf	 d fdZd Zd Z	d Z
d Z ee      Z xZS )	_InputEqualizationObservera.  Observer for tracking the running min/max values of input columns, and
    computing the quantization parameters for the overall min/max input values.

    Args:
        dtype: Quantized data type
        qscheme: Quantization scheme
        quant_min: Minimum quantization value. If unspecified, it will
            follow the 8-bit setup.
        quant_max: Maximum quantization value. If unspecified, it will
            follow the 8-bit setup.

    The running minimum/maximum :math:`x_\text{min/max}` are computed in the
    same way as :class:`~torch.ao.quantization.observer.PerChannelMinMaxObserver`,
    with the difference that the running min/max values are stored per column.
    This observer is intended to be used along with a WeightEqualizationObserver
    to calculate the equalization scale.
    Nc                    t         |           |t        j                  t        j                  hvrt        d      || _        || _        t        |   }t        d|||||      | _
        t        j                  d      | _        g | _        y )Nz Input qscheme must be per-tensorr   ch_axisdtypeqscheme	quant_min	quant_maxfactory_kwargs)super__init__torchper_tensor_affineper_tensor_symmetric	TypeErrorr'   r(   (qsheme_mapping_per_tensor_to_per_channelr   	input_obstensorequalization_scaleequalization_shapeselfr'   r(   r)   r*   r+   per_channel_qscheme	__class__s          r   r-   z#_InputEqualizationObserver.__init__?   s     	522E4N4NOO>??
FwO1')
 #(,,q/-/r!   c                     |j                   dk\  r|j                   dk  st        d      dg|j                   z  | _        |j                  d      | j                  d<   | j	                  |      S )N      >InputEqualizationObserver only supports Linear and Conv layersr   )r   
ValueErrorr6   r   r3   )r8   x_origs     r   forwardz"_InputEqualizationObserver.forward\   se    q V[[A%5P 
 $%#"3%+[[^"~~f%%r!   c                 Z    | j                   j                  | j                   j                  fS N)r3   min_valmax_valr8   s    r   get_input_minmaxz+_InputEqualizationObserver.get_input_minmaxh   s!    &&(>(>??r!   c                     |j                         dk(  r|t        j                  d      k(  ry t        j                  || j                        | _        y )Nr   )nelementr.   r4   reshaper6   r5   r8   r5   s     r   set_equalization_scalez1_InputEqualizationObserver.set_equalization_scalek   sE     &&(A-2DUV2W"'-- 7 7#
r!   c                    | j                   j                         dk(  r8| j                   t        j                  d      k(  rt	        j
                  d       y| j                         \  }}t        | j                   d|      }t        j                  t        j                  ||            }t        j                  t        j                  ||            }||fS )z!Returns the scaled min/max inputsr   z}Must call calculate_equalization_scale before calling calculate_scaled_minmax. Will not scale the next quantization observer.NNr   )r5   rI   r.   r4   warningswarnrG   r    minmulmax)r8   
min_inputs
max_inputsequalization_scale_reshapedmin_input_scaledmax_input_scaleds         r   calculate_scaled_minmaxz2_InputEqualizationObserver.calculate_scaled_minmaxt   s     ##,,.!3''5<<?:MMC 
 $(#8#8#: Z&3##Q
'
# !99UYYz;V%WX 99UYYz;V%WX!111r!   r   N)__name__
__module____qualname____doc__r.   quint8r/   r-   rA   rG   rL   rY   classmethodr
   	with_args__classcell__r:   s   @r   r#   r#   ,   sS    ( ll''0 
0:
&@
20 J'Ir!   r#   c                   |     e Zd ZdZej
                  ej                  dddf	 d fdZd Zd Z	d Z
 ee      Z xZS )_WeightEqualizationObservera  Observer for tracking the running min/max values of weight columns and
    rows, and computing the quantization parameters for the weight rows.

    Args:
        dtype: Quantized data type
        qscheme: Quantization scheme
        quant_min: Minimum quantization value. If unspecified, it will
            follow the 8-bit setup.
        quant_max: Maximum quantization value. If unspecified, it will
            follow the 8-bit setup.

    This observer is made up of 1 PerChannelMinMaxObserver `weight_col_obs` used
    to record the running minimum and maximum of columns of incoming weight
    tensors. This observer is intended to be used along with an
    InputEqualizationObserver to calculate the equalization scale.

    The running minimum/maximum :math:`w_\text{min/max}` are computed in the
    same way as :class:`~torch.ao.quantization.observer.PerChannelMinMaxObserver`.
    Nc                    t         |           || _        || _        d| _        |}|t
        j                  t
        j                  hv r	t        |   }t        d|||||      | _
        t        j                  d      | _        y )Nr   r%   )r,   r-   r'   r(   r&   r.   r/   r0   r2   r   weight_col_obsr4   r5   r7   s          r   r-   z$_WeightEqualizationObserver.__init__   s     	
%u..0J0JKK"J7"S6')
 #(,,q/r!   c                 v    |j                   dk\  r|j                   dk  st        d      | j                  |      S )Nr<   r=   r>   )r   r?   rg   )r8   w_origs     r   rA   z#_WeightEqualizationObserver.forward   s<    q V[[A%5P  ""6**r!   c                 Z    | j                   j                  | j                   j                  fS rC   )rg   rD   rE   rF   s    r   get_weight_col_minmaxz1_WeightEqualizationObserver.get_weight_col_minmax   s%    ##++T-@-@-H-HIIr!   c                     || _         y rC   )r5   rK   s     r   rL   z2_WeightEqualizationObserver.set_equalization_scale   s
    "4r!   rZ   )r[   r\   r]   r^   r.   qint8r/   r-   rA   rk   rL   r`   r
   ra   rb   rc   s   @r   re   re      sN    , kk''2 
28+J5 J'Ir!   re   r3   
weight_obsc                    | j                         \  }}|j                         \  }}t        ||      rt        ||      s*t        j                  d       t        j                  d      S |j                  |j                  k(  s)t        dd|j                   d|j                   dz         t        j                  ||z
  ||z
  z        }d||dk(  <   t        j                  |ddd      }|S )	a  Calculates the equalization scale and sets the equalization_scale value
    in the observers.

    Args:
        input_obs: Observer that tracks the ranges for the input columns
        weight_obs: Observer that tracks the ranges for the weight columns
    ztMust run observer before calling calculate_equalization_scale. Returning default equalization scale torch.tensor(1).r   z6Input and Weight must have the same column dimension. zFound z and z shapes instead.g        )nanposinfneginf)rG   rk   r   rO   rP   r.   r4   shaper?   sqrt
nan_to_num)r3   rn   rT   rU   min_weightsmax_weightsr5   s          r   calculate_equalization_scalerx      s     )99;Z!+!A!A!C[+ 	J
3[9F	
 ||A 1 11Dz''(k.?.?-@@PQR
 	

 	{	"zJ'>? 56)S01))*<!AVWXr!   c                   x     e Zd ZdZej
                  j                  ej
                  j                  f fd	Z xZS )EqualizationQConfigab  
    Describes how to quantize a layer or a part of the network specifically for
    input-weight equalization by providing settings (observer classes) for
    inputs, outputs, and weights.

    Note that EqualizationQConfig needs to contain observer **classes** (like
    MinMaxObserver) or a callable that returns instances on invocation, not the
    concrete observer instances themselves.
    Quantization function will instantiate observers multiple times for each of
    the layers.

    Observer classes have usually reasonable default arguments, but they can be
    overwritten with `with_args` method (that behaves like functools.partial):

    my_qconfig = EqualizationQConfig(input_activation=_InputEqualizationObserver.with_args(dtype=torch.qint8),
                                    weight=_WeightEqualizationObserver.with_args(dtype=torch.qint8))
    c                     t        |t        j                        st        |t        j                        rt        d      t        |   | ||      }|S )NzEqualizationQConfig received observer instance, please pass observer class instead. Use MyObserver.with_args(x=1) to override arguments to constructor if needed)
isinstancennModuler?   r,   __new__)clsinput_activationweightr8   r:   s       r   r   zEqualizationQConfig.__new__  sL    &		2j6Sa  ws$4f=r!   )	r[   r\   r]   r^   r.   r}   Identityr   rb   rc   s   @r   rz   rz      s-    $ ',hh&7&7@Q@Q  r!   rz   r   r   )r'   r(   )r   r   c                     t        |       t        j                  t        j                  t        j                  t        j
                  fv S )z/Checks if the fused node supports equalization.)typenni
LinearReLU
ConvReLU1d
ConvReLU2d
ConvReLU3dmodules    r   "fused_module_supports_equalizationr   "  s4    <	  r!   c                     t        |       t        j                  t        j                  t        j                  t        j
                  fv S )z2Checks if the torch.nn node supports equalization.)r   r}   LinearConv1dConv2dConv3dr   s    r   nn_module_supports_equalizationr   ,  s*    <BIIryy"))RYYGGGr!   c                 $    t        |       t        v S )z0Checks if the custom node supports equalization.)r   r   r   s    r   #custom_module_supports_equalizationr   1  s    <222r!   nodec                    | j                   dk(  rgt        |t        | j                                 xsD t	        |t        | j                                 xs! t        |t        | j                                 S | j                   dk(  rJ| j                  t        j                  t        j                  t        j                  t        j                  fv S y)zChecks if the current node supports equalization
    Currently we only support nn.Linear/F.Linear and nn.Conv/F.conv layers
    call_modulecall_functionF)opr   strtargetr   r   Flinearconv1dconv2dconv3d)r   moduless     r   node_supports_equalizationr   6  s     ww-+GC4D,EF N1'#dkk:J2KLN273t{{;K3LM	

 
O	#{{qxx188QXXFFFr!   observerc                 .    t        | t        t        f      S rC   )r|   r#   re   )r   s    r   is_equalization_observerr   E  s    -/JK r!   input_eq_obs_nodemodelr   c                    d}| j                   j                         D ]  }t        ||      s|} n |J |j                  dk(  rpt	        |d      }|J |}|j                  |j                  d      J |j                  |j                  d      j                         }t        |t              sJ ||fS |j                  dk(  r<t        ||      }|.|t        |j                           }t        |t              sJ ||fS y)a  Gets the following weight equalization observer. There should always
    exist a weight equalization observer after an input equalization observer.

    Returns the operation node that follows the input equalization observer node
    and the weight equalization observer
    Nr   !equalization_node_name_to_qconfigr   rN   )userskeysr   r   r	   getnamer   r|   re   maybe_get_weight_eq_obs_noder   r   )	r   r   r   op_nodeuser&maybe_equalization_node_name_to_configr   weight_eq_obsweight_nodes	            r   get_op_node_and_weight_eq_obsr   P  s+    G!'',,. %dG4G
 zz]" 2Q62
. 6AAA<b)044W\\4HTTT9==LL$

&( 	 -)DEEE%%		&27GD"#C(:(:$;<Mm-HIIIM))r!   r   c                     | j                   dk(  sJ | j                  D ]Y  }t        | |      st        |t              r5|j                   dk(  r&t        |t        |j                           t              sJ |c S  y)z8Gets the weight equalization observer node if it exists.r   r   N)r   argsr   r|   r   r   r   re   )r   r   node_args      r   r   r   |  s{     ::(((LL 	gx08T*KK=0C013N O	 r!   c                 J   t        | |      sJ t        | |t        j                        }|t        | |t        j
                        }|t        | |t              nt        ||t              }|yt        ||t              }|y|t        |         }t        |t              sJ |S )a  Gets the following input equalization observer if it exists.

    For example, in the case of connecting linear layers:
        x -> inp_obs1 -> eq_obs1 -> linear1 -> out_obs1 -> eq_obs2 -> linear2 -> out_obs2
    If the node being passed in is the linear1 node, then we want to return eq_obs2,
    the following equalization observer for linear2.

    However, if there are no connecting layers:
        x -> inp_obs1 -> eq_obs1 -> linear1 -> out_obs1 -> add
    Then we want to return None.

    In the case of an unfused linear-relu layer with a connecting linear layer:
        linear1 -> relu -> out_obs1 -> eq_obs2 -> linear2 -> out_obs2
    Since it is unfused, we want to skip over the relu layer and return eq_obs2,
    the following equalization observer for linear2.
    N)target_functional_type)
r   r   r}   ReLUr   relur   r#   r   r|   )r   r   maybe_relu_nodemaybe_obs_nodemaybe_eq_obs_nodemaybe_eq_obss         r   maybe_get_next_input_eq_obsr     s    ( &dG444 ,D'277CO/'!&&
 " 	dG\:"?G\J 
 -!;  3012Ll$>???r!   c                     t        | |      }|rL|j                  j                         dk(  r#|j                  t        j                  d      k(  ry|j                  S y)aU  If the next next node is an InputEqualizationObserver then we want to
    return its equalization scale, else we return 1

    This is used in the case where there are two connecting linear layers:
        linear1 -> LinearOutObs -> InputEqObs -> linear2
    In this case, the node given is linear1 and we want to locate the InputEqObs.
    r   N)r   r5   rI   r.   r4   )r   r   next_inp_eq_obss      r   !maybe_get_next_equalization_scaler     sR     2$@O..779Q>22ell1oE111r!   c                 8   |t        | j                           }t        |t              sJ | j                  d   }t        |t
              sJ |t        |j                           }t        |t              sy|j                         \  }}||y||_        ||_	        y)zScales the following input quantization observer's min/max values by
    updating the values with the scaled min/max values calculated by the input
    equalization observer
    r   N)
r   r   r|   r#   r   r   r   rY   rD   rE   )r   r   input_eq_obsinput_quant_obs_nodeinput_quant_obsrW   rX   s          r   scale_input_observerr     s    
 3t{{+,Ll$>???99Q<*D111c"6"="=>?Oo|4)5)M)M)O&&$4$<.O.Or!   r5   next_equalization_scalec                 $   |yt        |t        | j                                 r|t        | j                           d   }n|t        | j                           }t        |      st	        |      sJ |j
                  }t        |t        j                        sJ t        |d|      }t        j                  |t        j                  |            }|t        j                  |      |_        yt        |d|      }t        j                  ||      }t        j                  |      |_        |j                  }	|	yt        |	t        j                        sJ t        |d|	      }t        j                  |	|      }
t        j                  |
      |_        y)a  Scale the weights for input-weight equalization by multiplying the
    weight by 1/equalization_scale and next_equalization_scale

    Args:
        node: Current node whose weights we want to scale
        equalization_scale: Current node's calculated equalization scale
        next_equalization_scale: Next node's calculated equalization scale if
           the following node needs to be equalized, 1 otherwise
    Nr   r   )r   r   r   r   r   r   r|   r.   Tensorr    rR   
reciprocalr}   	Parameterbias)r   r   r5   r   	op_moduler   rV   scaled_weight next_equalization_scale_reshapedr   scaled_biass              r   scale_weight_noder     sd    !)'#dkk2B*CDC,-a0	C,-	*	,Y	78 8 Ffell+++ #00BAv"NIIfe&6&67R&STM&<<6	 (55LaQW'X$IIm-MNM||M2I >>D|dELL))) (55LaQU'V$))D"BCK\\+.INr!   c                 "   |yt        | |      }|y|j                  d   }|yt        |t              r&t        |t	        |j
                           t              sJ |j                  d   }|yt        |t              r|j                  dk(  sJ t        |j
                        \  }}	t        ||   |	      }
t        |d|
      }t        j                  |
t        j                  |            }|t        ||   |	|       yt        |d|      }t        j                  ||      }t        ||   |	|       t        j                  |j!                  t	        |j
                              |      sJ d}| j                  D ]5  }t        |t              s|j                  dk(  s$d|j"                  v s3|} n |yt        |j
                        \  }}t        ||   |      }t        |d|      }t        j                  ||      }t        ||   ||       y)z-Scales the weight value for functional layersNr   get_attrr   r   )r   r   r|   r   r   r   r   r   r   getattrr    r.   rR   r   setattrallclose
get_bufferr   )r   r   r   r5   r   weight_eq_obs_nodeweight_quant_obs_noder   weight_parent_nameweight_namer   rV   r   r   	bias_noder   bias_parent_name	bias_namer   r   s                       r   scale_weight_functionalr   &  s(    ! 6gwG! /33A6$+T2z)0012L8  
 (,,Q/Kk4([^^z-III&2;3E3E&F#W/0+>F
 #00BAv"NIIfe&6&67R&STM&*+[-H (5M($ IIm-MNMG&'mD>>%**3{/A/A+BC]SSS I dD!dgg&;$))@SI	
 ".y/?/?"@i7+,i8D (55LaQU'V$))D"BCKG$%y+>r!   c                 
   t        | |      }|y|j                  d   }|yt        |t              sJ |t	        |j
                           }t        |t	        |j
                           t              sJ |j                          y)ztGiven the operation node, we want find the corresponding quantization
    observer and reset its min/max values
    Nr   )r   r   r|   r   r   r   r   reset_min_max_vals)r   r   r   r   weight_quant_obss        r   clear_weight_quant_obs_noder   t  s     6gwG!.33A6$+T222s#8#?#?@Agc"7">">?@,OOO'')r!   	prev_nodec                     t        |j                  j                               }|D ]  }|j                  ||        | j                  j                  |       y)ziRemoves the given node from the model by replacing all of its users with
    the given previous node
    N)listr   r   replace_input_withgraph
erase_node)r   r   r   
orig_users	user_nodes        r   remove_noder     sL     djjoo'(J 6	$$T956 
KK4 r!   c                    i }| j                   j                  D ]7  }|j                  dk(  st        ||j                     t
              s2||j                     }t        |t
              sJ t        || |      \  }}||h|j                  dk(  rt        |t        |j                                 r;|t        |j                           d   }t        |      sJ  ||j                         n( ||t        |j                           j                         t        ||      }|j                  |       |j                  |       |||j                  <   : |S )a  Update all of the observer's equalization scale. For each
    InputEqualizationObserver, we will find the location of the next
    WeightEqualizationObserver, create it, and calculate the equalization scale
    based on the two observers.

    We will then return a dictionary mapping operation node names to
    the corresponding WeightEqualizationObservers for that operation.
    r   r   )r   nodesr   r|   r   r#   r   r   r   r   r   rx   rL   r   )	r   r   weight_eq_obs_dictr   r   r   r   r   r5   s	            r   update_obs_for_equalizationr     sB    !! =77m#
DKK "<)
 #4;;/Ll,FGGG%B4PW%X"G]-"7zz]* 6gc'..>Q6RS$S%89!<F:6BBB!&--0!'#gnn*=">"E"EF ">m" //0BC001CD/<w||,9=< r!   r   c           	         | j                   j                  D ]  }|j                  dk(  rht        ||j                     t
              rJ|j                  d   }|j                  d   }t        ||      sd|j                  v rt        | ||       xt        ||       | j                   j                  |      5  t        |j                  dz         } ||      }t        | |||j                     j                         | j                   j                  d|      }ddd       | j                   j!                        5  ||f}	| j                   j                  dt"        j$                  |	      }
ddd       |j'                  |
       t        | ||       ||j)                  |j                  d      |j)                  |j                        }t        |t*              sJ |j                  }|j-                         dk(  r|t#        j.                  d      k(  rd}t1        ||      }|j                  dk(  rt3        ||||       +|j                  dk(  rqt5        || |||       t7        ||      }| yt        |t9        |j                           t*              sJ t;        ||       |j                  d   }t        | ||       t=        d	d
|j                   d|j                   dz          y# 1 sw Y   xY w# 1 sw Y   xY w)a	  Converts the equalization operations and updates the other nodes in the
    following way:
        - Removes the input equalization observers and inserts a mul operator
          along with an equalization scale node wherever applicable (we do not
          want to insert a mul operator between connecting linear layers).
        - Updates the input quantization observers with the scaled input min/max
          values.
        - Scales the weights by the current and next equalization scales.
        - Removes the weight equalization observer node if it exists.

    Before (after prepare):
                                    weight values
                                          |
                                    WeightQuantObs
                                          |
                                      WeightEqObs
                                          |
        x -> InpQuantObs -> InpEqObs -> linear -> OutQuantObs

    After this function:
                                              scaled weight values
                                                      |
       equalization scale                       WeightQuantObs
              |                                       |
        x -> mul -> InpQuantObs (scaled min/max) -> linear -> OutQuantObs

    After convert:
       equalization scale                 scaled weight values
              |                                    |
        x -> mul -> quantize_per_tensor -> quantized::linear

    Note that although the equalization observer appeared after the quantization
    observer after prepare_fx, the mul node appears before the quantization node
    after convert_fx. This is because placing the equalization observer after
    the quantization observer in prepare_fx would allow us to keep the invariant
    that the graph before the current node inserts its observers is not
    modified.

    Having the equalization observer before the quantization observer would also
    cause some inconsistences between the ordering of the quantization and
    equalization observers.
    For example, a single linear layer would look like:
        x -> InpEqObs1 -> InpQuantObs1 -> linear1 -> OutQuantObs1
    But between two connected linear layers, it would look like:
        linear1 -> OutQuantObs1 -> InpEqObs2 -> linear2 -> OutQuantObs2
    r   r   r   _equalization_scaler   Nr   r   z=Expected operation node to be 'call_module' or 'call_functionzInstead got node z as 'z'.)r   r   r   r|   r   r#   r   r   r   r   r   inserting_beforer   r   r5   create_nodeinserting_afterr.   rR   r   r   re   rI   r4   r   r   r   r   r   r   r?   )r   r   r   r   inp_quant_obs_noder   get_new_eq_scale_namer   eq_scale_nodeinputsmul_noder   r5   maybe_next_equalization_scaler   s                  r   convert_eq_obsr     s   f !! ]77m#
DKK "<)
 "&1*//2I +9g>Y^^+E4);< !w/ --.@A J(ENN%::)% -W5tWT[[%9%L%LM % 7 7
D IJ ,,]; W#]3 ;;22?EIIvVW 11)XFt%78##DIIt4@.22499=Mm-HIII!.!A!A #++-2&%,,q/9%)",Mg-)
 ww-'!'#57T O+'&1 &B$%P"%-!C 2 9 9:;=X   ,D': /33A6	E#5yA S)$))E$''"EF u]:J JW Ws   6A!K-:0K:-K7	:L	c                     t        | j                  d            }t        | |      }t        | ||       t	        | | j
                        S )zjReference function which applies changes needed for equalization, but
    does not quantize the nodes
    F)remove_duplicate)dictnamed_modulesr   r   r   r   )r   r   r   s      r   _convert_equalization_refr  T  sI     5&&&>?G 5UGD5'#56uekk**r!   model_amodel_bxc                    ddl mc mc m} ddlm}  |       }|d   j                  t        j                         |j                  d| d||j                  |      \  }} ||        ||       |j                  |||j                  d      }|j                  |ddt        j                  j                  j                  j                  j                  d       i }	|D ],  }
||
   d	   d   d   d
   }||
   d	   d   d   d   d   }||	|<   . |	S )aG  Runs the Numeric Suite on model_a and model_b and returns a dictionary
    containing the SQNR between layers in model_a and model_b.

    Note: In order to support equalized models, this function has a hacky fix in
    which we do not match any torch.mul operators. This is because equalized
    models contain extra mul operators to scale the input by the equalization
    scale, but this edge case has not been resolved yet within the numeric suite code.

    Args:
        model_a: A float model
        model_b: A quantized model
        x: Inputs to use during calibration
    r   N)get_unmatchable_types_mapfuns_unmatchablefp32int8)unmatchable_types_mapsqnrnode_outputfqn)torch.ao.ns._numeric_suite_fxaons_numeric_suite_fxtorch.ao.ns.fx.mappingsr	  addr.   rR   add_loggersOutputLoggerextract_logger_info%extend_logger_results_with_comparisonfxutilscompute_sqnr)r  r  r  r  r	  r  
model_a_ns
model_b_nsactivation_comparison_dictlayer_sqnr_dictkeylayerr  s                r   get_layer_sqnr_dictr$  g  s+     /.A57,-11%))<^^
3 , J
 qMqM!#!7!7J" ,,")) O) &*3/>vFqI%P)#.}=fEaHPQRS!%&
 r!   r!  num_layers_to_equalizec                     t        | j                         t        j                  d            }|d| }|D cg c]  }|d   t        f }}d|i}|S c c}w )a  Given the layer to SQNR dictionary, find the layers with the highest
    quantization errors, and return an equalization_qconfig_dict
    specifying to only equalize those top layers.

    Args:
        layer_sqnr_dict: Dictionary mapping layer names to SQNR values (found
            when comparing an equalized model against a float model)
        num_layers_to_equalize: Number of layers with the highest quantization
           errors to equalize
    r   )r"  Nr   module_name)sorteditemsoperator
itemgetterdefault_equalization_qconfig)r!  r%  layer_sqnr_sortedlayers_to_equalizeitemmodule_to_qconfig_listequalization_qconfig_dicts          r   get_equalization_qconfig_dictr2    sv      446H<O<OPQ<RS*+B,BC
 =O48a./  "/0F G$$	s   A)Pr*  rO   collectionsr   typingr   r   r   r   r   r.   torch.ao.nn.intrinsicr  r}   	intrinsicr   torch.nntorch.nn.functional
functionalr   %torch.ao.quantization.fx.graph_moduler	   torch.ao.quantization.observerr
   r   r   torch.ao.quantization.utilsr   r   torch.fxr   torch.fx.graphr   r  r   r   r   r   __annotations__r   intr    r/   per_channel_affiner0   per_channel_symmetricr2   r~   r#   re   rx   rz   ra   r_   input_equalization_observerrm   weight_equalization_observerr,  boolr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  floatr$  r2   r!   r   <module>rH     s     " 3 3  # #    Q 
 J     &( c '! !S ! !%,, ! 
U55	 ; ;, (`( `(F?(")) ?(D$)$7R$
\\$N$'98&DE> 9BB
,, : : C    ;DD
++u::  E     309U  
$ Ht H
34 3
T t ryy T ))$/):>sBII~:N)
8D>8$?@@A)X bii0d^$/
/c299n-/()/d
c299n-ell*/t /d3		>.B /t /,8/
8/#ryy.!8/ 8/ &ell3	8/
 
8/vK?K?K? #ryy.!K? 	K?
 &ell3K? 
K?\* *S"))^8L *QU *$!{ !$ !4 !**!%c299n!5*	#*
*+*ZPP#ryy.!P S"==>P 
	Pf+[ +&4YY4!#4/4||4	#u*4n%#u*%%?B%%r!   