import sys
import os
import pennylane as qml
from pennylane import numpy as np
import tensorflow as tf
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.decomposition import PCA
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Dense
from example_utils import get_parser
from importance_sampling.training import ConstantTimeImportanceTraining
from pennylane.qnn.keras import KerasLayer

# 重定向 stdout 以捕获打印输出
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.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'])
    ax.plot(history.history['val_accuracy'])
    ax.set_title('模型准确率')
    ax.set_ylabel('准确率')
    ax.set_xlabel('轮次')
    ax.legend(['训练', '验证'], loc='upper left')
    save_plot(fig, 'accuracy_plot.png')

    # 绘制训练和验证损失
    fig, ax = plt.subplots(figsize=(10, 5))
    ax.plot(history.history['loss'])
    ax.plot(history.history['val_loss'])
    ax.set_title('模型损失')
    ax.set_ylabel('损失')
    ax.set_xlabel('轮次')
    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

# 定义设备，使用 8 个量子比特
n_qubits = 8  # 根据需要的特征数量调整量子比特数
dev = qml.device("default.qubit", wires=n_qubits)

def encode_features(inputs, n_qubits):
    """
    使用角度编码将输入特征编码到量子电路中。
    将特征映射到量子比特的旋转角度。
    """
    # 将特征映射到量子比特
    for i in range(n_qubits):
        qml.RY(inputs[i], wires=i)

@qml.qnode(dev, interface='tf', diff_method='backprop')
def quantum_neural_network(inputs, rot_weights):
    # 编码输入特征到量子比特
    encode_features(inputs, n_qubits)

    # 应用变分层
    for layer in range(rot_weights.shape[0]):
        for qubit in range(n_qubits):
            rot_angles = rot_weights[layer, qubit, :]  # 形状：(3,)
            qml.Rot(rot_angles[0], rot_angles[1], rot_angles[2], wires=qubit)
        # 添加量子比特之间的纠缠
        for qubit in range(n_qubits - 1):
            qml.CNOT(wires=[qubit, qubit + 1])
        qml.CNOT(wires=[n_qubits - 1, 0])  # 闭合环路

    # 测量期望值
    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]

# 定义用于绘制电路的 QNode
@qml.qnode(dev)
def quantum_neural_network_draw(inputs, rot_weights):
    # 编码输入特征到量子比特
    encode_features(inputs, n_qubits)

    # 应用变分层
    for layer in range(rot_weights.shape[0]):
        for qubit in range(n_qubits):
            rot_angles = rot_weights[layer, qubit, :]  # 形状：(3,)
            qml.Rot(rot_angles[0], rot_angles[1], rot_angles[2], wires=qubit)
        # 添加量子比特之间的纠缠
        for qubit in range(n_qubits - 1):
            qml.CNOT(wires=[qubit, qubit + 1])
        qml.CNOT(wires=[n_qubits - 1, 0])  # 闭合环路

    # 测量期望值
    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]

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

        # 定义旋转权重
        self.rot_weights = self.add_weight(
            shape=(n_layers, n_qubits, 3),
            initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=np.pi),
            trainable=True,
            name='rot_weights',
            dtype=tf.float32
        )

    def call(self, inputs):
        def apply_qnode(x):
            x = tf.cast(x, dtype=tf.float32)
            result = quantum_neural_network(x, self.rot_weights)
            return tf.cast(result, dtype=tf.float32)

        outputs = tf.map_fn(
            apply_qnode,
            inputs,
            dtype=tf.float32
        )

        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)

        # 经典层
        self.classical_layer = Dense(num_classes, activation='softmax')

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

if __name__ == "__main__":
    parser = get_parser("Train model on Wine dataset")
    args = parser.parse_args()

    # 加载 Wine 数据集
    wine = load_wine()
    X = wine['data'].astype(np.float32)
    y = wine['target'].reshape(-1, 1)

    # 特征标准化
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    # 使用 PCA 进行降维
    n_features = n_qubits  # 将特征数量降至与量子比特数量一致
    pca = PCA(n_components=n_features)
    X_reduced = pca.fit_transform(X_scaled)

    # 将特征归一化到 [0, π]
    X_normalized = (X_reduced - X_reduced.min(axis=0)) / (X_reduced.max(axis=0) - X_reduced.min(axis=0))
    X_normalized = X_normalized * np.pi

    # One-hot 编码目标标签
    encoder = OneHotEncoder(sparse_output=False)
    y_encoded = encoder.fit_transform(y).astype(np.float32)
    num_classes = y_encoded.shape[1]

    x_train, x_test, y_train, y_test = train_test_split(
        X_normalized, y_encoded, test_size=0.3, random_state=42
    )

    learning_rate = 0.01  # 降低学习率以获得更好的收敛性
    epochs = 100  # 增加训练轮次
    batch_size = 8
    n_layers = 6  # 增加层数以提高模型的学习能力

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

    # 使用 Adam 优化器编译模型
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )

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

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

    print("\n量子电路图：")
    sample_inputs = np.ones(n_qubits, dtype=np.float32) * (np.pi / 2)  # 使用 n_qubits 个特征
    sample_rot_weights = np.zeros((n_layers, n_qubits, 3), dtype=np.float32)  # rot_weights

    print(qml.draw(quantum_neural_network_draw)(sample_inputs, sample_rot_weights))  # 使用绘制专用的 QNode

    # 早停和学习率调整回调函数
    callbacks = [
        tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True),
        tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5)
    ]

    if args.importance_training:
        wrapped_model = ConstantTimeImportanceTraining(model, score="loss")
        history = wrapped_model.fit(
            x_train, y_train,
            batch_size=batch_size,
            epochs=epochs,
            verbose=1,
            validation_data=(x_test, y_test),
            callbacks=callbacks
        )
    else:
        history = model.fit(
            x_train, y_train,
            batch_size=batch_size,
            epochs=epochs,
            verbose=1,
            validation_data=(x_test, y_test),
            callbacks=callbacks
        )

    score = model.evaluate(x_test, 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)

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

    print("脚本执行完成。查看 'output.log' 获取打印输出，查看当前目录获取保存的图表。")
上述是wine_Designbygpt_try.py文件
import sys
import numpy as np
import logging
from collections.abc import Iterable
import tensorflow as tf
from tensorflow.python.keras.callbacks import BaseLogger, CallbackList, History, ProgbarLogger
from tensorflow.python.keras.layers import Dropout
#from tensorflow.python.keras.utils import Sequence
from blinker import signal
from .datasets import InMemoryDataset, GeneratorDataset
from .model_wrappers import OracleWrapper
from .samplers import ConditionalStartSampler, VarianceReductionCondition, \
    AdaptiveAdditiveSmoothingSampler, AdditiveSmoothingSampler, ModelSampler, \
    ConstantVarianceSampler, ConstantTimeSampler, SCSGSampler
from .reweighting import BiasedReweightingPolicy, NoReweightingPolicy
from .utils.functional import ___, compose, partial


class _BaseImportanceTraining(object):
    def __init__(self, model, score="gnorm", layer=None):
        """重要性采样训练的基类。

        参数：
            model: 要训练的 Keras 模型。
        """
        # 检查模型是否兼容，并包装模型以支持重要性采样
        self._check_model(model)
        self.original_model = model
        self.model = OracleWrapper(
            model,
            self.reweighting,
            score=score,
            layer=layer
        )
        self.original_model.optimizer = self.model.optimizer
        signal("is.sample").connect(self._on_sample)
        signal("is.score").connect(self._on_scores)

    def _on_sample(self, event):
        self._latest_sample_event = event

    def _on_scores(self, scores):
        self._latest_scores = scores

    def _check_model(self, model):
        """检查模型是否使用了 Dropout 和 BatchNorm，并警告可能影响重要性计算。"""
        if any(
                isinstance(l, (tf.keras.layers.BatchNormalization, Dropout))
                for l in model.layers
        ):
            sys.stderr.write(("[NOTICE]: 您正在使用 BatchNormalization 和/或 Dropout。\n"
                              "这些层可能会影响重要性计算，建议您用 LayerNormalization 或 "
                              "在测试模式下的 BatchNormalization 以及 L2 正则化替代它们。\n"))

    @property
    def reweighting(self):
        """控制重要性采样时偏置的重加权策略。"""
        raise NotImplementedError()

    def sampler(self, dataset, batch_size, steps_per_epoch, epochs):
        """为给定的数据集创建一个新的采样器，以使用重要性采样进行采样。"""
        raise NotImplementedError()

    def fit(self, x, y, batch_size=32, epochs=1, verbose=1, callbacks=None,
            validation_split=0.0, validation_data=None, steps_per_epoch=None,
            on_sample=None, on_scores=None):
        """使用给定的数据创建一个 InMemoryDataset 实例，并使用重要性采样训练模型。

        参数：
            x: 训练数据的 Numpy 数组或 Numpy 数组列表。
            y: 目标数据的 Numpy 数组。
            batch_size: 每个梯度更新的样本数。
            epochs: 遍历整个训练集的次数。
            verbose: {0, >0}，是否使用 Keras 的进度条回调。
            callbacks: 在训练期间调用的 Keras 回调列表。
            validation_split: [0, 1) 之间的浮点数，用于验证的数据百分比。
            validation_data: tuple，(x_val, y_val)，用于模型验证的数据。
            steps_per_epoch: int 或 None，每个 epoch 的步数。
            on_sample: 接受 sampler, idxs, w, scores 的回调函数。
            on_scores: 接受 sampler 和 scores 的回调函数。
        返回：
            包含训练期间收集的信息的 Keras `History` 对象。
        """
        # 处理验证数据集
        if validation_data is not None:
            x_train, y_train = x, y
            x_test, y_test = validation_data
        elif validation_split > 0:
            assert validation_split < 1, "验证集比例必须小于 1"
            n = int(round(validation_split * len(x)))
            idxs = np.arange(len(x))
            np.random.shuffle(idxs)
            x_train, y_train = x[idxs[n:]], y[idxs[n:]]
            x_test, y_test = x[idxs[:n]], y[idxs[:n]]
        else:
            x_train, y_train = x, y
            x_test, y_test = np.empty(shape=(0, x.shape[1])), np.empty(shape=(0, y.shape[1]))

        # 创建数据集
        dataset = InMemoryDataset(
            x_train,
            y_train,
            x_test,
            y_test,
            categorical=False  # 不需要转换标签
        )

        return self.fit_dataset(
            dataset=dataset,
            batch_size=batch_size,
            epochs=epochs,
            steps_per_epoch=steps_per_epoch,
            verbose=verbose,
            callbacks=callbacks,
            on_sample=on_sample,
            on_scores=on_scores
        )

    def fit_generator(self, train, steps_per_epoch=None, batch_size=32,
                      epochs=1, verbose=1, callbacks=None,
                      validation_data=None, validation_steps=None,
                      on_sample=None, on_scores=None):
        """创建一个 GeneratorDataset 实例，并使用重要性采样训练模型。

        参数：
            train: 返回 (inputs, targets) 的生成器或可迭代对象。
            steps_per_epoch: 每个 epoch 的梯度更新次数。
            batch_size: 每个梯度更新的样本数。
            epochs: 训练轮数。
            verbose: {0, >0}，是否使用 Keras 的进度条回调。
            validation_data: 生成器、可迭代对象或 (inputs, targets) 元组。
            validation_steps: 如果 validation_data 是生成器，则需要指定此参数。
            on_sample: 接受 sampler, idxs, w, scores 的回调函数。
            on_scores: 接受 sampler 和 scores 的回调函数。
        """

        def infinite_iterator(iterable):
            while True:
                yield from iterable

        # 处理验证数据集
        if validation_data is not None:
            if isinstance(validation_data, (tuple, list)):
                test = validation_data
                test_len = None
            elif isinstance(validation_data, Iterable):
                test = infinite_iterator(validation_data)
                try:
                    test_len = len(validation_data) * batch_size
                except TypeError:
                    test_len = validation_steps * batch_size if validation_steps else None
            else:
                test = validation_data
                test_len = validation_steps * batch_size if validation_steps else None
        else:
            test = (np.empty(shape=(0, 1)), np.empty(shape=(0, 1)))
            test_len = None
            logging.info("未提供验证数据")

        # 设置训练数据和每个 epoch 的步数
        if isinstance(train, Iterable):
            if steps_per_epoch is None:
                try:
                    steps_per_epoch = len(train)
                except TypeError:
                    raise ValueError("使用没有 __len__ 方法的生成器时必须设置 steps_per_epoch")
            train = infinite_iterator(train)
        elif steps_per_epoch is None:
            raise ValueError("steps_per_epoch 未设置")

        dataset = GeneratorDataset(train, test, test_len)

        return self.fit_dataset(
            dataset=dataset,
            batch_size=batch_size,
            epochs=epochs,
            steps_per_epoch=steps_per_epoch,
            verbose=verbose,
            callbacks=callbacks,
            on_sample=on_sample,
            on_scores=on_scores
        )

    def fit_dataset(self, dataset, steps_per_epoch=None, batch_size=32,
                    epochs=1, verbose=1, callbacks=None, on_sample=None,
                    on_scores=None):
        """在给定的数据集上训练模型。

        参数：
            dataset: `BaseDataset` 的实例，提供要训练的数据。
            steps_per_epoch: int，每个 epoch 的梯度更新次数。
            batch_size: 每个梯度更新的样本数。
            epochs: 训练轮数。
            verbose: {0, >0}，是否使用 Keras 的进度条回调。
            callbacks: Keras 回调列表。
            on_sample: 接受 sampler, idxs, w, scores 的回调函数。
            on_scores: 接受 sampler 和 scores 的回调函数。
        """
        try:
            if len(dataset.train_data) < batch_size:
                raise ValueError("batch_size 大于训练集大小")
        except RuntimeError as e:
            assert "no size" in str(e)

        # 设置 steps_per_epoch
        if steps_per_epoch is None:
            steps_per_epoch = len(dataset.train_data) // batch_size

        # 创建回调列表
        self.history = History()
        callbacks = [BaseLogger()] + (callbacks or []) + [self.history]
        if verbose > 0:
            callbacks += [ProgbarLogger(count_mode="steps")]
        callbacks = CallbackList(callbacks)
        callbacks.set_model(self.original_model)
        callbacks.set_params({
            "epochs": epochs,
            "steps": steps_per_epoch,
            "verbose": verbose,
            "do_validation": len(dataset.test_data) > 0,
            "metrics": self.metrics_names + [
                "val_" + n for n in self.metrics_names
            ]
        })

        # 创建采样器
        sampler = self.sampler(dataset, batch_size, steps_per_epoch, epochs)

        # 开始训练循环
        epoch = 0
        self.original_model.stop_training = False
        callbacks.on_train_begin()
        while epoch < epochs:
            callbacks.on_epoch_begin(epoch)

            for step in range(steps_per_epoch):
                batch_logs = {"batch": step, "size": batch_size}
                callbacks.on_batch_begin(step, batch_logs)

                # 在这里进行重要性采样
                idxs, (x_batch, y_batch), w = sampler.sample(batch_size)

                # 将 x_batch 和 y_batch 转换为 TensorFlow 张量
                x_batch = tf.convert_to_tensor(x_batch)
                y_batch = tf.convert_to_tensor(y_batch)
                w = tf.convert_to_tensor(w, dtype=tf.float32)

                # 自定义训练步骤
                loss_value, metrics_results, scores = self.train_batch(x_batch, y_batch, w)

                # 更新采样器
                sampler.update(idxs, scores)

                # 记录指标
                batch_logs['loss'] = loss_value
                for metric_name, metric_value in zip(self.metrics_names[1:], metrics_results):
                    batch_logs[metric_name] = metric_value
                callbacks.on_batch_end(step, batch_logs)

                if on_scores is not None and hasattr(self, "_latest_scores"):
                    on_scores(
                        sampler,
                        self._latest_scores
                    )

                if on_sample is not None:
                    on_sample(
                        sampler,
                        self._latest_sample_event["idxs"],
                        self._latest_sample_event["w"],
                        self._latest_sample_event["predicted_scores"]
                    )

                if self.original_model.stop_training:
                    break

            # 在每个 epoch 结束时进行评估
            epoch_logs = {}
            if len(dataset.test_data) > 0:
                val_loss, val_metrics = self.evaluate(dataset.test_data)
                epoch_logs['val_loss'] = val_loss
                for name, value in zip(self.metrics_names[1:], val_metrics):
                    epoch_logs['val_' + name] = value
            callbacks.on_epoch_end(epoch, epoch_logs)
            if self.original_model.stop_training:
                break
            epoch += 1
        callbacks.on_train_end()

        return self.history

    @property
    def metrics_names(self):
        def name(x):
            if isinstance(x, str):
                return x
            if hasattr(x, "name"):
                return x.name
            if hasattr(x, "__name__"):
                return x.__name__
            return str(x)

        metrics = self.model.model.metrics or []
        return (
                ["loss"] +
                [name(m) for m in metrics]
        )

    def train_batch(self, x, y, w):
        with tf.GradientTape() as tape:
            y_pred = self.model(x, training=True)
            loss_fn = self.model.loss

            # 获取每个样本的损失
            if isinstance(loss_fn, (str, dict)):
                loss_fn = tf.keras.losses.get(loss_fn)

            if isinstance(loss_fn, tf.keras.losses.Loss):
                loss_config = loss_fn.get_config()
                per_example_loss_fn = loss_fn.__class__(reduction=tf.keras.losses.Reduction.NONE, **loss_config)
                per_sample_losses = per_example_loss_fn(y, y_pred)
            else:
                per_sample_losses = loss_fn(y, y_pred)

            # 计算得分
            if self.score_method == "loss":
                scores = per_sample_losses
            else:
                scores = tf.ones_like(per_sample_losses)

            # 计算样本权重
            weights = self.reweighting.weight_layer()([scores, w])

            # 计算加权损失
            weighted_losses = per_sample_losses * weights
            loss = tf.reduce_mean(weighted_losses)

        # 获取模型的可训练参数
        trainable_vars = self.model.trainable_variables

        # 计算梯度
        gradients = tape.gradient(loss, trainable_vars)

        # 更新模型参数
        self.model.optimizer.apply_gradients(zip(gradients, trainable_vars))

        # 更新指标
        self.model.compiled_metrics.update_state(y, y_pred, sample_weight=weights)
        metrics = [metric.result().numpy() for metric in self.model.metrics]

        # 将 scores 转换为 numpy 数组，并处理 NaN 或无穷值
        scores = scores.numpy()
        if np.any(np.isnan(scores)) or np.any(np.isinf(scores)):
            print("警告：scores 中存在 NaN 或无穷值，将其替换为有限值")
            scores = np.nan_to_num(scores, nan=1e6, posinf=1e6, neginf=-1e6)

        return loss.numpy(), metrics, scores

class _UnbiasedImportanceTraining(_BaseImportanceTraining):
    def __init__(self, model, score="gnorm", layer=None):
        self._reweighting = BiasedReweightingPolicy(1.0)  # 无偏

        super(_UnbiasedImportanceTraining, self).__init__(model, score, layer)

    @property
    def reweighting(self):
        return self._reweighting

class ConstantTimeImportanceTraining(_UnbiasedImportanceTraining):
    """使用常时间重要性采样训练模型。

    参数：
        model: 要训练的 Keras 模型。
        score: {"gnorm", "loss", "full_gnorm"}，用于重要性采样的重要性度量。
        layer: None 或 int 或 Layer，计算 gnorm 时使用的层。
    """

    def __init__(self, model, backward_pass_multiplier=2.0, extra_samples=0.1,
                 score="gnorm", layer=None):
        self._backward_pass_multiplier = backward_pass_multiplier
        self._extra_samples = extra_samples

        super(ConstantTimeImportanceTraining, self).__init__(
            model,
            score,
            layer
        )

    def sampler(self, dataset, batch_size, steps_per_epoch, epochs):
        return ConstantTimeSampler(
            dataset,
            self.reweighting,
            self.model,
            backward_time=self._backward_pass_multiplier,
        )
上述是training.py文件
class ModelWrapper(object):
    """ModelWrapper 的目的是包装一个神经网络，并添加一些额外的层，以生成得分、损失和样本权重，以执行重要性采样。"""

    def _iterate_batches(self, x, y, batch_size):
        bs = batch_size
        for s in range(0, len(y), bs):
            if isinstance(x, (list, tuple)):
                xs = [xi[s:s+bs] for xi in x]
            else:
                xs = x[s:s+bs]
            yield xs, y[s:s+bs]

    def evaluate(self, x, y, batch_size=128):
        result = np.mean(
            np.vstack([
                self.evaluate_batch(xi, yi)
                for xi, yi in self._iterate_batches(x, y, batch_size)
            ]),
            axis=0
        )

        signal("is.evaluation").send(result)
        return result

    def score(self, x, y, batch_size=128):
        bs = batch_size
        result = np.hstack([
            self.score_batch(xi, yi).T
            for xi, yi in self._iterate_batches(x, y, batch_size)
        ]).T

        signal("is.score").send(result)
        return result

    def set_lr(self, lr):
        """设置模型的学习率。"""

        try:
            K.set_value(
                self.optimizer.lr,
                lr
            )
        except AttributeError:
            try:
                K.set_value(
                    self.model.optimizer.lr,
                    lr
                )
            except AttributeError:
                raise NotImplementedError()

        try:
            K.set_value(
                self.small.optimizer.lr,
                lr
            )
        except AttributeError:
            pass

    def evaluate_batch(self, x, y):
        raise NotImplementedError()

    def score_batch(self, x, y):
        raise NotImplementedError()

    def train_batch(self, x, y, w):
        raise NotImplementedError()

class OracleWrapper(ModelWrapper):
    AVG_LOSS = 0
    LOSS = 1
    WEIGHTED_LOSS = 2
    SCORE = 3
    METRIC0 = 4

    FUSED_ACTIVATION_WARNING = ("[WARNING]: 最后一层具有融合激活函数，例如 Dense(..., activation=\"sigmoid\")。\n"
                                "为了自动提取预激活张量，请使用单独的激活层（参见示例）。\n")

    def __init__(self, model, reweighting, score="loss", layer=None):
        self.reweighting = reweighting
        self.layer = self._gnorm_layer(model, layer)
        self._augment_model(model, score, reweighting)

    def _gnorm_layer(self, model, layer):
        # 如果提供了层，则直接使用
        if isinstance(layer, Layer):
            return layer

        # 如果提供了层的索引，则提取该层
        if isinstance(layer, int):
            return model.layers[layer]

        # 对于量子神经网络，可能无法自动获取梯度范数层
        # 这里暂时返回 None，表示不使用特定的层
        return None

    def _augment_model(self, model, score, reweighting):
        # 保存原始模型
        self.original_model = model

        # 创建包装后的模型，不修改原始模型的结构
        self.model = model  # 使用原始模型

        # 保存必要的信息
        self.score_method = score
        self.reweighting = reweighting

        # 提取模型的一些信息
        self.loss_fn = model.loss
        self.optimizer = model.optimizer.__class__.from_config(model.optimizer.get_config())
        self.metrics = model.metrics or []

    def evaluate_batch(self, x, y):
        # 使用 model.evaluate
        outputs = self.model.evaluate(
            x=x,
            y=y,
            verbose=0,
            return_dict=True
        )
        loss = outputs['loss']
        metrics = [outputs[m] for m in self.model.metrics_names[1:]]

        signal("is.evaluate_batch").send(outputs)

        # 返回损失和其他指标
        return [loss] + metrics

    # 在 model_wrappers.py 文件中
    # 在 model_wrappers.py 文件中
    def score_batch(self, x, y):
        y_pred = self.model(x, training=False)
        loss_fn = self.model.loss

        # 如果 loss_fn 是字符串或字典，使用 tf.keras.losses.get 获取损失函数对象
        if isinstance(loss_fn, (str, dict)):
            loss_fn = tf.keras.losses.get(loss_fn)

        # 确保损失函数具有 reduction 参数，并设置为 NONE
        if isinstance(loss_fn, tf.keras.losses.Loss):
            # 获取损失函数配置
            loss_config = loss_fn.get_config()
            # 创建一个新的损失函数实例，设置 reduction=NONE
            per_example_loss_fn = loss_fn.__class__(reduction=tf.keras.losses.Reduction.NONE, **loss_config)
            per_sample_losses = per_example_loss_fn(y, y_pred)
        else:
            # 如果损失函数不是 tf.keras.losses.Loss 的实例，直接调用
            per_sample_losses = loss_fn(y, y_pred)

        if self.score_method == "loss":
            scores = per_sample_losses
        else:
            scores = tf.ones_like(per_sample_losses)

        # 将 scores 转换为 numpy 数组
        scores = scores.numpy()

        # 检查是否存在 NaN 或无穷值，并进行处理
        if np.any(np.isnan(scores)) or np.any(np.isinf(scores)):
            print("警告：scores 中存在 NaN 或无穷值，将其替换为有限值")
            scores = np.nan_to_num(scores, nan=1e6, posinf=1e6, neginf=-1e6)

        return scores

    def train_batch(self, x, y, w):
        with tf.GradientTape() as tape:
            y_pred = self.model(x, training=True)
            # 计算每个样本的损失（不进行 reduction）
            per_sample_losses = self.model.compiled_loss(
                y,
                y_pred,
                sample_weight=None,
                regularization_losses=self.model.losses,
                reduction=tf.keras.losses.Reduction.NONE
            )
            # 计算得分
            if self.score_method == "loss":
                scores = per_sample_losses
            else:
                # 实现其他得分方式
                scores = tf.ones_like(per_sample_losses)

            # 计算样本权重
            weights = self.reweighting.weight_layer()([scores, w])

            # 计算加权损失
            weighted_losses = per_sample_losses * weights
            loss = tf.reduce_mean(weighted_losses)

        # 获取模型的可训练参数
        trainable_vars = self.model.trainable_variables

        # 计算梯度
        gradients = tape.gradient(loss, trainable_vars)

        # 更新模型参数
        self.model.optimizer.apply_gradients(zip(gradients, trainable_vars))

        # 更新指标
        self.model.compiled_metrics.update_state(y, y_pred, sample_weight=weights)
        metrics = [metric.result().numpy() for metric in self.model.metrics]

        # 返回损失、指标和得分
        return loss.numpy(), metrics, scores.numpy()

    def __call__(self, x, training=False):
        return self.model(x, training=training)
def _tolist(x, acceptable_iterables=(list, tuple)):
    if not isinstance(x, acceptable_iterables):
        x = [x]
    return x

def _get_scoring_layer(score, y_true, y_pred, loss_fn="categorical_crossentropy",
                       layer=None, model=None):
    """获取一个计算每个 y_true 和 y_pred 对的得分的层"""
    assert score in ["loss", "gnorm", "full_gnorm", "acc"]

    if score == "loss":
        return LossLayer(loss_fn, name='score_layer')([
            y_true,
            y_pred
        ])
    elif score == "gnorm":
        return GradientNormLayer(
            layer.output,
            loss_fn,
            fast=False,
            name='score_layer'
        )([
            y_true,
            y_pred
        ])
    elif score == "full_gnorm":
        return GradientNormLayer(
            model.trainable_weights,
            loss_fn,
            fast=False,
            name='score_layer'
        )([
            y_true,
            y_pred
        ])
    elif score == "acc":
        return MetricLayer(categorical_accuracy, name='score_layer')([
            y_true,
            y_pred
        ])

class ModelWrapperDecorator(ModelWrapper):
    def __init__(self, model_wrapper, implemented_attributes=set()):
        self.model_wrapper = model_wrapper
        self.implemented_attributes = (
            implemented_attributes | set(["model_wrapper"])
        )

    def __getattribute__(self, name):
        _getattr = object.__getattribute__
        implemented_attributes = _getattr(self, "implemented_attributes")
        if name in implemented_attributes:
            return _getattr(self, name)
        else:
            model_wrapper = _getattr(self, "model_wrapper")
            return getattr(model_wrapper, name)
上述是model_wrappers.py文件
class BaseSampler(object):
    """BaseSampler denotes the interface for all the samplers.

    Samplers should provide the rest of the program with data points to train
    on and corresponding relative weights."""

    def __init__(self, dataset, reweighting):
        self.dataset = dataset
        self.reweighting = reweighting

    def _slice_data(self, x, y, idxs):
        if isinstance(x, (list, tuple)):
            return [xi[idxs] for xi in x], y[idxs]
        else:
            return x[idxs], y[idxs]

    def _send_messages(self, idxs, xy, w, predicted_scores):
        signal("is.sample").send({
            "idxs": idxs,
            "xy": xy,
            "w": w,
            "predicted_scores": predicted_scores
        })

    def _get_samples_with_scores(self, batch_size):
        """Child classes should implement this method.

        Arguments
        ---------
        batch_size: int
                    Return at least that many samples

        Return
        ------
        idxs: array
              The indices of some samples in the dataset
        scores: array or None
                The predicted importance scores for the corresponding idxs or
                None for uniform sampling
        xy: tuple or None
            Optionally return the data for the corresponding idxs
        """
        raise NotImplementedError()

    def sample(self, batch_size):
        # Get the importance scores of some samples
        idxs1, scores, xy = self._get_samples_with_scores(batch_size)

        # Sample from the available ones
        if scores is not None:
            # 使用 argsort 对 scores 进行降序排序，并取最大的 batch_size 个索引
            idxs_sorted = np.argsort(scores)[::-1]
            idxs2 = idxs_sorted[:batch_size]
        else:
            # 如果 scores 为空，随机选择 batch_size 个样本的索引
            idxs2 = np.random.choice(len(idxs1), batch_size, p=None)
        w = self.reweighting.sample_weights(idxs2, scores)

        # Make sure we have the data
        if xy is None:
            xy = self.dataset.train_data[idxs1[idxs2]]
        else:
            x, y = xy
            xy = self._slice_data(x, y, idxs2)

        scores = scores[idxs2] if scores is not None else np.ones(batch_size)
        self._send_messages(idxs1[idxs2], xy, w, scores)
        return idxs1[idxs2], xy, w

    '''

    def sample(self, batch_size):
        # Get the importance scores of some samples
        idxs1, scores, xy = self._get_samples_with_scores(batch_size)

        # Sample from the available ones
        p = scores / scores.sum() if scores is not None else None
        idxs2 = np.random.choice(len(idxs1), batch_size, p=p)
        w = self.reweighting.sample_weights(idxs2, scores)

        # Make sure we have the data
        if xy is None:
            xy = self.dataset.train_data[idxs1[idxs2]]
        else:
            x, y = xy
            xy = self._slice_data(x, y, idxs2)

        scores = scores[idxs2] if scores is not None else np.ones(batch_size)
        self._send_messages(idxs1[idxs2], xy, w, scores)
        return idxs1[idxs2], xy, w
    '''

    def update(self, idxs, results):
        pass
import numpy as np
class ConstantTimeSampler(BaseSampler):
    def __init__(self, dataset, reweighting, model, backward_time=2.0,
                 tau_th=0.9, ratio=0.5, min_a=0.2):
        self.condition = VarianceReductionCondition(tau_th)
        self.backward_time = backward_time
        self.ratio = ratio
        self.min_a = min_a
        self.model = model
        self.N = len(dataset.train_data) if hasattr(dataset, 'train_data') else _get_dataset_length(dataset, default=1)
        print(f"Dataset size (self.N): {self.N}")
        self.usage_counts = np.zeros(self.N, dtype=int)

        super(ConstantTimeSampler, self).__init__(dataset, reweighting)

    def sample(self, batch_size):
        print(f"Entering sample method")
        print(f"batch_size: {batch_size}, self.N: {self.N}")
        print(f"Condition satisfied: {self.condition.satisfied}")
        print(f"Requested batch_size: {batch_size}")
        if batch_size > self.N:
            batch_size = min(batch_size, self.N)
        if self.condition.satisfied:
            print("Entering satisfied condition block")
            t = (1.0 + self.backward_time) * batch_size
            a = 8
            B = 32
            idxs = np.random.choice(self.N, B, replace=False)
            x, y = self.dataset.train_data[idxs]
            z = 20
            # 确保scores中没有0值
            while (z != 0):
                z = z - 1
                scores = self.model.score(x, y, batch_size=batch_size)
                if not np.any(scores == 0):
                    break
                zero_indices = np.where(scores == 0)[0]
                new_idxs = np.random.choice(self.N, len(zero_indices), replace=False)
                idxs[zero_indices] = new_idxs
                x[zero_indices], y[zero_indices] = self.dataset.train_data[new_idxs]

            print(f"Length of scores before processing: {len(scores)}")
            # 确保 scores 的长度为B
            if len(scores) > B:
                scores = scores[:B]
            elif len(scores) < B:
                scores = np.pad(scores, (0, B - len(scores)), 'constant')

            # 保存原始scores
            original_scores = scores.copy()
            print("Original scores:", original_scores)

            # 重整化scores到0-7的范围
            if np.max(scores) != np.min(scores):  # 避免除以零
                # 先归一化到0-1范围
                scores = (scores - np.min(scores)) / (np.max(scores) - np.min(scores))
                # 然后缩放到0-7范围
                scores = scores * 7
            else:
                # 如果所有值都相同，设置为中间值
                scores = np.full_like(scores, 3.5)

            # 向上取整
            scores = np.ceil(scores)

            # 确保范围在0-7之间
            scores = np.clip(scores, 0, 7)

            print("Normalized and ceiled scores:", scores)
            print(f"Length of scores after processing: {len(scores)}")
            print(f"a: {a}, B: {B}")

            # 添加检查
            if np.any(np.isnan(scores)):
                print("警告：数值scores存在nan，回退为随机采样")
                print("scores:", scores)
                return self._random_sample(batch_size)
            if np.any(np.isinf(scores)):
                print("警告：数值scores存在无穷，回退为随机采样")
                print("scores:", scores)
                return self._random_sample(batch_size)

            N_Q = int(a)
            num_value_qubits = 4
            num_key_qubits = 5
            num_iterations = 3
            optimizer = GroverOptimizer(num_value_qubits, num_key_qubits, num_iterations=num_iterations)
            result = optimizer.solve(scores, N_Q)
            idxs2 = np.array(result.keys)
            print(f"result.keys", result.keys)
            print(f"Length of top_a_idxs: {len(idxs2)}")
            print(f"Max value in top_a_idxs: {idxs2.max() if len(idxs2) > 0 else 'N/A'}")

            if len(idxs2) == 0:
                print("Warning: top_a_idxs is empty after filtering. Falling back to random sampling.")
                return self._random_sample(batch_size)

            w = self.reweighting.sample_weights(idxs2, scores)
            x, y = self._slice_data(x, y, idxs2)
            self.condition.update(scores)
        else:
            idxs = np.random.choice(self.N, batch_size)
            idxs2 = np.arange(len(idxs))
            x, y = self.dataset.train_data[idxs]
            scores = np.ones(len(idxs))
            w = np.ones((len(idxs), self.reweighting.weight_size))

        self._send_messages(idxs[idxs2], (x, y), w, scores[idxs2])
        print(f"Returned batch shapes: x: {x.shape}, y: {y.shape}, w: {w.shape}")
        return (idxs[idxs2], (x, y), w)

    def update(self, idxs, scores):
        if not self.condition.previously_satisfied:
            self.condition.update(scores)

    def _random_sample(self, batch_size):
        # 实现随机采样作为后备策略
        random_indices = np.random.choice(self.N, size=batch_size, replace=False)
        x, y = self.dataset.train_data[random_indices]
        w = np.ones((batch_size, self.reweighting.weight_size))
        return random_indices, (x, y), w
上述是samplers.py文件
我想将wine_Designbygpt_try.py文件修改为批处理形式，从而提升运行速度，使用tensorflow包来进行实现，tensorflow版本为2.10.0，pennylane的版本为0.15.0，给出修改后的完整代码