---
title: "编译与复用"
description: "对固定网络和收缩序准备一次执行所需的信息，再用于多组数值数组。"
eyebrow: "数值执行"
---

## 编译一次，重复执行 {#compile-once}

当网络结构、维度和收缩序不变，只有数组值发生变化时，可以先编译一次，再对多组数组执行。这样既不重新寻路，也不必每次重新准备同一收缩表达式。

寻路决定“先收缩哪两个张量”；编译进一步确定“每一步怎样排列轴、怎样组织矩阵乘法”；执行才将这些步骤应用到实际数组上。复用编译对象省去了重复的准备工作，每组新数组仍需完成相应的数值计算。

这里的编译是准备执行所需的收缩步骤，不是为每组输入重新生成机器代码。编译对象不保存输入数值。

## 原生后端准备哪些信息 {#native-plan}

Rust 的 `CompiledContraction` 保存一元处理所需的轴、每步输入的轴排列、批量矩阵乘法的形状，以及最终输出的轴顺序。后续 `execute` 直接使用这些信息。

每次执行仍会检查输入数量、形状和 `DenseTensor` 布局。

```pseudocode
steps = 根据网络和收缩序准备轴排列、矩阵形状与输出顺序
对每组输入数组 arrays:
    检查数组与网络是否匹配
    result = 按 steps 执行数值收缩
```

## Python 复用对象 {#python-usage}

假设已取得 `ssa_path`，`arrays0` 和 `arrays1` 是两组数值不同的输入数组。两组数组都按 `inputs` 的顺序排列，并保持各个数组的形状和轴含义一致：

```python
from arctn import ArcTNCompiledContraction

compiled = ArcTNCompiledContraction.compile(
    inputs, output, size_dict,
    ssa_path=ssa_path,
    backend="numpy",
)
y0 = compiled.execute(arrays0)
y1 = compiled.execute(arrays1)
print(compiled.stats())
```

`y0`、`y1` 分别是两组数组的收缩结果；第二次 `execute` 继续使用同一个 `compiled`。例如，在固定量子线路结构中更新门参数，只要门张量的连接关系和维度不变，就可以按这种方式复用。

| backend | compile 保存的对象 | execute |
| --- | --- | --- |
| native | Rust CompiledContraction | 调用 Rust CPU 执行器 |
| 已安装的 opt\_einsum 数组后端，如 numpy、cupy、torch、jax | 固定的 opt\_einsum contract\_expression | 使用指定后端执行表达式 |

默认使用 `backend="native"`；可显式选择其他后端，不接受 `"auto"`。外部后端及其依赖需要单独安装。

`ssa_path=` 必须是 SSA 格式，不是 opt\_einsum 的 linear path。SSA 使用固定的张量编号；linear path 使用当前张量列表中的位置，收缩后这些位置会改变。

## 复用条件 {#lifecycle}

`ArcTNCompiledContraction` 用于当前进程；改变网络结构、维度或 SSA path 后，需要重新编译。

需要保存到文件的是网络、收缩序和切片信息，而不是编译对象。可使用 `ArcTNExecutionPlan` 保存和加载；无切片时再调用一次 `plan.compile()`，复用其返回对象。对于无切片收缩，直接重复调用 `plan.execute()` 也不会重新寻路，但每次都会重新创建编译对象。

Python 的编译接口不支持非空的切片集合。执行已保存的切片集合时，使用 `ArcTNExecutionPlan.execute()`，或通过 `to_tree()` 交给 Cotengra。

Rust 还提供 `execute_stripped`：每步矩阵乘法后，若最大幅值为正且有限，则按该幅值归一化，并单独累计以 10 为底的缩放指数。这用于避免数值尺度过大或过小，不做截断或近似。
