在import torch
import torch.nn as nn
import torch.nn.functional as F

# 为结合vit中的attention优势,设计了新的交叉注意力
class Cross_Attention(nn.Module):
    def __init__(self, input_channels, output_channels):
        super(Cross_Attention, self).__init__()
        self.query = nn.Linear(input_channels, output_channels)
        self.key = nn.Linear(input_channels, output_channels)
        self.value = nn.Linear(input_channels, output_channels)
        self.scale = output_channels ** -0.5  # 缩放因子，防止softmax的梯度消失/爆炸
    
    def forward(self, assistant, present):
        # 生成查询、键和值
        q = self.query(assistant)
        k = self.key(present)
        v = self.value(present)
        
        # 计算注意力分数
        attention_scores = torch.bmm(q, k.transpose(1, 2)) * self.scale
        attention = F.softmax(attention_scores, dim=-1)
        
        # 应用注意力机制
        attended_features = torch.bmm(attention, v)
        return attended_features



class MultiHeadCrossAttention(nn.Module):
    def __init__(self, input_channels, output_channels, num_heads):
        super(MultiHeadCrossAttention, self).__init__()
        assert output_channels % num_heads == 0, 'output_channels must be divisible by num_heads'

        self.num_heads = num_heads
        self.dim_per_head = output_channels // num_heads

        self.query = nn.Linear(input_channels, output_channels)
        self.key = nn.Linear(input_channels, output_channels)
        self.value = nn.Linear(input_channels, output_channels)

        self.scale = self.dim_per_head ** -0.5 
        
        self.linear_final = nn.Linear(output_channels, output_channels)
        self.dropout = nn.Dropout(p=0.1)

    def split_heads(self, x):
        '''Split the last dimension into (num_heads, depth).
        Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth)
        '''
        new_shape = x.size()[:-1] + (self.num_heads, self.dim_per_head)
        x = x.view(*new_shape)
        return x.permute(0, 2, 1, 3)
    
    def forward(self, assistant, present):
        batch_size = assistant.size(0)

        # Linear projections
        q = self.query(assistant)
        k = self.key(present)
        v = self.value(present)

        # Split into multiple heads
        q = self.split_heads(q)  # (batch_size, num_heads, seq_len_q, depth)
        k = self.split_heads(k)  # (batch_size, num_heads, seq_len_k, depth)
        v = self.split_heads(v)  # (batch_size, num_heads, seq_len_v, depth)

        # Scaled dot-product attention
        attention_scores = torch.matmul(q, k.transpose(-2, -1)) * self.scale
        attention = F.softmax(attention_scores, dim=-1)
        attention = self.dropout(attention)

        # Apply attention
        attended_features = torch.matmul(attention, v)  # (batch_size, num_heads, seq_len_q, depth)

        # Concatenate heads
        attended_features = attended_features.permute(0, 2, 1, 3).contiguous()
        new_shape = attended_features.size()[:-2] + (self.num_heads * self.dim_per_head,)
        attended_features = attended_features.view(*new_shape)

        # Final linear projection
        attended_features = self.linear_final(attended_features)

        return attended_features

class Multi_Context(nn.Module):
    def __init__(self, input_channels, output_channels):  # 修正了def init为def init
        super(Multi_Context, self).__init__()
        self.linear1 = nn.Linear(input_channels, output_channels)
        self.linear2 = nn.Linear(input_channels, output_channels)
        # self.linear3 = nn.Linear(input_channels, output_channels)
        self.linear_final = nn.Linear(output_channels * 2, output_channels)

    def forward(self, x):
        x1 = F.relu(self.linear1(x))
        x2 = F.relu(self.linear2(x))
        # x3 = F.relu(self.linear3(x))
        # x = torch.cat([x1, x2, x3], dim=-1)  # Concatenate along the feature dimension
        x = torch.cat([x1, x2], dim=-1) 
        x = self.linear_final(x)
        return x

class Adaptive_Weight(nn.Module):
    def __init__(self, input_channels):  # 修正了def init为def init
        super(Adaptive_Weight, self).__init__()
        self.fc1 = nn.Linear(input_channels, input_channels // 4)
        self.fc2 = nn.Linear(input_channels // 4, input_channels)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        x_avg = torch.mean(x, dim=1)  # Global Average Pooling along the ‘sequence length’ dimension
        weight = F.relu(self.fc1(x_avg))
        weight = self.fc2(weight)
        weight = self.sigmoid(weight).unsqueeze(1)
        out = x * weight
        return out

# class Counter_Guide_Enhanced(nn.Module):
#     def __init__(self, input_channels, output_channels):
#         super(Counter_Guide_Enhanced, self).__init__()
#         self.mc = Multi_Context(input_channels, output_channels)
#         self.cross_attention = Cross_Attention(output_channels, output_channels)
#         # 结合自适应
#         self.adaptive_weight = Adaptive_Weight(output_channels)

#         # 增加一个全连接层作为动态调节因子的生成器
#         self.dynamic_scale_generator = nn.Sequential(
#             nn.Linear(output_channels, output_channels // 2),
#             nn.ReLU(),
#             nn.Linear(output_channels // 2, 1),
#             nn.Sigmoid()  # 确保输出是正的且在[0,1]范围内
#         )
    
#     def forward(self, x, event_x):
#         # 首先使用Multi-Context增强事件特征
#         enhanced_event_x = self.mc(event_x)

#         # 使用Cross-Attention进行特征交互,(x和event_x互相增强)
#         enhanced_x = self.cross_attention(x, enhanced_event_x)
#         enhanced_event_x_via_x = self.cross_attention(event_x, enhanced_x)
        
#         # 进一步通过adaptive weight调整增强的特征权重
#         adaptive_enhanced_x = self.adaptive_weight(enhanced_x)
#         adaptive_enhanced_event_x = self.adaptive_weight(enhanced_event_x_via_x)

#         # 根据adaptive_enhanced_x生成动态调节因子
#         dynamic_scale = self.dynamic_scale_generator(adaptive_enhanced_x)

#         # 使用动态调节因子调整增强特征的强度
#         adaptive_enhanced_x = adaptive_enhanced_x * dynamic_scale
#         adaptive_enhanced_event_x = adaptive_enhanced_event_x * dynamic_scale
        
#         return adaptive_enhanced_x, adaptive_enhanced_event_x



class Counter_Guide_Enhanced_with_MultiHead(nn.Module):
    def __init__(self, input_channels, output_channels, num_heads):
        super(Counter_Guide_Enhanced_with_MultiHead, self).__init__()
        self.mc = Multi_Context(input_channels, output_channels)
        self.cross_attention = MultiHeadCrossAttention(output_channels, output_channels, num_heads)
        self.adaptive_weight = Adaptive_Weight(output_channels)

        # # 动态调节因子生成器不变
        # self.dynamic_scale_generator = nn.Sequential(
        #     nn.Linear(output_channels, output_channels // 2),
        #     nn.ReLU(),
        #     nn.Linear(output_channels // 2, 1),
        #     nn.Sigmoid() 
        # )
    
    def forward(self, x, event_x):
        # 使用Multi-Context增强事件特征
        enhanced_event_x = self.mc(event_x)

        # 使用多头交叉注意力机制进行特征交互
        enhanced_x = self.cross_attention(x, enhanced_event_x)
        enhanced_event_x_via_x = self.cross_attention(event_x, enhanced_x)
        
        # 通过自适应权重调整增强的特征权重
        adaptive_enhanced_x = self.adaptive_weight(enhanced_x)
        adaptive_enhanced_event_x = self.adaptive_weight(enhanced_event_x_via_x)
        
        return adaptive_enhanced_x, adaptive_enhanced_event_x中，我们修改Counter_Guide_Enhanced_with_MultiHead，使其仅仅使用cross_attention和adaptivate_weights