o
    hm                     @   sv  d Z ddlZddlZddlmZ ddlmZmZ ddlm	Z	m
Z
mZmZ ddlZddlmZ ddlmZ ddlmZ eeeef eeeeef f e	f Zd	e_e	Zee
ee
e
f ee
ee
e
f f e	f Zd	e_G d
d dZejjejjejjejjejjejj ejj!ejj"ejj#ejj$ejj%ejj&ejj'ejj(ejj)hZ*ejj+j,ejj+j-ejj+j.ejj+j/ejj+j0ejj+j1ejj+j2ejj+j3ejj+j4ejj+j5ejj+j6ejj+j7ejj+j8ejj+j9ejj+j:ejj+j;ejj+j<ejj+j=ejj+j>ej?ej@ej>ejAejBejCejDejEejFhZGejHddddddddddddddddddddd d!d"d#hZId$d% ZJd&d' ZKd(d) ZLd*d+ ZMd,e	d-eNd.e	fd/d0ZOd1d2 ZPd3d4 ZQd5d6 ZRd7d8 ZSd9d: ZTd;d< ZUd=d> ZVd?d@ ZWdAdB ZXdCdD ZYdEdF ZZd.e[fdGdHZ\dIdJ Z]dKdL Z^dMej_dNej_d.e[fdOdPZ`dQeadReadSe[dTejbdUe[d.eeaeaf fdVdWZcdXdY ZddZd[ Zed\ejjfd]ejjfd.eeN fd^d_Zgd`e
daeheNe	f d.eheNe	f fdbdcZid`e
d.ddfdedfZjdge
daeheNe	f d.ddfdhdiZkdQeadRead.dfdjdkZlejmfdMej_dNej_dQeadReadTejbdlej_dSe[dmejnd.eej_ej_f fdndoZod`e
d.eafdpdqZpdrejjfdsee	dtf d.eheNee	dtf f fdudvZqdwejjfd.e	fdxdyZrg dzZsdS ){z?
Utils shared by different modes of quantization (eager/graph)
    N)OrderedDict)getfullargspec	signature)AnyCallableOptionalUnion)	QuantType)Node)is_parametrizedztorch.ao.quantization.utilsc                   @   s   e Zd ZdZdS )MatchAllNodeznA node pattern that matches all nodes, used in defining
    fusion patterns in FX Graph Mode Quantization
    N)__name__
__module____qualname____doc__ r   r   o/var/www/html/construction_image-detection-poc/venv/lib/python3.10/site-packages/torch/ao/quantization/utils.pyr   %   s    r   relurelu_
contiguousdetachdetach_hardsigmoidhardsigmoid_permuterepeatrepeat_interleavereshaperesize_shapesigmoidsigmoid_sizesqueezesqueeze_tanhtanh_	transpose	unsqueeze
unsqueeze_viewc                 C   sR   | j dko	| jtv }| j dko| jtv }| j dko#t|t| j tv }|||fS )Ncall_functioncall_methodcall_module)optarget	func_listmethod_listtypestrmodule_type_list)nodemodulesis_call_functionis_call_methodis_call_moduler   r   r   
check_nodex   s
   
r:   c                 C   s   |   }|| |S )a  
    Combines two dictionaries.

    This function takes two dictionaries as input and returns a new dictionary
    that contains all the key-value pairs from both input dictionaries.
    If there are any duplicate keys in the `additional_dict`, the values
    from the `additional_dict` will overwrite those in the `default_dict`.
    Args:
        default_dict (dict): The main dictionary that will be used as the base
        additional_dict (dict): The dictionary used to update `default_dict`

    Returns:
        dict: The resulting dictionary
    Example:
        >>> x = dict(a=1, b=1)
        >>> y = dict(b=2, c=3)
        >>> get_combined_dict(x, y)
        {'a': 1, 'b': 2, 'c': 3}
    )copyupdate)default_dictadditional_dictdr   r   r   get_combined_dict   s   
r@   c                 C   s   | t jkp	| t jkS N)torchper_tensor_affineper_tensor_symmetricqschemer   r   r   is_per_tensor   s   rG   c                 C   s   | t jt jt jfv S rA   )rB   per_channel_affine per_channel_affine_float_qparamsper_channel_symmetricrE   r   r   r   is_per_channel   s
   rK   objfqnreturnc                 C   s   t t|d| S )zO
    Given an obj and a fqn such as "foo.bar.baz", returns gm.foo.bar.baz.
    .)	functoolsreducegetattrsplit)rL   rM   r   r   r   getattr_from_fqn      rT   c                 C   s   t jt jt jt jt jt jt jt jt jt jt jt jt jt jt j	t j	t j
t j
t jt jt jt jt jt ji}| |v s>J dt|  ||  S )NzUnsupported dtype: )rB   quint8uint8qint8int8qint32int32quint4x2quint2x4uint16int16float8_e5m2float8_e4m3fnr3   )qdtypeDTYPE_MAPPINGr   r   r   to_underlying_dtype   s   rd   c                 C   s   ddl m} t| dd }| j}||d}|rt| |r d |dS t|r(tj}nt|r:|tj	kr4tj
}| j|d< ntd| ||d< |  \}}||d< ||d< t| d	r]| j|d	< t| d
rg| j|d
< |S )Nr   )PlaceholderObserverrF   )rF   dtypeaxiszUnrecognized qscheme: scale
zero_point	quant_min	quant_max)torch.ao.quantization.observerre   rR   rf   
isinstancerG   rB   rC   rK   rJ   rH   ch_axisRuntimeErrorcalculate_qparamshasattrrj   rk   )observer_or_fake_quantre   rF   rf   qparamsrh   ri   r   r   r   get_qparam_dict   s,   






rt   c                 C   sD   t |}||i }t| |v sJ dt|  d| |t|  S )a  Get the observed/quantized custom module class that we need
    to swap `custom_module` to
    Input:
        custom_module: input, can be an instance of either a float or observed custom module
        custom_module_class_mapping: the float to observed or observed to quantized custom module class mapping
        qconfig: qconfig configured for the custom module

    Output:
        corresponding observed/quantized custom module class for input custom module instance
    z5did not find corresponding observed module class for z in mapping: )get_quant_typegetr2   )custom_modulecustom_module_class_mappingqconfig
quant_typeclass_mappingr   r   r   get_swapped_custom_module_class   s   r|   c                 C      | d usJ |   }|jS rA   )
activationrf   )ry   r~   r   r   r   activation_dtype      r   c                 C   r}   rA   )weightrf   )ry   r   r   r   r   weight_dtype  r   r   c                 C   s>   t | tjtjtjtjtjtjtjtj	tj
tjf
v ot|  S )zGiven a qconfig, decide if the activation needs to be
    quantized or not, this includes quantizing to quint8, qint8 and qint32 and float16
    )r   rB   rV   rX   rZ   float16rW   rY   r_   r[   r`   ra   #activation_is_dynamically_quantizedry   r   r   r   "activation_is_statically_quantized  s   r   c                 C   s   t | \}}}|S )zGiven a qconfig, decide if the activation needs to be
    dynamically quantized or not, this includes dynamically quantizing to
    quint8, qint8 and float16
    )get_qconfig_dtypes)ry   _activation_dtype_activation_is_dynamicr   r   r   r     s   r   c                 C      t | tjtjtjtjfv S )zGiven a qconfig, decide if the activation needs to be
    quantized to int8 or not, this includes quantizing to quint8, qint8
    )r   rB   rV   rX   rW   rY   r   r   r   r   activation_is_int8_quantized"  s   r   c                 C   s   t | tjtjfv S )zXGiven a qconfig, decide if the activation needs to be
    quantized to int32 or not
    )r   rB   rZ   r[   r   r   r   r   activation_is_int32_quantized.  rU   r   c                 C   s4   t | tjtjtjtjtjtjtjtj	tj
tjf
v S )zKGiven a qconfig, decide if the weight needs to be
    quantized or not
    )r   rB   rV   rX   r   r\   rW   rY   r_   r[   r`   ra   r   r   r   r   weight_is_quantized5  s   r   c                 C   r   )zVGiven a qconfig, decide if the weight needs to be statically
    quantized or not
    )r   rB   rV   rX   rW   rY   r   r   r   r   weight_is_statically_quantizedG  s   r   c                 C   s2   t | \}}}|tjtjfv o|tjtjfv o|S )zTGiven a qconfig, returns True if this op is using int8 dynamic
    quantization
    )r   rB   rV   rW   rX   rY   )ry   r   r   r   r   r   r    op_is_int8_dynamically_quantizedN  s   r   c                 C   s6   | dusJ |   }|  }t|dd}|j|j|fS )zgreturns the qconfig tuple for qconfig:
    (activation_dtype, weight_dtype, activation_is_dynamic)
    N
is_dynamicF)r~   r   rR   rf   )ry   r~   r   act_is_dynamicr   r   r   r   \  s
   r   c              
   C   s   | d usJ |   }|  }tjtjtjtjtjtjtj	tj
tjtjg
}|j|v r?t|dr4|jr4tjS |j|v r<tjS tjS |jtjkrYt|drP|jrPtjS |jtjkrYtjS td|j d|j d)Nr   z=Unrecognized dtype combination in get_quant_type: activation(z	),weight())r~   r   rB   rV   rX   r\   rZ   rW   rY   r_   r[   r`   ra   rf   rq   r   r	   DYNAMICSTATICWEIGHT_ONLYr   	Exception)ry   r~   r   static_dtypesr   r   r   ru   g  s<   


ru   min_valmax_valc                 C   s   |   dks|  dkrtd dS |  dks| dkrB| tdkr2|tdkr2td dS | |ks@J d|  d| dS t| |ksSJ d|  d| dS )	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floatrB   all)r   r   r   r   r   check_min_max_valid  s&   r   rj   rk   has_customized_qrangerf   reduce_rangec           
      C   s  |ro|t jt jfv rd\}}nd\}}| |}}|dur%|dur%||}}|| d }	|t jt jfv rFd|	  k r@dksEJ d J dn|t jt jfv r`d|	  k r[dks`J d	 J d	|rk| d
 |d
 } }| |fS |t jt jfv r|rd\} }| |fS d\} }| |fS |t jt jfv r|rd\} }| |fS d\} }| |fS |t jt jfv rd\} }| |fS |t jfv rd\} }| |fS |t jfv rd\} }| |fS d\} }| |fS )ztCalculates actual qmin and qmax based on the quantization range,
    observer datatype and if range is reduced.
    )r   l    )r      N   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   r   )i   i)r   i  )i i  )r      )	rB   rZ   r[   rX   rY   rV   rW   r^   r_   )
rj   rk   r   rf   r   initial_quant_mininitial_quant_maxcustom_quant_mincustom_quant_max
qrange_lenr   r   r   calculate_qmin_qmax  s`   

	r   c                 C   s4   |  dd}t|dkrd|d fS |d |d fS )z,
    Turn 'foo.bar' into ['foo', 'bar']
    rO   r    r   )rsplitlen)r/   rr   r   r   _parent_name  s   r   c                 C   s6   t | jdkr	dS t| rt | jdkod| jv S dS )z
    Checks if module._modules is empty or
    if module is a parametrization, checks that module._modules only has
    the 'parametrizations' module
    r   Tr   parametrizationsF)r   _modulesr   )moduler   r   r   )has_no_children_ignoring_parametrizations  s
   r   root	submodulec                 C   s&   |   D ]\}}||u r|  S qdS )aZ  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pr   r   r   _get_path_of_module  s
   r   flocc                    s    fdd|  D S )zGet local keyword arguments

    Example::

    >> def f(self, a, b=9):
           pass
    >> loc = {"a": 6, "c": 7}
    >> _get_signature_locals(f, loc)
    {"a": 6}
    c                    s$   i | ]\}}|t  jv r||qS r   )r   
parameters).0kvr   r   r   
<dictcomp>"  s   $ z)_get_signature_locals.<locals>.<dictcomp>)items)r   r   r   r   r   _get_signature_locals  s   r   zOrderedDict[str, Any]c                 C   sf   i }t | j D ]%\}}|j|jur|j||< q	|j|ju r$d||< q	|j|ju r.i ||< q	t|S )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   r   defaultemptykindVAR_POSITIONALVAR_KEYWORDr   )r   kwargsnameparamr   r   r   _get_default_kwargs%  s   

r   funcc                 C   s@   t | }t| |}| }| D ]\}}||v r|||< q|S )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   r;   r   )r   r   default_kwargslocal_kwargsnormalized_kwargsattrvalr   r   r   _normalize_kwargs:  s   
r   c                 C   s8   | d  kr|ksJ d J d| |k sJ ddS )ae  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   )rj   rk   r   r   r   validate_qmin_qmaxP  s   
r   epsrF   c                 C   s*  t | |stjdg| jjdtjdg| jjdfS t| t| }t|t|}	|j}
tj|	 tj
|
d}tj|	 tj|
d}||
}|tjksS|tjkrt| |	}	|	t|| d  }t||}|tjtjfv r|r||	 || d }nO||	 d}nF|tjkr||  t||  }t||k|t|}d|  | }n$|	| t||  }t||}|t|| tj }t|||}t|jdkrtjt|g|j|
d}t|jdkr	tjt|g|j|
d}|tjkr	tjt|g|j|
d}|tj
|tjfS )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   )rf   r   r      )r   rB   tensorr   r2   min
zeros_likemaxonesr"   doublezerosint64torD   rJ   r   rW   rV   new_fullrI   where	ones_likeroundintclampr   r   rf   )r   r   rj   rk   rf   r   r   rF   min_val_negmax_val_posr   rh   ri   r   r   r   determine_qparamsl  sP   



r   c                 C   s   t t| jS )zGet number of positional args for a function

    Example::

    >> def f(self, key1=3, key2=3):
           pass
    >> _get_num_pos_args(f)
    3
    )r   r   argsr   r   r   r   _get_num_pos_args  s   
r   modelexample_inputs.c                    sR   | i   fdd}t jjj|t jj_z| |  W t jj_ S t jj_w )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           	         s   t | }t| j|}t| jd }|t| }|r+|r+|jdd |d8 }|r+|s||  t	|}t
| }|d urC| |< | g|R i |S )Nr   F)last)listr;   r   forwardr   r   popitemextendvaluestupler   )	selfr   r   submodule_example_inputsr   num_args
num_to_popsubmodule_example_inputs_tuplerM   fqn_to_example_inputsorig_module_callr   r   r   _patched_module_call  s   
z7get_fqn_to_example_inputs.<locals>._patched_module_call)rB   nnModule__call__)r   r   r  r   r  r   get_fqn_to_example_inputs  s   



r
  r   c                 C   s   dd |   D dd |  D B }	 tdtdh|kr*td tdh}	 t|dks8J d| t|dkrFtt|}|S d	}|S )
z
    Returns the unique device for a module, or None if no device is found.
    Throws an error if multiple devices are detected.
    c                 S   s   h | ]}|j qS r   r   )r   r   r   r   r   	<setcomp>  s    z0_assert_and_get_unique_device.<locals>.<setcomp>cpumetazfBoth 'meta' and 'cpu' are present in the list of devices. Module can have one device. We Select 'cpu'.r   zKprepare only works with cpu or single-device CUDA modules, but got devices r   N)	r   buffersrB   r   r   r   r   nextiter)r   devicesr   r   r   r   _assert_and_get_unique_device   s&   r  )NodePatternPatternr   r:   r@   rG   rK   rT   rt   r|   r   r   r   r   r   r   r   r   r   r   ru   r   r   r   r
  rd   r   r   )tr   rP   r   collectionsr   inspectr   r   typingr   r   r   r   rB    torch.ao.quantization.quant_typer	   torch.fxr
   torch.nn.utils.parametrizer   r   r  r   QuantizerClsr  r   r  ReLUReLU6AdaptiveAvgPool1dAdaptiveAvgPool2dAdaptiveAvgPool3d	AvgPool1d	AvgPool2d	AvgPool3d	MaxPool1d	MaxPool2d	MaxPool3dIdentityHardsigmoidSigmoidTanhr4   
functionaladaptive_avg_pool1dadaptive_avg_pool2dadaptive_avg_pool3delu	hardswishinstance_norm
layer_norm
leaky_relusilumishdropout
max_pool1d
max_pool2d
max_pool3dr   hardtanh	hardtanh_r   r    r'   r   r#   stacksumr%   r(   catr0   meanr1   r:   r@   rG   rK   r3   rT   rd   rt   r|   r   r   r   r   r   r   r   r   boolr   r   ru   Tensorr   r   rf   r   r   r   r  r   dictr   r   r   r   rC   rF   r   r   r
  r  __all__r   r   r   r   <module>   sJ  &"
$	$

>
&$	
N

9