---
title: "检查输入与处理错误"
description: "说明网络、收缩路径、数值数组和收缩序文件分别检查什么，并用小例子解释如何修改常见错误。"
eyebrow: "API 参考"
---

## 输入和路径检查 {#layers}

一次收缩需要先说明张量之间怎样连接，再指定收缩顺序，最后传入数值数组。ArcTN 会在相应步骤检查这些信息是否相符。例如，维度写错会让数组形状与网络不符，路径中重复使用已参与收缩的张量则会使后续步骤无法执行。

### 网络：索引和维度是否定义完整

`inputs` 列出每个输入张量各个轴的索引，`output` 列出结果保留的索引，`size_dict` 给出每个索引的维度。即使只调用 `arctn_path` 搜索收缩序、不传入数组，网络也会接受检查：

- 网络至少包含一个输入张量。每个使用到的索引都必须在 `size_dict` 中有维度，也不能多出未使用的条目。
- 维度必须是正整数。例如，`3` 合法，`0`、`-1`、`3.5` 和 `True` 都不合法。
- `output` 中的索引必须出现在输入中，而且不能重复；它们的排列顺序就是结果的轴顺序。
- 同一个索引可以出现在多个输入张量中；在同一个输入张量内，目前最多出现两次。

以[快速开始](/docs/quick-start#network)的矩阵乘法为例，`inputs` 使用了 `"a"`、`"b"`、`"c"`、`"d"` 四个索引。如果漏写 `"b": 3`，就无法确定两块相连矩阵的公共维度，需要把这一条补回 `size_dict`。若把 `output` 写成 `("a", "a")`，则重复保留了同一个索引；这个例子应改回 `("a", "d")`。

### 路径：每一步是否能接着执行

收缩路径说明每一步合并哪两个张量。检查路径时，ArcTN 会确认两个编号不同、引用的张量已经存在且尚未被前面的步骤使用，并确认全部步骤执行后只剩一个结果。

以三个输入张量为例，SSA 路径中的输入编号为 `0`、`1`、`2`，第一步生成的结果编号为 `3`。路径 `[(0, 1), (3, 2)]` 表示先收缩输入 `0` 和 `1`，再把结果 `3` 与输入 `2` 收缩。若写成 `[(0, 1), (0, 2)]`，第二步就错误地再次引用了输入 `0`；这里应将第二步改为 `(3, 2)`。若只保留第一步，仍有两个张量未合并，路径也不完整。

SSA 路径中的编号不会重复分配；opt_einsum 使用的 linear path 则按当前剩余张量列表的位置编号。`ArcTNExecutionPlan` 保存的是 SSA 路径，可用 `plan.to_linear_path()` 将它转换为 linear path。

### 数组：数量、形状和数据类型是否匹配

执行时，`arrays[i]` 必须对应 `inputs[i]`，各个轴的大小也必须符合 `size_dict`。快速开始中的三个数组应依次具有形状 `(2, 3)`、`(3, 4)`、`(4, 2)`。把第一个数组转置为 `(3, 2)` 后传入，就会与 `inputs[0] = ("a", "b")` 的要求不符；应按所需轴顺序传入数组，或在确实改变了网络时同步修改网络定义并重新生成收缩序。

通过 `arctn_contract`、`plan.execute()` 或 `ArcTNCompiledContraction.execute()` 执行时，各数组必须使用同一种受支持的数据类型：`float32`、`float64`、`complex64` 或 `complex128`。例如，用 NumPy 创建示例数组时明确设置 `dtype=np.float64`，可以避免默认整数数组不被支持的问题。处理复数数据时应选择复数类型，以保留虚部。若只把 ArcTN 的路径或树交给 Quimb/Cotengra 执行，数组类型由实际执行库检查。

## 捕获参数错误 {#parameter-example}

沿用[快速开始](/docs/quick-start#network)中的 `inputs`、`output` 和 `size_dict`，下面故意省略 Dynamic 切片必需的 `target_size`：

```python
from arctn import arctn_schedule

try:
    report = arctn_schedule(
        inputs, output, size_dict,
        slicing_mode="dynamic",
    )
except ValueError as exc:
    print(f"规划参数或网络无效：{exc}")
```

异常信息会指出，`slicing_mode="dynamic"` 必须同时提供 `target_size`。可以在调用中补上 `target_size=4`，表示每个切片内，每一步生成的张量（包括最终输出）最多有 4 个元素。这里的 `4` 是小型示例的设置；实际使用时应根据网络和可用内存选择上限。

如果参数有效，但仍未找到满足 `target_size` 的切片方案，调用也会抛出 `ValueError`。这时应根据错误信息考虑增大上限，或在原来设置了时间限制的情况下增加 `max_time`；搜索不会自行放宽 `target_size`。

## 检查保存的收缩序文件 {#plan-example}

先按[保存与复用收缩序](/docs/tutorial-reuse#save-load)保存 `plan.json`，再用快速开始中的网络定义和 `arrays` 检查：

```python
from arctn import ArcTNExecutionPlan, PlanValidationError

try:
    plan = ArcTNExecutionPlan.load("plan.json")
    plan.validate(inputs, output, size_dict, arrays=arrays)
except PlanValidationError as exc:
    print(f"文件、网络或数组检查失败：{exc}")
else:
    result = plan.execute(arrays, backend="native")
```

`ArcTNExecutionPlan.load()` 会读取文件，并检查保存的网络、SSA 路径、切片索引和大小限制是否相符；文件中已有的浮点运算量、中间张量大小等核心指标，也会根据网络和路径重新计算后核对。加载过程不会重新搜索收缩序。

随后，`plan.validate()` 将当前网络的连接关系、维度、输入及输出顺序与文件中的网络比较，并检查数组的数量、形状和数据类型。单独调用 `plan.validate(arrays=arrays)` 也可以检查数组；如果还要传入网络定义，则 `inputs`、`output` 和 `size_dict` 必须一起提供。检查通过时返回 `None`，失败时抛出异常。

例如，文件保存的是第一个数组形状为 `(2, 3)` 的网络，后来传入了 `(2, 4)` 的数组，错误信息会列出实际形状和所需形状。若只是拿错数组，应传入原网络对应的数组；若维度确实改变了，应使用新网络重新生成并保存收缩序。文件缺失、无法读取或 JSON 格式损坏，也会由 `load()` 包装为 `PlanValidationError`；可根据异常信息检查路径，或从原来的网络重新保存文件。

这些检查核对的是网络、路径和数组信息。需要验证计算出的数值时，可沿用快速开始中的 `np.testing.assert_allclose(result, a @ b @ c)`，将结果与已知的参考计算比较。

## 时间限制 {#deadline}

`max_time` 以秒为单位，必须是有限的正数。搜索会在运行过程中检查已用时间，因此设置 `max_time=1.0` 后，函数也可能超过 1 秒才返回。成功返回的仍是完整路径，可以继续执行收缩。若任务要求到时立即结束整个进程，需要在调用程序外设置超时控制，例如使用作业调度器的时间限制。

## 常见异常 {#errors}

| 异常 | 常见情况与处理方式 |
| --- | --- |
| `TypeError` | 参数类型不符，例如 `target_size=4.0`；这里应传入整数 `4`。 |
| `ValueError` | 网络、路径或权重无效，或未找到满足限制的方案；按错误信息检查对应参数。 |
| `PlanValidationError` | 收缩序文件无法加载，或文件、网络、数组检查失败；它继承 `ValueError`，可单独捕获以区分这类问题。 |
| `NotImplementedError` | 通过仅返回路径的 `ArcTNOptimizer.__call__` 传入 `memory_limit` 或请求切片；需要保留切片信息时，改用支持 `target_size` 的 `arctn_tree` 或 `arctn_contract`。 |
| `ImportError` | 缺少 Cotengra、opt_einsum 等可选依赖；按[安装说明](/docs/installation#python)补装所用功能的依赖。 |
| `OverflowError` | 整数参数超出当前平台可表示的范围；检查输入值和单位，并将其改为接口支持的范围。 |

Rust 高层接口通常用 `Result` 返回成功值或错误信息，可用 `?` 继续向调用方传递错误。各函数的返回类型见 [Rust API](/docs/rust-api)。
