o
    iB                  	   @   s  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
 ddlmZmZ e rGd dlZd dlmZ dZej rFd dlZd	ZndZe
eZd
d ZedZdedefddZdd Z	d.dedB dededB fddZd.dedB dededB fddZ dej!fddZ"dd Z#edZ$d d! Z%d"d# Z&dedB fd$d%Z'	&				d/ded'edefd(d)Z(ed*d+e					d0dedB d'edefd,d-Z)dS )1    N)contextmanagerredirect_stdout)StringIO   )logging)is_torch_availablerequires)	save_fileFTc                   C   s    t rtj s	dS tj dkS )z7Return True if rank=0 or we aren't running distributed.Tr   )_torch_distributed_availabletorchdistributedis_initializedget_rank r   r   d/sda-disk/www/egybert/egybert_env/lib/python3.10/site-packages/transformers/model_debugging_utils.py_is_rank_zero,   s   r   zobject at 0x[0-9A-Fa-f]+x_strreturnc                 C   s   t d| S )z
    Replace memory addresses in an object's repr with a stable placeholder
    so that beautiful JSON diffs won't be ruined by ephemeral addresses.
    zobject at 0xXXXXXXXX)MEMORY_ADDRESS_REGEXsub)r   r   r   r   _sanitize_repr_for_diff6   s   r   c                 C   s   t  rdt| j S dS )z@Return a stable string representation for a DTensor-like object.zDTensor (rank0) -> zDTensor(non-rank0))r   repr_local_tensor)xr   r   r   _dtensor_repr>   s   r   
debug_pathuse_reprpath_to_valuec              	   C   s   t jdd |rt| }n4|r6|ds|d7 }|r!tj||n|}td|  	 
 i| d| }ntd|d|dt| jt| j|d	}| jt jt jt jhv rz|tt|  tt|  tt|  tt|  d
 |S )a  
    Converts Tensors and DTensors to a JSON-serializable dictionary representation.

    Args:
        value: Any Python object, often including torch Tensors, lists, dicts, etc.
        debug_path (`str`, *optional*, defaults to `None`): Directory to dump debug JSON and SafeTensors files.
        use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensor as the
            `value` property in the asscoiated FULL_TENSORS.json file, or to store the full tensors in separate
            SafeTensors file and store the relative path to that file in the `value` property in the dictionary.
        path_to_value (`str`, *optional*, defaults to `None`): The file name for the SafeTensors file holding the full
            tensor value if `use_repr=False`.

    Returns:
        A nested Python structure (list, dict, or sanitized string) that is safe to json.dump.
    T)sci_modez.safetensorsdataz./z	use_repr=z and path_to_value=z cannot both be falsy.)shapedtypevalue)meanstdminmax)r   set_printoptions_repr_to_listendswithospathjoinr	   
contiguousdetachcpu
ValueErrorr   r    r!   float16float32bfloat16updater   r#   r$   r%   r&   )r"   r   r   r   	value_outfilepathoutr   r   r   _serialize_tensor_like_ioE   s.   

r8   c                    s   t | ttfr fddt| D S t | tr& fdd|  D S t| dr4t| j dS t | t	j
rBt|  dS tt| S )a  
    Recursively build a JSON-serializable Python structure from `value`.
    Tensors and DTensors become either sanitized repr strings, or are saved to disk as SafeTensors files and their
    relative paths are recorded in the returned Python structure.
    Lists/tuples/dicts are recursed into.
    All memory addresses are replaced with a stable placeholder.

    Args:
        value: Any Python object, often including torch Tensors, lists, dicts, etc.
        debug_path (`str`, *optional*, defaults to `None`): Directory to dump debug JSON and SafeTensors files.
        use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensors as the
            `value` property in the asscoiated FULL_TENSORS.json file, or to store full tensors in separate SafeTensors
            files and store the relative path to that file in the `value` property.
        path_to_value (`str`, *optional*, defaults to `None`): The file name for the SafeTensors file holding the full
            tensor value if `use_repr=False`.

    Returns:
        A nested Python structure (list, dict, or sanitized string) that is safe to json.dump.
    c              	      s*   g | ]\}}t |  d | dqS _r   r   r   _serialize_io).0ivr   r   r   r   r   
<listcomp>   s    z!_serialize_io.<locals>.<listcomp>c              
      s,   i | ]\}}|t |  d | dqS r9   r<   )r>   kr@   rA   r   r   
<dictcomp>   s    z!_serialize_io.<locals>.<dictcomp>r   r;   )
isinstancelisttuple	enumeratedictitemshasattrr8   r   r   Tensorr   r   )r"   r   r   r   r   rA   r   r=   v   s   


r=   r"   c              	   C   sx   t jddd t $}t| t|  | }W d   n1 s"w   Y  W d   n1 s1w   Y  t| S )z
    Converts a tensor into a sanitized multi-line string representation.

    Args:
        value (`torch.Tensor`): The tensor to represent.

    Returns:
        `list[str]`: List of string lines representing the tensor.
    Tx   )r   	linewidthN)r   r'   r   r   printgetvaluer   
splitlines)r"   bufrawr   r   r   r(      s   

 r(   c                 C   s4   |  dr| dd  | d D ]}t| qd S d S )Nchildrenoutputs)getpopprune_outputs_if_childrennodechildr   r   r   rX      s   

rX   z(.*)\.(\d+)$c                    sH   t | dd}|r| dsdS |d t fdd| d D S )z
    Checks whether a node represents a layer block with submodules.

    Args:
        node (`dict`): A node from the call tree.

    Returns:
        `bool`: Whether the node is a layer block.
    module_path rT   F   c                 3   s(    | ]}d   d | ddv V  qdS ).r\   r]   NrV   )r>   r[   numberr   r   	<genexpr>   s   & z!is_layer_block.<locals>.<genexpr>)LAYER_SUFFIX_REmatchrV   groupany)rZ   re   r   ra   r   is_layer_block   s
   

rh   c                    s~   |  dsdS dd t| d D }t|dkr2dd |dd D   fd	dt| d D | d< | d D ]}t| q6dS )
z
    Recursively removes intermediate layers from the tree to improve readability.
    Keeps at least the first and last layers if many consecutive layers are present.

    Args:
        node (`dict`): The root or subnode to prune recursively.
    rT   Nc                 S   s    g | ]\}}t |r||fqS r   )rh   r>   r?   r[   r   r   r   rB      s     z-prune_intermediate_layers.<locals>.<listcomp>r^   c                 S   s   g | ]\}}|qS r   r   )r>   r?   r:   r   r   r   rB      s    r   c                    s   g | ]
\}}| vr|qS r   r   ri   	to_remover   r   rB      s    )rV   rH   lenprune_intermediate_layers)rZ   layer_blocksr[   r   rk   r   rn      s   

rn   c              
      s.  | r+zt j| dd t j| |jd }W n ty* } z	td|  d|d }~ww |jd }td| d |d }|d	 }t	|j
 t|d
}tj|j
|dd W d    n1 s_w   Y   fdd tt|j
} | t|d
}tj||dd W d    d S 1 sw   Y  d S )NTexist_ok_debug_tree"Unexpected or existing debug_path=r_   zWriting model trace at z.jsonz_FULL_TENSORS.jsonz_SUMMARY.jsonwr^   )indentc                    sJ    fdd  |  di   |  di  |  dg D ]}| qd S )Nc                    sT   t | tr| dd  |  D ]} | qd S t | tr&| D ]} | qd S d S )Nr"   )rE   rI   rW   valuesrF   )valr@   itemcleanr   r   rz      s   



z:log_model_debug_trace.<locals>.strip_values.<locals>.cleaninputsrU   rT   r`   rY   strip_valuesry   r   r}      s   	
z+log_model_debug_trace.<locals>.strip_values)r*   makedirsr+   r,   _debugger_module_dump_name	Exceptionr0   loggerinforX   
_call_treeopenjsondumploadsdumps)r   modelbasee	full_pathsummary_pathf	tree_copyr   r|   r   log_model_debug_trace   s,   

"r   r_   do_prune_layersc           	   
      s   j j  ddg d_g _ _r3z	tjdd W n ty2 } z	td d|d}~ww fdd}	 D ]\}}|d	krHq?||  d|  q?j
t fd
d}|_
dS )a  
    Attaches a debugging wrapper to every module in the model.

    This records structured inputs and outputs during the forward pass into a call tree.

    Args:
        model (`PreTrainedModel`, `nn.Module`): Model to wrap.
        debug_path (`str`): Optional directory to dump debug JSON files.
        do_prune_layers (`bool`, *optional*, defaults to `True`): Whether to prune intermediate layers.
        use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensors as the
            `value` property in the associated FULL_TENSORS.json file, or to store full tensors in separate SafeTensors
            files and store the relative path to that file in the `value` property.
    Nr\   r{   rU   rT   Trp   rs   r_   c                    s0   j t fdd}|_ d S )Nc                     s  t  r'| |d  fdd D  t  ddd g d}j| t  | i |}W d    n1 s=w   Y  t  rtdd  D d	krWd |d
< nt| dd|d
< j }|d sr|d jrjd d | |S )Nargskwargsc                    s&   i | ]}t  | d kr| | qS )r   )rm   r>   rC   dict_inputsr   r   rD   5  s   & zY_attach_debugger_logic.<locals>.wrap_forward.<locals>.wrapped_forward.<locals>.<dictcomp>_inputsr;   r   c                 s   s    | ]}d V  qdS )r   Nr   )r>   r:   r   r   r   rc   F  s    zX_attach_debugger_logic.<locals>.wrap_forward.<locals>.wrapped_forward.<locals>.<genexpr>r   rU   _outputsrT   rj   )	r   r=   _debugger_model_call_stackappendr   no_gradsumnamed_childrenrW   )inpskwsrZ   r7   finished)r   r   r   moduleorig_forwardr   r   r   wrapped_forward1  s@   





zE_attach_debugger_logic.<locals>.wrap_forward.<locals>.wrapped_forward)forward	functoolswraps)r   r   r   )r   r   r   )r   r   r   r   wrap_forward.  s   
'z,_attach_debugger_logic.<locals>.wrap_forwardr]   c                     s   t  r  dt| |d  ddd g d}j| | i |}t  rnjrnt|  dd|d< j }|d jd< |d jd< |d	 jd	< fd
dtj D  rhtj t	d |S )Nz (top-level)r   r   r;   r   r   rU   r{   rT   c                    s$   g | ]} j | s j |d qS )N)r   rW   r   )r   r   r   rB     s   $ zG_attach_debugger_logic.<locals>.top_wrapped_forward.<locals>.<listcomp>)r   r   )
r   r=   r   r   rW   r   rF   keysrn   r   )r   r   top_noder7   r   
class_namer   r   r   real_top_forwardr   r   r   top_wrapped_forwardd  s:   


z3_attach_debugger_logic.<locals>.top_wrapped_forward)	__class____name__r   r   r   r*   r~   r   r0   named_modulesr   r   r   )	r   r   r   r   r   r   name	submoduler   r   r   r   _attach_debugger_logic  s(   .
%r   )r   )backendsc              	   c   sl    dd |   D }| j|| < t| ||| z| V  W | D ]\}}||_qdS | D ]\}}||_q-w )a  
    # Model addition debugger - context manager for model adders
    This context manager is a power user tool intended for model adders.

    It tracks all forward calls within a model forward and logs a slice of each input and output on a nested JSON file.
    If `use_repr=True` (the default), the JSON file will record a `repr()`-ized version of the tensors as a list of
    strings. If `use_repr=False`, the full tensors will be stored in separate SafeTensors files and the JSON file will
    provide a relative path to that file.

    To note, this context manager enforces `torch.no_grad()`.

    ## Usage

    add the context manager to a model to debug

    ```python
    import torch

    from PIL import Image
    from transformers import LlavaProcessor, LlavaForConditionalGeneration, model_addition_debugger_context

    torch.random.manual_seed(673)

    # load pretrained model and processor
    model_id = "llava-hf/llava-1.5-7b-hf"
    processor = LlavaProcessor.from_pretrained(model_id)
    model = LlavaForConditionalGeneration.from_pretrained(model_id)

    # create random image input
    random_image = Image.fromarray(torch.randint(0, 256, (224, 224, 3), dtype=torch.uint8).numpy())

    # prompt
    prompt = "<image>Describe this image."

    # process inputs
    inputs = processor(text=prompt, images=random_image, return_tensors="pt")

    # call forward method (not .generate!)
    with model_addition_debugger_context(model, debug_path="Your_debug_path", do_prune_layers=False):
        output = model.forward(**inputs)
    ```

    c                 S   s   i | ]\}}||j qS r   )r   )r>   r:   mr   r   r   rD     s    z3model_addition_debugger_context.<locals>.<dictcomp>N)r   r   r   rJ   )r   r   r   r   orig_forwardsmodule_instanceforward_methodr   r   r   model_addition_debugger_context  s   3
r   )NTN)r_   TT)NTT)*r   r   r*   re
contextlibr   r   ior   utilsr   utils.import_utilsr   r   r   safetensors.torchr	   r
   r   is_availabletorch.distributed.tensor
get_loggerr   r   r   compiler   strr   r   boolr8   r=   rL   r(   rX   rd   rh   rn   r   r   r   r   r   r   r   <module>   sv   



 1+
	-
