
    h"                        S r SSKrSSKrSSKrSSKrSSKJr  SSKJrJ	r	  SSK
JrJrJrJr  SSKrSSKJs  Jr  SSKJr  SSKJrJr  SSKJrJrJr  SS	KJr  SS
KJ r   SSK!J"r"  SSK#J$r$  / SQr%S r&S r'S\S\(S\4S jr)S r*S r+S\RX                  S\RX                  S\-4S jr.S\/S\/S\-S\R`                  S\-S\1\/\/4   4S jr2S r3S  r4S!\R"                  Rj                  S"\R"                  Rj                  S\\(   4S# jr6S$\S%\7\(\4   S\7\(\4   4S& jr8S$\SS'4S( jr9S)\S%\7\(\4   SS'4S* jr:S\/S\/SS4S+ jr;\Rx                  4S\RX                  S\RX                  S\/S\/S\R`                  S,\RX                  S\-S-\Rz                  S\1\RX                  \RX                  4   4S. jjr>S$\S\/4S/ jr?S0\R"                  Rj                  S1\1\S24   S\7\(\1\S24   4   4S3 jr@S4\(S\4S5 jrA SaS6\R"                  Rj                  S7\S4\(S8\S9\\R                     S\4S: jjrC\R                  R                  R                  R                  \R                  R                  R                  R                  \R                  R                  R                  R                  /rJ\R                  R                  R                  R                  \R                  R                  R                  R                  \R                  R                  R                  R                  /rMS;\R                  R8                  S<\R                  R8                  S\-4S= jrOS> rPS? rQS@\4SA jrRSB\4SC jrSSB\4SD jrTSB\4SE jrUSF\4SG jrVSB\4SH jrW SbSI\SJ\SK\\   SL\SM\SN\-SS4SO jjrXSM\SS4SP jrYS0\S\7\(\1\(\Z4   4   4SQ jr[ SbSR\S1\1\S24   SS\-S\4ST jjr\SU\SS4SV jr]SW r^  ScSX\R                  R6                  SY\-SZ\\_\      4S[ jjr`  SdSX\R                  R6                  SZ\\_\      S\\\7\\a\/\-\R`                  4   \/4      4S] jjrbS0\4S^ jrcS@\4S_ jrdS@\R                  R8                  S\_\R                  R8                     4S` jreg)ez?
Utils shared by different modes of quantization (eager/graph)
    N)OrderedDict)getfullargspec	signature)AnyCallableOptionalUnion)quantized_decomposed_lib)_assign_attr	_AttrKind)GraphGraphModuleNode)fuse_conv_bn_weights)is_parametrized)LeafSpec)_assert_and_get_unique_device)is_per_tensoris_per_channelgetattr_from_fqnget_qparam_dictcheck_min_max_validcalculate_qmin_qmax)has_no_children_ignoring_parametrizationsget_fqn_to_example_inputsto_underlying_dtypedetermine_qparamsvalidate_qmin_qmaxget_new_attr_name_with_prefixcreate_getattr_from_value"_get_aten_graph_module_for_pattern_is_conv_node_is_conv_transpose_node_is_sym_size_node_filter_sym_size_usersc                 \    U [         R                  :H  =(       d    U [         R                  :H  $ N)torchper_tensor_affineper_tensor_symmetricqschemes    Y/home/james-whalen/.local/lib/python3.13/site-packages/torchao/quantization/pt2e/utils.pyr   r   :   s#    e---VE<V<V1VV    c                 f    U [         R                  [         R                  [         R                  4;   $ r'   )r(   per_channel_affine per_channel_affine_float_qparamsper_channel_symmetricr+   s    r-   r   r   >   s/      ..##  r.   objfqnreturnc                 X    [         R                  " [        UR                  S5      U 5      $ )zG
Given an obj and a fqn such as "foo.bar.baz", returns gm.foo.bar.baz.
.)	functoolsreducegetattrsplit)r3   r4   s     r-   r   r   F   s!     GSYYs^S99r.   c                    [         R                  [         R                  [         R                  [         R                  [         R
                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  [         R                  0nX;   d   S[        U 5      -   5       eX   $ )NzUnsupported dtype: )r(   quint8uint8qint8int8qint32int32quint4x2quint2x4uint16int16float8_e5m2float8_e4m3fnstr)qdtypeDTYPE_MAPPINGs     r-   r   r   M   s    ekkUZZekkU[[

EJJellU[[U[[5,,U00M "G$9CK$GG"  r.   c                 &   SSK Jn  [        U SS 5      nU R                  nX#S.nU(       a  [	        X5      (       a  S US.$ [        U5      (       a  [        R                  nOR[        U5      (       a4  U[        R                  :X  a  [        R                  nU R                  US'   O[        SU 35      eX$S'   U R                  5       u  pVXTS'   XdS'   [        U S	5      (       a  U R                  US	'   [        U S
5      (       a  U R                   US
'   U$ )Nr   )PlaceholderObserverr,   )r,   dtypeaxiszUnrecognized qscheme: scale
zero_point	quant_min	quant_max)"torchao.quantization.pt2e.observerrM   r:   rN   
isinstancer   r(   r)   r   r2   r0   ch_axisRuntimeErrorcalculate_qparamshasattrrR   rS   )observer_or_fake_quantrM   r,   rN   qparamsrP   rQ   s          r-   r   r   `   s
   F,i>G"((E!2Gj!7MM%00W))		 	  e111..G0883G9=>> !I.@@BEG&L%{335??%{335??Nr.   min_valmax_valc                    U R                  5       S:X  d  UR                  5       S:X  a  [        R                  " S5        gU R                  5       S:X  d  UR                  5       S:X  aI  U [	        S5      :X  a&  U[	        S5      :X  a  [        R                  " S5        gX::  d   SU  SU 35       e g[
        R                  " X:*  5      (       d   SU  SU 35       eg)	zChecks if the given minimum and maximum values are valid, meaning that
they exist and the min value is less than the max value.
r   zMmust run observer before calling calculate_qparams. Returning default values.Finfz-infzmin z should be less than max T)numelwarningswarndimfloatr(   all)r\   r]   s     r-   r   r      s     }}!w}}!3*	
 {{}W[[]a/eEl"w%-'?MM.
 !UT'2KG9#UU! 	 yy+,, 	
7)4WI>	
, r.   rR   rS   has_customized_qrangerN   reduce_rangec                 r   U(       a  U[         R                  [         R                  4;   a  Su  pVOSu  pVXpUb  Ub  UUpeXe-
  S-   n	U[         R                  [         R                  4;   a  SU	s=:  a  S::  d   S5       e   S5       eO@U[         R                  [         R                  4;   a  SU	s=:  a  S::  d   S5       e   S5       eU(       a	  U S	-  US	-  pX4$ U[         R                  [         R                  4;   a  U(       a  S
u  pX4$ Su  p X4$ U[         R
                  [         R                  4;   a  U(       a  Su  pX4$ Su  p X4$ U[         R                  [         R                  4;   a  Su  pX4$ U[         R                  4;   a  Su  pX4$ U[         R                  4;   a  Su  pX4$ Su  pX4$ )zlCalculates actual qmin and qmax based on the quantization range,
observer datatype and if range is reduced.
)r   l    )r         r      zRquantization range should be positive and not exceed the maximum bit range (=256).l        zYquantization range should be positive and not exceed the maximum bit range (=4294967296).   )i?   )i   )r   rn   )i   i)r   i  )i i  )r      )	r(   rA   rB   r?   r@   r=   r>   rE   rF   )
rR   rS   rf   rN   rg   initial_quant_mininitial_quant_maxcustom_quant_mincustom_quant_max
qrange_lens
             r-   r   r      s     U\\5;;//3?00390 .7*',<,H    1
 ':Q>
U[[%**--z(S( d( d( u||U[[11z*U* k* k* #,>9>y* % U[[%**--'.$	   (1$	9  u||U[[11'-$	  (.$	9  u||U[[11#< I  u||n$#/ I
 	 u{{m##6 I  $) Ir.   c                 f    U R                  SS5      n[        U5      S:X  a  SUS   4$ US   US   4$ )z$
Turn 'foo.bar' into ['foo', 'bar']
r7   rj    r   )rsplitlen)targetrs     r-   _parent_namer{      s>     	c1A
1v{1Q4xtQqTzr.   c                     [        U R                  5      S:X  a  g[        U 5      (       a.  [        U R                  5      S:H  =(       a    SU R                  ;   $ g)z
Checks if module._modules is empty or
if module is a parametrization, checks that module._modules only has
the 'parametrizations' module
r   Trj   parametrizationsF)rx   _modulesr   )modules    r-   r   r      sI     6??q 		 	 6??#q(R-?6??-RRr.   root	submodulec                 F    U R                  5        H  u  p#XL d  M  Us  $    g)a.  Get the path (fully qualified name) of a submodule

Example::

>> class M(torch.nn.Module):
       def __init__(self) -> None:
           self.linear = torch.nn.Linear(5, 5)
       def forward(self, x):
           return self.linear(x)

>> m = M()
>> l = m.linear
>> _get_path_of_module(m, l)
"linear"
N)named_modules)r   r   nps       r-   _get_path_of_moduler      s(    $ ""$>H % r.   flocc                     UR                  5        VVs0 s H"  u  p#U[        U 5      R                  ;   d  M   X#_M$     snn$ s  snnf )zGet local keyword arguments

Example::

>> def f(self, a, b=9):
       pass
>> loc = {"a": 6, "c": 7}
>> _get_signature_locals(f, loc)
{"a": 6}
)itemsr   
parameters)r   r   kvs       r-   _get_signature_localsr     s9     !YY[I[TQA11H1H,HDAD[IIIs
   AAzOrderedDict[str, Any]c                 B   0 n[        U 5      R                  R                  5        Hl  u  p#UR                  UR                  La  UR                  X'   M.  UR
                  UR                  L a  SX'   MM  UR
                  UR                  L d  Mh  0 X'   Mn     [        U5      $ )zGet all default keyword arguments from function signature

Example::

>> def f(self, a, b=9):
       pass
>> _get_default_kwargs(f)
{"b": 9}
 )	r   r   r   defaultemptykindVAR_POSITIONALVAR_KEYWORDr   )r   kwargsnameparams       r-   _get_default_kwargsr     s     F |..446==+ ==FLZZ5///FLZZ5,,,FL 7 vr.   funcc                     [        U 5      n[        X5      nUR                  5       nUR                  5        H  u  pVXT;   d  M  XdU'   M     U$ )a  Given a function and local function arguments, normalize the keyword
arguments by filling in default arguments from function signature

Example::

>> def f(self, key1=3, key2=3):
       pass
>> loc = {"key2": 6}
>> _normalize_kwargs(f, loc)
{"key1": 3, "key2": 6}
)r   r   copyr   )r   r   default_kwargslocal_kwargsnormalized_kwargsattrvals          r-   _normalize_kwargsr   3  sR     ).N(3L&++-!'')	$&)d# * r.   c                 T    U Ss=::  a  U::  d   S5       e   S5       eX:  d   S5       eg)a=  Validates that the user-specified quantization range is properly initialized
and within the given bound supported by the observer dtype.

To accommodate lower-bit quantization with respect to the existing torch.qint8 and
torch.quint8 datatypes, the user can choose to use dynamic quantization range by passing
in a tuple of initial qmin and qmax values. One use case is these customized qmin and qmax
values are used to calculate static estimates of the scale and zero point for aggressive lower-bit
fake quantization. These estimates are compared against parameters learned through backpropagation.
The related literatures for scale and zero point via backpropagation are as follows:

Learned Step Size Quantization: https://openreview.net/pdf?id=rkgO66VKDS
Trained Quantization Thresholds: https://arxiv.org/pdf/1903.08066.pdf
r   z1Used-specified quantization range must include 0.zKqmin must be strictly less than qmax for user-specified quantization range.Nr   )rR   rS   s     r-   r   r   I  sI      &Y& ;& ;&   U r.   epsr,   c                    [        X5      (       dT  [        R                  " S/U R                  R                  S9[        R                  " S/U R                  R                  S94$ [        R
                  " U [        R                  " U 5      5      n[        R                  " U[        R                  " U5      5      n	UR                  n
[        R                  " UR                  5       [        R                  U
S9n[        R                  " UR                  5       [        R                  U
S9nUR                  U
5      nU[        R                  :X  d  U[        R                  :X  a  [        R                  " U* U	5      n	U	[!        X2-
  5      S-  -  n[        R                  " X5      nU[        R"                  [        R$                  4;   aM  U(       a&  UR'                  UR                  5       X#-   S-  5      nOUR'                  UR                  5       S5      nOU[        R(                  :X  aI  X-
  [!        X2-
  5      -  n[        R*                  " X:  U[        R,                  " U5      5      nSU -  U-  nOwX-
  [!        X2-
  5      -  n[        R                  " X5      nU[        R.                  " X-  5      R                  [        R0                  5      -
  n[        R2                  " XU5      n[5        UR6                  5      S:X  a*  [        R                  " [!        U5      /UR8                  U
S9n[5        UR6                  5      S:X  ah  [        R                  " [1        U5      /UR8                  U
S9nU[        R(                  :X  a*  [        R                  " [!        U5      /UR8                  U
S9nUR                  [        R                  5      UR                  [        R                  5      4$ )aD  Calculates the quantization parameters, given min and max
value tensors. Works for both per tensor and per channel cases

Args:
    min_val: Minimum values per channel
    max_val: Maximum values per channel

Returns:
    scales: Scales tensor of shape (#channels,)
    zero_points: Zero points tensor of shape (#channels,)
g      ?devicer   )rN   r   rl      )r   r(   tensorr   typemin
zeros_likemaxonessizedoublezerosint64tor*   r2   rd   r>   r=   new_fullr1   where	ones_likeroundintclamprx   shaperN   )r\   r]   rR   rS   rN   r   rf   r,   min_val_negmax_val_posr   rP   rQ   s                r-   r   r   e  s   * w00||SE'..*=*=>C++A
 
 	
 ))GU%5%5g%>?K))GU%5%5g%>?KFJJ{'')fME[--/u{{6RJ
&&.C%,,,5;V;V0Viik:uY%:;a?@		%%U[[%,,//$'00OO%	(=!'C
 (001BCH
	E::	:"eI,A&BBEK0FG
 '\E)
*eI4I.JJ		%%[-@!A!D!DUYY!OO
[[	B
 5;;1eEl^5;;vN
:!\\_Z%5%5f

 e<<<z"#:+;+;FJ 88ELL!:==#===r.   c                 >    [        [        U 5      R                  5      $ )zGet number of positional args for a function

Example::

>> def f(self, key1=3, key2=3):
       pass
>> _get_num_pos_args(f)
3
)rx   r   args)r   s    r-   _get_num_pos_argsr     s     ~a %%&&r.   modelexample_inputs.c                 <  ^^^ U m0 mUUU4S jn[         R                  R                  R                  mU[         R                  R                  l         U " U6   T[         R                  R                  l        T$ ! T[         R                  R                  l        f = f)a  Given a model and its example inputs, return a dictionary from
fully qualified name of submodules to example_inputs for that submodule,
e.g. {"linear1": (tensor1,), "linear2": (tensor2,), "sub": (tensor3,),
      "sub.linear1": (tensor4,), ...}

Used to make quantizing submodules easier now that FX Graph Mode Quantization requires
example inputs.

Also works for keyword arguments with default values, we would flatten keyword
arguments as positional arguments and fill in the missing keyword args with default
values, e.g. if we have a forward function:
def forward(self, x, key1=3, key2=3):
    ...

and we call it with self.submodule(x, key2=6)
we'll get example_inputs: (x, 3, 6)

user can also override `key1` with positional arguments as well:
for self.submodule(x, 5, key2=6)
we'll get: (x, 5, 6)

variable positional arguments and variable positional keyword arguments in forward
function are not supported currently, so please make sure no submodules is using
them.
c                   > [        U5      R                  5       n[        U R                  U5      n[	        U R                  5      S-
  nU[        U5      -
  nU(       a+  U(       a$  UR                  SS9  US-  nU(       a	  U(       a  M$  UR                  UR                  5       5        [        U5      n[        TU 5      nUb  UT	U'   T
" U /UQ70 UD6$ )Nrj   F)last)listr   r   forwardr   rx   popitemextendvaluestupler   )selfr   r   submodule_example_inputsr   num_args
num_to_popsubmodule_example_inputs_tupler4   fqn_to_example_inputsorig_module_callr   s            r-   _patched_module_call7get_fqn_to_example_inputs.<locals>._patched_module_call  s    #':??#4 -dllFC$T\\2Q6$< ==
.%%5%1!OJ .. 	!''(9(@(@(BC)./G)H&!$-?)G!#&6t6v66r.   )r(   nnModule__call__)r   r   r   r   r   r   s      @@@r-   r   r     st    8 D7  xx//3EHHOO4~ $4    $4 s   A: :!Bprefixc                 p   ^  T R                  SS5      m S[        R                  R                  4U 4S jjnU$ )Nr7   _r   c                    > S[         4U4S jjnSnU" U5      n[        X5      (       a  US-  nU" U5      n[        X5      (       a  M  U$ )Nic                     > T[        U 5      -   $ r'   )rI   )r   r   s    r-   get_attr_nameOget_new_attr_name_with_prefix.<locals>.get_new_attr_name.<locals>.get_attr_name  s    CF?"r.   r   rj   )r   rY   )r   r   r   	attr_namer   s       r-   get_new_attr_name8get_new_attr_name_with_prefix.<locals>.get_new_attr_name  sS    	#S 	# !!$	f((FA%a(I f(( r.   )replacer(   r   r   )r   r   s   ` r-   r   r     s/    ^^C%F	%((// 	 r.   r   graphvaluer   c                 0   [        U5      nU" U 5      nUc  [        U 5      n[        U[        R                  5      (       a  UR                  5       R                  5       O[        R                  " X4S9nU R                  Xg5        UR                  SU5      nU$ )z
Given a value of any type, creates a getattr node corresponding to the value and
registers the value as a buffer to the module.
r   get_attr)
r   r   rU   r(   Tensordetachcloner   register_buffercreate_node)	r   r   r   r   r   r   r   	new_value	attr_nodes	            r-   r    r      s     6f=!&)I~.v6 eU\\** 	\\%/ 
 90!!*i8Ir.   sourcedestc                    [         [        -   nUR                  [        R                  R
                  R                  R                  5        UR                  U;   ar  [        UR                  S   [        R                  R                  5      (       d  [        SUR                  S    35      eUR                  S   nUR                  U;   a  Mr  X:H  $ )z
Assuming dest is one of the ops inserted by quant workflow, this function
finds if source and dest are connected. Assumption is that only quant workflow
inserted ops exist between source and dest
r   z=expected arg[0] of quant workflow ops to be a node but found )_QUANTIZE_OPS_DEQUANTIZE_OPSappendr(   opsquantized_decomposedchoose_qparamsr   ry   rU   r   fxr   
ValueError)r   r   quant_workflow_opss      r-   _is_connectedr   7  s     '8eii<<KKRRS
+++
+$))A,66OPTPYPYZ[P\~^  yy| +++
+ >r.   c           	      
   U c  g U R                   S:X  d   eU R                  R                  S5      nUn[        U5       H@  u  pE[	        X55      (       d   [        SSR                  US U 5       35      e[        X55      nMB     U$ )Nr   r7   z#Node referenced nonexistent target )opry   r;   	enumeraterY   rW   joinr:   )nodemtarget_atomsattr_itrr   atoms         r-   _get_tensor_constant_from_noder  H  s    |77j   ;;$$S)LH\*x&&5chh|BQ?O6P5QR  8* + Or.   c                 0   / n[        U5       H  u  pEUR                  U;   a  UR                  XR                     5        M4  UR                  (       d$  U[	        U 5      :  a  UR                  X   5        Mi  UR                  UR
                  5        M     U$ r'   )r   r   r   
kwarg_onlyrx   default_value)	orig_argsorig_kwargsargs_schemaall_argsr   schemas         r-   _get_all_argumentsr  W  sr    H{+	;;+%OOK45""q3y>'9OOIL)OOF001 , Or.   r   c                    [         R                  R                  R                  R                  [         R                  R                  R
                  R                  [         R                  R                  R                  R                  [         R                  R                  R                  R                  /nU R                  U;   $ )zM
Return True if the given node refers to an aten batch norm op QAT supports.
)	r(   r   aten
batch_normr   _native_batch_norm_legitcudnn_batch_normmiopen_batch_normry   )r   supported_opss     r-   %_is_supported_batch_norm_for_trainingr  c  sz    
 			!!))		//77 			''//		((00M ;;-''r.   r   c                 j   U R                   S:H  =(       Ga    U R                  [        R                  R                  R
                  R                  [        R                  R                  R
                  R                  [        R                  R                  R                  R                  [        R                  R                  R                  R                  [        R                  R                  R                  R                  [        R                  R                  R                  R                  4;   $ )z4
Return whether the node refers to an aten conv op.
call_function)
r   ry   r(   r   r  conv1dr   paddingconv2dconv3dr   s    r-   r"   r"   s  s     44?"  qxx		%%		%%		%%		%%		%%		%%4 ( r.   c                 ,   U R                   S:H  =(       a    U R                  [        R                  R                  R
                  [        R                  R                  R
                  R                  [        R                  R                  R                  [        R                  R                  R                  R                  [        R                  R                  R                  [        R                  R                  R                  R                  4;   $ )z>
Return whether the node refers to an aten conv_transpose op.
r  )
r   ry   r(   r   r  conv_transpose1dr   conv_transpose2dinputconv_transpose3dr  s    r-   r#   r#     s     44?" qxx		''		''//		''		''--		''		''--4 ( r.   c                 <    [        U 5      =(       d    [        U 5      $ )zF
Return whether the node refers to an aten conv or conv transpose op.
)r"   r#   r  s    r-   _is_conv_or_conv_transpose_noder$    s     96q99r.   conv_fnc                 f    U [         R                  [         R                  [         R                  4;   $ r'   )Fr  r   r"  )r%  s    r-   _is_conv_transpose_fnr(    s&    q))1+=+=q?Q?QRRRr.   c                     [        U 5      =(       d;    U R                  [        R                  R                  R
                  R                  :H  $ r'   )r  ry   r(   r   r  $_native_batch_norm_legit_no_trainingr   r  s    r-   _is_bn_noder+    s5    -a0 	S88uyy~~JJRRRr.   	conv_nodeconv_weight_nodeconv_bias_nodebn_noder  	fake_fusec                    [        X5      n[        X$5      n[        U 5      nUR                  R                  R                  n	[        UR                  UR                  U	5      n
[        U
S   U5      n[        U
S   U5      n[        U
S   U5      n[        U
S   U5      nUR                  [        R                  R                  R                  R                  :X  a  SnO)[        U5      (       a  SnO[        SUR                  5      eX   n[        XgXUXUS9u  nn[!        U R                  5      n[#        U5      S:X  a  UR%                  S 5        U(       aS  [        R&                  R)                  XfR*                  5      [        R&                  R)                  XwR*                  5      nnO[        XgXUXUS9u  nnUR                  n[-        U[.        5      (       d   e[1        UUU[2        R4                  5        Ub2  UR                  n[1        UU[/        U5      [2        R4                  5        OeUS	-   n[1        UUU[2        R4                  5        UR6                  R9                  U 5         UR6                  R;                  U5      nS S S 5        WUS'   [=        U5      U l        UR                  [        R                  R                  R>                  R                  :X  a  URA                  U 5        OgURB                   HW  nURD                  S
:w  d1  UR                  [F        RH                  :w  d  UR                  S   S:w  a  MF  URA                  U 5        MY     UR6                  RK                  5         URL                  (       d6  [#        URB                  5      S:X  a  UR6                  RO                  U5        g g g ! , (       d  f       GN;= f)Nrj   rl               zBN node target is unexpected )	transpose_biasr  r   )(r  r#   ry   _schema	argumentsr  r   r   r(   r   r  r*  r   r  r   r   r   rx   r   r   	Parameterrequires_gradrU   rI   r   r   	PARAMETERr   inserting_beforer   r   r  replace_all_uses_withusersr   operatorgetitemeliminate_dead_code_erased
erase_node)r,  r-  r.  r/  r  r0  conv_wconv_br6  bn_args_schemabn_argsbn_wbn_bbn_rmbn_rveps_arg_indexbn_epsfused_weight
fused_bias	conv_argsweight_attr_namebias_attr_nameget_bias_nodeusers                           r-   fold_bn_weights_into_conv_noderV    s	    ,,<@F+N>F'	2I ^^++55N w~~~NG)'!*a8D)'!*a8D*71:q9E*71:q9E~~LLTTT	.w	7	78'..II#F3fdI L*
 Y^^$I
9~HHv';';<HHv';';< !j
 $8E&$	$
 j
 (..&,,,,q"2I4G4GH!'..ZC$79L9LM)G3ZNI4G4GHWW%%i0GG,,^<M 1 %	!9%IN
 ~~22::: 	%%i0 MMD?*;;("2"2299Q<1$&&y1 " GG!??s7==1Q6	7#  7?[ 10s   *N::
O	c           
          [        S U R                  R                   5       5      nU(       d  g [        5       nU R                  R                   GH  nUR                  S:w  dj  UR
                  [        R                  R                  R                  R                  [        R                  R                  R                  R                  4;  a  M  UnUR                  S   n[        U5      (       d  M  UnUR                  S   n[        UR                  5      S:  a  UR                  S   OS n[        UUUUU Xb;   5        UR!                  U5        GM     U R                  R#                  5         U R%                  5         g )Nc              3   8   #    U  H  n[        U5      v   M     g 7fr'   )r+  ).0r   s     r-   	<genexpr>!_fuse_conv_bn_.<locals>.<genexpr>  s     7AQs   r  r   rj   rl   )anyr   nodessetr   ry   r(   r   r  r*  r   r  r   r$  rx   rV  addrB  	recompile)r  has_bnfused_convs_weight_nodesr   r/  r,  r-  r.  s           r-   _fuse_conv_bn_rc    s(   777F  #uWW]]44?"ahhIINN??GGIINN%%--7
 '
 LLO.q11	$>>!,.1)...AA.E*4&9	
 	!$$%56+ , GG!KKMr.   c                 0   0 nU R                   R                   Hy  nUR                  R                  SS 5      nS[	        S 5      4nU(       a8  [        UR                  5       5      S   nUS   R                  S5      S   US   4nXAUR                  '   M{     U$ )Nnn_module_stackrv   r   r   r7   rj   )	r   r]  metagetr   r   r   r;   r   )r   node_name_to_scoper   re  current_scopebts         r-   _get_node_name_to_scoperk  /  s    68[[&&**%6=T$Z(o,,./3BU[[-b12a59M%2166"  r.   patternis_cudac           	         U(       aK  [        U Vs/ s H4  n[        U[        R                  5      (       a  UR	                  5       OUPM6     sn5      n[        R
                  R                  U UUSS9R                  5       nUR                  R                  5         UR                  5         UR                  R                   H  nUR                  S:X  d  M  UR                  [        R                  R                  R                  R                   :X  d  MS  [#        UR$                  5      S:X  d  Mn  UR                  R'                  U5        M     UR                  R                  5         UR                  5         U$ s  snf )z>
Convert the pattern to an FX graph with decomposed aten ops.
T)strictr  r   )r   rU   r(   r   cudaexportr   r   rB  r`  r]  r   ry   r   r  copy_r   rx   r?  rD  )rl  r   rm  r   xaten_patternr   s          r-   r!   r!   <  s%    EST^Au||44QVVX!;^T
 <<&&	 ' 
 fh  **, ""((GG&uyy~~33;;;DJJ1$))$/ ) **,5 Us   ;E:match_patternc                 P   [         R                  R                  R                  R                  [         R                  R                  R                  [         R                  R                  R
                  R                  [         R                  R                  R
                  [         R                  R                  R                  R                  [         R                  R                  R                  [         R                  R                  R
                  R                  [         R                  R                  R
                  [         R                  R                  R                  R                  [         R                  R                  R                  [         R                  R                  R
                  R                  [         R                  R                  R
                  [         R                  R                  R                  R                  [         R                  R                  R                  [         R                  R                  R                  R                  [         R                  R                  R                  [         R                  R                  R                  R                  [         R                  R                  R                  0	nU R                  R                   H:  nUR                  S:w  a  M  UR                   U;   d  M'  XR                      Ul        M<     g)zRemove .tensor overload for quantize/dequantize ops so that we can
use the match_pattern that we get from torchdynamo export to match the output of convert_pt2e
r  N)r(   r   r   quantize_per_tensorr   dequantize_per_tensorr   tensor2quantize_per_channeldequantize_per_channelr  r   r   r   r]  r   ry   )ru  _MAPr   s      r-   "remove_tensor_overload_for_qdq_opsr}  d  s   
 			&&::BBEIIDbDbDvDv		&&<<DDeiiFdFdFzFz		&&::AA599CaCaCuCu		&&<<CCUYYEcEcEyEy		&&::BBEIIDbDbDvDv		&&<<DDeiiFdFdFzFz		&&;;CCUYYEcEcExEx		&&==EEuyyGeGeG|G|		##UYY^^%9%9
D   &&44?"88tHH~AH	 'r.   c                     [        U [        [        45      (       a  g[        U [        [        45      (       a  [        [        [        U 5      5      $ g)NTF)rU   r   rd   r   r   re   map_is_literal)args    r-   r  r  z  s<    #U|$$#t}%%3{C())r.   gm	merge_dupexclude_literalsc           	      &   SnSn0 nUc  / nU R                   nUR                  S   nU R                  R                   GH  nUR                  S:X  a	  UnUS-  nM  U R                  R                  U5         / n	UR                   H  n
[        U
5      (       a  X;  a  U(       a  X;   a  U	R                  XZ   5        M9  U R                  R                  S[        U5      -   5      nU	R                  U5        UR                  R                  [        5       5        US-  nU(       a  XU
'   M  M  U	R                  U
5        M     [        U	5      n	SSS5        W	Ul        GM!     UR                  5         UR                  5         U $ ! , (       d  f       N<= f)aC  Replace the literals in the graph with placeholder nodes that's created on the fly while we
traverse the graph, so that the literal arguments in the graph can be matched and replaced

To use this, the pattern and replacement graph should have the exact same number of literal args
and they should be used in the exact same order in the pattern and replacement graph.

If the literal arguments are not used in the same order in pattern and replacement graph, please
use `_replace_literals_with_existing_placeholders` instead

Args:
    `gm`: input GraphModule that we'll transform
    `merge_dup`: boolean flag to indicate that if the same literal appears multiple times in
     the graph, whether they should correspond to the same placeholder or not
    `exclude_literals`: a list of literals that will not be replaced with placeholders

Example:

# 1. Original Graph
def pattern(self, x):
    return x + 3

def replacement(self, x):
    return x - 3

example_inputs = (torch.randn(1, 3, 3, 3),)
pattern_gm = _get_aten_graph_module_for_pattern(pattern, example_inputs)
replacement_gm = _get_aten_graph_module_for_pattern(pattern, example_inptus)

# 2. Before calling replace literals we'll see the following graph:
def pattern(self, x):
    return x + 3

def replacement(self, x):
    return x - 3

pattern_gm = _replace_literals_with_new_placeholders(pattern_gm)
replacement_gm = _replace_literals_with_new_placeholders(replacement_gm)

# 3. After replacing literals with new placeholder nodes

def pattern(self, x, new_ph):
    return x + new_ph

def pattern(self, x, new_ph):
    return x - new_ph

Nr   placeholderrj   r  )_in_specchildren_specsr   r]  r   inserting_afterr   r  r   r  rI   r   r   __post_init__)r  r  r  last_phcntliteral_to_phin_spec	args_specr   new_argsr  ph_nodes               r-   '_replace_literals_with_new_placeholdersr    s[   h G
CFHMkkG&&q)I77m#G1HCXX%%g.Hyys##(C S%9 (:;"$(("6"6us3x7G"H 0!0077
Cq$18#. % OOC( ! XH /" 	- 2 I- /.s   5CF
F	literal_to_ph_idxc                    Uc  / nUc  0 nU R                   R                   Vs/ s H  o3R                  S:X  d  M  UPM     nnU R                   R                   H  nUR                  S:w  a  M  / nUR                   Hi  n[	        U[
        5      (       a  [        U5      n[        U5      (       a%  Xa;  a   Xb;   a  X&   nXG   nUR                  U5        MX  UR                  U5        Mk     [        U5      nXSl        M     U $ s  snf )aX	  Replace the literals in the graph with **existing** placeholder nodes, so that the literal arguments
in the graph can be matched and replaced

To use this, all literal args in the graph should be unique and each of them should correspond
to exactly one placeholder node

# 1. Original Graph
def pattern(self, x_i8, scale, zero_point, quant_min, quant_max):
    return torch.dequantize_per_tensor(x_i8, scale, zero_point, quant_min, quant_max)

def replacement(x_i8, scale, zero_point, quant_min, quant_max):
    x_i8 = torch.clamp(x_i8, quant_min, quant_max)
    return ((x_i8.to(torch.float32) - zero_point) * scale).to(dtype=torch.float32)

example_inputs = (
    torch.randn(1, 3, 3, 3),
    1.0,
    0,
    -128,
    127,
)
pattern_gm = _get_aten_graph_module_for_pattern(pattern, example_inputs)
replacement_gm = _get_aten_graph_module_for_pattern(pattern, example_inptus)

# 2. Before calling replace literals we'll see the following graph:
def pattern(self, x_i8, scale, zero_point, quant_min, quant_max):
    # scale/zero_point/quant_min/quant_max are burnt in since they are scalar values
    return torch.dequantize_per_tensor(x_i8, 1.0, 0, -128, 127)

def replacement(x_i8, scale, zero_point, quant_min, quant_max):
    # scale/zero_point/quant_min/quant_max are burnt in since they are scalar values
    x_i8 = torch.clamp(x_i8, -128, 127)
    return ((x_i8.to(torch.float32) - 0) * 1.0).to(dtype=torch.float32)

# Note that literal args appear in different order in pattern and replacement graph, so
# we can't use _replace_literals_with_new_placeholders

literal_to_ph_idx = {1.0: 1, 0: 2, -128: 3, 127: 4}
pattern_gm = _replace_literals_with_existing_placeholders(pattern_gm, literal_to_ph_idx)
replacement_gm = _replace_literals_with_existing_placeholders(replacement_gm, literal_to_ph_idx)

# 3. After replacing literals with existing placeholder nodes

def pattern(self, x_i8, scale, zero_point, quant_min, quant_max):
    # scale/zero_point/quant_min/quant_max are burnt in since they are scalar values
    return torch.dequantize_per_tensor(x_i8, scale, zero_point, quant_min, quant_max)

def replacement(x_i8, scale, zero_point, quant_min, quant_max):
    # scale/zero_point/quant_min/quant_max are burnt in since they are scalar values
    x_i8 = torch.clamp(x_i8, quant_min, quant_max)
    return ((x_i8.to(torch.float32) - zero_point) * scale).to(dtype=torch.float32)
r  r  )	r   r]  r   r   rU   r   r   r  r   )	r  r  r  r   phsr  r  ph_idxr  s	            r-   ,_replace_literals_with_existing_placeholdersr    s    r  HHNN
GNDgg.F4NC
G77o%99C#t$$CjC  /,*/+($  ?	% & I+ Hs
   C?C?c                    ^ SmSS[         4U4S jjjnSS[         4U4S jjjn[        R                  " X5      U l        [        R                  " X 5      U l        U $ )z
Disallow calling `model.train()` or `model.eval()` on the given GraphModule.
This is useful for exported models, where these methods don't actually behave as expected.
ap  
        Calling train() or eval() is not supported for exported models.
        Please call `torchao.quantization.pt2e.move_exported_model_to_train(model)` (or eval) instead.

        If you cannot replace the calls to `model.train()` and `model.eval()`, you may override
        the behavior for these methods by calling `torchao.quantization.pt2e.allow_exported_model_train_eval(model)`,
        which does the above automatically for you. Note that this has limited effect on switching
        behavior between train and eval modes, and should be used only for special ops such as dropout
        and batchnorm.
        modec                    > [        T5      er'   NotImplementedErrorr   r  error_messages     r-   _train$_disallow_eval_train.<locals>._trainE      !-00r.   c                    > [        T5      er'   r  r  s     r-   _eval#_disallow_eval_train.<locals>._evalH  r  r.   )T)booltypes
MethodTypetraineval)r   r  r  r  s      @r-   _disallow_eval_trainr  5  sW    
	M14 1 11$ 1 1 ""61EK!!%/EJLr.   c                    U R                   S:H  =(       a;    U R                  [        R                  R                  R
                  R                  :H  =(       d    U R                  [        R                  R                  R                  R                  :H  =(       di    U R                  [        R                  R                  R                  :H  =(       d1    U R                  [        R                  R                  R
                  :H  $ )Nr  )r   ry   r(   r   r  sym_sizer   	sym_numel)r   s    r-   r$   r$   P  s    ?" 	;KK599>>22:::	2;;%))..22:::	2 ;;%))..222	2 ;;%))..111r.   c                 F    [        [        S U R                  5      5      nU$ )Nc                     [        U 5      SL $ )NF)r$   )rs  s    r-   <lambda>(_filter_sym_size_users.<locals>.<lambda>[  s    ):1)=)Fr.   )r   filterr?  )r   
node_userss     r-   r%   r%   Z  s    fG$**UVJr.   r'   )F)FN)NN)f__doc__r8   r@  r  ra   collectionsr   inspectr   r   typingr   r   r   r	   r(   torch.nn.functionalr   
functionalr'  $torch.ao.quantization.fx._decomposedr
   torch.export.unflattenr   r   torch.fxr   r   r   torch.nn.utils.fusionr   torch.nn.utils.parametrizer   torch.utils._pytreer   torchao.utilsr   __all__r   r   rI   r   r   r   r   r  r   r   rN   r   r   r{   r   r   r   dictr   r   r   r   r)   r,   r   r   r   r   r   r    r   r   rw  r   r   rz  r   rx  r{  r   r   r   r  r  r  r"   r#   r$  r(  r+  rV  rc  r   rk  r!   r}  r  r   r  rd   r  r  r$   r%   r   r.   r-   <module>r     s       # - 1 1    J : - - 6 6 ( 7.W:# :C :C :!&!H   :; ; ;   ;  ;;	; 
 ;  38_; |
((//&+hhooc]0JX JDcN JtCH~ J8 (? *H 4S> >U ,# # $ H #44K>\\K>\\K> K> 	K>
 ;;K> 
K>  K> ]]K> 5<<%&K>\
' 
'c 
'6!88??6!,1#s(O6!	#uS#X
6!|# ( , &*HHOO  	
 U\\" 
6 
II""66>>	II""66==	II""77?? 
II""88@@	II""88??	II""99AA%((-- uxx}}  "	( ( T t :t :S8 S4  i$i$i$ TNi$ 	i$
 i$ i$ 
i$Zk d B
; 
4U39=M8M3N 
  %%#s(O% %
 %P&k &d &, ,0WWW tCy)Wx -1SWTTtCy)T  U5#tU[[+H%I3%N OPTr 6D  43F r.   