o
    pi&                     @   st   d dl Z d dlmZ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 eeZG dd dZdS )    N)DictListTupleUnion   )	AttentionAttentionProcessor"PAGCFGIdentitySelfAttnProcessor2_0PAGIdentitySelfAttnProcessor2_0)loggingc                   @   s   e Zd ZdZdd Zdd Zdd Zdd	 Ze e	 ffd
e
eee f 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 )PAGMixinzRMixin class for [Pertubed Attention Guidance](https://arxiv.org/abs/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   _/home/ubuntu/SoloSpeech/.venv/lib/python3.10/site-packages/diffusers/pipelines/pag/pag_utils.pyis_self_attn5   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_scaleX   s   zPAGMixin._get_pag_scalec           	      C   s`   |  |}|r|d\}}}||||   |||   }|S |d\}}||||   }|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.

        Returns:
            torch.Tensor: The updated noise prediction tensor after applying perturbed attention guidance.
        r      )r9   chunk)	r,   
noise_predr.   guidance_scaler7   r5   noise_pred_uncondnoise_pred_textnoise_pred_perturbr   r   r   #_apply_perturbed_attention_guidancee   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.
        r:   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 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   Nr:   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   r5         zPAGMixin.pag_scalec                 C   rO   )zCGet the adaptive scale factor for the perturbed attention guidance.)_pag_adaptive_scalerQ   r   r   r   r6      rR   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   )rS   rP   r*   r-   rQ   r   r   r   r4      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   )rP   r*   r-   rQ   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itemsrU   )r,   valid_attn_processors
processorsdenoiser_moduler   procr   r   r   r/      s   



zPAGMixin.pag_attn_processorsN)__name__
__module____qualname____doc__r3   r9   rA   rG   r	   r
   r   rK   r   r   r   rN   propertyfloatr5   r6   r#   r4   rT   r   r/   r   r   r   r   r   !   s0    4

,r   )r%   typingr   r   r   r   rC   torch.nnr!   models.attention_processorr   r   r	   r
   utilsr   
get_loggerr_   r'   r   r   r   r   r   <module>   s   
