
    h'9              
       $   S SK r S SKrS SKJr  S SKJr  S SKJrJr  S SK	r	S SK
Jr  S SKJr  S SKJrJr  S SKJr  S S	KJr  S
SKJr  SrSrSr\R6                  " \5      r\" SS9 " S S5      5       rS\SS4S jrS\S\\   4S jr S\!S\!4S jr"S\!S\!S\#4S jr$S\\	RJ                  \	RJ                  /\	RJ                  4   S\!S\!S\!4S jr& " S S\	RN                  RP                  5      r)S \S\S!\S\4S" jr*S \S\4S# jr+\" SS9 " S$ S%5      5       r,\" SS9 " S& S'5      5       r-S(\!S\.4S) jr/S \S\0\\1\\.   \!\2\!   4   4   4S* jr3S+\0\\1\\.   \!\2\	RJ                     4   4   S,\0\\1\\.   \!\2\	RJ                     4   4   S\0\\-4   4S- jr4g).    N)Sequence)	dataclass)CallableOptional)compute_sqnr)ExportedProgram)GraphModuleNode)
NodeSource)
functional   )bfs_trace_with_node_processnumeric_debug_handlecustom	from_nodeT)frozenc                   .    \ rS rSr% Sr\\S'   \\S'   Srg)NodeSourceDebugInfo   z
Contains node source information for locating the node in the original graph.
This replaces the numeric debug handle approach with direct node source info.
namegraph_id N)	__name__
__module____qualname____firstlineno____doc__str__annotations__int__static_attributes__r       e/home/james-whalen/.local/lib/python3.13/site-packages/torchao/quantization/pt2e/_numeric_debugger.pyr   r      s     I Mr"   r   epreturnc                 0  ^ [        U [        5      (       d  [        S[        [        5       35      eSmS[        R
                  R                  SS4U4S jjnS[        R
                  R                  SS4U4S jjn[        X5        TS-  m[        X5        g)	a:  
Attach numeric_debug_handle_id for all nodes in the graph module of the given
ExportedProgram, like conv2d, squeeze, conv1d, etc, except for placeholder.
Notice that nodes like getattr are out of scope since they are not in the graph.

The graph nodes of input exported program are modified inplace.

Here's an example of using debug handle quantize flow::

    ep = torch.export.export(eager_model, example_inputs)
    generate_numeric_debug_handle(ep)

    m = ep.module()
    quantizer = XNNPACKQuantizer()
    m = prepare_pt2e(m, quantizer)
    m = convert_pt2e(m)
z'Expected ep to be ExportedProgram, got r   noder%   Nc                    > [        TU R                  R                  [        0 5      R                  [        S5      5      mg )Nr   )maxmetaget
CUSTOM_KEYNUMERIC_DEBUG_HANDLE_KEYr'   	unique_ids    r#   _find_max_id3generate_numeric_debug_handle.<locals>._find_max_idG   s0    tyy}}Z4889QSTU
	r"   c                    > [         U R                  ;  a  0 U R                  [         '   [        U R                  [            ;  a   TU R                  [            [        '   TS-  mg g )Nr   )r,   r*   r-   r.   s    r#   _assign_debug_handle;generate_numeric_debug_handle.<locals>._assign_debug_handleM   sQ    TYY&$&DIIj!#499Z+@@>GDIIj!":;NI Ar"   r   )
isinstancer   
ValueErrortypetorchfxr
   r   )r$   r0   r3   r/   s      @r#   generate_numeric_debug_handler:   ,   s    ( b/**5d?6K5LM
 	
 I
588== 
T 
588== T   1NI  9r"   r'   c                     S[         SS4S jnS[         S[        4S jnU R                  S:X  d  U R                  S:X  a  gU" U 5      (       d  gU" U 5      n[        UR                  UR
                  S	9$ )
a  
Extract node source debug info from a node, or return None if the node
does not need to be traced.

Returns NodeSourceDebugInfo containing the name and graph_id from the
node's greatest ancestor node source, or None if the node is not in
the original graph.
r'   r%   r   c                     U R                   R                  [        5      S   n[        UR                  5      S:  a*  UR                  S   n[        UR                  5      S:  a  M*  U$ )Nr   )r*   r+   FROM_NODE_KEYlenr   )r'   node_sources     r#   "_get_greatest_ancestor_node_sourceK_extract_node_source_debug_info.<locals>._get_greatest_ancestor_node_sourcel   sY    iimmM226+''(1,%//3K +''(1, r"   c                     [         U R                  ;  d  U R                  [            c  gU R                  [            S   R                  S:X  a  gg)NFr=   z!ExportedProgram.module().unlift()T)r>   r*   	pass_name)r'   s    r#   _is_node_in_original_graphC_extract_node_source_debug_info.<locals>._is_node_in_original_grapht   sJ    		)TYY}-E-M  IIm$R(2223 r"   placeholderoutputN)r   r   )r
   boolopr   r   r   )r'   rA   rE   greatest_ancestor_node_sources       r#   _extract_node_source_debug_inforL   b   s|     ,  $   ww-477h#6%d++$Ft$L!*//.77 r"   xc                    S n[        U [        R                  5      (       a  U R                  5       nU$ [        U [        [
        45      (       a-  [        U 5      " U  Vs/ s H  n[        U5      PM     sn5      nU$ [        U [        5      (       a0  U R                  5        VVs0 s H  u  p2U[        U5      _M     nnnU$ U nU$ s  snf s  snnf N)
r5   r8   Tensordetachlisttupler7   _detachdictitems)rM   detachedeks       r#   rT   rT      s    H!U\\""88: O 
Ae}	%	%721GAJ23
 O	 
At		./ggi8idaAwqzMi8 O O 38s   C$C
yc                 x   [        U [        R                  5      (       a8  [        U[        R                  5      (       a  U R                  UR                  :H  $ [        U [        [
        45      (       a6  [        U[        [
        45      (       a  [        S [        X5       5       5      $ [        U [        5      (       aF  [        U[        5      (       a1  SnU  H'  nU=(       a    X1;   =(       a    [        X   X   5      nM)     U$ [        R                  SX5        [        U 5      [        U5      :H  =(       a    X:H  $ )Nc              3   <   #    U  H  u  p[        X5      v   M     g 7frO   )_tensor_shape_equals).0e1e2s      r#   	<genexpr>'_tensor_shape_equals.<locals>.<genexpr>   s     HiFB'//is   Tz4Comparing non Tensors: %s and %s, they must be equal)r5   r8   rP   shaperR   rS   allziprU   r]   logdebugr7   )rM   rZ   	all_equalrY   s       r#   r]   r]      s    !U\\""z!U\\'B'Bww!''!!	Ae}	%	%*Qu*F*FHc!iHHH	At		At!4!4	A!SafS2FqtQT2RI 		H!OAw$q'!,af,r"   lossc                    [        U[        R                  5      (       ab  [        U[        R                  5      (       aC  U " UR                  [        R                  5      UR                  [        R                  5      5      $ [        U[
        [        45      (       aT  [        U[
        [        45      (       a9  [        U5      " [        X5       VVs/ s H  u  p4[        XU5      PM     snn5      $ [        U[        5      (       aG  [        U[        5      (       a2  UR                  5        VVs0 s H  u  pVU[        XX%   5      _M     snn$ gs  snnf s  snnf )a	  The returned loss will have the same structure as `x` and `y`, e.g.
if both are Tensor, we'll return a Tensor
if both are list, we'll return a list of Tensors
if both are dict, we'll return a dict with the same key, and value being the loss between the
two Tensors
N)r5   r8   rP   tofloat32rR   rS   r7   re   _loss_fnrU   rV   )ri   rM   rZ   r_   r`   rY   rX   s          r#   rm   rm      s     !U\\""z!U\\'B'BADD'emm)<==	Ae}	%	%*Qu*F*FAwSYGY622.YGHH	At		At!4!478wwyAytq8DQT**yAA	 HAs   E
.Ec            	       x   ^  \ rS rSrSrSr  SS\S\\   S\\	   SS4U 4S	 jjjr
S
\	S\	4S jrS\4S jrSrU =r$ )OutputLogger   z
Base class for capturing output values for nodes in a GraphModule, it only captures
Tensor output currently, but we can extend it to work for other types of inputs later if needed
TN
debug_info	node_namenn_module_stackr%   c                 T   > [         TU ]  5         X l        X0l        Xl        / U l        g rO   )super__init__rr   rs   rq   stats)selfrq   rr   rs   	__class__s       r#   rv   OutputLogger.__init__   s'     	".$#%
r"   rM   c                 N    U R                   R                  [        U5      5        U$ rO   )rw   appendrT   )rx   rM   s     r#   forwardOutputLogger.forward   s    

'!*%r"   c                 <    SU R                    SU R                   S3$ )Nzdebug_info=z, node_name=zF, nn_module_stack={self.nn_module_stack}, num_stats={len(self.stats)}))rq   rr   rx   s    r#   __extra_repr__OutputLogger.__extra_repr__   s+    $//*,t~~6F GS S	
r"   )rq   rs   rr   rw   )NN)r   r   r   r   r   
_is_impurer   r   r   objectrv   r}   r   r!   __classcell__)ry   s   @r#   ro   ro      st     J
 $(,0	
&'
& C=
& "&)	
&
 

& 
& F 
 
 
r"   ro   modelrq   c                    SSK Jn  U R                  R                  U5         U" UR                   S35      nU" U 5      n[        U U[        X!R                  UR                  R                  S5      5      5        U R                  R                  XQ40 5      nSSS5        [        UR                  R                  5       5      nU H  nUWL a  M
  UR                  X5        M     W$ ! , (       d  f       NT= f)zFor a given node, adds an OutputLogger that observes the output of that node,
and all its users use the OutputLogger output instead.
The OutputLogger will contain the debug_info which can be used to compare
graphs after transformsr   )get_new_attr_name_with_prefix_loggerrs   N)torchao.quantization.pt2e.utilsr   graphinserting_afterr   setattrro   r*   r+   call_modulerR   userskeysreplace_input_with)	r   r'   rq   r   get_new_attr_namelogger_namelogger_node
orig_users	user_nodes	            r#   _insert_loggerr      s     N 
	$	$T	*9TYYKw:OP'.YY		>O0PQ	

 kk--k7BG 
+ djjoo'(J	#$$T7  
 ! 
+	*s   A5C%%
C3c                     [         R                  " U 5      n U R                  R                   H  n[	        U5      =nc  M  [        XU5        M!     U R                  5         U $ )zAdd output loggers to unlifted node

Args:
    model (GraphModule): original model
Returns:
    a model with output loggers for all unlifted nodes
)copydeepcopyr   nodesrL   r   	recompile)r   nrq   s      r#   "prepare_for_propagation_comparisonr     sQ     MM% E[[9!<<JI5Z0  
OOLr"   c                       \ rS rSr% \R
                  \S'   \R
                  \S'   \S\4S j5       r	\S\4S j5       r
S\\R
                  \R
                  /\R
                  4   S\4S jrS\4S	 jrSS jrSrg
)QuantizationComparisonResulti  actualrefr%   c                 @    U R                  [        R                  5      $ rO   )ri   Fmse_lossr   s    r#   r   %QuantizationComparisonResult.mse_loss  s    yy$$r"   c                 ,    U R                  [        5      $ rO   )ri   r   r   s    r#   sqnr!QuantizationComparisonResult.sqnr  s    yy&&r"   loss_functionc                 B    [        XR                  U R                  5      $ rO   )rm   r   r   )rx   r   s     r#   ri   !QuantizationComparisonResult.loss   s     {{DHH==r"   c                 <    SU R                    SU R                   S3$ )Nz&QuantizationComparisonResult(mse_loss=z, sqnr=))r   r   r   s    r#   __repr__%QuantizationComparisonResult.__repr__%  s$     5T]]O7499+UVW	
r"   Nc                    [        U R                  [        R                  [        [
        [        45      (       d  [        SU R                   35      e[        U R                  [        R                  [        [
        [        45      (       d  [        SU R                   35      e[        U R                  U R                  5      (       d%  [        SU R                   SU R                   35      eg )Nz@`self.actual` value must be a Tensor, list, tuple or dict, got: z=`self.ref` value must be a Tensor, list, tuple or dict, got: z2Cannot compare tensors with different shapes: ref=z vs actual=)
r5   r   r8   rP   rR   rS   rU   r6   r   r]   r   s    r#   __post_init__*QuantizationComparisonResult.__post_init__,  s    $++dE4'HIIRSWS^S^R_`  $((U\\4$EFFOPTPXPXzZ  $DHHdkk::DTXXJkZ^ZeZeYfg  ;r"   r   )r%   N)r   r   r   r   r8   rP   r   propertyr   r   r   r   ri   r   r   r   r!   r   r"   r#   r   r     s    LL	%& % % 'f ' '>%u||U\\&BELL&PQ>	>

# 
r"   r   c                   X    \ rS rSr% \\S'   \\S'   \\S'   \\S'   \\S'   \\   \S'   Sr	g	)
NodeAccuracySummaryi=  rq   actual_node_nameactual_module_stackref_node_nameref_module_stackresultsr   N)
r   r   r   r   r   r   r   r   r   r!   r   r"   r#   r   r   =  s,    ##233r"   r   module_stackc                     [        U [        5      (       d  [        U 5      $ [        U R	                  5       5      n[        U5      S:  a  US   S   n[        U5      $ [        U 5      $ )zdSimplifies the stack from ("mod", "mod.foo", "mod.foo.0", "mod.foo.0.linear")
to "mod.foo.0.linear"
r   r=   )r5   rU   r   rR   valuesr?   )r   module_values_listowning_modules      r#   _module_stack_to_strr   G  sc     lD))<  l1134
"*2.q1=!!<  r"   c                     0 nU R                  5        Hf  u  p#[        U[        5      (       d  M  [        UR                  5      S:  d  M7  UR
                  UR                  UR                  4XR                  '   Mh     U$ )al  For a given model, extract the tensors stats and related information for each debug info.
The reason we have a list of object, instead of Tensor is because the output of node may not be
a Tensor, it could be (nested) list, tuple or dict as well.

Returns:
    A dict is keyed by the NodeSourceDebugInfo and the values are a list of object recorded
    in loggers

r   )named_childrenr5   ro   r?   rw   rr   rs   rq   )r   handles_modules       r#   extract_results_from_loggersr   U  sm     VXG))+	fl++FLL0AA0E  &&*G%%& , Nr"   ref_resultsactual_resultsc           
         0 nU R                  5        H  u  nu  pEnX1;  a  [        R                  SU5        M&  X   u  pxn	 [        X5       V
Vs/ s H  u  p[	        XS9PM     nn
n[        UU=(       d    S[        U5      U=(       d    S[        U5      US9X#'   M     U$ s  snn
f ! [
         a  n[        SU SU SU 35      UeSnAff = f)	a  Given two dict mapping from `NodeSourceDebugInfo` to list of tensors
return a map from `NodeSourceDebugInfo` to `NodeAccuracySummary` that contains
comparison information like SQNR, MSE etc.

Args:
    ref_results (Dict[NodeSourceDebugInfo, Tuple[str, object, List[torch.Tensor]]]): reference results for each debug info
    actual_results (Dict[NodeSourceDebugInfo, Tuple[str, object, List[torch.Tensor]]]): actual results for each debug info

Returns:
    Dict[NodeSourceDebugInfo, NodeAccuracySummary]
zQCannot compare for debug info %s because it wasn't found in the transformed model)r   r   zFor debug_info=z from ref node z and actual node N )rq   r   r   r   r   r   )	rV   rf   rg   re   r   	Exceptionr6   r   r   )r   r   comparisonsrq   ref_name	ref_stack	ref_statsactual_nameactual_stackactual_statsabr   rX   s                 r#   compare_resultsr   n  s   & K8C8I8I8K4
4X)+IIc 2@2L/<
	  88DA -A=8   #6!(.B 4\ B".b1)<#
) 9L: )  	 !*_XJFWXcWde	s*   B'B!$B'!B''
C1CC)5r   loggingcollections.abcr   dataclassesr   typingr   r   r8   torch.ao.ns.fx.utilsr   torch.exportr   torch.fxr	   r
   torch.fx.tracebackr   torch.nnr   r   graph_utilsr   r-   r,   r>   	getLoggerr   rf   r   r:   rL   r   rT   rI   r]   rP   rm   nnModulero   r   r   r   r   r   r   rU   rS   rR   r   r   r   r"   r#   <module>r      sN     $ ! %  - ( & ) $ 41 
! $
 
 
3:o 3:$ 3:l.$ .8<O3P .b
v 
& 
-F -v -$ -
ELL%,,/=
>CINT&
588?? 
@"0C	>k k $ $& & &R $4 4 4!v !# !	
uXc]FDL%HI
IJ21U8C=&$u||:L#LMM1 U8C=&$u||:L#LMM	1 

2
231r"   