# 使用 Python 构建量子神经网络分类器

本教程使用 ArcQML Python API 对德国信用数据集进行二分类，对应 [Rust QNN 教程](../rust/qnn_german_credit.md)和 `benchmarks/qnn` 的任务结构。完整程序位于 [`examples/python/qnn_german_credit.py`](../../../examples/python/qnn_german_credit.py)。

## 任务结构

- 从 [`examples/data/german_credit.csv`](../../../examples/data/german_credit.csv) 读取 1000 个样本。
- 选择 benchmark 使用的 10 个离散特征。
- 将每列缩放到 `[π/2, 3π/2]`。
- 使用 `RX(x) → RZ(x)` 将特征编码到 10 量子比特乘积态。
- 使用每层 37 参数、52 个门的 QNN ansatz。
- 读取 `Z(q5)` 期望值作为 logit。
- 使用 BCE-with-logits、batch 伴随反传和 Adam 训练。
- 报告验证准确率、ROC-AUC 和 PR-AUC。

教程默认 1 层、5 个 epoch；benchmark 会测试更深线路。

## 数据预处理

```python
features, labels = load_data(path=DATA_PATH)
rng = np.random.default_rng(seed=4)
indices = rng.permutation(len(features))
train, validation, test = indices[:800], indices[800:900], indices[900:]
```

每列使用：

$$
x' = \frac{\pi}{2} + \frac{x-x_{\min}}{x_{\max}-x_{\min}}\pi.
$$

原始 `Creditability=0` 被作为正类 `1`，表示信用不良。

## 特征编码

每个局部量子态为：

$$
R_Z(x)R_X(x)|0\rangle =
e^{-ix/2}\cos(x/2)|0\rangle-i e^{ix/2}\sin(x/2)|1\rangle.
$$

程序直接计算这些局部态的张量积，并生成 C 连续数组：

```python
states = np.ones((len(features), 1), dtype=np.complex128)
for qubit in range(QUBITS - 1, -1, -1):
    angles = features[:, qubit]
    local_states = np.column_stack(
        (
            np.exp(-0.5j * angles) * np.cos(0.5 * angles),
            -1j * np.exp(0.5j * angles) * np.sin(0.5 * angles),
        )
    )
    states = np.einsum("bi,bj->bij", states, local_states).reshape(len(features), -1)
```

这样每个样本拥有不同初态，而整个 batch 共用同一组可训练 ansatz 参数。

## 构造线路和可观测量

```python
circuit = arcqml.Circuit(num_qubits=QUBITS)
append_ansatz_layer(
    circuit=circuit,
    values=rng.standard_normal(PARAMETERS_PER_LAYER),
)

observable = arcqml.PauliSum.z(
    num_qubits=QUBITS,
    qubit=5,
    coefficient=1.0,
)
```

`append_ansatz_layer` 的完整 37 参数线路见示例源码。增加到 `L` 层后，线路具有 `37L` 个独立可训练参数。

## Batch 训练

```python
simulator = arcqml.BatchStateVectorSimulator.from_amplitudes(
    num_qubits=QUBITS,
    amplitudes=np.ascontiguousarray(states[batch_indices]),
)
logits = simulator.run(circuit=circuit, observable=observable)
targets = arcqml.tensor(np.ascontiguousarray(labels[batch_indices]))
loss = arcqml.binary_cross_entropy_with_logits(
    logits=logits,
    targets=targets,
)

loss.backward()
optimizer.step(circuit=circuit)
optimizer.zero_grad(circuit=circuit)
```

`logits` 的 shape 为 `[batch_size]`。`backward()` 通过 BCE 节点把每个样本的梯度传给 batch 量子节点，再由伴随算法累计线路参数梯度。

验证和测试使用：

```python
with arcqml.no_grad():
    scores = simulator.run(
        circuit=circuit,
        observable=observable,
    ).numpy()
```

分类时以 `logit >= 0` 为正类。ROC-AUC 与 PR-AUC 直接使用连续分数，不依赖分类阈值。

## 运行

```bash
maturin develop --release
python examples/python/qnn_german_credit.py
```

输出形式：

```text
samples: train=800, validation=100, test=100
qubits=10, layers=1, parameters=37
epoch 01/5: train_loss=..., validation_loss=..., validation_accuracy=...%
...
test ROC-AUC=..., PR-AUC=...
```

