import pandas as pd
import time
import numpy as np
import matplotlib.pyplot as plt
import os
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
from collections import Counter
import torch.nn.functional
import pennylane as qml
from pennylane import numpy as np
from datasets import load_dataset
from tqdm import tqdm  # 添加进度条
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import re
from sklearn.model_selection import train_test_split
from qiskit import QuantumCircuit
from qiskit.visualization import circuit_drawer
from sklearn.metrics import f1_score, recall_score
import random
seed = 42
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)

min_freq = 1

sequence_length = 10 # 10

embedding_dim = 100  # 词嵌入维度 与mat1有关

batch_size = 32

hidden_size = 64

dropout_rate = 0.5

lr = 0.0001

n_qlayers = 1

heads = 2

epoch = 40

n_qubits = 4

weight_decay = 0.00001

# 读取数据文件
def load_data(file_path):
    data = []
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            text, label = line.strip().split('\t')
            data.append((text, int(label)))
    return pd.DataFrame(data, columns=['text', 'label'])

# 文本清洗函数
def clean_text(text):
    # 转换为小写
    text = text.lower()
    # 移除 HTML 标签
    text = re.sub(r'<.*?>', '', text)
    # 移除 URL
    text = re.sub(r'http\S+|www\S+|https\S+', '', text, flags=re.MULTILINE)
    # 移除标点符号和数字
    text = re.sub(r'\d+', '', text)
    text = re.sub(r'[^\w\s]', '', text)
    # 分词
    words = word_tokenize(text)
    # 移除停用词
    stop_words = set(stopwords.words('english'))
    words = [word for word in words if word not in stop_words]
    return ' '.join(words)


# 加载数据
file_path = 'amazon_cells_labelled.txt'
dataset = load_data(file_path)

# 随机抽取一个新的训练集（800条）、验证集（100条）和测试集（100条）
train_val_set, test_set = train_test_split(dataset, train_size=900, test_size=100, random_state=seed, stratify=dataset['label'])
train_set, val_set = train_test_split(train_val_set, train_size=800, test_size=100, random_state=seed, stratify=train_val_set['label'])

# 清洗所有数据集的文本
train_set['text'] = train_set['text'].apply(clean_text)
val_set['text'] = val_set['text'].apply(clean_text)
test_set['text'] = test_set['text'].apply(clean_text)

# 定义一个函数来标记和编码句子
def tokenize_and_encode(sentences, vocab, seq_length):
    token_ids = [
        [vocab.get(token, vocab['<UNK>']) for token in sentence.split()]
        for sentence in sentences
    ]
    # 填充序列
    token_ids = [
        t_ids[:seq_length] if len(t_ids) > seq_length else t_ids + [vocab['<PAD>']] * (seq_length - len(t_ids))
        for t_ids in token_ids
    ]
    return token_ids

# 从训练数据集创建词汇表
all_train_tokens = [token for sentence in train_set['text'] for token in sentence.split()]

# 计算所有训练数据中单词的频率
word_counts = Counter(all_train_tokens)

# 打印词汇表中每个单词的频率，按照频率从高到低排序
for word, count in word_counts.most_common():
    print(f"{word}: {count}")

# 只保留出现次数大于等于阈值的单词

filtered_words = [word for word, count in word_counts.items() if count >= min_freq]
vocabulary = {word: i + 2 for i, word in enumerate(filtered_words)}
vocabulary['<UNK>'] = 1
vocabulary['<PAD>'] = 0

# 打印词汇表大小
print(f"真实Vocabulary size: {len(vocabulary)}")

# 对数据集进行标记和编码

train_token_ids = tokenize_and_encode(train_set['text'], vocabulary, sequence_length)
val_token_ids = tokenize_and_encode(val_set['text'], vocabulary, sequence_length)
#test_token_ids = tokenize_and_encode(test_set['text'], vocabulary, sequence_length)

# 创建自定义 PyTorch 数据集
class SentimentDataset(Dataset):
    def __init__(self, token_ids, labels):
        self.token_ids = token_ids
        self.labels = labels

    def __len__(self):
        return len(self.token_ids)

    def __getitem__(self, idx):
        return torch.tensor(self.token_ids[idx], dtype=torch.long), torch.tensor(self.labels[idx], dtype=torch.long)

# 创建自定义 Dataset 实例

train_dataset = SentimentDataset(train_token_ids, train_set['label'].tolist())
val_dataset = SentimentDataset(val_token_ids, val_set['label'].tolist())
#test_dataset = SentimentDataset(test_token_ids, test_set['label'].tolist())

# 打印标记和编码后的数据样本
print("\n标记和编码后的数据样本:")
for sample in train_token_ids[:100]:
     print(" ".join(map(str, sample)))



# 创建 DataLoader
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
#test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


# 打印训练集、验证集和测试集的标签统计信息
train_positive_count = train_set[train_set['label'] == 1].shape[0]
train_negative_count = train_set[train_set['label'] == 0].shape[0]
print(f"Train Positive count: {train_positive_count}")
print(f"Train Negative count: {train_negative_count}")

val_positive_count = val_set[val_set['label'] == 1].shape[0]
val_negative_count = val_set[val_set['label'] == 0].shape[0]
print(f"Validation Positive count: {val_positive_count}")
print(f"Validation Negative count: {val_negative_count}")

test_positive_count = test_set[test_set['label'] == 1].shape[0]
test_negative_count = test_set[test_set['label'] == 0].shape[0]
print(f"Test Positive count: {test_positive_count}")
print(f"Test Negative count: {test_negative_count}")


#自注意力类
class MultiHeadSelfAttention(nn.Module):
    def __init__(self, embed_dim, heads):
        super().__init__()
        self.embed_dim = embed_dim
        self.heads = heads
        self.head_dim = embed_dim // heads

        assert self.head_dim * heads == embed_dim, "Embed size needs to be divisible by heads"

        self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.fc_out = nn.Linear(heads * self.head_dim, embed_dim)

    def forward(self, values, keys, query, mask):
        N = query.shape[0]
        value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]

        # Split the embedding into self.heads different pieces
        values = values.reshape(N, value_len, self.heads, self.head_dim)
        keys = keys.reshape(N, key_len, self.heads, self.head_dim)
        queries = query.reshape(N, query_len, self.heads, self.head_dim)

        values = self.values(values)
        keys = self.keys(keys)
        queries = self.queries(queries)

        energy = torch.einsum("nqhd,nkhd->nhqdk", [queries, keys])

        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))

        attention = torch.softmax(energy / (self.embed_dim ** (1 / 2)), dim=3)
        out = torch.einsum("nhqdk,nqhd->nhqd", [attention, values]).reshape(N, query_len, self.heads * self.head_dim)
        out = self.fc_out(out)
        return out

# 量子LSTM
class QLSTM(nn.Module):
    def __init__(self,
                 input_size,
                 hidden_size,
                 n_qubits=n_qubits,
                 n_qlayers=n_qlayers,
                 n_vrotations=3,
                 batch_first=True,
                 return_sequences=False,
                 return_state=False,
                 backend="default.qubit",
                 dropout_rate=dropout_rate):
        super(QLSTM, self).__init__()
        self.n_inputs = input_size
        self.hidden_size = hidden_size
        self.concat_size = self.n_inputs + self.hidden_size
        self.n_qubits = n_qubits
        self.n_qlayers = n_qlayers
        self.n_vrotations = n_vrotations
        self.backend = backend

        self.batch_first = batch_first
        self.return_sequences = return_sequences
        self.return_state = return_state

        self.wires_forget = [f"wire_forget_{i}" for i in range(self.n_qubits)]
        self.wires_input = [f"wire_input_{i}" for i in range(self.n_qubits)]
        self.wires_update = [f"wire_update_{i}" for i in range(self.n_qubits)]
        self.wires_output = [f"wire_output_{i}" for i in range(self.n_qubits)]
        self.wires_output_h = [f"wire_output_h_{i}" for i in range(self.n_qubits)]
        # 定义线性层
        self.fc = nn.Linear(self.hidden_size, 2)
        # 初始化权重形状
        self.weight_shapes = {"weights": (self.n_qlayers, self.n_vrotations, self.n_qubits)}
        # 定义量子设备
        # self.dev = qml.device('default.qubit', wires=4)
        self.dev_forget = qml.device(self.backend, wires=self.wires_forget)
        self.dev_input = qml.device(self.backend, wires=self.wires_input)
        self.dev_update = qml.device(self.backend, wires=self.wires_update)
        self.dev_output = qml.device(self.backend, wires=self.wires_output)
        self.dev_output_h = qml.device(self.backend, wires=self.wires_output_h)
        # 定义 ansatz 方法
        self.ansatz = self.ansatz
        # 初始化dropout层
        self.dropout = nn.Dropout(dropout_rate)

        # QNode实例化
        self.qlayer_forget = qml.QNode(self._circuit_forget, self.dev_forget, interface="torch")
        self.qlayer_input = qml.QNode(self._circuit_input, self.dev_input, interface="torch")
        self.qlayer_update = qml.QNode(self._circuit_update, self.dev_update, interface="torch")
        self.qlayer_output = qml.QNode(self._circuit_output, self.dev_output, interface="torch")
        self.qlayer_output_h = qml.QNode(self._circuit_output_h, self.dev_output_h, interface="torch")
        # 定义权重形状
        weight_shapes = {"weights": (self.n_qlayers, self.n_vrotations, self.n_qubits)}

        # print(f"weight_shapes = (n_qlayers, n_vrotations, n_qubits) = ({self.n_qlayers}, {self.n_vrotations}, {self.n_qubits})")

        # 定义线性层
        self.clayer_in = nn.Linear(self.concat_size, self.n_qubits)
        self.clayer_out = nn.Linear(self.n_qubits, self.hidden_size)
        self.clayer_out_f = nn.Linear(self.n_qubits, self.hidden_size)
        self.clayer_out_i = nn.Linear(self.n_qubits, self.hidden_size)
        self.clayer_out_g = nn.Linear(self.n_qubits, self.hidden_size)
        self.clayer_out_o = nn.Linear(self.n_qubits, self.hidden_size)
        self.TorchLayers = {
            'forget': qml.qnn.TorchLayer(self.qlayer_forget, self.weight_shapes),
            'input': qml.qnn.TorchLayer(self.qlayer_input, self.weight_shapes),
            'update': qml.qnn.TorchLayer(self.qlayer_update, self.weight_shapes),
            'output': qml.qnn.TorchLayer(self.qlayer_output, self.weight_shapes),
            'output_h': qml.qnn.TorchLayer(self.qlayer_output_h, self.weight_shapes)
        }

    def ansatz(self, params, wires_type):

        # Entangling layer.
        for i in range(1, 3):
            for j in range(self.n_qubits):
                if j + i < self.n_qubits:
                    qml.CNOT(wires=[wires_type[j], wires_type[j + i]])
                else:
                    qml.CNOT(wires=[wires_type[j], wires_type[j + i - self.n_qubits]])

            # Variational layer.
            for i in range(self.n_qubits):
                qml.RX(params[0][i], wires=wires_type[i])
                qml.RY(params[1][i], wires=wires_type[i])
                qml.RZ(params[2][i], wires=wires_type[i])

    def vqc(self, features, weights, wires_type):
        # 将features转换为PyTorch张量（如果它是一个NumPy数组）
        if isinstance(features, np.ndarray):
            features = torch.tensor(features, dtype=torch.float32)
        # 检查features是否为一维张量，如果是，则添加一个新的批次维度
        if features.dim() == 1:
            features = features.unsqueeze(0)  # 将其变为二维张量，形状为[1, 特征数]
        if not isinstance(features, torch.Tensor):
            features = torch.tensor(features, dtype=torch.float32)
        # print("features形状:", features.shape)
        # print("Weights shape:", weights.shape)

        # 确保 features 的长度等于量子位的数量
        features = features[:, :self.n_qubits]
        ry_params = torch.atan2(features, torch.ones_like(features))
        rz_params = torch.atan2(features ** 2, torch.ones_like(features))

        # ry_params = [torch.arctan(feature) for feature in features]
        # rz_params = [torch.arctan(feature ** 2) for feature in features]
        # 应用Hadamard门到每个量子位上
        for i in range(self.n_qubits):
            qml.Hadamard(wires=wires_type[i])

        # 对量子位进行RY和RZ旋转
        for i in range(self.n_qubits):
            qml.RY(ry_params[:, i], wires=wires_type[i])
            qml.RZ(rz_params[:, i], wires=wires_type[i])

        # 创建量子位之间的纠缠
        for i in range(1, 3):
            for j in range(self.n_qubits):
                if j + i < self.n_qubits:
                    qml.CNOT(wires=[wires_type[j], wires_type[j + i]])
                else:
                    qml.CNOT(wires=[wires_type[j], wires_type[j + i - self.n_qubits]])

        # 再次对量子位进行RX、RY、RZ旋转
        for layer in range(self.n_qlayers):
            for i in range(self.n_qubits):
                qml.RX(weights[layer, 0, i], wires=wires_type[i])
                qml.RY(weights[layer, 1, i], wires=wires_type[i])
                qml.RZ(weights[layer, 2, i], wires=wires_type[i])
    # 定义量子电路，模拟LSTM中的门控机制
    def _circuit_forget(self, inputs, weights):
        self.vqc(inputs, weights, self.wires_forget)
        return [qml.expval(qml.PauliZ(wires=i)) for i in self.wires_forget]

    def _circuit_input(self, inputs, weights):
        self.vqc(inputs, weights, self.wires_input)
        return [qml.expval(qml.PauliZ(wires=i)) for i in self.wires_input]

    def _circuit_update(self, inputs, weights):
        self.vqc(inputs, weights, self.wires_update)
        return [qml.expval(qml.PauliZ(wires=i)) for i in self.wires_update]

    def _circuit_output(self, inputs, weights):
        self.vqc(inputs, weights, self.wires_output)
        return [qml.expval(qml.PauliZ(wires=i)) for i in self.wires_output]
    # 计算隐藏状态输出

    def _circuit_output_h(self, inputs, weights):
        self.vqc(inputs, weights, self.wires_output_h)
        return [qml.expval(qml.PauliZ(wires=i)) for i in self.wires_output_h]

    # 利用qiskit可视化电路图
    def to_qiskit_circuit(self, inputs, weights, wires_type):
        # 添加经典位用于存储测量结果
        circuit = QuantumCircuit(self.n_qubits, self.n_qubits)
        features = torch.tensor(inputs[:, :self.n_qubits], dtype=torch.float32)
        ry_params = torch.atan2(features, torch.ones_like(features))
        rz_params = torch.atan2(features ** 2, torch.ones_like(features))

        # 应用Hadamard门到每个量子位上
        for i in range(self.n_qubits):
            circuit.h(i)

        # 对量子位进行RY和RZ旋转
        for i in range(self.n_qubits):
            circuit.ry(ry_params[:, i].item(), i)
            circuit.rz(rz_params[:, i].item(), i)

        # 创建量子位之间的纠缠
        for i in range(1, 3):
            for j in range(self.n_qubits):
                if j + i < self.n_qubits:
                    circuit.cx(j, j + i)
                else:
                    circuit.cx(j, (j + i) % self.n_qubits)

        # 再次对量子位进行RX、RY、RZ旋转
        for layer in range(self.n_qlayers):
            for i in range(self.n_qubits):
                circuit.rx(weights[layer, 0, i].item(), i)
                circuit.ry(weights[layer, 1, i].item(), i)
                circuit.rz(weights[layer, 2, i].item(), i)

        # 对每个量子位进行测量，并将结果存储到对应的经典位中
        for i in range(self.n_qubits):
            circuit.measure(i, i)

        return circuit

    def visualize_qiskit_circuit(self):
        example_inputs = np.random.random((1, self.n_qubits))
        example_weights = np.random.random((self.n_qlayers, self.n_vrotations, self.n_qubits))
        qiskit_circuit = self.to_qiskit_circuit(example_inputs, example_weights, self.wires_forget)
        circuit_drawer(qiskit_circuit, output='mpl').show()

    def forward(self, x, init_states=None):

        batch_size, seq_length, _ = x.size()

        if init_states is None:
            h_t = torch.zeros(batch_size, self.hidden_size, dtype=torch.float32)  # hidden state (output)
            c_t = torch.zeros(batch_size, self.hidden_size, dtype=torch.float32)  # cell state
        else:
            h_t, c_t = init_states

            # 批处理整个序列
        for t in range(seq_length):
            x_t = x[:, t, :]  # 获取当前时间步的特征
            # 选择 h_t 的最后一层，以匹配 x_t 的维度
            h_t_last_layer = h_t[-1, :, :]  # 假设 h_t 的形状是 [num_layers, batch_size, hidden_size]

            # 现在 h_t_last_layer 的形状是 [batch_size, hidden_size]，可以和 x_t 连接
            combined = torch.cat((h_t_last_layer, x_t), dim=1)

            # 将拼接后的数据通过一个线性层以匹配量子位的维度
            combined = self.clayer_in(combined)

            # 用量子层处理整个批次的数据
            f_t_intermediate = self.TorchLayers['forget'](combined)
            i_t_intermediate = self.TorchLayers['input'](combined)
            g_t_intermediate = self.TorchLayers['update'](combined)
            o_t_intermediate = self.TorchLayers['output'](combined)

            # 将中间结果通过另一个线性层转换回隐藏状态的维度
            f_t = torch.sigmoid(self.clayer_out_f(f_t_intermediate))
            i_t = torch.sigmoid(self.clayer_out_i(i_t_intermediate))
            g_t = torch.tanh(self.clayer_out_g(g_t_intermediate))
            o_t = torch.sigmoid(self.clayer_out_o(o_t_intermediate))

            # 根据 LSTM 的更新规则更新隐藏状态和细胞状态
            c_t = f_t * c_t + i_t * g_t
            h_t = o_t * torch.tanh(c_t)
            # print("h_t shape:", h_t.shape)
            # 使用最后的隐藏状态通过一个线性层以得到最终输出
            h_t = self.dropout(h_t)  # 将dropout应用到隐藏状态

        output = self.fc(h_t)
        # print(f"Output shape: {output.shape}")
        return output

# 电路可视化实现
qlstm_instance = QLSTM(input_size=10, hidden_size=64, n_qubits=4, n_qlayers=1, n_vrotations=3, backend="default.qubit")
qlstm_instance.visualize_qiskit_circuit()
class QShallowClassificationLSTM(nn.Module):
    def __init__(self, num_sensors, embedding_dim, hidden_units, n_qubits=n_qubits, n_qlayers=n_qlayers, num_classes=2, dropout_rate=dropout_rate, heads=heads):
        super().__init__()
        self.num_sensors = num_sensors
        self.hidden_units = hidden_units
        self.num_layers = 1
        self.num_classes = num_classes
        self.embedding = nn.Embedding(vocab_size, embedding_dim)

        # 添加自注意力层
        self.attention = MultiHeadSelfAttention(embedding_dim, heads)

        # 添加 Dropout 层
        self.dropout = nn.Dropout(dropout_rate)
        self.lstm = QLSTM(
            # input_size=num_sensors,
            # 使用embedding_dim 作为 LSTM 的输入维度
            input_size=embedding_dim,
            hidden_size=hidden_units,
            batch_first=True,
            n_qubits=n_qubits,
            n_qlayers=n_qlayers,

        )
        self.linear = nn.Linear(in_features=2, out_features=self.num_classes)


    def forward(self, x):
        batch_size = x.shape[0]

        x = self.embedding(x)
        # print(f"Embedding layer output shape: {x.shape}")
        # 应用自注意力层


        attended_x = self.attention(x, x, x, None)  # 注意力层没有遮罩，所以传递None

        # 应用 Dropout
        x = self.dropout(x)

        h0 = torch.zeros(self.num_layers, batch_size, self.hidden_units)
        c0 = torch.zeros(self.num_layers, batch_size, self.hidden_units)

        # QLSTM 返回所有时间步的输出
        outputs = self.lstm(x, (h0, c0))

        # 选择最后一个时间步的隐藏状态，它的形状为 [batch_size, hidden_units]
        last_hidden_state = outputs.squeeze(0)

        # 将最后一个隐藏状态传递给线性层
        out = self.linear(last_hidden_state)

        return out

#  计算模型分类准确率
def calculate_accuracy_and_scores(data_loader, Qmodel):
    correct = 0
    total = 0
    true_positives = 0
    predicted_positives = 0
    true_negatives = 0
    predicted_negatives = 0
    all_labels = []
    all_predictions = []

    Qmodel.eval()
    with torch.no_grad():
        for X, y in data_loader:
            outputs = Qmodel(X)
            _, predicted = torch.max(outputs.data, 1)
            total += y.size(0)

            # 累积条件统计数据
            true_positives += (y == 1).sum().item()  # 真实正类
            predicted_positives += (predicted == 1).sum().item()  # 预测正类
            true_negatives += (y == 0).sum().item()  # 真实负类
            predicted_negatives += (predicted == 0).sum().item()  # 预测负类

            correct += (predicted == y).sum().item()

            # 收集所有标签和预测值
            all_labels.extend(y.cpu().numpy())
            all_predictions.extend(predicted.cpu().numpy())


    # 计算准确率
    accuracy = 100 * correct / total

    # 计算召回率
    recall = recall_score(y, predicted, pos_label=1)

    # 计算F1分数
    f1 = f1_score(y, predicted, pos_label=1)

    return accuracy, recall, f1

# 词汇表大小
vocab_size = len(vocabulary)




# 实例化分类模型和量子分类模型

Qmodel = QShallowClassificationLSTM(num_sensors=len(vocabulary),
                                    embedding_dim=embedding_dim,
                                    hidden_units=hidden_size,
                                    n_qubits=n_qubits,
                                    n_qlayers=n_qlayers,
                                    num_classes=2)

# 定义损失函数和优化器
loss_function = nn.CrossEntropyLoss()  # 更新为分类任务的损失函数

Qoptimizer = torch.optim.Adam(Qmodel.parameters(), lr=lr, weight_decay=weight_decay)

# 定义学习率调度器
# scheduler = StepLR(Qoptimizer, step_size=34, gamma=0.2)

#  训练函数
def train_model(data_loader, Qmodel, loss_function, optimizer,  progress_bar):
    total_loss = 0
    Qmodel.train()

    for X, y in data_loader:
        output = Qmodel(X)
        loss = loss_function(output, y)

        # 添加L2正则化
        # reg_loss = weight_decay * sum(p.sum() for p in Qmodel.parameters() if p.requires_grad)
        # loss += reg_loss

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_loss += loss.item()
        progress_bar.update(1)  # 更新进度条

    avg_loss = total_loss / len(data_loader)
    return avg_loss

#  测试函数
def test_model(data_loader, Qmodel, loss_function, progress_bar):

    total_loss = 0
    Qmodel.eval()
    with torch.no_grad():
        for X, y in data_loader:
            output = Qmodel(X)
            total_loss += loss_function(output, y).item()
            progress_bar.update(1)  # 更新进度条

    avg_loss = total_loss / len(data_loader)
    return avg_loss

# 初始化训练、测试、验证损失，验证集准确率
quantum_loss_train = []
quantum_loss_test = []
quantum_loss_val = []
quantum_accuracy_val = []
print("Quantum Untrained test\n--------")

# 初始化存储准确率的列表
train_accuracy_list = []
train_recall_list = []
train_f1_list = []

val_accuracy_list = []
val_recall_list = []
val_f1_list = []

# test_accuracy_list = []
# test_recall_list = []
# test_f1_list = []

for ix_epoch in range(epoch):
    #  print(f"Epoch {ix_epoch+1}\n---------")
    total_steps = len(train_loader) + len(val_loader)
    progress_bar = tqdm(total=total_steps, desc=f"Epoch {ix_epoch + 1}")

    # 训练损失
    train_loss = train_model(train_loader, Qmodel, loss_function, Qoptimizer, progress_bar)

    # 测试损失
    # test_loss = test_model(test_loader, Qmodel, loss_function, progress_bar)

    # 验证损失
    val_loss = test_model(val_loader, Qmodel, loss_function, progress_bar)

    # 计算量子模型的准确率、召回率和F1分数
    quantum_train_accuracy, train_recall, train_f1 = calculate_accuracy_and_scores(train_loader, Qmodel)
    # quantum_test_accuracy, test_recall, test_f1 = calculate_accuracy_and_scores(test_loader, Qmodel)

    # 计算验证集准确率、召回率和F1分数
    val_accuracy, val_recall, val_f1 = calculate_accuracy_and_scores(val_loader, Qmodel)

    # 关闭进度条
    progress_bar.close()

    train_accuracy, train_recall, train_f1 = calculate_accuracy_and_scores(train_loader, Qmodel)
    val_accuracy, val_recall, val_f1 = calculate_accuracy_and_scores(val_loader, Qmodel)
    # test_accuracy = calculate_accuracy(test_loader, Qmodel)

    # 将准确率添加到相应的列表中
    train_accuracy_list.append(train_accuracy)
    train_recall_list.append(train_recall)
    train_f1_list.append(train_f1)

    val_accuracy_list.append(val_accuracy)
    val_recall_list.append(val_recall)
    val_f1_list.append(val_f1)

    # test_accuracy_list.append(test_accuracy)
    # test_recall_list.append(test_recall)
    # test_f1_list.append(test_f1)

    # 打印损失和评估指标信息
    print(f"\nTrain Loss: {train_loss:.4f}")
    # print(f"Test Loss: {test_loss:.4f}")
    print(f"Validation Loss: {val_loss:.4f}")

    print(f"Train Accuracy: {quantum_train_accuracy:.2f}%")
    # print(f"Test Accuracy: {quantum_test_accuracy:.2f}%")
    print(f"Validation Accuracy: {val_accuracy:.2f}%\n")

    print(f"Train Recall: {train_recall:.2f}")
    # print(f"Test Recall: {test_recall:.2f}")
    print(f"Train F1 Score: {train_f1:.2f}")

    print(f"Validation Recall: {val_recall:.2f}")
    # print(f"Test F1 Score: {test_f1:.2f}")
    print(f"Validation F1 Score: {val_f1:.2f}\n")

    # 记录训练、测试、验证损失
    quantum_loss_train.append(train_loss)
    # quantum_loss_test.append(test_loss)
    quantum_loss_val.append(val_loss)
    quantum_accuracy_val.append(val_accuracy)
train_eval_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False)

# 绘制量子 QLSTM 模型的损失图表

plt.figure(figsize=(12, 6))
plt.plot(quantum_loss_train, label='Quantum Train Loss')
plt.plot(quantum_loss_test, label='Quantum Test Loss')
plt.plot(quantum_loss_val, label='Validation Loss')
plt.title('Quantum LSTM Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Validation Loss across Folds')
plt.xlabel('Epoch')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()

# 绘制准确率图表
plt.figure(figsize=(12, 6))
plt.plot(train_accuracy_list, label='Train Accuracy')
plt.plot(val_accuracy_list, label='Validation Accuracy')
# plt.plot(test_accuracy_list, label='Test Accuracy')

plt.plot(train_recall_list, label='Train Recall')
plt.plot(val_recall_list, label='Validation Recall')
# plt.plot(test_recall_list, label='Test Recall')

plt.plot(train_f1_list, label='Train F1 Score')
plt.plot(val_f1_list, label='Validation F1 Score')
# plt.plot(test_f1_list, label='Test F1 Score')

# 模型性能指标
plt.title('Model Evaluation Metrics Across Epochs')
plt.xlabel('Epoch')
plt.ylabel('Metrics')
plt.legend()
plt.show()
请你综合分析我的模型，其中我所使用的数据集总数为1000条数据，其中经过数据预处理操作后，句子长度为10，并且其中涉及到了较多的0填充。请你结合我的数据集特点判断我的模型是否适合使用多头注意力机制，如果不适合，那为什么在使用多头注意力机制的情况下得到了一组较好效果的参数。