import sys
import os
import pennylane as qml
from pennylane import numpy as np
import tensorflow as tf
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, StandardScaler, MinMaxScaler
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Dense
from importance_sampling.training import ConstantTimeImportanceTraining
from importance_sampling.samplers import ConstantTimeSampler
from importance_sampling.datasets import InMemoryDataset
from importance_sampling.reweighting import BiasedReweightingPolicy
from tensorflow.keras.callbacks import EarlyStopping

# 重定向标准输出以捕获打印输出
class Tee(object):
    def __init__(self, *files):
        self.files = files
    def write(self, obj):
        for f in self.files:
            f.write(obj)
            f.flush()
    def flush(self):
        for f in self.files:
            f.flush()

output_file = open('output_modified.log', 'w', encoding='utf-8')
original_stdout = sys.stdout
sys.stdout = Tee(sys.stdout, output_file)

def save_plot(fig, filename):
    fig.savefig(filename)
    plt.close(fig)

def plot_history(history):
    # 绘制训练和验证准确率
    fig, ax = plt.subplots(figsize=(10, 5))
    ax.plot(history.history['accuracy'], label='Train Accuracy')
    ax.plot(history.history['val_accuracy'], label='Validation Accuracy')
    ax.set_title('Model Accuracy')
    ax.set_ylabel('Accuracy')
    ax.set_xlabel('Epoch')
    ax.legend(loc='upper left')
    save_plot(fig, 'accuracy_plot.png')

    # 绘制训练和验证损失
    fig, ax = plt.subplots(figsize=(10, 5))
    ax.plot(history.history['loss'], label='Train Loss')
    ax.plot(history.history['val_loss'], label='Validation Loss')
    ax.set_title('Model Loss')
    ax.set_ylabel('Loss')
    ax.set_xlabel('Epoch')
    ax.legend(loc='upper left')
    save_plot(fig, 'loss_plot.png')

def calculate_accuracy_fluctuation(history):
    val_accuracies = history.history['val_accuracy']
    mean_accuracy = np.mean(val_accuracies)
    fluctuation = np.sum(np.abs(val_accuracies - mean_accuracy))
    return mean_accuracy, fluctuation

# 定义具有14个量子比特的量子设备
dev = qml.device("default.qubit", wires=14)

def encode_group(control_qubits, data_qubits, angle_q2, angle_q3):
    control_values = [[0, 0], [0, 1], [1, 0], [1, 1]]

    for i in range(4):
        cv = control_values[i]
        angle_q3_val = angle_q3[i]

        # 翻转为0的控制量子比特以进行受控操作
        for qubit, val in zip(control_qubits, cv):
            if val == 0:
                qml.PauliX(wires=qubit)

        # 应用受控RY旋转
        qml.ctrl(qml.RY, control=control_qubits)(angle_q2, wires=data_qubits['q2'])
        qml.ctrl(qml.RY, control=control_qubits)(angle_q3_val, wires=data_qubits['q3'])

        # 恢复控制量子比特
        for qubit, val in zip(control_qubits, cv):
            if val == 0:
                qml.PauliX(wires=qubit)

def apply_controlled_swap(swap_qubit, q_swap1, q_swap2):
    """
    应用受控SWAP门的模块化方法。

    参数：
    - swap_qubit (int): SWAP门的控制量子比特。
    - q_swap1 (int): 第一个交换的量子比特。
    - q_swap2 (int): 第二个交换的量子比特。
    """
    qml.Hadamard(wires=swap_qubit)
    qml.CSWAP(wires=[swap_qubit, q_swap1, q_swap2])
    qml.Hadamard(wires=swap_qubit)

@qml.qnode(dev, interface='tf', diff_method='backprop')
def quantum_neural_network(inputs, encoding_weights, rot_weights, entangle_weights):
    control_qubits = [0, 1]

    encoding_data = [
        {'data_qubits': {'q2': 2, 'q3': 3}, 'swap_qubit': 4},
        {'data_qubits': {'q2': 5, 'q3': 6}, 'swap_qubit': 7},
        {'data_qubits': {'q2': 8, 'q3': 9}, 'swap_qubit': 10},
        {'data_qubits': {'q2': 11, 'q3': 12}, 'swap_qubit': 13}
    ]

    # 角度编码：将输入数据映射到 [0, pi/2] 范围
    angles_q2_list = inputs  # inputs的形状为 (4,)，表示一个样本的四个特征

    # 编码权重映射到 angles_q3
    angles_q3_list = encoding_weights  # encoding_weights的形状为 (4, n_layers, 4)

    # 编码过程
    for idx, data_set in enumerate(encoding_data):
        data_qubits = data_set['data_qubits']
        swap_qubit = data_set['swap_qubit']

        angle_q2 = angles_q2_list[idx]  # 标量张量，对应一个特征

        for layer in range(rot_weights.shape[0]):
            angle_q3_values = angles_q3_list[idx, layer, :]  # 形状: (4,)
            encode_group(control_qubits, data_qubits, angle_q2, angle_q3_values)

    # 应用交换操作
    for data_set in encoding_data:
        swap_qubit = data_set['swap_qubit']
        data_qubits = data_set['data_qubits']
        apply_controlled_swap(swap_qubit, data_qubits['q3'], data_qubits['q2'])

    # 应用旋转层和参数化纠缠门
    for layer in range(rot_weights.shape[0]):
        for qubit in range(rot_weights.shape[1]):
            rot_angles = rot_weights[layer, qubit, :]  # 形状: (3,)
            qml.Rot(rot_angles[0], rot_angles[1], rot_angles[2], wires=qubit)
        # 应用参数化纠缠门
        for qubit in range(rot_weights.shape[1] - 1):
            qml.CRZ(entangle_weights[layer, qubit], wires=[qubit, qubit + 1])

    # 在多个量子比特上测量 PauliZ 期望值
    measured_qubits = [4, 7, 10, 13]
    results = [qml.expval(qml.PauliZ(qubit)) for qubit in measured_qubits]

    return results

# 定义量子神经网络层
class QuantumNeuralNetworkLayer(tf.keras.layers.Layer):
    def __init__(self, n_qubits, n_layers, num_classes, **kwargs):
        super(QuantumNeuralNetworkLayer, self).__init__(**kwargs)
        self.n_qubits = n_qubits
        self.n_layers = n_layers
        self.num_classes = num_classes

        # 定义编码权重，具有 L2 正则化
        self.encoding_weights = self.add_weight(
            shape=(4, n_layers, 4),  # 4 个编码组，每层有 4 个角度
            initializer=tf.keras.initializers.GlorotUniform(),
            regularizer=tf.keras.regularizers.l2(0.01),
            trainable=True,
            name='encoding_weights',
            dtype=tf.float64
        )

        # 定义旋转层权重，具有 L2 正则化
        self.rot_weights = self.add_weight(
            shape=(n_layers, n_qubits, 3),
            initializer=tf.keras.initializers.GlorotUniform(),
            regularizer=tf.keras.regularizers.l2(0.01),
            trainable=True,
            name='rot_weights',
            dtype=tf.float64
        )

        # 定义纠缠权重，具有 L2 正则化
        self.entangle_weights = self.add_weight(
            shape=(n_layers, n_qubits - 1),
            initializer=tf.keras.initializers.RandomUniform(0, 2 * np.pi),
            regularizer=tf.keras.regularizers.l2(0.01),
            trainable=True,
            name='entangle_weights',
            dtype=tf.float64
        )

    def call(self, inputs):
        def apply_qnode(x):
            x = tf.cast(x, dtype=tf.float64)
            result = quantum_neural_network(x, self.encoding_weights, self.rot_weights, self.entangle_weights)
            return result  # 结果为 float64

        outputs = tf.map_fn(
            apply_qnode,
            inputs,
            dtype=tf.float64  # 指定输出类型为 float64
        )

        return outputs

# 定义量子神经网络模型
class QuantumNeuralNetwork(tf.keras.Model):
    def __init__(self, n_qubits, n_layers, num_classes, **kwargs):
        super(QuantumNeuralNetwork, self).__init__(**kwargs)
        self.n_qubits = n_qubits
        self.n_layers = n_layers
        self.num_classes = num_classes

        # 量子层
        self.quantum_layer = QuantumNeuralNetworkLayer(n_qubits, n_layers, num_classes)

        # 输出层，直接连接到量子输出
        self.output_layer = Dense(num_classes, activation='softmax',
                                  kernel_regularizer=tf.keras.regularizers.l2(0.01),
                                  dtype=tf.float64)  # 确保输出层为 float64

    def call(self, inputs):
        q_output = self.quantum_layer(inputs)
        return self.output_layer(q_output)

def is_quantum_model(model):
    for layer in model.layers:
        if isinstance(layer, QuantumNeuralNetworkLayer):
            return True
    return False

if __name__ == "__main__":
    # 加载数据
    iris = load_iris()
    X = iris['data'].astype(np.float64)  # 确保数据类型为 float64
    y = iris['target'].reshape(-1, 1)

    # 对标签进行 One-Hot 编码
    encoder = OneHotEncoder(sparse_output=False)
    y_encoded = encoder.fit_transform(y).astype(np.float64)  # 确保数据类型为 float64
    num_classes = y_encoded.shape[1]

    # 拆分数据集
    x_temp, x_test, y_temp, y_test = train_test_split(
        X, y_encoded, test_size=0.2, random_state=116, stratify=y_encoded
    )

    x_train, x_val, y_train, y_val = train_test_split(
        x_temp, y_temp, test_size=0.2, random_state=116, stratify=y_temp
    )

    # 数据标准化和归一化
    scaler = StandardScaler()
    X_train_standardized = scaler.fit_transform(x_train).astype(np.float64)
    X_val_standardized = scaler.transform(x_val).astype(np.float64)
    X_test_standardized = scaler.transform(x_test).astype(np.float64)

    min_max_scaler = MinMaxScaler(feature_range=(0, 1))
    X_train_normalized = min_max_scaler.fit_transform(X_train_standardized).astype(np.float64)
    X_val_normalized = min_max_scaler.transform(X_val_standardized).astype(np.float64)
    X_test_normalized = min_max_scaler.transform(X_test_standardized).astype(np.float64)

    # 将数据映射到 [0, pi/2] 范围
    X_train_mapped = X_train_normalized * (np.pi / 2)
    X_val_mapped = X_val_normalized * (np.pi / 2)
    X_test_mapped = X_test_normalized * (np.pi / 2)

    # 超参数
    learning_rate = 0.02  # 学习率
    epochs = 200          # 训练轮数
    batch_size = 16       # 批量大小
    num_classes = 3
    n_qubits = 14         # 14 个量子比特（0 到 13）
    n_layers = 3          # 层数

    model = QuantumNeuralNetwork(
        n_qubits=n_qubits,
        n_layers=n_layers,
        num_classes=num_classes
    )

    if is_quantum_model(model):
        print("该模型包含量子神经网络（QNN）层。")
    else:
        print("该模型不包含量子神经网络（QNN）层。")

    print("\n量子电路图：")
    sample_inputs = np.ones(4, dtype=np.float64) * (np.pi / 2)  # 使用 4 个输入特征的 float64 类型
    sample_encoding_weights = np.zeros((4, n_layers, 4), dtype=np.float64)  # 编码权重
    sample_rot_weights = np.zeros((n_layers, n_qubits, 3), dtype=np.float64)  # 旋转权重
    sample_entangle_weights = np.zeros((n_layers, n_qubits - 1), dtype=np.float64)  # 纠缠权重

    print(qml.draw(quantum_neural_network)(sample_inputs, sample_encoding_weights, sample_rot_weights, sample_entangle_weights))  # 绘制量子电路

    # 提前停止回调，增加耐心值
    early_stopping = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)

    steps_per_epoch = len(X_train_mapped) // batch_size
    total_steps = epochs * steps_per_epoch

    # 使用重要性采样训练

    # 创建数据集
    dataset = InMemoryDataset(
        X_train_mapped,
        y_train,
        X_val_mapped,  # 使用验证集作为测试数据
        y_val,
        categorical=False
    )

    # 初始化重加权策略
    reweighting_policy = BiasedReweightingPolicy(k=1.0)

    # 初始化重要性采样训练
    wrapped_model = ConstantTimeImportanceTraining(model, score="loss")

    # 修改采样器以使用动态的 alpha 和 beta
    def custom_sampler(dataset, batch_size, steps_per_epoch, epochs):
        return ConstantTimeSampler(
            dataset,
            reweighting_policy,
            model=wrapped_model.model,
            B=64,                      # 第一层随机采样的样本数
            alpha=0.6,                 # 控制优先级的参数，0 表示均匀采样，1 表示完全按照优先级采样
            beta_start=0.4,            # beta 的初始值
            beta_frames=total_steps    # beta 从 beta_start 增加到 1 所需的总步骤数
        )

    # 将自定义采样器赋给 wrapped_model
    wrapped_model.sampler = custom_sampler

    # 使用 per-sample 的损失函数
    loss_fn = tf.keras.losses.CategoricalCrossentropy(from_logits=False, reduction=tf.keras.losses.Reduction.NONE)

    # 编译模型，确保运行时数据类型一致
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
        loss=loss_fn,
        metrics=['accuracy'],
        run_eagerly=True  # 帮助调试
    )

    # 使用 fit_dataset 训练模型
    history = wrapped_model.fit_dataset(
        dataset=dataset,
        batch_size=batch_size,
        epochs=epochs,
        steps_per_epoch=steps_per_epoch,
        verbose=1,
        callbacks=[early_stopping]
    )

    # 在测试集上评估模型
    score = model.evaluate(X_test_mapped, y_test, verbose=0)
    print('测试损失:', score[0])
    print('测试准确率:', score[1])

    mean_accuracy, fluctuation = calculate_accuracy_fluctuation(history)
    print('验证集平均准确率:', mean_accuracy)
    print('准确率波动:', fluctuation)

    plot_history(history)

    # 关闭输出文件并恢复标准输出
    sys.stdout = original_stdout
    output_file.close()

    print("脚本执行完毕。请检查 'output_modified.log' 以获取打印输出，并在当前目录下查看保存的图表。")
上述代码中采用的量子神经网络比较复杂，你能不能设计一个较为简洁的量子神经网络？给出修改后的完整代码