U
    Kc                     @   sL  U d dl 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
 d dlmZ d dlmZ d dlmZ d dlmZmZmZ d dlmZ d d	lmZmZ d d
lmZmZ ddlmZ d dlm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( d dl)m*Z* d dl+Z+d dl,Z,dddddddddddddddddd d!d"d#d$d%d&d'd(d)d*d+gZ-ejj.j/ejj.j0ejj.j1hZ2ee$e	e3d,d-d'Z4ee$e	e3d,d.d&Z5d|e6d0d1d!Z7d2d Z8e e!e&e6e'e e6f e#e6e$f f  d3d4dZ9d}eejj:ee#e6ejj:f ee#e6e&e6e;f f e3e6ed6	d7d*Z<e#ee#e(e(f f e"e$ d8d9dZ=d:d Z>e e d;d<dZ?e e3e d=d>dZ@e6e d?d@dZAee!e"e  dAdBdZBee"e edCdDd ZCejj:e$dEdFdZDejj:ee6e$edGdHdZEe6e$e$e#e6ejj:f ee#e6e&e6e;f f e&eef dIdJdZFee#e6ejj:f e#ee3f e3dKdLdZGee"eH dAdMdZIe"eH e ege"eH f dNdOd+ZJe*d$dPZKeKdQdRej3eJdgeLeJdSgieKdQdTeHeIieKdQdUeHeIieKdQdVeHeIieKdQdWeHeJdgieKdQdXeHeIieKdQejMeHeIieKdQdYeHeJdgieKdQdZeHeJdgieKdQejNeHeJdgieKdQd[eHeIiiZOe#eKe#e'e;ejPf e ege"eH f f f eQd(< i ZRe#e'e;ejPf e ege"eH f f eQd< ee#e'e;ejPf e ege"eH f f dAd\dZSee3dAd]d%ZTee3dAd^d"ZUd~ee#e6ej:f e!e(ej:  e$e!e d_d`d#ZVee&e$daf eedbdcdZWee3e&e"e6 e"e(e$  f dddedZXdee#e6ejj:f ee!e$ e3dfdgdhZYee#e6ejj:f e!ejj: didjdkZZeejj:e#e6ejj:f eedldmdnZ[eejj:e#e6ejj:f eedldodpZ\ee#e6ejj:f e!e dqdrdsZ]edtdudvZ^e'eef ed3dwdxZ_dee
e3e3dydzd{Z`dS )    N)
QConfigAny	QuantType)BackendConfigDTypeWithConstraints)FakeQuantizeBase)ObserverBase)DeQuantStub)"activation_is_statically_quantizedis_per_tensoris_per_channel)is_activation_post_process)GraphModulemap_arg)GraphNode   )PrepareCustomConfig)	CallableOptionalListDictAnySetTupleUnionType)
namedtupleall_node_args_except_firstall_node_args_have_no_tensorsassert_and_get_unique_devicecollect_producer_nodescreate_getattr_from_value'create_node_from_old_node_preserve_metacreate_qparam_nodesEMPTY_ARG_DICTget_custom_module_class_keysget_linear_prepack_op_for_dtypeget_new_attr_name_with_prefix(get_non_observable_arg_indexes_and_typesget_per_tensor_qparamsget_qconv_opget_qconv_prepack_opget_quantize_node_info#get_skipped_module_name_and_classes graph_module_from_producer_nodesgraph_pretty_stris_get_tensor_info_nodemaybe_get_next_moduleNodeInfonode_return_type_is_intnode_arg_is_biasnode_arg_is_weightNON_OBSERVABLE_ARG_DICTNON_QUANTIZABLE_WEIGHT_OPSquantize_nodereturn_arg_list)nodeargbackend_configreturnc                 C   sp   t | trl| jdkrl| j|jkrl|j| j jd}|dk	r\|t| jk r\| j| |kr\dS | j	d|kS dS )zReturns if node arg is weightcall_functionweightNTF

isinstancer   optargetZconfigsZ_input_type_to_indexgetlenargskwargs)r:   r;   r<   Zweight_index rH   B/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/quantization/fx/utils.pyr5   G   s     $c                 C   sp   t | trl| jdkrl| j|jkrl|j| j jd}|dk	r\|t| jk r\| j| |kr\dS | j	d|kS dS )zReturns if node arg is biasr>   ZbiasNTFr@   )r:   r;   r<   Z
bias_indexrH   rH   rI   r4   P   s     $T)r=   c                 C   s  t d}t d}dddddd}i }d	}|D ]}t|||< q0g }| jD ]}	t|	j}
|rl|
d
d}
t|	j}|r||kr|| }t|	j}|r|	|}|rd|
d d}|	|}|rd|
d d}|d
d}t|	j}|r|d
d}t|	j}t||
||||fD ] \}}t|| t|||< q||
||||g qLd}d}||j||7 }|D ]}||j||7 }qp|r|d7 }|S )zyReturns a printable representation of the ops in the graph of g.
    If shorten is True, tries to abbreviate fields.
    z<built-in function (.*)>z <built-in method (.*) of type.*>ZplchdrZgt_prmZcl_funZcl_modZcl_meth)placeholderget_attrr>   call_modulecall_method)namerB   rC   rF   rG   activation_post_processZobsz<bi_fun r   >z	<bi_meth  z9{:<{name}} {:<{op}} {:<{target}} {:<{args}} {:<{kwargs}}
z'*obs_{n} = activation_post_process_{n}
)recompilerE   nodesstrrN   replacerB   rC   searchgrouprF   rG   zipmaxappendformat)gshortenZbuilt_in_func_reZbuilt_in_meth_reZop_dictZmax_lensZ	col_namessresultsnrN   rB   rC   Zbuilt_in_funcZbuilt_in_methrF   rG   kvres_strZ
format_strresultrH   rH   rI   r/   Y   sX    








c                 C   s>   t | jstd|  \}}t|}t|}| j}|||fS )Nz)Only per tensor quantization is supported)r
   qschemeAssertionErrorcalculate_qparamsfloatintdtype)rO   scale
zero_pointrk   rH   rH   rI   r)      s    )rO   r=   c           
      C   s  | j }d}t| dr| j}d}|tjtjfkrt| dsd}|  \}}t| jrrt	| j
}||||d}tj}qt|}t	|}|||d}tj}nh|tjtjtjfkrd}tj}tjjjdk}	||	d}n0|tjkrd}d	}d
|i}ntd|   dS |||fS )z Given an activation_post_process module,
    return node_type(e.g. call_function), quantize op(e.g. quantize_per_tensor) and a dictionary
    of extracted qparams from the module
    Ncompute_dtyper>   )_scale__zero_point_Z_axis__dtype_)ro   rp   rq   )Zfbgemmx86)rq   Z_reduce_range_rM   torq   z?Unsupported activation_post_process in get_quantize_node_info: )rk   hasattrrn   torchZquint8qint8rh   r   rf   rj   ch_axisZquantize_per_channelri   Zquantize_per_tensorfloat16Zquantize_per_tensor_dynamicbackends	quantizedZenginewarningswarn)
rO   rk   rn   quantize_op	node_typerl   rm   rw   qparamsZreduce_rangerH   rH   rI   r,      s<    




_output)	in_node
obs_moduleobs_nodemodulesquantized_graphnode_name_to_scopeis_inputoutput_prefixr=   c                 C   s  |r\t |j}|r|d nd}	d}
|D ]&}|jdkr&|jtjjjkr&|}
 qNq&|
rV|
}	d}n| }	|}|	r|	j|kr||	j \}}nd}|d }|}t	|}|dk	st
d| |\}}}| g}| D ]<\}}|dkrt|||| | |}|| q|| q|||t|i S )a
   Add quantization nodes (eg. quantize_per_tensor/per_channel) for given node to graph
    with the qparams calculated from activation_post_process (obs_module).
    The observer node (obs_node) is used to find the FQN of the user of act_post_process.
    e.g. Given input `node` in `node = self.conv(x)`, insert node:
    `quantized_node = torch.quantize_per_tensor(x, self._scale_0, self._zer_point_0, self._dtype_0)`
    where self._scale_0, self._zero_point_0 and self._dtype_0 are
    calculated from `obs_module`
    r   Nr>   _inputrQ   z7Expecting quantize node info not to be None, observer: )ro   rp   )listusersrB   rC   ru   nn
functionalZlinearrN   r,   rg   itemsr!   r[   create_nodetuple)r   r   r   r   r   r   r   r   r   Zfirst_linear_use_or_first_useZlinear_nodera   prefixmodule_path_root_modulegraphZmaybe_quantize_node_infor~   r}   r   inputskeyvalueZqparam_noderH   rH   rI   r8      s<    


)custom_module_mappingr=   c                 C   sD   t  }tjtjtjfD ]$}| |i }t | }||O }qt|S )a   Get all the unique custom module keys in the custom config dict
    e.g.
    Input:
    {
        QuantType.STATIC: {
            CustomModule1: ObservedCustomModule
        },
        QuantType.DYNAMIC: {
            CustomModule2: DynamicObservedCustomModule
        },
        QuantType.WEIGHT_ONLY: {
            CustomModule3: WeightOnlyObservedCustomModule
        },
    }

    Output:
    # extract the keys across all inner STATIC, DYNAMIC, and WEIGHT_ONLY dicts
    [CustomModule1, CustomModule2, CustomModule3]
    )setr   ZSTATICZDYNAMICZWEIGHT_ONLYrD   keysr   )r   Zfloat_custom_module_classesZ
quant_modeZquant_mode_custom_module_configZ quant_mode_custom_module_classesrH   rH   rI   r%   
  s    
c                 C   s6   | t jkrt jjjS | t jkr(t jjjS td| d S )Nz&can't get linear prepack op for dtype:)ru   rx   opsrz   Zlinear_prepack_fp16rv   Zlinear_prepack	Exception)rk   rH   rH   rI   r&   &  s
    



)conv_opr=   c                 C   sV   t jjjt jjjt jjjt jjjt jjj	t jjj
i}|| d }|sRtd| |S )NzDidn't find prepack op for {})ru   r   r   conv1dr   rz   Zconv1d_prepackconv2dZconv2d_prepackconv3dZconv3d_prepackrD   rg   r\   )r   Zprepack_opsZ
prepack_oprH   rH   rI   r+   .  s       )r   has_relur=   c                 C   s   t jjjt jjjt jjjt jjjt jjj	t jjj
it jjjt jjjt jjjt jjjt jjj	t jjj	id}|| | }|std| ||S )N)TFz4Can't find corresponding quantized conv op for {} {})ru   r   r   r   r   rz   Zconv1d_relur   Zconv2d_relur   Zconv3d_relurD   rg   r\   )r   r   Zqconv_opZqconvrH   rH   rI   r*   8  s$          )r   r=   c                    s&     dd tjjd fdd}|S )N.r   )modulec                    s>   t d fdd}d}||}t| |r:|d7 }||}q|S )Nic                    s    t |  S N)rU   r   r   rH   rI   get_attr_nameS  s    zOget_new_attr_name_with_prefix.<locals>.get_new_attr_name.<locals>.get_attr_namer   r   )rj   rt   )r   r   r   	attr_namer   rH   rI   get_new_attr_nameR  s    

z8get_new_attr_name_with_prefix.<locals>.get_new_attr_name)rV   ru   r   Module)r   r   rH   r   rI   r'   O  s    	r:   r=   c                 C   s   | g}| g}|r|  } t| jt| j  }|D ]H}t|tsDq4|jdkrT dS || |jdkrr|j	t
ks4|| q4q|S )a   Starting from a target node, trace back until we hit inpu or
    getattr node. This is used to extract the chain of operators
    starting from getattr to the target node, for example
    def forward(self, x):
      observed = self.observer(self.weight)
      return F.linear(x, observed)
    collect_producer_nodes(observed) will either return a list of nodes that
    produces the observed node or None if we can't extract a self contained
    graph without free variables(inputs of the forward function).
    rJ   Nr>   )popr   rF   rG   valuesrA   r   rB   r[   rC   getattr)r:   rT   ZfrontierZall_argsr;   rH   rH   rI   r    ]  s    


)rootproducer_nodesr=   c                    sl   t |dkstd|  t }i   fdd}|D ]}||| |< q6|||d  t| |}|S )a-   Construct a graph module from extracted producer nodes
    from `collect_producer_nodes` function
    Args:
      root: the root module for the original graph
      producer_nodes: a list of nodes we use to construct the graph
    Return:
      A graph module constructed from the producer nodes
    r   z'list of producer nodes can not be emptyc                    s   t |  fddS )Nc                    s    |  S r   rH   r:   envrH   rI   <lambda>      zDgraph_module_from_producer_nodes.<locals>.load_arg.<locals>.<lambda>)r   ar   rH   rI   load_arg  s    z2graph_module_from_producer_nodes.<locals>.load_arg)rE   rg   reverser   Z	node_copyoutputr   )r   r   r   r   Zproducer_nodeZgraph_modulerH   r   rI   r.   x  s    

)r   r=   c                 C   s^   dd |   D dd |  D B }t|dks>td|t|dkrVtt|n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 rH   device.0prH   rH   rI   	<setcomp>  s     z/assert_and_get_unique_device.<locals>.<setcomp>c                 S   s   h | ]
}|j qS rH   r   r   rH   rH   rI   r     s     r   zMprepare only works with cpu or single-device CUDA modules, but got devices {}r   N)
parametersbuffersrE   rg   r\   nextiter)r   Zdevicesr   rH   rH   rI   r     s    )r   r   r   r   r=   c           	      C   sZ   t |}|| }t| }t|tjr0|  ntj||d}| || |	d|}|S )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   rK   )
r'   r   rA   ru   ZTensorclonedetachZtensorZregister_bufferr   )	r   r   r   r   r   r   r   	new_valueZ	attr_noderH   rH   rI   r!     s    )	node_namerl   rm   r   r   r   r=   c                 C   s@   |d }||  \}}t |||d |}	t |||d |}
|	|
fS )z
    Create getattr nodes in the quantized graph for scale and zero point values.
    The nodes are registered with the root_module of the model.
    rQ   ro   rp   )r!   )r   rl   rm   r   r   r   r   r   r   Z
scale_nodeZzero_point_noderH   rH   rI   r#     s
    )r:   r   cacher=   c           	      C   s  |r| |kr||  S d}t | ts*d}n| jdkr<d}n| jdkrzt | jtsVtt|| j rvt| jd ||}nb| jdkrd}nP| jdkr| jt	j
krt| jd ||}n$| jdkrd}n| jtkr| jd d	krd}n| jd
kr| jdkrd}nd}| jD ]}t |trx|D ]P}t |tr$t|||}|pJ| }|r$| }|rf||| < |    S q$nZt |trnLt |trt|||}|p| }|r| }|r||| < |  S nd}| }q|r||| < |S )z
    If we know for sure that all of this node's args have no
    tensors (are primitives), return True.  If we either
    find a tensor or are not sure, return False. Note: this
    function is not exact.
    FTrJ   rL   r   r>   rK   r   )ndimshaperM   size)rA   r   rB   rC   rU   rg   r   r   rF   operatorgetitemr   r   rj   )	r:   r   r   re   Zfound_one_tensorr;   Zlist_elZ!this_list_el_args_have_no_tensorsZthis_arg_args_have_no_tensorsrH   rH   rI   r     sj    








c                 C   s   t tdt| jS )z2
    Returns all node arg indices after first
    r   )r   rangerE   rF   r   rH   rH   rI   r     s    )arg_indicesr=   c                    s   t tt d fdd}|S )zu
    Constructs a function that takes a node as arg and returns the arg_indices
    that are valid for node.args
    r   c                    s    fddD S )Nc                    s   g | ]}|t  jk r|qS rH   )rE   rF   )r   r   r   rH   rI   
<listcomp>  s      z=return_arg_list.<locals>.arg_indices_func.<locals>.<listcomp>rH   r   r   r   rI   arg_indices_func  s    z)return_arg_list.<locals>.arg_indices_func)r   r   rj   )r   r   rH   r   rI   r9     s    z	op targetrM   Zmasked_fill   ZpermuterepeatZreshaper   	transpose	unsqueezeZ
unsqueeze_viewc                 C   s   t | j| j}t|tS )z
    Returns a dict with of non float tensor types as keys and values which correspond to a
    function to retrieve the list (which takes the node as an argument)
    )r2   rB   rC   r6   rD   r$   )r:   inforH   rH   rI   r(   G  s    c                 C   s   | j dko| jdkS )zd
    Returns true if this node results in an integer, even if some of the args
    are Tensors.
    rM   r   )rB   rC   r   rH   rH   rI   r3   P  s    c                 C   s&   | j dko | jtko | jd dk}|S )z Returns True if this node is a node that takes a Tensor as input and output some
    meta information about the Tensor, e.g. shape, size etc.
    r>   r   r   )rB   rC   r   rF   )r:   re   rH   rH   rI   r0   X  s     )r:   r   target_module_typetarget_functional_typer=   c                 C   sj   | j  D ]Z\}}|jdkr@|dk	r@t|t|j |r@|  S |jdkr
|dk	r
|j|kr
|  S q
dS )a%   Gets the next module that matches what is needed in
    is_target_module_type if it exists

    Args:
        node: The node whose users we want to look at
        target_module_type: Module type that we want to check
        target_functional_type: Functional type that we want to check
    rL   Nr>   )r   r   rB   rA   rU   rC   )r:   r   r   r   userr   rH   rH   rI   r1   `  s    
.)r   create_node_argsold_noder=   c                 C   s   | j | }|j|_|S )zU
    Creates `new_node` and copies the necessary metadata to it from `old_node`.
    )r   Zstack_trace)r   r   r   Znew_noderH   rH   rI   r"   y  s    
)prepare_custom_configis_standalone_moduler=   c                 C   sV   t  | j}t  | j}|sN|t| j 7 }|t| j 7 }|t| j7 }||fS r   )	copyZnon_traceable_module_namesZnon_traceable_module_classesr   Zstandalone_module_namesr   Zstandalone_module_classesr%   Zfloat_to_observed_mapping)r   r   Zskipped_module_namesZskipped_module_classesrH   rH   rI   r-     s    )r:   named_modulesqconfigqhandlerr=   c                 C   sf   t | |}|dk	rP|dk	rPt|tjjjjjs2tt|tj	j
oNt|oN| S t|tjj	jj
S dS )zD
    Return whether this refers to the custom module LSTM flow.
    N)_get_modulerA   ru   ZaoZquantizationZfxZquantization_patternsZQuantizeHandlerrg   r   ZLSTMr	   Zis_custom_moduleZquantizable)r:   r   r   r   modrH   rH   rI   _is_custom_module_lstm  s    

r   )r:   r   r=   c                 C   s.   | j dkr&t| j|kr&|t| j S dS dS )zO
    If `node` refers to a call_module node, return the module, else None.
    rL   N)rB   rU   rC   )r:   r   rH   rH   rI   r     s    r   )r:   modelr   r   r=   c              
   C   s^   d}t |}||}t }t||| |||< ||  ||| fW  5 Q R  S Q R X dS )z
    Attach a `DeQuantStub` to the model and create a node that calls this
    `DeQuantStub` on the output of `node`, similar to how observers are inserted.
    Zdequant_stub_N)r'   r   setattrinserting_afterrL   )r:   r   r   r   r   Zget_new_dequant_stub_nameZdequant_stub_nameZdequant_stubrH   rH   rI   _insert_dequant_stub  s    
r   c              	   C   s\  | | & |tj| df}t||||}W 5 Q R X | | |tj| df}W 5 Q R X | |& |tj|df}t||||}W 5 Q R X | |& |tj|df}	t|	|||}
W 5 Q R X | |
 |t||
gf}W 5 Q R X | | |t||gf}W 5 Q R X t| j D ]&}||kr(||kr(|	| | q(t
| |S )a8  
    Insert DeQuantStubs after each internal output node of custom module LSTM.

    Custom module LSTM outputs are nested tuples of the sturcture (output, (hidden0, hidden1)),
    Since we cannot dequantize a tuple as a whole, we must first break down the tuple into its
    components through `getitem`. This function transforms the graph as follows:

      (1) Split the LSTM node into (output, (hidden0, hidden1))
      (2) Insert a DeQuantStub after each internal node
      (3) Recombine the DeQuantStubs into the same structure as before
      (4) Reroute all consumers of the original LSTM node and its sub-nodes
          (e.g. lstm[0])

    Before:
                   lstm_output
                        |
                        v
                  original_user(s)
    After:
                   lstm_output
                  /           \
                 /  (getitem)  \
                /               \
               v                 v
             output            hidden
               |               /   \
         (DeQuantStub)        (getitem)
               |             /       \
               v            v         v
           output_dq     hidden0    hidden1
               |            |         |
               |    (DeQuantStub) (DeQuantStub)
               |            |         |
               |            v         v
               |      hidden0_dq  hidden1_dq
               |            \       /
               |              (tuple)
               |              \   /
               |               v  v
               |             hidden_dq
               \               /
                \   (tuple)   /
                 v            v
                 lstm_output_dq
                       |
                       v
                original_user(s)

    For step (4), reroute all users of the original LSTM node(s) as follows:
      lstm_output -> lstm_output_dq
      lstm_output[0] -> output_dq
      lstm_output[1] -> hidden_dq
      lstm_output[1][0] -> hidden0_dq
      lstm_output[1][1] -> hidden1_dq

    Return the node `lstm_output_dq`.
    r   r   )r   r>   r   r   r   r   r   r   r   replace_input_with_reroute_tuple_getitem_pattern)r:   r   r   r   r   Z	output_dqhiddenZhidden0Z
hidden0_dqZhidden1Z
hidden1_dqZ	hidden_dqZlstm_output_dqr   rH   rH   rI   3_insert_dequant_stubs_for_custom_module_lstm_output  s(    Ar   )r;   r   r=   c           	         s   fdd}fdd}dd }dd t t tt d	 fd
d}|||g||||g||||g|||gg}|D ]}||}|dk	rv|  S qvdS )aC  
    Given an argument of a node, if the argument refers to the path through which the node
    is a consumer of custom module LSTM, return the custom module LSTM node, or None otherwise.

    This is used to determine whether a node is a consumer of custom module LSTM, and, if so,
    skip inserting input observers for this node. This is because custom module LSTM produces
    quantized outputs, so inserting an input observer for the consumer of custom module LSTM
    would unnecessarily quantize the outputs again.

      lstm -> consumer

    In practice, however, custom module LSTM outputs a tuple (output, (hidden0, hidden1)) with
    DeQuantStubs attached to each internal node (see `_insert_dequant_stubs_for_custom_module_lstm_output`).
    This tuple can be consumed in one of four ways:

      lstm -> getitem -> DeQuantStub -> consumer                       # consume lstm[0]
      lstm -> getitem -> getitem -> DeQuantStub -> tuple -> consumer   # consume lstm[1]
      lstm -> getitem -> getitem -> DeQuantStub -> consumer            # consume lstm[1][0] or lstm[1][1]
      lstm -> getitem -> DeQuantStub -> tuple -> consumer              # consume lstm

    Thus, we must match against the above patterns instead of simply checking the parent node
    to determine whether this node is a consumer of a custom module LSTM.
    c                    s   t t|  tS r   )rA   r   r   r   r   rH   rI   match_dq:  s    z=_maybe_get_custom_module_lstm_from_node_arg.<locals>.match_dqc                    s
   t |  S r   )r   r   r   rH   rI   
match_lstm=  s    z?_maybe_get_custom_module_lstm_from_node_arg.<locals>.match_lstmc                 S   s   | j dko| jtjkS Nr>   )rB   rC   r   r   r   rH   rH   rI   match_getitem@  s    zB_maybe_get_custom_module_lstm_from_node_arg.<locals>.match_getitemc                 S   s   | j dko| jtkS r   )rB   rC   r   r   rH   rH   rI   match_tupleC  s    z@_maybe_get_custom_module_lstm_from_node_arg.<locals>.match_tuple)match_patternr=   c                    sZ    }t | D ]H\}}||s" dS |t| d k r|krJ|jd d }q|jd }q|S )z
        Traverse up the graph and match the args one by one.
        If there is a match, return the last matched node, or None otherwise.
        Nr   r   )	enumeraterE   rF   )r   r   r   match)r;   r   rH   rI   _match_patternF  s    zC_maybe_get_custom_module_lstm_from_node_arg.<locals>._match_patternN)r   r   r   r   )	r;   r   r   r   r   r   Zall_match_patternsr   Zmatched_noderH   )r;   r   r   rI   +_maybe_get_custom_module_lstm_from_node_arg  s    


r   )r   c           
   
      s   t tt tt  ttt   ttt ttdf f  d fdd g }t }| jD ]} |g g || qN|D ]~}|d }|d }|jdkr|jt	kst
|jdkr|jtjkst
|jd }|jd | }t|j D ]}	|	|| qqhd	S )
a  
    Search for patterns where N consecutive `tuple` call_function nodes are followed by
    N consecutive `getitem` call_function nodes that are "reverses" of the `tuple` nodes.
    If we find this pattern, reroute the consumers of the last `getitem` to skip these
    N `tuple` and `getitem` nodes.

    Before:

        a   b     c
        |   \   /
        \   tuple
         \   /
          tuple
            |
        getitem(1)
            |
        getitem(0)
            |
            d

    After:

        b
        |
        d
    .)r:   index_stackcurrent_patternmatched_patternsseenc           	         s  t |dkr0t |dkr0|t| |  | t|f}||krHdS || | jD ]}|jdkr|jtkrt	|j
d D ]4\}}|| kr~|| ||  ||||| q~qX|jdkrX|jtjkrXt |dkrX|j
d |d krX|  ||  ||||| qX|S )aP  
        Traverse the graph recursively to match for the N-tuple - N-getitem patterns,
        starting at the given node.

        We use a stack to keep track of the expected `getitem` indices, since these are
        reversed from the `tuple` indices. In the above example, the stack after
        (b -> tuple -> tuple) will be [0, 1], which will be popped by getitem(1) first
        and then by getitem(0).

        TODO: traverse upwards from the output and handle the case when tuple is not a
        separate node, e.g. graph.call_function(operator.getitem, args=(a, (b, c)))
        r   Nr>   r   r   )rE   r[   r   clearr   addr   rB   rC   r   rF   r   r   r   )	r:   r   r   r   r  stater   r   Zuser_argfind_patternsrH   rI   r    s*    




z5_reroute_tuple_getitem_pattern.<locals>.find_patternsr   r   r>   r   N)r   r   rj   r   r   r   rT   rB   rC   r   rg   r   r   rF   r   r   r   r   )
r   r   r  r:   patternZfirst_tupleZlast_getitemZlast_getitem_indexZ	new_inputr   rH   r  rI   r   d  s&    
-

r   c                 C   s&   t | tr| S t | tst| jS dS )z
    If `activation_post_process` is an observer, return the observer.
    If `activation_post_process` is a fake quantize, return the internal observer.
    N)rA   r   r   rg   rO   )rO   rH   rH   rI   *_get_observer_from_activation_post_process  s    
r  )r   dtype_with_constraintsis_activationr=   c                    s   t ttf tttd fdd} dks2|jdkr6dS |r@ jn j}|rNdnd}d}|dk	r| }t	|spt
|j|jkrdS ||||}|S )a  
    Return whether `qconfig` satisfies the following constraints from the backend,
    specified through the activation and weight DTypeWithConstraints.

        1. QConfig specified a quantization range that falls within the backend's, if any
        2. QConfig specified a min scale value that is >= the backend's, if any

    If `is_activation` is True, we check `qconfig.activation`, else we check `qconfig.weight`.
    If `qconfig` or `dtype_with_constraints.dtype` is None, or the dtypes do not match, return True.
    )rO   r	  debug_stringr=   c           
   	      s   t | }t|dd }t|dd }t|dd }|j}|j}|j}	|d k	r|d k	r|d ks^|d krttd| f  dS ||k s||krtd||||| f  dS |	d k	r|d krtd| f  dS ||	k rtd|||	 f  dS d	S )
NZ	quant_minZ	quant_maxZepsz@QConfig %s must specify 'quant_min' and 'quant_max', ignoring %sFzQConfig %s quantization range must fall within the backend's:
QConfig range = (%s, %s), BackendConfig range = (%s, %s), ignoring %sz*QConfig %s must specify 'eps', ignoring %szdQConfig %s eps (%s) must be greater than or equal to the backend's min scale value (%s), ignoring %sT)r  r   Zquant_min_lower_boundZquant_max_upper_boundZscale_min_lower_boundr{   r|   )
rO   r	  r  ZobserverZapp_quant_minZapp_quant_maxZapp_scale_minZbackend_quant_minZbackend_quant_maxZbackend_scale_minr   rH   rI   ;_activation_post_process_satisfies_dtype_config_constraints  s@      
zp_qconfig_satisfies_dtype_config_constraints.<locals>._activation_post_process_satisfies_dtype_config_constraintsNT
activationr?   )r   r   r   r   rU   boolrk   r  r?   r   rg   )r   r	  r
  r  Zactivation_post_process_ctrr  Zsatisfies_constraintsrO   rH   r  rI   +_qconfig_satisfies_dtype_config_constraints  s*    
 %  r  )T)r   )NN)NN)T)ar   rR   ru   Ztorch.nnr   Ztorch.ao.quantizationr   r   Z$torch.ao.quantization.backend_configr   r   Z#torch.ao.quantization.fake_quantizer   Ztorch.ao.quantization.observerr   Ztorch.ao.quantization.stubsr   Ztorch.ao.quantization.utilsr	   r
   r   Ztorch.ao.quantization.quantizer   Ztorch.fxr   r   Ztorch.fx.graphr   r   Zcustom_configr   typingr   r   r   r   r   r   r   r   r   collectionsr   r   r{   __all__r   Z
layer_normZ
group_normZinstance_normr7   r  r5   r4   rU   r/   r)   r,   r   typer8   r%   r&   r+   r*   r'   r    r.   r   r!   r#   r   rj   r   r9   r2   ri   r   r   r6   rk   __annotations__r$   r(   r3   r0   r1   r"   r-   r   r   r   r   r   r   r  r  rH   rH   rH   rI   <module>   s   , 		B.1  >$
  
(K"	
       
   
  4%,0	  
   &
_EZ
  