o
    Gi'                     @   s\   d dl 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
 e
eZG dd dZdS )    N   )	AttentionAttentionProcessor"PAGCFGIdentitySelfAttnProcessor2_0PAGIdentitySelfAttnProcessor2_0)loggingc                   @   s   e Zd ZdZdd Zdd Z	dddZd	d
 Ze e	 ffde
ee
 B deeef fddZedefddZedefddZedefddZedefddZedee
ef fddZdS )PAGMixinzZMixin class for [Pertubed Attention Guidance](https://huggingface.co/papers/2403.17377v1).c                 C   s   | j }|du rtd|r|d n|d }t| dr| j}n| j}dtjdtfdd	}d
d }|D ]@}g }	| D ]"\}
}||r[t	
||
dur[|||
s[td|
  |	| q9t|	dkritd| |	D ]}||_qkq1dS )zA
        Set the attention processor for the PAG layers.
        NzNo PAG attention processors have been set. Set the attention processors by calling `set_pag_applied_layers` and passing the relevant parameters.r      unetmodulereturnc                 S   s   t | to| j S )zY
            Check if the module is self-attention module based on its name.
            )
isinstancer   is_cross_attention)r    r   U/home/ubuntu/.local/lib/python3.10/site-packages/diffusers/pipelines/pag/pag_utils.pyis_self_attn4   s   z6PAGMixin._set_pag_attn_processor.<locals>.is_self_attnc                 S   s4   |  dd } | dd }|  o| o| |kS )N.)split	isnumeric)layer_idnamer   r   r   is_fake_integral_match:   s   z@PAGMixin._set_pag_attn_processor.<locals>.is_fake_integral_matchzApplying PAG to layer: z6Cannot find PAG layer to set attention processor for: )_pag_attn_processors
ValueErrorhasattrr
   transformernnModuleboolnamed_modulesresearchloggerdebugappendlen	processor)selfpag_applied_layersdo_classifier_free_guidancepag_attn_processorspag_attn_procmodelr   r   r   target_modulesr   r   r   r   r   _set_pag_attn_processor#   s8   

z PAGMixin._set_pag_attn_processorc                 C   s0   | j r| j| jd|   }|dk rd}|S | jS )z\
        Get the scale factor for the perturbed attention guidance at timestep `t`.
        i  r   )do_pag_adaptive_scaling	pag_scalepag_adaptive_scale)r(   tsignal_scaler   r   r   _get_pag_scaleW   s   zPAGMixin._get_pag_scaleFc           
      C   sj   |  |}|r|d\}}}	||||   |||	   }n|d\}}	||||	   }|r3||fS |S )a  
        Apply perturbed attention guidance to the noise prediction.

        Args:
            noise_pred (torch.Tensor): The noise prediction tensor.
            do_classifier_free_guidance (bool): Whether to apply classifier-free guidance.
            guidance_scale (float): The scale factor for the guidance term.
            t (int): The current time step.
            return_pred_text (bool): Whether to return the text noise prediction.

        Returns:
            torch.Tensor | tuple[torch.Tensor, torch.Tensor]: The updated noise prediction tensor after applying
            perturbed attention guidance and the text noise prediction.
        r      )r5   chunk)
r(   
noise_predr*   guidance_scaler3   return_pred_textr1   noise_pred_uncondnoise_pred_textnoise_pred_perturbr   r   r   #_apply_perturbed_attention_guidanced   s   


z,PAGMixin._apply_perturbed_attention_guidancec                 C   s.   t j|gd dd}|rt j||gdd}|S )a  
        Prepares the perturbed attention guidance for the PAG model.

        Args:
            cond (torch.Tensor): The conditional input tensor.
            uncond (torch.Tensor): The unconditional input tensor.
            do_classifier_free_guidance (bool): Flag indicating whether to perform classifier-free guidance.

        Returns:
            torch.Tensor: The prepared perturbed attention guidance tensor.
        r6   r   )dim)torchcat)r(   conduncondr*   r   r   r   %_prepare_perturbed_attention_guidance   s   z.PAGMixin._prepare_perturbed_attention_guidancer)   r+   c                 C   s   t | dsd| _t|ts|g}|dur#t|trt|dkr#tdtt|D ]}t|| ts=tdt	||  q)|| _
|| _dS )a  
        Set the self-attention layers to apply PAG. Raise ValueError if the input is invalid.

        Args:
            pag_applied_layers (`str` or `list[str]`):
                One or more strings identifying the layer names, or a simple regex for matching multiple layers, where
                PAG is to be applied. A few ways of expected usage are as follows:
                  - Single layers specified as - "blocks.{layer_index}"
                  - Multiple layers as a list - ["blocks.{layers_index_1}", "blocks.{layer_index_2}", ...]
                  - Multiple layers as a block name - "mid"
                  - Multiple layers as regex - "blocks.({layer_index_1}|{layer_index_2})"
            pag_attn_processors:
                (`tuple[AttentionProcessor, AttentionProcessor]`, defaults to `(PAGCFGIdentitySelfAttnProcessor2_0(),
                PAGIdentitySelfAttnProcessor2_0())`): A tuple of two attention processors. The first attention
                processor is for PAG with Classifier-free guidance enabled (conditional and unconditional). The second
                attention processor is for PAG with CFG disabled (unconditional only).
        r   Nr6   z,Expected a tuple of two attention processorsz:Expected either a string or a list of string but got type )r   r   r   listtupler&   r   rangestrtyper)   )r(   r)   r+   ir   r   r   set_pag_applied_layers   s   


zPAGMixin.set_pag_applied_layersr   c                 C      | j S )z:Get the scale factor for the perturbed attention guidance.)
_pag_scaler(   r   r   r   r1         zPAGMixin.pag_scalec                 C   rL   )zCGet the adaptive scale factor for the perturbed attention guidance.)_pag_adaptive_scalerN   r   r   r   r2      rO   zPAGMixin.pag_adaptive_scalec                 C   s"   | j dko| jdkot| jdkS )zNCheck if the adaptive scaling is enabled for the perturbed attention guidance.r   )rP   rM   r&   r)   rN   r   r   r   r0      s   "z PAGMixin.do_pag_adaptive_scalingc                 C   s   | j dkot| jdkS )z5Check if the perturbed attention guidance is enabled.r   )rM   r&   r)   rN   r   r   r   do_perturbed_attention_guidance   s   z(PAGMixin.do_perturbed_attention_guidancec                 C   sx   | j du ri S dd | j D }i }t| dr| j}nt| dr#| j}ntd|j D ]\}}|j|v r9|||< q,|S )z
        Returns:
            `dict` of PAG attention processors: A dictionary contains all PAG attention processors used in the model
            with the key as the name of the layer.
        Nc                 S   s   h | ]}|j qS r   )	__class__).0xr   r   r   	<setcomp>   s    z/PAGMixin.pag_attn_processors.<locals>.<setcomp>r
   r   zNo denoiser module found.)r   r   r
   r   r   attn_processorsitemsrR   )r(   valid_attn_processors
processorsdenoiser_moduler   procr   r   r   r+      s   



zPAGMixin.pag_attn_processorsN)F)__name__
__module____qualname____doc__r/   r5   r>   rD   r   r   rH   rE   rF   r   rK   propertyfloatr1   r2   r   r0   rQ   dictr+   r   r   r   r   r       s2    4
 


,r   )r!   r@   torch.nnr   models.attention_processorr   r   r   r   utilsr   
get_loggerr\   r#   r   r   r   r   r   <module>   s   
