
    ybj                       S SK J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	  S SK
r
S SKrS SKJrJrJr  S SKJrJrJr  S SKJr  S SKJrJrJrJr  S S	KJr  S S
KJrJrJr   S SK J!r!  Sr#Sr$Sr%Sr&Sr'Sr(Sr)Sr*Sr+Sr,0 r-\R"                  R\                  \R"                  R^                  \R"                  R`                  \R"                  Rb                  4r2\3" \5       V s0 s H$  n \4" \5" \U 5      \65      (       d  M  \5" \U 5      U _M&     sn r7 " S S\5      r8 " S S\5      r9 " S S\5      r: " S S\5      r;\R"                  Rx                  \
Rz                  " S5      \R"                  R|                  \
Rz                  " S5      \R"                  R~                  \
Rz                  " S 5      \R"                  R                  \
Rz                  " S!5      \R"                  R\                  \\R"                  R                  \\R"                  R                  \0rC\R"                  R|                  \
R                  " S \
R                  S"9\
R                  " S#\
R                  S"94\R"                  Rx                  \
R                  " S$\
R                  S"9\
R                  " S%\
R                  S"94\R"                  R                  \
R                  " S \
R                  S"9\
R                  " S&\
R                  S"94\R"                  R~                  \
R                  " S'\
R                  S"9\
R                  " S(\
R                  S"94\R"                  R                  \
R                  " S \S"9\
R                  " S)\S"94\R"                  R                  \
R                  " S*\S"9\
R                  " S+\S"940rI\R"                  Rx                  \
R                  " S,\
R                  S"9\
R                  " S%\
R                  S"94\R"                  R~                  \
R                  " S-\
R                  S"9\
R                  " S(\
R                  S"940rJ\R"                  R|                  \
R                  " S \
R                  S"9\
R                  " S%\
R                  S"94\R"                  Rx                  \
R                  " S.\
R                  S"9\
R                  " S/\
R                  S"94\R"                  R                  \
R                  " S \
R                  S"9\
R                  " S(\
R                  S"94\R"                  R~                  \
R                  " S0\
R                  S"9\
R                  " S1\
R                  S"94\R"                  R                  \
R                  " S \S"9\
R                  " S+\S"94\R"                  R                  \
R                  " S2\S"9\
R                  " S3\S"940rKS4S5.S6 jrLSbS7 jrMScS8 jrNSdSeS9 jjrOS: rP            SfS; jrQ    Sg               ShS< jjrR Sg SiS= jjrS   Sj               SkS> jjrTSlS? jrUSlS@ jrVSmSA jrWSnSB jrX " SC SD5      rY " SE SF5      rZ " SG SH5      r[SI r\SJ r]SK r^SL r_SoSM jr`SN raSpSO jrbSqSP jrcSrSQ jrdSsSR jreStSS jrfSuST jrgStSU jrhSuSV jriSvSW jrj   Sj           SwSX jjrkSxSY jrlSySZ jrmSzS[ jrnS{S\ jroS{S] jrpS|S^ jrqS|S_ jrrS|S` jrsS|Sa jrtg! \" a    Sr! GNf = fs  sn f )}    )annotationsN)Enum)Path)float8_e4m3fnint4uint4)
ModelProtoTensorProtoexternal_data_helper)onnx_pb)
make_graph
make_model	make_nodemake_tensor_value_info)ReferenceEvaluator)GraphOptimizationLevelInferenceSessionSessionOptions)to_array_extendedzonnx.quantizez0.1.0ai.onnxzcom.microsoftQuantizeLinear_QuantizeLinear_InputDequantizeLinear_DequantizeLinear_Output
_quantizedl        c                  2    \ rS rSrSrSrS r\S 5       rSr	g)QuantizationMode:   r      c                    U R                   $ Nnameselfs    ڠ/tmp/claude-0/-home-danvics-docker-quiz/c1e0577a-e42c-4a3d-b1ea-3edd61103a4e/scratchpad/stt/lib/python3.13/site-packages/onnxruntime/quantization/quant_utils.py__str__QuantizationMode.__str__>       yy    c                F     [         U    $ ! [         a    [        5       ef = fr!   )r   KeyError
ValueError)modes    r&   from_stringQuantizationMode.from_stringA   s)    	#D)) 	,	      N)
__name__
__module____qualname____firstlineno__
IntegerOps
QLinearOpsr'   staticmethodr/   __static_attributes__r2   r*   r&   r   r   :   s%    JJ  r*   r   c                  2    \ rS rSrSrSrS r\S 5       rSr	g)QuantizedValueTypeI   r   r   c                    U R                   $ r!   r"   r$   s    r&   r'   QuantizedValueType.__str__M   r)   r*   c                F     [         U    $ ! [         a    [        5       ef = fr!   )r<   r,   r-   )vs    r&   r/   QuantizedValueType.from_stringP   s)    	%a(( 	,	r1   r2   N)
r3   r4   r5   r6   InputInitializerr'   r9   r/   r:   r2   r*   r&   r<   r<   I   s%    EK  r*   r<   c                  V    \ rS rSrSrSrSrSrSrSr	Sr
S	 r\S
 5       r\S 5       rSrg)	QuantTypeX   r   r                  c                    U R                   $ r!   r"   r$   s    r&   r'   QuantType.__str__a   r)   r*   c                F     [         U    $ ! [         a    [        5       ef = fr!   )rF   r,   r-   )ts    r&   r/   QuantType.from_stringd   s(    	Q< 	,	r1   c                   U [         R                  :X  a  [        R                  $ U [         R                  :X  a  [        R
                  $ U [         R                  :X  a  [        R                  $ U [         R                  :X  a  [        R                  $ U [         R                  :X  a  [        R                  $ U [         R                  :X  a  [        R                  $ U [         R                  :X  a  [        R                  $ [!        SU < S35      e)NzUnexpected value qtype=.)rF   QInt8r
   INT8QUInt8UINT8QUInt16UINT16QInt16INT16QFLOAT8E4M3FNFLOAT8E4M3FNQUInt4UINT4QInt4INT4r-   r$   s    r&   tensor_typeQuantType.tensor_typek   s    9??"###9###$$$9$$$%%%9###$$$9***+++9###$$$9??"###24(!<==r*   r2   N)r3   r4   r5   r6   rT   rV   r\   rZ   rX   r`   r^   r'   r9   r/   propertyrb   r:   r2   r*   r&   rF   rF   X   sR    EFMFGEF   > >r*   rF   c                  2    \ rS rSrSrSrS r\S 5       rSr	g)QuantFormat~   r   r   c                    U R                   $ r!   r"   r$   s    r&   r'   QuantFormat.__str__   r)   r*   c                F     [         U    $ ! [         a    [        5       ef = fr!   )rf   r,   r-   )formats    r&   r/   QuantFormat.from_string   s)    	v&& 	,	r1   r2   N)
r3   r4   r5   r6   	QOperatorQDQr'   r9   r/   r:   r2   r*   r&   rf   rf   ~   s%    I
C  r*   rf   int8uint8int16uint16dtype   i   i  i i     i   iii@   i i @  rI   zero_point_indexc                d   / n[        U5       GH   u  p4[        R                  " [        U5      [        R                  5      (       a&  UR                  [        R                  " U5      5        OB[        U[        R                  5      (       a  UR                  U5        O[        SU SU 35      eX0:X  d  M  US   nUR                  [        R                  :X  d   UR                  [        R                  :X  d  M  [        SUR                   35      e   [        U5      S:  a  [        U5      $ US   $ )Nzarg z is not an array: r{   zzero_point cannot be r   r   )	enumeratenumpy
issubdtypetypenumberappendarray
isinstancendarray	TypeErrorrt   float32float16lentuple)r}   argsnew_argsiarA   s         r&   _check_typer      s    H$DGU\\22OOEKKN+5==))OOAd1#%7s;<< Aww%--'177emm+C"7y ABB   "(ma/5?@Xa[@r*   c                   U [         ;   d   SU  S35       eU [        R                  R                  [        R                  R                  [        R                  R
                  [        R                  R                  4;   Ga/  US:w  a  [        SU< S35      eUR                  [        R                  :X  a  [        R                  nOHUR                  [        R                  :X  a  [        R                  nO[        SUR                   S35      e[        [!        [#        S/ S/[$        R&                  R)                  SU / S/5      S	9[#        S
/ SQS/5      /S[+        SUS 5      [+        SUS 5      /[+        SU S 5      /5      5      n[-        U5      n[/        UR1                  S XS.5      S   5      $ [         U    n	[3        U SSS9u  pUb  [5        X5      OU
nUb  [7        X5      OUn[        R8                  " UR;                  [        R                  5      U-  R=                  5       U-   5      n[        R>                  " XXS9  [/        UR;                  U	5      5      $ )NUnexpected data type > requested. Only INT8, UINT8, INT16, and UINT16 are supported.r   z2zero_point is expected to be null for float 8 not rS   zUnexpected dtype Constant
zero_point)valuer   )Xscaler   Yqur   r   )r   r   Freduce_range	symmetric)out) ONNX_TYPE_TO_NP_TYPE
onnx_protor
   r]   FLOAT8E4M3FNUZ
FLOAT8E5M2FLOAT8E5M2FNUZNotImplementedErrorrt   r   r   FLOATr   FLOAT16r-   r   r   r   onnxhelpermake_tensorr   r   r   runget_qmin_qmax_for_qTypemaxminasarrayastyperoundclip)qTypearrr   r   lowhigh	onnx_type
onnx_modelrefrt   qminqmaxcliplowcliphigharr_fp32s                  r&   quantize_nparrayr      s!   (( 
w&de( ++--))--	  ?%(Z[eZhhi&jkk99%#))IYY%--'#++I01=>>"Bdkk>U>UVbdikmpqor>s .0LseT	 *3	4@*7ItD (UD9:

  !,3774s)CDQGHH %U+,URWX
$'O#d.&*&63t?D==#**U]]";e"C!J!J!Lz!YZ

8h=8??5122r*   c           
        US:  d  US:  a  [        SU SU 35      e[        R                  " U [        R                  " SU R                  S95      n [        R
                  " U[        R                  " SUR                  S95      nUb*  [        X[        R                  " XPR                  S9-   5      nU(       aE  [        R
                  " [        R                  " U 5      [        R                  " U5      5      nU* n U7nX#::  d   SU  SU 35       e[        R                  " X-
  [        R                  S9n[        R                  " U[        R                  S9[        R                  " U[        R                  S9-
  n[        R                  " Xx-  5      n	U	S:  d   S5       eU	[        R                  " UR                  5      R                  :  aA  [        R                  " SUR                  S9n	[        R                  " SUR                  S9n
X/$ U(       aZ  [        R                  " [        R                  " X#-   [        R                  " S	[        R                  S9-  5      UR                  S9n
O8[        R                  " [        R                  " X U	-  -
  5      UR                  S9n
U	R                  UR                  5      n	X/$ )
a  Calculate the scale s and zero point z for the quantization relation
r = s(q-z), where r are the original values and q are the corresponding
quantized values.

r and z are calculated such that every value within [rmin,rmax] has an
approximate representation within [qmin,qmax]. In addition, qmin <= z <=
qmax is enforced. If the symmetric flag is set to True, the interval
[rmin,rmax] is symmetrized to [-absmax, +absmax], where
absmax = max(abs(rmin), abs(rmax)).

:parameter rmin: minimum value of r
:parameter rmax: maximum value of r
:parameter qmin: minimum value representable by the target quantization data type
:parameter qmax: maximum value representable by the target quantization data type
:parameter symmetric: True if the floating-point range should be made symmetric. Defaults to False.
:parameter min_real_range: Minimum floating-point range (i.e., rmax - rmin) to enforce. Defaults to None.
:return: zero and scale [z, s]

r   Bqmin and qmax must meet requirement: qmin <= 0 <= qmax while qmin:, qmmax:rs   zqmin=z > qmax=zscale issue      ?       @)r-   r   minimumr   rt   maximumr   r   absfloat64finfotinyr   r   )rminrmaxr   r   r   min_real_rangeabsmaxdrdqr   r   s              r&   compute_scale_zpr      s   ( ax4!8]^b]ccklpkqrss
 ==u{{1DJJ?@D==u{{1DJJ?@D !4nJJ OOPuyy		$@ww<55htf55<	T[	6B	T	/%++d%--2X	XBKK EA:$}$:u{{4::&+++Ctzz2[[$**5
   T[EKK5==,QQRZ^ZdZdJ U[[u1D%ETZZXJTZZ(r*   c                   [        U5      n[        U5      nXV-   S-   S-  n[        [        R                  " U 5      5      n [        [        R                  " U5      5      n[	        U S5      n [        US5      nUb  US:  a  [        X[        U5      -   5      nX::  a  U S:  a  UOUn[        [        U 5      [        U5      5      n	X:X  a  Xe-
  O[        SXe-
  S-  5      n
U	S:  a  U	OS[        SU
5      -  nUb  XXe-
  -  :  a  XFU-
  -  n[        R                  " U[        R                  S9[        R                  " U[        R                  S94$ U S:  aL  [        R                  " U[        R                  S9n[        R                  " XU-
  -  [        R                  S9nO^[        R                  " U[        R                  S9nU * Xu-
  -  nXU-
  -  n[        R                  " [        X5      [        R                  S9nUb<  [        U5      XFU-
  -  :  a(  [        R                  " XFU-
  -  [        R                  S9nX4$ )a  Snap a uint8 activation zero-point to qmin (when rmin >= 0) or mid (when rmin < 0).

Used by the ActivationRestrictedAsymmetric quantization option. Recomputes scale so the
dequantized range still covers [rmin, rmax] without clipping.

:parameter rmin: calibrated minimum activation value (numpy scalar)
:parameter rmax: calibrated maximum activation value (numpy scalar)
:parameter qmin: minimum quantized value (int, default 0)
:parameter qmax: maximum quantized value (int, default 255)
:parameter min_real_range: minimum floating-point range to enforce (same semantics as compute_scale_zp).
    When not None and > 0, rmax is adjusted to max(rmax, rmin + min_real_range) before scale computation.
:return: (zero_point, scale) with zero_point dtype uint8 and scale dtype float32
r   rH           r   r   rs   )
intfloatr   squeezer   r   r   r   rp   r   )r   r   r   r   r   qmin_valqmax_valmiddegenerate_zpabs_maxdenom	scale_valr   r   	scale_neg	scale_poss                   r&   snap_zero_point_to_uint8r   2  s    4yH4yH"q
(Ct$%Dt$%D tS>DtS>D !nq&84n 556| %)CKSc$iT+)6)B$APXPchiOiHj '!WAuE	%)H[6\*\&X*=>I{{=<ekk)[`[h[h>iiis{[[=
Dx$78N [[EKK8
ES^,	sN+	C	5U]]K !eEl^RZGZ5[&[N.AB%--Xr*   c                   SnU [         ;  a  U [        R                  :X  a  SSKJn  Un[
        R                  " S[
        R                  S9R                  U5      R                  [
        R                  5      n[
        R                  " U Vs/ s H?  n[
        R                  " U5      (       a  M   [
        R                  " U5      (       a  M=  UPMA     sn[
        R                  S9nO[        SU  S35      eU[         U '   OU [        R                  :X  a  SSKJn  UnUc  [        SU  S	35      e[
        R                   " [         U    5      n[
        R                  " SUS9n[
        R                  " X-  UR"                  S9n	X/$ s  snf )
aZ  Calculate the scale s for a float8 type (E4M3FN).
The function assumes the coefficient distribution and the float 8
distribution are similar to two gaussian laws.

:return: zero and scale [z, s]

More details in notebook `quantization_fp8.ipynb
<https://github.com/microsoft/onnxruntime/blob/main/docs/python/notebooks/quantization_fp8.ipynb>`_.
Nr   )r      rs   zQuantization to element_type=z not implemented.zUnexpected element_type rS   )FLOAT8_DISTRIBUTIONSr
   r]   	ml_dtypesr   r   arangerp   viewr   r   r   isnanisinfr-   r   stdrt   )
element_typer   zp_dtyper   
all_valuesfvaluesstd_f8zeror   s
             r&   compute_scale_zp_float8r   m  s5    H//;333/$Hc=BB=QXXY^YfYfgJ[[&TJqekk!nU[[QR^JT\a\i\iF <\NJ[\]]-3\*	11	1+ 2<.BCCYY+L9:F;;q)DKKCII6E= Us   F/FFc           
        U R                   S:w  a&  [        SU R                    SU R                   S35      eU R                  U   nXS-   S-
  U-  n[        [        R
                  " [        U R                  5       VVs/ s H  u  pxXr:w  d  M  UPM     snn5      5      n	[        R                  " XS5      n
U
R                  XY5      n
[        USUS9u  p[        U   S   R                  nXc-  U-
  nUS:  a7  [        R                  " X4U
R                  S	9n[        R                  " X/SS
9nOU
nUR                  XcU	5      nUR                  SS
9nUR                  SS
9n[        R                   " U[        R"                  " U5      5      n[        R$                  " U[        R"                  " U5      5      nU(       aD  [        R$                  " [        R&                  " U5      [        R&                  " U5      5      nU* nUn[        R(                  " U5      n[        R(                  " U5      nUU-
  R+                  [        R(                  5      nUU-
  nUU-  R+                  U R                  5      n[        R,                  " U R                  5      R.                  nUU:  n[        R0                  " U[        R2                  " U5      U5      nU(       a=  [        [        R4                  " UU-   S-  5      5      n[        R6                  " Xi4UUS	9nO[        R4                  " UUR+                  [        R(                  5      UR+                  [        R(                  5      -  -
  5      n[        R8                  " UUU5      nUR+                  U5      nSUU'   [        R                  " USU5      n[        R                  " USU5      nUU4$ s  snnf )as  Compute per-block scale and zero-point for a weight tensor.

The weight is sliced along *axis* into blocks of *block_size* elements.
Per the ONNX opset-21 spec, QuantizeLinear/DequantizeLinear require the
scale and zero_point tensors to have the **same rank** as the input tensor.
Only rank-2 weight tensors are supported; rank > 2 is explicitly rejected.

Returns arrays with the same rank and dimensions as *weight*, except
``shape[axis] == ceil(weight.shape[axis] / block_size)``. This matches
the ONNX opset-21 QuantizeLinear/DequantizeLinear blocked-quantization spec.

:param weight: Float32/float16 weight array (must be rank-2).
:param quant_type: ONNX tensor data type for quantization.
:param axis: Axis along which to apply block-wise quantization.
:param block_size: Number of elements per block along *axis*.
:param symmetric: Whether to use symmetric quantization per block.
:return: Tuple of (zero_point, scale), each with shape matching *weight*
    except ``shape[axis] == n_blocks``, where ``n_blocks == ceil(weight.shape[axis] / block_size)``.
:raises NotImplementedError: If weight rank is not 2 (opset-21 constraint).
rH   zXPer-block (opset-21) quantization is only supported for rank-2 weight tensors. Got rank-z tensor with shape zY. For rank > 2 tensors, reshape to 2-D before quantizing or use per-channel quantization.r   r   Fr   rs   )axisr   )ndimr   shaper   r   prodr   moveaxisreshaper   ONNX_INT_TYPE_RANGErt   zerosconcatenater   r   r   
zeros_liker   r   r   r   r   r   where	ones_liker   fullr   ) weight
quant_typer   
block_sizer   kn_blocksr   dothermovedr   r   r   pad_lenpadmoved_paddedblocksr   r   r   r   r   r   r   	raw_scaler   
degeneratescaleszp_valzero_pointsraw_zps                                    r&   compute_scale_zp_blockedr    s   6 {{a!}$7~ Fff
 	
 	TA"z1H 

)FLL*AO*A$!QYA*AOPQENN6+EMM!#E(%S\]JD":.q177H #a'G{kk7*%++>((%A> !!(>F ::1:D::1:D ==u//56D==u//56Duyy		$@w}}T"H}}T"H
+		emm	,B	H	Bb  .I;;v||$))DT!J[[U__Y%?KFU[[(X"5!<=>jj(!2F(KXEMM(BV]]SXS`S`Ea(aabFHh7mmH-"#J
 ^^FAt,F..a6Ku Ps   =OOc                   [        U [        R                  5      (       d  [        S[	        U 5       S35      eUb  UnO"[        U 5      (       a  U R                  5       OSnUb  UnO"[        U 5      (       a  U R                  5       OSn[        R                  " XpR                  S9n[        R                  " XR                  S9n[        R                  " SU R                  S9n	U[        R                  :X  a?  U(       a  [        S5      e[        R                  " U 5      n
[        X5      u  p[        XSS9$ U[        R                   [        R"                  [        R$                  [        R&                  [        R(                  [        R*                  4;   aU  [-        XUS	9u  p[        U 5      (       a  [/        XxXX$5      u  pO[        R                  " SUR                  S9n[        XSS9$ [1        S
U S35      e)a  
Returns the zero_point and scale for the given data.

:param data: The data for which to compute quantization parameters.
:param quant_type: The quantization data type.
:param symmetric: whether symmetric quantization is used or not.
:parameter reduce_range: True if the quantization range should be reduced. Defaults to False.
:parameter min_real_range: Minimum floating-point range (i.e., rmax - rmin) to enforce. Defaults to None.
:parameter rmin_override: The value of rmin to use if not None. Otherwise, uses min(data).
:parameter rmax_override: The value of rmax to use if not None. Otherwise, uses max(data).
:return: zero point and scale
z%Weight must be given as an array not rS   r   rs   r   z1Unsupported option reduce_range=True for float 8.r   r|   r   z Unexpected value for quant_type=)r   r   r   r   r   r   r   r   r   rt   r
   r]   RuntimeErrorr   r   r   rU   rW   r[   rY   ra   r_   r   r   r-   )datar  r   r   r   rmin_overridermax_overrider   r   r   r   r   r   r   s                 r&   compute_data_quant_paramsr    s   * dEMM**?T
|1MNN  YYtxxzC  YYtxxzC;;t::.D;;t::.DKK4::.E[---RSSiio3JD
:qAA  -ZQZ[
t99 0T cJQdjj9J:qAA
7
|1E
FFr*   c                   [        U UUUUUU5      u  pxU[        R                  :X  a  [        XX5      n	[	        U	R                  [        R                  5      R                  5       S-  S:H  5      (       af  [        R                  " U 5      n
[        SU
R                  5        SU
R                  5        SU	R                  5        SU	R                  5        S3	5      eXxU	4$ U[        R                  [        R                  [        R                  [        R                   [        R"                  [        R$                  4;   a  [        XX5      n	XxU	4$ ['        SU S35      e)a   
:param data: data to quantize
:param qType: data type to quantize to.
:param symmetric: whether symmetric quantization is used or not.
:parameter reduce_range: True if the quantization range should be reduced. Defaults to False.
:parameter min_real_range: Minimum floating-point range (i.e., rmax - rmin) to enforce. Defaults to None.
:parameter rmin_override: The value of rmin to use if not None. Otherwise, uses min(data).
:parameter rmax_override: The value of rmax to use if not None. Otherwise, uses max(data).
:return: minimum, maximum, zero point, scale, and quantized weights

To pack weights, we compute a linear transformation

- when data `type == uint8` mode, from `[rmin, rmax]` -> :math:`[0, 2^{b-1}]` and
- when data `type == int8`, from `[-m , m]` -> :math:`[-(2^{b-1}-1), 2^{b-1}-1]` where
    `m = max(abs(rmin), abs(rmax))`

and add necessary intermediate nodes to transform quantized weight to full weight using the equation

:math:`r = S(q-z)`, where

- *r*: real original value
- *q*: quantized value
- *S*: scale
- *z*: zero point
rv   z+One of the quantized value is NaN data in [z, z], quantized_data in [z].zUnexpected value for qType=rS   )r  r
   r]   r   anyr   r   rp   ravelr   r  r   r   rU   rW   r[   rY   ra   r_   r-   )r  r   r   r   r   r  r  r   r   quantized_datanp_datas              r&   quantize_datar  2  sO   8 2J ((()%uI##EKK06683>3FGGmmD)G=gkkm_Bw{{}o ^&&4&8&8&:%;2n>P>P>R=SSUW  .00  *%uI.00
25';
<<r*   c                t
   [        U 5      nSnUGb  US:  Ga  UR                  U   n	[        [        R                  " [        UR                  5       V
Vs/ s H  u  pX:w  d  M  UPM     snn
5      5      n[        R                  " XtS5      R                  X5      n[        R                  " X4S5      n[        R                  " X$S5      nUR                  S   n[        R                  R                  U5      n[        R                  " UUS9n[        U5       H\  nUU-  n[        UU-   U	5      n[        U5       H6  n[        XUU2U4   R                  5       UUU4   UUU4   5      UUU2U4'   M8     M^     [        R                  " UR                  U	/[        UR                  5       V
Vs/ s H  u  pX:w  d  M  UPM     snn
-   5      SU5      nOUc  [        XR                  5       X25      nOUR                  U   n[!        UR                  5      nSUU'   / n[        U5       Hl  n
UR#                  X5      nX:   nX*   n[        UUR                  5       UU5      nUR%                  [        R&                  " U5      R                  U5      5        Mn     [        R(                  " UU5      nU(       a  UOU R*                   [,         3nU[        R.                  R0                  :X  Ga&  [        R.                  " 5       nUUl        UR4                  R7                  U R4                  5        UUl        UR9                  5       R;                  5       R=                  5       Ul        [@        b  [A        U5      n U R                  UR                  :w  d"  U R=                  5       UR=                  5       :w  a]  [C        SUR                   SUR=                  5       SS  SU R=                  5       SS  S	U R                   S
[E        U5      SS  S35      eU$ U[        R.                  RF                  [        R.                  RH                  4;   aw  URJ                  [L        [N        4;  a  [C        SU S35      e[Q        [S        UR=                  5       5      5      n![        R                  RU                  UXR4                  U!SS9nU$ [        R                  R                  U5      n[        R&                  " UUS9R                  U R4                  5      n[        RV                  RY                  UU5      nU$ s  snn
f s  snn
f )a  
Returns a quantized version of the given ONNX initializer.

:param weight: The ONNX initializer to quantize.
:param quant_type: The final quantized data type.
:param zero_point: The zero-point value to use for quantization.
:param scale: The scale value to use for quantization.
:param axis: The quantization axis if quantizing per-channel or per-block. Defaults to None.
:param quant_weight_name: The name of the quantized initializer.
                          If not specified, the quantized name is generated.
:param block_size: Block size for opset-21 block-wise quantization. 0 means disabled.
:return: The quantized ONNX initializer.
Nr   rs   r   zThe initializer of shape z! could not be created, expecting 
   z, got z and shape=z
raw=   rS   zQuantized weights for z. must be 8-bit before packing as 4-bit values.T)raw)-tensor_proto_to_arrayr   r   r   r   r   r   r   r   r   tensor_dtype_to_np_dtype
empty_likeranger   r   r  listtaker   r   r   r#   TENSOR_NAME_QUANT_SUFFIXr
   r]   	data_typedimsextendflattencopytobytesraw_datar   r  strra   r_   rt   r   r   bytespack_bytes_to_4bitr   numpy_helper
from_array)"r   r  r   r   r   quant_weight_namer  weight_dataq_weight_datar  r   r  r  r  scale_movedzp_movedr  quant_np_dtypeq_movedblkstartendcolchannel_countchannel_dimsquantized_channel_data_listchannel_datachannel_scalechannel_zero_pointquantized_channel_dataq_weight_nameq_weight_initializercheckpacked_datas"                                     r&   quantize_onnx_initializerrM  o  s   , (/K*.MJNd#EJJi8I8I.JX.Jdaai.JXYZ{!4<<QF nnU!4>>*A6$$Q'==jI""5??C*$Eej(!,CU|*:eCin 5 ; ; ={3PS8?TV^_bdg_gVh+c	3' $ # OOQC;;L;L1M"[1MQRQZ11M"[[\^_ae
 
(5F5F5H%\#))$/K--.T&(#}%A&++A4L!HM!+%5L..0-AS&" (..u}}=S/T/\/\]i/jk & ))*EtL):%6;;-PhOi@jMT%%222#//1)3&!!((5$1!(5(=(=(?(D(D(F(N(N(P%( &&:;E{{k///5==?mF[F[F]3]"/0A0A/BBc$,,.s34F5==?3B;O:PP[\b\h\h[iS!56t<=Q@ (   
((--t/?/?/E/EF	FtUm3!7Ftuvv .}/D/D/FGH  ${{66}jR]R]_jpt6u  	 ==jIm>JRRSYS^S^_#00;;M=YQ  Y" #\s   T.$T.-T4<T4c                   U [         R                  R                  :X  a  [        S5      eSnU(       a  [        R                  U 5      nO0U(       a  U [        ;   a
  [        U    nO[        R                  U 5      nU(       d  [        SU  S35      eUu  pEUS:  d  US:  a'  [        SU SU SUR                   S	U S
U SU  35      eU$ )z
Return qmin and qmax, the minimum and maximum value representable by the given qType
:parameter qType: onnx.onnx_pb.TensorProto.UINT8 or onnx.onnx_pb.TensorProto.UINT8
:return: qmin, qmax
z;This function is not implemented for float 8 as not needed.Nr   r   r   r   r   z, dtype=z, reduce_range=z, symmetric=z, qType=)
r   r
   r]   r   ONNX_INT_TYPE_REDUCED_RANGEgetONNX_INT_TYPE_SYMMETRIC_RANGEr   r-   rt   )r   r   r   qranger   r   s         r&   r   r     s     
&&333!"_``F,007	u ==.u5$((/07uvwwJDax4!86$x

|?<. Y"8E74
 	
 Mr*   c                "    [        XUS9u  p4XC-
  $ )z
Helper function to get the quantization range for a type.
    parameter qType: quantization type.
    return: quantization range.
r  )r   )r   r   r   r   r   s        r&   get_qrange_for_qTyperT    s     )	RJD;r*   c                @    U S:  a  X-   OU nUS:  =(       a    X!:  nX24$ )z
Helper function that tries to return a normalized axis in the range [0, rank - 1].
:parameter axis: The axis to normalize.
:parameter rank: The tensor rank (number of dimensions).
:return (is_valid, axis_norm)
r   r2   )r   rank	axis_normis_valids       r&   normalize_axisrY    s-      $axTIA~2)"2Hr*   c                    [        U 5      nUS:X  a
  [        5       $ US-   S-  n[        U5      nSnSnXAS-
  :  a+  XS-      S-  S-  X   S-  -  X5'   US-  nUS-  nXAS-
  :  a  M+  XA:  a	  X   S-  X5'   U$ )a.  
Copies a source array of 8-bit values into a destination bytearray of packed 4-bit values.
Assumes that the source values are already in the appropriate int4 range.
:parameter src_8bit: The 8-bit element values to pack.
:return A bytearray with every two 8-bit src elements packed into a single byte.
r   r   rH   rw   rJ   )r   	bytearray)src_8bit	num_elemsdst_sizedstsrc_idst_is         r&   r4  r4    s     HIA~{A!#H
H
CEE a-
	*S0Q68?S;PQ


 a-

 _s*
Jr*   c                  (    \ rS rSrSr/ / S4S jrSrg)QuantizedInitializeri)  zB
Represents a linearly quantized weight input from ONNX operators
Nc
                p    Xl         X l        X0l        X@l        XPl        X`l        Xpl        Xl        Xl        g r!   )	r#   initializerrminsrmaxsr  r  r  r  r   )
r%   r#   re  rf  rg  r  r  r  r  r   s
             r&   __init__QuantizedInitializer.__init__.  s4     	&

&	,	r*   )	r   r  re  r#   r  rg  rf  r  r  r3   r4   r5   r6   __doc__rh  r:   r2   r*   r&   rc  rc  )  s     r*   rc  c                  *    \ rS rSrSr    SS jrSrg)QuantizedValueiH  zA
Represents a linearly quantized value (input\output\intializer)
Nc
                p    Xl         X l        X0l        X@l        XPl        X`l        Xpl        Xl        Xl        g r!   )	original_nameq_name
scale_namezp_name
value_typer   	node_type
node_qtype
scale_type)
r%   r#   new_quantized_namerq  zero_point_namequantized_value_typer   rt  ru  rv  s
             r&   rh  QuantizedValue.__init__M  s2     "($&.	"$$r*   )	r   ru  rt  ro  rp  rq  rv  rs  rr  )NNNNrj  r2   r*   r&   rm  rm  H  s     %r*   rm  c                      \ rS rSrSrS rSrg)BiasToQuantizeid  z#
Represents a bias to be quantized
c                (    Xl         X l        X0l        g r!   	bias_name
input_nameweight_name)r%   r  r  r  s       r&   rh  BiasToQuantize.__init__i  s    "$&r*   r~  Nrj  r2   r*   r&   r|  r|  d  s    'r*   r|  c                   U R                   S:X  a  [        SU R                   S35      eU R                   S:X  a  U R                  nGO,U R                   S:X  a  U R                  nGOU R                   S:X  a  U R
                  nOU R                   S:X  a  U R                  nOU R                   S:X  a  U R                  nOU R                   S	:X  a  U R                  nOU R                   S
:X  a  U R                  nO}U R                   S:X  a  U R                  nO`U R                   S:X  a  U R                  nOCU R                   S:X  a  U R                  nO&[        SU R                   SU R                    S35      eU R                  U0$ )z
Convert attribute to kwarg format for use with onnx.helper.make_node.
    :parameter attribute: attribute in AttributeProto format.
    :return: attribute in {key: value} format.
r   z
attribute z does not have type specified.r   rH   rI   rJ   rK   rL   rx      	   r!  z has unsupported type rS   )r   r-   r#   r   r   srP   gfloatsintsstringstensorsgraphs)	attributer   s     r&   attribute_to_kwargr  o  s;    ~~:inn%55STUU ~~	1		1		1		1		1	  	1		1	!!	1	!!	2	  :inn%55KINNK[[\]^^NNE""r*   c                ~    U Vs/ s H  o"R                   U :X  d  M  UPM     nn[        U5      S:  a  US   $ S$ s  snf )z
Helper function to find item by name in a list.
    parameter item_name: name of the item.
    parameter item_list: list of items.
    return: item if found. None otherwise.
r   N)r#   r   )	item_name	item_listitemitemss       r&   find_by_namer    s@     (Bid99	+ATiEB5zA~58/4/ Cs   ::c                X    Sn[        [        U5      5       H  nX   U :X  d  M  UnM     U$ )z;
Helper function to return index of an item in a node list
r{   )r'  r   )	elem_name	elem_listelem_idxr   s       r&   get_elem_indexr    s2     H3y>"<9$H # Or*   c                F    [         R                  R                  SX/U5      $ )z
Helper function to create a Mul node.
    parameter inputs: list of input names.
    parameter output: output name.
    parameter name: name of the node.
    return: Mul node in NodeProto format.
Mul)r   r   r   )inputsoutputr#   s      r&   get_mul_noder    s     ;;  $??r*   c                l    U R                   R                  U R                  U-   U R                  -   5      $ )zh
Helper function to generate a identifiable filepath by concatenating the given identifier as a suffix.
)parentjoinpathstemsuffix)filename
identifiers     r&   generate_identified_filenamer    s+     ??##HMMJ$>$PQQr*   c                R   SS K nSS KJn  SS KnUR                  " UR
                  S9  [        S5        [        U 5        [        S5        [        U5        UR                  XSS9  UR                  S5        UR                  S5        UR                  S	5        UR                  5         g )
Nr   )	thresholdz
Histogram:zHistogram Edges:T)fillzTensor valueCountszTensor value V.S. Counts)sysmatplotlib.pyplotpyplotr   set_printoptionsmaxsizeprintstairsxlabelylabeltitleshow)hist
hist_edgesr  pltr   s        r&   
apply_plotr    s{    #	S[[1	,	$K	
	*JJtdJ+JJ~JJxII()HHJr*   c           	     $	   SSK nSSKnSSKnSSKJs  Js  Jn  SSKJs  Js  Jn  SSK	J
n  [        R                  " SU  35        UR                  XS9n[        [        R                   R#                  US5      S5       n	U	R%                  U5        SSS5        UR'                  S5      n
UR)                  S5      n/ n[+        U R-                  5       5       H  nX   nUR/                  5       n[1        UR3                  S	U
5      R5                  5       5      [1        UR3                  S
U
5      R5                  5       5      /n[7        [9        U5      5      nUR;                  U5      nUR;                  U5      nUR=                  U5        UR?                  UU5        URA                  UU5        URC                  U5      nURE                  U5        M     URG                  U[I        U5      5        U H  nURK                  U5        M     URM                  5       nURO                  U5        URQ                  UU5        URS                  U5      nURU                  U5        URW                  5       n[        [        R                   R#                  US5      S5       n	U	R%                  U5        SSS5        [        RX                  R3                  SS5      S;   a  UR                  R[                  US5      nUR]                  5       n[_        U5       H\  nURa                  U5      n[        R                  " URc                  5       5        [        R                  " URe                  5       5        M^     [        [        R                   R#                  US5      S5       n	[+        U R-                  5       5       H  nX   nUR/                  5       n[1        UR3                  S	U
5      R5                  5       5      [1        UR3                  S
U
5      R5                  5       5      /nUS-   [7        [9        U5      5      -   nU	R%                  U5        U	R%                  S5        M     SSS5        g! , (       d  f       GN= f! , (       d  f       GN= f! , (       d  f       g= f)z6
Helper function to write calibration table to files.
r   N)CalibrationCacheEncoderzcalibration cache: )clszcalibration.jsonwi   highestlowestzcalibration.flatbufferswbQUANTIZATION_DEBUG0)r   1zcalibration.cache 
)3jsonflatbuffersr   5onnxruntime.quantization.CalTableFlatBuffers.KeyValuequantizationCalTableFlatBuffersKeyValue5onnxruntime.quantization.CalTableFlatBuffers.TrtTableTrtTable"onnxruntime.quantization.calibrater  logginginfodumpsopenospathjoinwriter   Buildersortedkeysto_dictr   rP  r  r2  r   CreateStringKeyValueStartKeyValueAddKeyKeyValueAddValueKeyValueEndr   TrtTableStartDictVectorr   PrependUOffsetTRelative	EndVectorTrtTableStartTrtTableAddDictTrtTableEndFinishOutputenvironGetRootAsTrtTable
DictLengthr'  DictKeyValue)calibration_cachedirr  r  npr  r  r  	json_datafiler   builderkey_value_listkeyr   d_valuesr  r   flat_key
flat_value	key_value	main_dict	cal_tablebufdict_lenr   s                             r&   write_calibration_tabler    s   
 LLLL KLL&'8&9:;

,
JI	bggll3 23S	9T

9 
: 88A;D!!$'GN',,./"'>>#(,,y$/4467(,,x.3356
 CK '',))%0
w'2!!':6((1	i(# 0& $$Wc..AB#	''	2 $!!#I7#Wi0$$W-INN9
..
C	bggll3 9:D	AT

3 
B 
zz~~*C0H<%%77Q?	'')xA!q)ILL)LL*+ ! 
bggll3 34c	:d+0023C&+F~~'Hhll9d388:;hll8T2779:F #ICK 00EJJuJJt 4 
;	:g 
:	9L 
B	A 
;	:s%   =QQ/CR
Q,/
Q>
Rc                   U S:H  R                  [        R                  5      nU S:g  R                  [        R                  5      nUR                  5       nU R                  U-
  nU(       d  gU[        U5      -  [        U5      -  nUS:  d   SU SU SU 35       eU R                  [        R                  5      nXqU-  U* U-  -   -  nUS:*  R                  5       S:X  d   eU$ )aj  Given a discrete distribution (may have not been normalized to 1),
smooth it by replacing zeros with eps multiplied by a scaling factor
and taking the corresponding amount off the non-zero values.
Ref: http://web.engr.illinois.edu/~hanj/cs412/bk3/KL-divergence.pdf
     https://github.com//apache/incubator-mxnet/blob/master/python/mxnet/contrib/quantization.py
r   Nr   zn_zeros=z, n_nonzeros=z, eps1=)r   r   r   sumsizer   )pepsis_zerosis_nonzerosn_zeros
n_nonzeroseps1r  s           r&   smooth_distributionr	  #  s     Qu}}-H6//%--0KllnG'!Jw%
"33D#:Q'-
|74&QQ:88EMM"D(Nte{222DAI??!!!Kr*   c                    [         R                  " U R                  5       SS9n[        S UR                  R
                   5       5      $ )NF)load_external_datac              3  N   #    U  H  n[         R                  " U5      v   M     g 7fr!   )r   uses_external_data).0
intializers     r&   	<genexpr>*model_has_external_data.<locals>.<genexpr>>  s!     mUlz#66zBBUls   #%)r   loadas_posixr  graphre  )
model_pathmodels     r&   model_has_external_datar  <  s9    IIj))+FEmUZU`U`UlUlmmmr*   c                    [        5       nUR                  5       Ul        [        R                  Ul        0 nS/US'   [        U R                  5       U4SS/0UD6ng)z
    Generate model that applies graph optimization (constant folding, etc.)
    parameter model_path: path to the original onnx model
    parameter opt_model_path: path to the optimized onnx model
:return: optimized onnx model
ConstantSharingdisabled_optimizers	providersCPUExecutionProviderN)r   r  optimized_model_filepathr   ORT_ENABLE_BASICgraph_optimization_levelr   )r  opt_model_pathsess_optionkwargs_s        r&   optimize_modelr$  A  sb     !"K+9+B+B+DK(+A+R+RK(F%6$7F !,,.jH^G_jcijAr*   c                    SS0nU R                   (       a:  U R                    H*  nUR                  UR                  UR                  05        M,     [        R
                  R                  X5        g)z>Tag the model that it went through quantization pre-processingonnx.quant.pre_processonnxruntime.quantNmetadata_propsupdater  r   r   r   set_model_props)r  r)  props      r&   add_pre_process_metadatar-  Q  sS    .0CDN((D!!488TZZ"89 )KK6r*   c                    U R                   (       a7  U R                    H'  nUR                  S:X  d  M  UR                  S:X  d  M'    g   g)zCCheck the model whether it went through quantization pre-processingr&  r'  TFr)  r  r   )r  r,  s     r&   model_has_pre_process_metadatar0  Z  s<    ((Dxx33

FY8Y ) r*   c                    SS0nU R                   (       a:  U R                    H*  nUR                  UR                  UR                  05        M,     [        R
                  R                  X5        g )N
onnx.inferr'  r(  )r  r)  r  s      r&   add_infer_metadatar3  c  sS    "$78N%%A!!155!''"23 &KK6r*   c                    U R                   (       a7  U R                    H'  nUR                  S:X  d  M  UR                  S:X  d  M'    g   g)Nr2  r'  TFr/  )r  r  s     r&   model_has_infer_metadatar5  k  s;    %%Auu$4G)G & r*   c                    U R                    Vs/ s H'  oR                  (       a  UR                  S:X  d  M%  UPM)     nn[        U5      S:w  a  [        S5      eUS   R                  nU$ s  snf )Nr   r   z$Failed to find proper ai.onnx domainr   )opset_importdomainr   r-   version)r  opsetai_onnx_domainopset_versions       r&   get_opset_versionr=  s  sf    ).););m);<<SXS_S_clSle);Nm
>a?@@"1%--M ns
   $A*A*c                   [        U 5      nUn[        USU5      nUb  [        USU5      OS n[        R                  R                  [        R                  R
                  4n	Xy;   =(       d    X;   n
U
(       d  U(       a  [        R                  [        R                  1n UR                  5        H`  nU HN  nUR                  S5      nX;   a  Sn
  O5UR                  S5      nUc  M3  UR                  S5      nUU;   d  ML  Sn
  O   U
(       d  M`    O    US:  a#  US:  a  [        R                  " SU S	35        SnOUS
:  a;  U[        R                  R                   :X  a  [        R                  " SU S35        S
nOmUS:  a$  U
(       a  [        R                  " SU S35        SnOCUS:X  a  [        R                  " SU S35        O"US:  a  [        R                  " SU S35        SnXe:w  a*  [        R"                  R%                  X5      n ['        U 5      n U $ ! [        [        4 a    [        R                  " S5         GN1f = f)Nrb   r  TconvertzYSkipping 16-bit opset bump heuristic for TensorQuantOverrides: structure not as expected.   r   z$The original model opset version is z, which does not support block-wise quantization natively. Please update the model to opset >= 21. Automatically updating the model to opset 21. Please verify the quantized model.   z, which does not support quantization to float 8. Please update the model to opset >= 19. Automatically update the model to opset 19. Please verify the quantized model.z, which does not support 16-bit integer quantization natively. Please update the model to opset >= 21. Automatically update the model to opset 21. Please verify the quantized model.r!  ze, which does not support node fusions. Please update the model to opset >= 11 for better performance.z, which does not support quantization. Please update the model to opset >= 11. Automatically update the model to opset 11. Please verify the quantized model.   )r=  getattrr   r
   rY   r[   rF   rZ   rX   r   rP  AttributeErrorr   r  debugwarningr]   version_converterconvert_version&save_and_reload_model_with_shape_infer)r  weight_typeactivation_typetensor_quant_overridesr  r<  target_opset_versionweight_quant_typeactivation_quant_type_int16_typesneeds_opset21_for_16bit_int16_quant_typesoverrides_listoverrideqtr?  
convert_qts                    r&   update_opset_versionrW  |  sg    &e,M(]KHDSD_@ei  $$++T-=-=-C-CDL/?hCXCh #'='..	0A0AB	w"8"?"?"A .H!l3B/26/&ll95G*%,[[%>
%);;6:3! !/ +* #B& rj1n2=/ B1 1	
  "		 1T5E5E5R5R R2=/ B1 1	

  "		 72=/ B1 1	
  "	"	2=/ BM M	

 
	2=/ B1 1	

  ",&&66uS 7u=Lg 	* 	w MMuv	ws+   AH 'H  H H H &IIc                    [        U S5      n[        R                  R                  [	        U 5      [	        U5      5        [        R
                  " UR                  5       5      n[        U5        UR                  5         U$ )Nz	-inferred)	r  r   shape_inferenceinfer_shapes_pathr2  r  r  r3  unlink)r  inferred_model_pathr  s      r&   load_model_with_shape_inferr]    s`    6z;O**3z?C@S<TUII)2245Eu Lr*   c                   [         R                  " SS9 n[        R                  " U 5      n[	        U5      R                  S5      n[        R                  " X#R                  5       SS9  [        U5      sS S S 5        $ ! , (       d  f       g = f)Nz
ort.quant.)prefixz
model.onnxT)save_as_external_data)
tempfileTemporaryDirectoryr/  deepcopyr   r  r   
save_modelr  r]  )r  quant_tmp_dir
model_copyr  s       r&   rI  rI    sa    		$	$L	9]]]5)
-(11,?

$7$7$9QUV*:6	 
:	9	9s   AA==
Bc                   U R                   [        R                  R                  [        R                  R                  4;   a  [
        R                  R                  U 5      $ [        SU R                   S[        U R                       35      e)Nz&Only float type is supported. Weights z is )r+  r   r
   r   r   r   r5  to_arrayr-   r#   type_to_name)re  s    r&   r$  r$    su    !7!7!=!=z?U?U?]?] ^^  ))+66

01A1A0B$|T_TiTiGjFkl r*   c                    U S-   $ )N_QuantizeLinearr2   tensor_names    r&   add_quant_suffixrn    s    ***r*   c                    U [         -   $ r!   )QUANT_INPUT_SUFFIXrl  s    r&   add_quant_input_suffixrq    s    +++r*   c                    U S-   $ )N_QuantizeLinear_Outputr2   rl  s    r&   add_quant_output_suffixrt    s    111r*   c                    U S-   $ )N_DequantizeLinearr2   rl  s    r&   add_dequant_suffixrw    s    ,,,r*   c                    U S-   $ )N_DequantizeLinear_Inputr2   rl  s    r&   add_dequant_input_suffixrz     s    222r*   c                    U [         -   $ r!   )DEQUANT_OUTPUT_SUFFIXrl  s    r&   add_dequant_output_suffixr}    s    ...r*   )NN)FN)r   ru   N)r   r   r   r   r   float | None)r   numpy.ndarrayr  r   r   r   r  r   r   boolreturn#tuple[numpy.ndarray, numpy.ndarray])FNNN)r  r  r  onnx.TensorProto.DataTyper   r  r   r  r   r~  r  r~  r  r~  r  r  )r  z2tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray])NNr   )r   onnx.TensorProtor  r  r   r  r   r  r   z
int | Noner7  z
str | Noner  r   r  r  )FF)r   r   rV  r   r  ztuple[bool, int])r\  r3  r  r[  )r  r   r  r2  r  r   )rS   )g-C6?)r  r   )r  r   r   r   )r  r	   )r  r	   r  r  )r  r	   r  r   )r  r	   rJ  rF   rK  zQuantType | NonerL  zdict | Noner  r   r  r	   )r  r   r  r	   )r  r	   r  r	   )re  r
   r  r  )rm  r2  r  r2  )r  r2  )u
__future__r   r/  r  r  ra  enumr   pathlibr   r   r   r   r   r   r   r	   r
   r   r   r   onnx.helperr   r   r   r   onnx.referencer   onnxruntimer   r   r   onnx.reference.op_runr   ImportError__producer____version__onnx_domain	ms_domainQUANT_OP_NAMErp  DEQUANT_OP_NAMEr|  r*  MODEL_SIZE_THRESHOLDr   r]   r   r   r   FLOAT8_TYPESr  r   rC  r   ri  r   r<   rF   rf   rU   rt   rW   r[   rY   ra   r_   r   r   rp   ro   rr   rq   r   rQ  rO  r   r   r   r   r   r  r  r  rM  r   rT  rY  r4  rc  rm  r|  r  r  r  r  r  r  r  r	  r  r$  r-  r0  r3  r5  r=  rW  r]  rI  r$  rn  rq  rt  rw  rz  r}  )r  s   0r&   <module>r     sL   #   	      0 0 > > & Q Q - P P7 	 , $2 ' !  ''))%%))	 584Dq4Dq
SZ[fhiSjloHp*Q'*4Dqt  #> #>L$   V!4  %++g"6  %++g"6!!5;;x#8''  %    5;;q#DekkRU]b]h]hFi"j%++d%**"Eu{{SV^c^h^hGi!j!!EKK$FTYafamamHn#o  5;;vU[[#I5;;W\didodoKp"q  5;;q#>BV[@\"]%++b"=u{{1TX?Y!Z  %++d%**"Eu{{SV^c^h^hGi!j  5;;vU[[#I5;;W\didodoKp"q!    5;;q#DekkRU]b]h]hFi"j%++c"DekkRT\a\f\fFg!h!!EKK$FTYafamamHn#o  5;;vU[[#I5;;W\didodoKp"q  5;;q#>AUZ@["\%++b"=u{{1TX?Y!Z  )+ A 13h<~8v!H``` ` 	`
 ` )`N #'"&"&;G
;G);G ;G 	;G
 !;G  ;G  ;G );G~ hl:=7:=D $(c c )c  c  	c 
 c  "c  c  c L@	< >% %8' '"#J0@R$Rj2n
k 77 )-*.WWW &W (	W
 W Wt7+,2-3/S'  0 rs   "[4 \;\4\ ?\ 