o
    ‡ZŽh-T  ã                   @   sä  d dl mZmZmZ d dlZd dlmZ d dlmZm	Z	m
Z
mZ d dlmZ d dlmZmZmZmZ d dlmZ d dlmZmZmZ d d	lmZmZmZ d d
lmZ dgZdede eef defdd„Z!dedede eef ddfdd„Z"dedede eef fdd„Z#dede eef de eef defdd„Z$dedede%fdd„Z&d ej'jde eef fd!d"„Z(d#d$„ Z)de eef de ee*f fd%d&„Z+d'e ee*f de eef d(e,de ee
f fd)d*„Z-d+eeef d,ed-ed ej.j/d.e e%ej.j/f d/e ee
f d(e,defd0d1„Z0d+ed-ed ej.j/d.e e%ej.j/f d/e ee
f d(e,ddfd2d3„Z1d+ed ej.j/d.e e%ej.j/f d4ed/e ee
f d(e,dee fd5d6„Z2d+ed ej'jd/e ee
f d(e,fd7d8„Z3	d;d ed9e e%e4e%e5f f d(e,defd:d„Z6dS )<é    )ÚAnyÚOptionalÚUnionN)Ú
FakeTensor)Ú
CUSTOM_KEYÚNUMERIC_DEBUG_HANDLE_KEYÚObserverOrFakeQuantizeÚQConfigMapping)ÚPrepareCustomConfig)Ú_create_obs_or_fq_from_qspecÚ_insert_obs_or_fqÚ _is_activation_post_process_nodeÚ_save_state)Ú
QConfigAny)Ú
EdgeOrNodeÚQuantizationSpecBaseÚSharedQuantizationSpec)ÚGraphÚGraphModuleÚNode)ÚArgumentÚprepareÚedge_or_nodeÚshared_with_mapÚreturnc                 C   s*   ||  }|| kr
| S t ||ƒ}||| < |S )zûFind the root node for the sharing tree
    Args:
        edge_or_node: edge/node that we want to find the root
        shared_with_map: each edge/node points to the parent, the root node will points to itself

    Returns:
        root edge/node
    ©Ú_find_root_edge_or_node)r   r   ÚparentÚroot© r   úQ/var/www/auris/lib/python3.10/site-packages/torch/ao/quantization/pt2e/prepare.pyr   #   s   
r   r   Úchildc                 C   s    t | |ƒ}t ||ƒ}|||< dS )zHMerge the subtree for `child` with `parent`, the order is important hereNr   )r   r!   r   Zroot_parentZ
root_childr   r   r    Ú_union7   s   

r"   Úqspecc                 C   s$   t |tƒr|j}t|| |ƒ dS dS )a  Update the `shared_with_map` based on the qspec, this applies the `SharedQuantizationSpec`
    configuration and established the relationship between `edge_or_node` with the edge/node that it
    is pointing to, we'll use this information in the end to get the group id
    N)Ú
isinstancer   r   r"   )r!   r#   r   r   r   r   r    Ú_update_shared_withC   s   
	ür%   Úedge_or_node_to_qspecc                 C   s2   t | tƒr| j}t||ƒ}|| } t| ||ƒS | S )a  Unwraps qspec to get the final root qspec (non SharedQuantizationSpec)
    if qspec is SharedQuantizationSpec
       (1). tries to find the root edge or node for the node that the qspec points to
       (2). recursively find the root qspec based on the qspec for the root node
    )r$   r   r   r   Ú_unwrap_shared_qspec)r#   r&   r   Zsharing_withr   r   r   r    r'   S   s   


r'   Úqspec_aÚqspec_bÚ	attr_namec                 C   s@   t | |ƒot ||ƒot| |ƒt||ƒkpt | |ƒ ot ||ƒ S ©N)ÚhasattrÚgetattr)r(   r)   r*   r   r   r    Ú_has_same_attre   s   
ÿýür.   Úmodelc                 C   st   i }| j jD ]1}t|dƒr7d|jv r7|jd }|j ¡ D ]\}}||f}|||< q|jdur7|}|j}|||< q|S )zPGet a map from EdgeOrNode to quantization spec based on annotations on the nodesÚmetaÚquantization_annotationN)ÚgraphÚnodesr,   r0   Zinput_qspec_mapÚitemsZoutput_qspec)r/   r&   ÚnZqaZ
input_to_nr#   Ú
input_edgeÚoutput_noder   r   r    Ú_get_edge_or_node_to_qspeco   s   


€r8   c                    sX   d‰||v r|| }t |||ƒ‰ˆdur(t‡ ‡fdd„dD ƒƒr*t|| |ƒ dS dS dS )zçUnion input edge with another edge or node, used in implicit sharing to point the current input
    edge to other user edges of the producer node, or the output of producer node since these are
    referring to the same Tensor
    Nc                 3   s    | ]	}t ˆˆ |ƒV  qd S r+   )r.   )Ú.0Úattr©Úinput_edge_root_qspecZ
root_qspecr   r    Ú	<genexpr>‘   s
   € 
ÿ
ÿz)_union_input_edge_with.<locals>.<genexpr>)ZdtypeZ
is_dynamicZ	quant_minZ	quant_maxZqschemeZch_axisÚscaleZ
zero_point)r'   Úallr"   )r6   r<   r   r&   r   r#   r   r;   r    Ú_union_input_edge_with   s   þïr@   c                 C   s.  dd„ |   ¡ D ƒ}|  ¡ D ]e\}}t|tjjƒr!|}t|||ƒ q|}t|| |ƒ}t|tƒs0J ‚|\}}|j	d j
rlt|tƒrDt|tƒsMtd||f› ƒ‚|jD ]}	|	|u rWqP||	f}
t|||
| |ƒ qPt|||| |ƒ t|||ƒ qd}i }|  ¡ D ]}t||ƒ}||vrŽ|||< |d7 }|| ||< q{|S )a  Map from edge/node to the group ID, generated from quantization annotations,
    edge/node with the same group ID should use the same observer/fake_quant instance

    This is applying SharedQuantizationSpec configuration and map each edge/node to a group
    There is another implicit sharing that's built in the quantization, when we have the following:
       * op1 -> op2
       * output of op1: int8_qspec
       * (op1 -> op2) input edge: int8_qspec
    we'll assume sharing between the output of op1 and input of (op1 -> op2) since these are the same Tensor.

    Figuring out the correct group ID for all edge/node is a standard union find problem:
    https://www.geeksforgeeks.org/introduction-to-disjoint-set-data-structure-or-union-find-algorithm/

    Args:
        edge_or_node_to_qspec: Dictionary from edge_or_node to the qspec, derived from annotations
    Returns:
        edge_or_node_to_group_id: Dictionary from edge_or_node to group_id (int), all edge or node that
        belongs to the same group should have the same id

    Example:
        op2 -> cat1 -> cat2
           op1 /        /
                     op3
        edge_or_node_to_qspec: {
            op1: int8_qspec,
            op2: int8_qspec,
            (op1, cat1): int8_qspc,
            (op2, cat1): SharedQuantizationSpec((op1, cat1)),
            cat1: SharedQuantizationSpec((op1, cat1)),
            (op3, cat2): int8_qspec,
            (cat1, cat2): SharedQuantizationSpec((op3, cat2)),
            cat2: SharedQuantizationSpec((op3, cat2)),
        }

        edge_or_node_to_group_id = _get_edge_or_node_to_group_id(edge_or_node_to_qspec)
        edge_or_node_to_group_id: {
            op1: 1,
            op2: 1,
            (op1, cat1): 1,
            (op2, cat1): 1,
            cat1: 1,
            (op3, cat2): 1,
            (cat1, cat2): 1,
            cat2: 1,
        }
        # everything are in the same group because (cat1) and (cat1, cat2) are implicitly shared, which
        # connects the two sharing group around cat1 and cat2 op due to transitive sharing
    c                 S   s   i | ]}||“qS r   r   )r9   Úkr   r   r    Ú
<dictcomp>Ú   s    ÿz1_get_edge_or_node_to_group_id.<locals>.<dictcomp>r1   z=Expected input_edge to have type Tuple[Node, Node], but got: r   é   )Úkeysr4   r$   ÚtorchÚfxr   r%   r'   Útupler0   Zallow_implicit_sharingÚ	ExceptionÚusersr@   r   )r&   r   r   r#   r7   r6   r<   Úargr5   ÚuserZarg_to_user_edgeZcur_group_idÚedge_or_node_to_group_idr   r   r   r    Ú_get_edge_or_node_to_group_id¥   s\   5ÿÿÿ
û	û
rM   rL   Úis_qatc                 C   sJ   i }i }|  ¡ D ]\}}| | }||vrt|||ƒ||< || ||< q|S )z¶Generates the EdgeOrNode to observer/fake_quant instances
    Makes sure that for EdgeOrNode that has the same group_id should have the same observer or fake quant
    instances
    )r4   r   )rL   r&   rN   Úobs_or_fq_mapZgroup_id_to_obs_or_fqr   r#   Zgroup_idr   r   r    Ú_get_obs_or_fq_map#  s   	ÿrP   ÚnoderJ   ÚqconfigÚnamed_modulesrO   c              	   C   sX  t |ttfƒr#g }|D ]}t| ||||||ƒ}	| |	¡ qt|ƒ|ƒS t |tƒs*|S t |tƒs1J ‚|}
|}t||ƒrD|jd }t||ƒs:t |tƒsRJ dt|ƒ› ƒ‚|| f}||vr\|
S || }|du rf|
S | 	|d¡}|durzt
|ƒt
|ƒkrz|
S |j ¡ D ]}t||ƒs‡q||j }t
|ƒt
|ƒkr˜|  S qt |jtƒs¡J ‚t|||||jƒ}
|
S )zk
    Given a `node` and an `arg`, inserts an input observer between
    `node` and `arg` if necessary.
    r   z0expect original argument to be a Node, but got: N)r$   ÚlistrG   Ú-_maybe_insert_input_observer_for_arg_or_kwargÚappendÚtyper   r   ÚargsÚgetÚidrI   rD   Útargetr2   r   r   )rQ   rJ   rR   r/   rS   rO   rN   Znew_arg_to_returnZ	inner_argZnew_inner_argÚnew_argZoriginal_argr6   Zinput_edge_obs_or_fqZarg_as_output_obs_or_fqZmaybe_obs_nodeZmaybe_obs_modr   r   r    rU   :  sd   ù	



ÿÿþÿ

ÿÿrU   c           	   	   C   sˆ   g }| j D ]}t| ||||||ƒ}| |¡ q| jtjjjjks=| jtjjj	jks=| jtjjj
jks=t| jƒdks=J dƒ‚t|ƒ| _ dS )a  
    If needed, inserts observers to the input args and kwargs of `node`.
    Note: modifies `node` inplace.

    For example, if cur_node needs an observer after prev_node, we change from

      prev_node -> cur_node

    To

      prev_node -> obs -> cur_node

    r   z, expecting kwargs for aten op IR to be emptyN)rX   rU   rV   r[   rE   ÚopsZatenÚcloneÚdefaultZ
zeros_likeZgeluÚlenÚkwargsrG   )	rQ   rR   r/   rS   rO   rN   Únew_argsrJ   r\   r   r   r    Ú&_maybe_insert_input_observers_for_nodeŽ  s&   
ù	ÿrc   r2   c                 C   s€   | |v r>||  }t | ||||ƒ}t| tƒr<t|tƒr<t| jv r<t| jt v r<t|jvr0i |jt< | jt t |jt t< |S d S r+   )r   r$   r   r   r0   r   )rQ   r/   rS   r2   rO   rN   Zoutput_act_obs_or_fqZ
new_outputr   r   r    Ú&_maybe_insert_output_observer_for_nodeÀ  s(   
ÿÿþ


ÿþrd   c           
      C   sº   d| j v r
| j d nd }|d u rd S t|jddƒ}t| d ||||ƒ d| j v o/t| j d tƒ}|s4d S t| |||j||ƒ}|d u rDd S t| j	 
¡ ƒ}|D ]}	|	|u rTqM|	 | |¡ qMd S )Nr1   F)Zremove_duplicateÚval)r0   ÚdictrS   rc   r$   r   rd   r2   rT   rI   rD   Zreplace_input_with)
rQ   r/   rO   rN   Z!this_node_quantization_annotationrS   Zoutput_is_a_tensorZmaybe_output_obs_nodeZ
orig_usersZ	user_noder   r   r    Ú1_maybe_insert_input_and_output_observers_for_nodeÝ  s:   

ÿýú	ÿýrg   Únode_name_to_scopec           	   	   C   sz   t | jjƒ}t| ƒ}t|ƒ}t|||ƒ}|r|| |ƒ |D ]	}t|| ||ƒ qt| | jƒ} t| i |t	ƒ i t
ƒ |tƒ ƒ | S r+   )rT   r2   r3   r8   rM   rP   rg   r   r   r
   r	   Úset)	r/   rh   rN   Zobs_or_fq_callbackZnodes_before_observationr&   rL   rO   rQ   r   r   r    r     s0   ÿ
ÿø
r+   )7Útypingr   r   r   rE   Ztorch._subclassesr   Ztorch.ao.quantizationr   r   r   r	   Z&torch.ao.quantization.fx.custom_configr
   Z torch.ao.quantization.fx.preparer   r   r   r   Ztorch.ao.quantization.qconfigr   Ztorch.ao.quantization.quantizerr   r   r   Ztorch.fxr   r   r   Ztorch.fx.noder   Ú__all__rf   r   r"   r%   r'   Ústrr.   rF   r8   r@   ÚintrM   ÚboolrP   ÚnnÚModulerU   rc   rd   rg   rG   rW   r   r   r   r   r    Ú<module>   s  ÿÿ
ÿ
þÿþ
ý
üÿþ

ýÿ
þ
ý
üÿÿ
ÿ
ÿ

þ$
ÿ

þ~
ÿ
þý

ü
ÿþýüû
úù
øTÿþýü
ûú
ù2ÿþýü
ûú
ùÿþ
ý
ü=üÿþýû