o
    Ơi{                     @  s  U d dl mZ d dlZd dlZd dlZd dlZd dlZd dl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 d dlZd dlZd dlZd dlZd dlZd dlmZ d dlm Z  d dl!m"Z" d d	l#m$Z$ d d
l%m&Z& d dl'm(Z( d dl)m*Z* d dl+m,Z, d dl-m.Z. d dl/m0Z0 d dl1m2Z2 d dl3m4Z4 ddl5m6Z6 e7e8Z9dZ:dZ;dZ<dZ=dZ>G dd deZ?ej@e?jAej@e?jBej@e?jCej@iejDe?jAejDe?jBejDe?jCejDiejEe?jAejEe?jBejEe?jCejEiejFe?jAejFe?jBejFe?jCejFiejGe?jAejGe?jBejGe?jCejGiejHe?jAeHe?jBejHe?jCejHiiZIdeJd< e*dr/e?jAejKe?jBejHe?jCejHieIejH< dd"d#ZLddd*d+ZMddd.d/ZNdd4d5ZOdd8d9ZP	:dddBdCZQ	:dddGdHZRddIdJZSddNdOZT	PdddSdTZUdUdV fddZd[ZVdd]d^ZWddbdcZX	:dddgdhZYddidjZZddodpZ[ddrdsZ\ddudvZ]ddxdyZ^dd|d}Z_ddddZ`	ddddZae<e=fdddZbdddZcdddZddddZedddZfdddZgdddZhddddZidddZjdddZkdS )    )annotationsN)fieldsreplace)Enum)AnyCallableDictListOptionalSequenceTupleUnion)
FakeTensor)unset_fake_temporarily)Device)dtype)ENABLED_FEATURES)Input)is_tensorrt_version_supported)	_defaults)default_device)BaseEngineCache)CompilationSettings)version   )TRTDataTypegGz?g{Gzt?cpuc                   @  s   e Zd ZdZdZdZdS )
FrameworksnumpytorchtrtN)__name__
__module____qualname__NUMPYTORCHTRT r(   r(   O/home/ubuntu/.local/lib/python3.10/site-packages/torch_tensorrt/dynamo/utils.pyr   4   s    r   zNDict[TRTDataType, Dict[Frameworks, Union[TRTDataType, np.dtype, torch.dtype]]]DataTypeEquivalencez7.0r   )Union[TRTDataType, torch.dtype, np.dtype]toreturn)Union[np.dtype, torch.dtype, TRTDataType]c                 C  s
  |t v sJ d| ttjdd }| tjtjtjfv r&ttj | S |dkr;| tj	tj
tj
fv r;ttj
 | S | tjtjtjfv rLttj | S | tjtjtjfv r]ttj | S | tjtjtjfv rnttj | S | tjtjtjfv rttj | S td|  )aK  
    Convert TensorRT, Numpy, or Torch data types to any other of those data types.
    Args:
        dtype (TRTDataType, torch.dtype, np.dtype): A TensorRT, Numpy, or Torch data type.
        to (Frameworks): The framework to convert the data type to.
    Returns:
        The equivalent data type in the requested framework.
    z.Expected valid Framework for translation, got .r      z%s is not a supported dtype)r   intr!   __version__splitnpint8r    r*   bool_boolint32int64float16float32	TypeError)r   r,   trt_major_versionr(   r(   r)   unified_dtype_convertere   s   r>   Tmoduletorch.fx.GraphModuledelete_moduler7   Nonec                 C  s&   |  t |r~ tj  t  dS )z
    This is a helper function to delete the instance of module. We first move it to CPU and then
    delete the object. This function ensures the GPU memory occupied by the module is released effectively after this call
    N)r,   
CPU_DEVICEr    cudaempty_cachegccollect)r?   rA   r(   r(   r)   deallocate_module   s
   

rH   use_python_runtimeOptional[bool]c                 C  sn   | }d}|durd}nzddl m} d}d}W n ty$   d}d	}Y nw td
|r,dnd d| d |S )zParses a user-provided input argument regarding Python runtime

    Automatically handles cases where the user has not specified a runtime (None)

    Returns True if the Python runtime should be used, False if the C++ runtime should be used
     Nzas requested by userr   )TorchTensorRTModuleFz,since C++ dependency was detected as presentTz1since import failed, C++ dependency not installedzUsing zPython-onlyDefaultz Torch-TRT Runtime ())torch_tensorrt.dynamo.runtimerL   ImportErrorloggerinfo)rI   using_python_runtimereasonrL   r(   r(   r)   use_python_runtime_parser   s    rU   	gt_tensortorch.Tensorpred_tensorfloatc                 C  s~   |   tj} |  tj}t| dkst|dkr*tj| |ddddr*dS tjjj| |ddd}|	 
  }|S )	Ng        g-C6?T)atolrtol	equal_nang      ?r   gư>)dimeps)flattenr,   r    r;   sumallclosenn
functionalcosine_similarityr   detachitem)rV   rX   res_tresr(   r(   r)   rd      s   rd   inputs0Sequence[Union[Input, torch.Tensor, FakeTensor]]c                 C  s^   | D ]*}t |tjrt|jr dS qt |tr#|jtjjkr" dS qt	dt
| ddS )zs
    Return true if any of inputs have dynamic shapes. Supported types are torch_tensorrt.Input | torch.Tensor
    TzInvalid input type (z@) found. Supported types are torch_tensorrt.Input | torch.TensorF)
isinstancer    Tensorcontains_sym_intshaper   
shape_mode
_ShapeModeDYNAMICAssertionErrortype)ri   inputr(   r(   r)   input_is_dynamic   s   

ru   rK   rt   r   devicetorch.devicemodestrUnion[int, torch.Tensor]c                 C  s<   | j r
| jd d S t|dkr| ||S | j|S )N	opt_shaper   )is_shape_tensorrn   lenexample_tensorr,   torch_tensor)rt   rv   rx   r(   r(   r)   get_torch_tensor   s
   r    Sequence[Input] | Dict[str, Any] Union[Device, torch.device, str]HSequence[Union[int, torch.Tensor]] | Dict[str, Union[int, torch.Tensor]]c                 C  s   t |}t| tr3i }|  D ]!\}}t|tttfr#t||||< qt|tr0t|||||< q|S g }| D ])}t|trH|	t||| q7t|t
jrW|	|| q7tdt| d|S )z
    Return the torch_tensor from the Input object. If mode is set, this implies
    user is using dynamic shaped inputs and return the corresponding input based
    on the mode requested.
    zInput type z is not a valid type)to_torch_devicerk   dictitemslisttupleget_torch_inputsr   r   appendr    rl   r,   rr   rs   )ri   rv   rx   result_dictkvresult_listrt   r(   r(   r)   r      s$   



r   c                 C  st   d}t |  D ]}t|tjjjtjfr|j  S qt | 	 D ]}t|tjr.|j  S q!|du r8t
t }|S )zB
    Returns the device on which the module parameters exist.
    N)r   
parametersrk   r    rb   	parameter	Parameterrl   rv   buffersr   r   )r?   rv   r   bufferr(   r(   r)   get_model_device   s   


r   parent_loggerr   levelc                 C  s   | r|  | tjrS|tjkrtjjj}n3|tj	kr tjjj	}n(|tj
kr+tjjj
}n|tjkr6tjjj}n|tjkrAtjjj}nt| dtjjt| dS dS )z
    Sets the log level to the user provided level.
    This is used to set debug logging at a global level
    at entry points of tracing, dynamo and torch_compile compilation.
    And set log level for c++ torch trt logger if runtime is available.
    z is not valid log levelN)setLevelr   torch_tensorrt_runtimeloggingDEBUGr!   ILoggerSeverityVERBOSEINFOWARNINGERRORCRITICALINTERNAL_ERRORrr   r    opstensorrtset_logging_levelr1   )r   r   	log_levelr(   r(   r)   set_log_level  s    





r   F5Input | torch.Tensor | Sequence[Any] | Dict[Any, Any]disable_memory_format_checkc                 C  s@  t   | du r	 W d   dS t| tr| W  d   S t| tjtttfr:tjt	| |dW  d   S t| t
tfrfg }| D ]}t||d}|| qEt| t
rZ|nt|	 W  d   S t| trt }|  D ]\}}t||d}|||< qr|W  d   S tdt|  dd 1 sw   Y  dS )zc
    We take a nested group of torch.Tensors or scalars and convert them into torchtrt.Input's
    N)r   Invalid input type z2 encountered in the dynamo_compile input parsing. LAllowed input types: {torch_tensorrt.Input, torch.Tensor, list, tuple, dict})r   rk   r   r    rl   r1   rY   r7   from_tensortensorr   r   prepare_inputsr   r   r   
ValueErrorrs   )ri   r   torchtrt_input_list	input_objtorchtrt_inputtorchtrt_inputs_dictkeyr(   r(   r)   r   ,  sP   	


'r   c                 C  s   | S Nr(   )xr(   r(   r)   <lambda>e  s    r   attribute_to_extractapply_fnCallable[[Any], Any]c           	      C  s   t | tjtfr|t| |dS t | tttfr%t| }|t||dS t | t	t
frIg }| D ]}t|||}|| q0t | t	rE|S t
|S t | trft }|  D ]\}}t|||}|||< qU|S tdt|  dd )zParses complex structures of Tensors and returns a mirrored structure
    Extracts key attributes of each singular element, while reconstructing the struct
    Optionally applies a function to each attribute before returning
    Nr   z* encountered during Dynamo input parsing. r   )rk   r    rl   r   getattrr1   rY   r7   r   r   r   parse_complex_tensor_structsr   r   r   r   rs   )	ri   r   r   inputs_torchr   r   r   r   r   r(   r(   r)   r   b  s<   	


r   r   c                 C  s   t dd | D S )z>
    Returns true if the given tensor has symbolic shape.
    c                 s  s    | ]	}t |tjV  qd S r   )rk   r    SymInt).0r]   r(   r(   r)   	<genexpr>  s    z#contains_sym_int.<locals>.<genexpr>)anyr   r(   r(   r)   rm     s   rm   symbolic_integertorch.SymIntDict[str, int]c           	      C  s   | j }|j}|j}|j|dp||}|j|dp(|j|dp(||j}|s/J |t	|j
t	|j}}|dkr@dn|}i }||d< ||d< t|tjjjt	fr\t	||d< |S )zO
    This function returns the min, max, opt values of a symbolic integer.
    N   r   minmaxopt)nodeexpr	shape_envvar_to_rangegetbound_sympy
var_to_valunbacked_var_to_valxreplacer1   lowerupperrk   sympycorenumbersInteger)	r   r   r   r   	var_rangevar_valmin_valmax_valmin_max_optr(   r(   r)   extract_var_range_info  s$   
r   -Union[torch.Tensor, FakeTensor, torch.SymInt]Optional[str]%Sequence[Union[int, Tuple[int, int]]]c                 C  s   g }t | tr||  t|S t | tjr6t| }|r'|||  t|S ||d |d f t|S t | tjrE|d t|S t | tjtfr\| j	D ]}|
t||d qPt|S )a8  
    This is a helper function used to print/return the shape of the tensor.
    For regular torch.tensor's, it returns the static shape.
    For symbolic tensors, eg:(1, s0, 4), this function returns [1, [min, max], 4]. The min
    and max correspond to the lower and upper values of s0 symbolic dimension.
    r   r   r   )rx   )rk   r1   r   r    r   r   SymFloatrl   r   rn   extendunwrap_tensor_shaper   )r   rx   tensor_shaper   	dimensionr(   r(   r)   r     s$   	




r   c                 C  st   t | tjtfr| jS t | tttfrt| jS t | tj	r"tj
S t | tjr+tjS | du r1dS tdt|  )za
    Returns the dtype of torch.tensor or FakeTensor. For symbolic integers, we return int64
    NzFound invalid tensor type )rk   r    rl   r   r   r1   rY   r7   r   r   r9   r   r;   r   rs   r   r(   r(   r)   unwrap_tensor_dtype  s   r   io_nodesSequence[torch.fx.Node]	attr_typeSequence[Any]c                 C  sx   |dv sJ |dkrt nt}g }| D ]'}d|jv r9|jd }t|ttfr2|D ]	}||| q'q||| q|S )zJ
    Returns a list of attributes (shapes or dtypes) of the I/O nodes
    )rn   r   rn   val)r   r   metark   r   r   r   )r   r   attr_fngraph_io_attrsr   metadatar   r(   r(   r)   get_graph_io_attrs  s   

r   dryrun_trackerc           
      C  s   dd | j jD }t|d}t|d}||_||_dd | j jD }g }|D ]}||j q&t|d}t|d}	||_|	|_dS )zb
    Parse the graph I/O shape/dtype info for the whole graph and store in the dryrun tracker
    c                 S     g | ]	}|j d kr|qS )placeholderopr   r   r(   r(   r)   
<listcomp>      z"parse_graph_io.<locals>.<listcomp>rn   r   c                 S  r   outputr   r   r(   r(   r)   r     r   N)	graphnodesr   input_shapesinput_dtypesr   all_input_nodesoutput_shapesoutput_dtypes)
r?   r   input_nodesr   r   mark_output_nodesoutput_nodesr   r  r  r(   r(   r)   parse_graph_io  s   




r  *Optional[Union[Device, torch.device, str]]c                 C  sH   t | tr| tjS t | tjr| S | du rttj S t| S )zSCast a device-type to torch.device

    Returns the corresponding torch.device
    N)rk   r   r,   r    rv   rD   current_devicerv   r(   r(   r)   r     s   

r   r   c                 C  s
   t | S )zeCast a device-type to torch_tensorrt.Device

    Returns the corresponding torch_tensorrt.Device
    )r   _fromr	  r(   r(   r)   to_torch_tensorrt_device$  s   
r  kwargs5Tuple[CompilationSettings, Optional[BaseEngineCache]]c                   s  t  }| rSd| v rt| dkr| d } d| v r7d| v r&| d tjur&td| d | d< tjdtdd | d= d	d
 t|D   fdd| 	 D }t
|fi |}d| v rudd
 | d D }t|dkrrtdtj  tj}||_t|j|_t|j|_d| vrtd|jj d |jrtd d|_d}| ds| dr| ddur| d}nddlm} | dtj}| dtj}|||}| dr| d|_td| ||fS )zParses the kwargs field of a Dynamo backend

    Args:
        kwargs: Keyword arguments dictionary provided to the backend
    Returns:
        CompilationSettings object with relevant kwargs
    optionsr   truncate_long_and_doubletruncate_doublez~Provided configuration for "truncate_double" and deprecated API "truncate_long_and_double". Please only use "truncate_double".zCompiler option "truncate_long_and_double" is deprecated in favor of "truncate_double" as int64 is now natively supported. This option will be removed in the next version.r   )
stacklevelc                 S  s   h | ]}|j qS r(   )name)r   attrr(   r(   r)   	<setcomp>X      z&parse_dynamo_kwargs.<locals>.<setcomp>c                   s   i | ]\}}| v r||qS r(   r(   )r   r   r   valid_attrsr(   r)   
<dictcomp>Y  s    z'parse_dynamo_kwargs.<locals>.<dictcomp>enabled_precisionsc                 S  s   h | ]}t |qS r(   )r   r
  )r   er(   r(   r)   r  ^  s    r   z&No precision specified, defaulting to rv   z@Device not specified, using Torch default current device - cuda:zO. If this is incorrect, please specify an input device, via the device keyword.zkDetected require_full_compilation=True for a torch.compile run. This option has no effect in torch.compile.FNcache_built_enginesreuse_cached_enginescustom_engine_cache)DiskEngineCacheengine_cache_direngine_cache_sizetorch_executed_opszCompilation Settings: %s
)r   r}   r   TRUNCATE_DOUBLEr   warningswarnDeprecationWarningr   r   r   rQ   rR   ENABLED_PRECISIONENABLED_PRECISIONSr  rU   rI   r  rv   gpu_idrequire_full_compilationwarningr   #torch_tensorrt.dynamo._engine_cacher  ENGINE_CACHE_DIRENGINE_CACHE_SIZEr!  )r  settingsvalid_kwargsr  engine_cacher  r  r   r(   r  r)   parse_dynamo_kwargs.  sr   


r1  2.devmin_torch_versionCallable[..., Any]c                   s   d fdd}|S )ak  
    Create a decorator which verifies the Torch version installed
    against a specified version range

    Args:
        min_torch_version (str): The minimum required Torch version
        for the decorated function to work properly

    Returns:
        A decorator which raises a descriptive error message if
        an unsupported Torch version is used
    fr4  r-   c                   s   d fdd}|S )Nargsr   r  r-   c                    sN   t }t tj}||k r td dd  dtj   | i |S )NzExpected Torch version z or greater, zwhen calling z. Detected version )r   parser    r2   rr   )r6  r  min_versioncurrent_version)r5  r3  r(   r)   function_wrapper  s   

zEreq_torch_version.<locals>.nested_decorator.<locals>.function_wrapper)r6  r   r  r   r-   r   r(   )r5  r:  r3  )r5  r)   nested_decorator  s   z+req_torch_version.<locals>.nested_decoratorN)r5  r4  r-   r4  r(   )r3  r<  r(   r;  r)   req_torch_version  s   r=  
new_modulerefitted_module
arg_inputskwarg_inputsc                 C  s@   || | |i |}}t |t |krtd dS t||S )Nz8The output types are different. Output check is skipped.T)rs   rQ   r*  check_output_equal)r>  r?  r@  rA  old_outputsnew_outputsr(   r(   r)   check_module_output  s   


rE  output1output2r[   rZ   c                 C  s   t | t |krtd dS t| tjr%| j|jkrdS t| |||S t| tt	frKt
| t
|kr6dS t| |D ]\}}t||sG dS  dS n't| trr|  | krZdS t|  | D ]\}}t||so dS qcdS td dS )NzLThe output types are different. Check_output_equal will always return false.FTz\The output type is not supported to be checked. Check_output_equal will always return false.)rs   rQ   r*  rk   r    rl   rn   ra   r   r   r}   ziprB  r   keysvalues)rF  rG  r[   rZ   abr(   r(   r)   rB    s:   


rB  exported_programtorch.export.ExportedProgramr6  	list[Any]dict[str, Any]tuple[Any, Any]c           	      C  sd   ddl m  m} ddlm} | jj}|dur|||}|||f\}}tdd |D }||fS )a  Flatten args, kwargs using pytree, then, check specs.

    Args:
        args: List[Any] original args passed to __call__
        kwargs: Dict[str, Any] original kwargs passed to __call

    Returns:
        A tuple of (flat_args, received_spec)
        flat_args is flattend args / kwargs
        received_spec is the pytree spec produced while flattening the
        tuple (args, kwargs)
    r   N)reorder_kwargsc                 s  s    | ]}|d  V  qdS )r   Nr(   )r   r   r(   r(   r)   r     s    z+get_flat_args_with_check.<locals>.<genexpr>)	torch.utils._pytreeutils_pytreetorch.export._tree_utilsrR  	call_specin_spectree_flatten_with_pathr   )	rM  r6  r  pytreerR  rX  flat_args_with_pathreceived_spec	flat_argsr(   r(   r)   get_flat_args_with_check  s   
r^  gm	target_optorch._ops.OpOverload	List[Any]c                   s    fdd| j jD S )a
    Return the list which has the metadata of all the target_op nodes present in the graph.
    c                   s   g | ]
}|j  kr|jqS r(   )targetr   r   r`  r(   r)   r     s    z get_metadata.<locals>.<listcomp>)r   r   )r_  r`  r(   re  r)   get_metadata
  s   rf  r   c                   sJ    fdd| j jD }t|t|ksJ t|D ]	\}}|| |_qdS )rc  c                   s   g | ]	}|j  kr|qS r(   )rd  r   re  r(   r)   r     r   z set_metadata.<locals>.<listcomp>N)r   r   r}   	enumerater   )r_  r`  r   target_nodesidxr   r(   re  r)   set_metadata  s
   rj  match_and_replacementsc                 C  sB   | D ]}|j |j }t|jdksJ d|jd }|j|_qdS )z
    Copy the metadata from anchor node to the replacement node. This should be used
    if the anchor node is replaced with only a single replacement node i.e one-one replacement.
    r   z3Found more than 1 replacements for the anchor node.r   N)	nodes_mapanchorr}   replacementsr   )rk  match_and_replacementanchor_nodereplacement_noder(   r(   r)   copy_metadata  s   

rr  r   List[torch.fx.node.Node]c                 C  s^   g }t | tjjjr||  |S t | ttfr&| D ]	}|t	| q|S t
dt| )Nzhexpect torch.fx.node.Node or a tuple/list of torch.fx.node.Node type, got unexpected types: type(nodes)=)rk   r    fxr   Noder   r   r   r   flatten_nodesr   rs   )r   retr   r(   r(   r)   rv  -  s   
rv  c                 C  sR   dd | j jD }t|dksJ |d j}t|}t|dks"J dd |D S )Nc                 S  r   r   r   r   r(   r(   r)   r   >  r   z'get_output_metadata.<locals>.<listcomp>r   c                 S  s   g | ]}|j qS r(   )r   r   r(   r(   r)   r   C  r  )r   r   r}   r6  rv  )r_  outputsr   r(   r(   r)   get_output_metadata;  s   
ry  r   r  List[dtype]c                 C  s  g }t | tjjjrcd| jv rE| jd }t |ttjfr7|r,|jtj	kr,|
tj |S |
t|j |S t |tjrC|
tj |S d| jv rZ| jd }|
t|j |S td| j dt | ttfrx| D ]	}|t| ql|S tdt|  d)Nr   tensor_metaz
node.name=zF: metadata does not exist, expect metadata exists for each output nodezgot unexpected type zM, expected type is a torch.fx.node.Node or a tuple/list of torch.fx.node.Node)rk   r    rt  r   ru  r   r   rl   r   float64r   r;   r
  r   r9   r   r  r   r   r   get_output_dtypesrs   )r   r  r  output_metaeler(   r(   r)   r}  F  s6   



r}  c                   C  s   t   jd d S )Ni   )psutilProcessmemory_inforssr(   r(   r(   r)   get_cpu_memory_usagec  s   r  c                  C  s   t   tj rtj  tj  tj  tj  t	 dkrSt
jdddkrUztd} | ddkrAtd W d S W d S  tyR   td Y d S w d S d S )	NLinux#TORCHTRT_ENABLE_BUILDER_MALLOC_TRIM01z	libc.so.6r   r   zFailed to release CPU memory.)rF   rG   r    rD   is_availablesynchronizerE   ipc_collectplatformsystemosenvironr   ctypesCDLLmalloc_trimrQ   r*  	Exception)libcr(   r(   r)   release_host_and_device_memoryg  s$   





r  )r   r+   r,   r   r-   r.   )T)r?   r@   rA   r7   r-   rB   r   )rI   rJ   r-   r7   )rV   rW   rX   rW   r-   rY   )ri   rj   r-   r7   )rK   )rt   r   rv   rw   rx   ry   r-   rz   )ri   r   rv   r   rx   ry   r-   r   )r?   r@   r-   rw   )r   r   r   r   r-   rB   )F)ri   r   r   r7   r-   r   )ri   r   r   ry   r   r   r-   r   )r   rW   r-   r7   )r   r   r-   r   )r   r   rx   r   r-   r   )r   r   r-   r   )r   r   r   ry   r-   r   )r?   r@   r   r   r-   rB   )rv   r  r-   rw   )rv   r  r-   r   )r  r   r-   r  )r2  )r3  ry   r-   r4  )
r>  r@   r?  r@   r@  r   rA  r   r-   r7   )
rF  r   rG  r   r[   rY   rZ   rY   r-   r7   )rM  rN  r6  rO  r  rP  r-   rQ  )r_  r@   r`  ra  r-   rb  )r_  r@   r`  ra  r   rb  r-   rB   )rk  rb  r-   rB   )r   r   r-   rs  )r_  r@   r-   rb  )r   r   r  r7   r-   rz  )r-   r   )r-   rB   )l
__future__r   r  rF   r   r  r  r#  dataclassesr   r   enumr   typingr   r   r   r	   r
   r   r   r   r   r4   r  r   r   r!   r    torch._subclasses.fake_tensorr   "torch.fx.experimental.proxy_tensorr   torch_tensorrt._Devicer   torch_tensorrt._enumsr   torch_tensorrt._featuresr   torch_tensorrt._Inputr   torch_tensorrt._utilsr   torch_tensorrt.dynamor   torch_tensorrt.dynamo._defaultsr   r+  r   torch_tensorrt.dynamo._settingsr   	packagingr   typesr   	getLoggerr"   rQ   COSINE_THRESHOLDDYNAMIC_DIMRTOLATOLrC   r   r5   r%   r&   r'   r8   r9   r:   r;   r7   r*   __annotations__r6   r>   rH   rU   rd   ru   r   r   r   r   r   r   rm   r   r   r   r   r  r   r  r1  r=  rE  rB  r^  rf  rj  rr  rv  ry  r}  r  r  r(   r(   r(   r)   <module>   s    (
	

#




 
9
0







i%
'

	



