Blog Edition 文本附件:保留技术内容,仅适配路径/链接与运行命令。 # HIPS/autograd Repository Map ## 问题 1. 当前 clone 的仓库版本和结构是什么? 2. 用户 API、tracing、reverse mode、forward mode、NumPy 接管和梯度规则分别位于哪里? 3. 如果只有一天,最值得读哪 10 个 symbol? ## 调查基线 - 调查日期:2026-09-05 - remote:`https://github.com/HIPS/autograd.git` - branch:`master` - commit:`f53a21734fdfae636f448744d9097d8d35a643a0` - package version:`1.9.1` - `pyproject.toml`:要求 Python `>=3.10`,依赖 `numpy<3`,`scipy` 是 optional dependency。 ## 顶层目录 | 路径 | 当前作用 | | --- | --- | | `autograd/` | 可导入的核心 Python package。 | | `docs/` | 当前包含 `tutorial.md` 与 `updateguide.md`。 | | `examples/` | 神经网络、ODE、变分推断、可视化和 tracing 等端到端示例。 | | `tests/` | 核心、NumPy、SciPy、容器、复数、高阶 operator、ufunc dispatch 等测试。 | | `benchmarks/` | 性能基准。 | | `.github/` | CI 与仓库协作配置。 | | `pyproject.toml` | 构建、包元数据、依赖、pytest、coverage 与 lint 配置。 | | `README.md` | 项目能力、最小用法、文档及示例入口。 | ## `autograd` package 地图 | 文件或目录 | 主要职责 | 代表 symbols | | --- | --- | --- | | `autograd/__init__.py` | 重导出用户 API。 | `grad`, `jacobian`, `hessian`, `make_vjp`, `make_jvp` | | `autograd/differential_operators.py` | 构建在底层 VJP/JVP 之上的用户求导 operators。 | `grad`(约 24 行), `jacobian`, `hessian`, `value_and_grad` | | `autograd/wrap_util.py` | 把底层一元求导 operator 适配到多参数 Python 函数。 | `unary_to_nary`, `wrap_nary_f` | | `autograd/tracer.py` | 动态 trace、primitive 包装、Box 与 trace stack。 | `trace`(约 14 行), `primitive`(约 44 行), `TraceStack`, `Box`(约 157 行) | | `autograd/core.py` | reverse/forward mode 的节点、传播、规则 registry 和 vector space 基础。 | `make_vjp`(约 11 行), `backward_pass`(约 26 行), `VJPNode`, `defvjp`, `make_jvp`, `JVPNode`, `defjvp` | | `autograd/util.py` | 小型通用工具;图遍历在这里。 | `toposort`(约 21 行) | | `autograd/extend.py` | 面向扩展作者重导出 primitive、VJP/JVP、Box、vspace API。 | `primitive`, `defvjp`, `defjvp`, `VJPNode`, `JVPNode` | | `autograd/builtins.py` | 对 tuple/list/dict 等 Python 容器进行可微包装。 | `SequenceBox`, `DictBox` 等 | | `autograd/test_util.py` | 梯度检查工具。 | `check_grads`, numerical JVP/VJP helpers | | `autograd/numpy/numpy_wrapper.py` | 遍历 NumPy namespace,把可调用对象包装为 primitives,并处理部分特殊函数。 | `wrap_namespace`, `array`, `stack` | | `autograd/numpy/numpy_boxes.py` | NumPy 数组/标量对应的 `ArrayBox` 及运算符转发。 | `ArrayBox`; `__mul__` 转到 `anp.multiply` | | `autograd/numpy/numpy_vjps.py` | NumPy reverse-mode 局部规则与 broadcasting 回收。 | `defvjp(...)`, `unbroadcast`, `grad_np_sum` | | `autograd/numpy/numpy_jvps.py` | NumPy forward-mode 局部规则。 | `defjvp(...)`, `def_linear(...)` | | `autograd/numpy/numpy_vspaces.py` | NumPy 值的 vector-space 行为。 | array/scalar vspace types | | `autograd/numpy/linalg.py`, `fft.py` | 线性代数和 FFT 的包装及求导规则。 | `solve`, `svd`, FFT rules 等 | | `autograd/scipy/` | SciPy linalg、signal、integrate、special、stats 集成。 | 各 SciPy primitive 的 VJP/JVP | | `autograd/misc/` | flatten、优化器、fixed point、辅助 tracer 等工具。 | `flatten`, optimizers 等 | ## 核心能力在哪里 | 问题 | 当前源码位置 | | --- | --- | | 用户 API | `autograd/__init__.py` 重导出;主体在 `differential_operators.py`。 | | tracing | `tracer.py :: trace`, `primitive`, `TraceStack`, `Box`, `new_box`。 | | reverse mode | `core.py :: make_vjp`, `VJPNode`, `backward_pass`, `defvjp`, `add_outgrads`。 | | forward mode | `core.py :: make_jvp`, `JVPNode`, `defjvp`。 | | NumPy wrapper | `numpy/numpy_wrapper.py` 与 `numpy/numpy_boxes.py :: ArrayBox`。 | | NumPy reverse rules | `numpy/numpy_vjps.py`。例如当前 `log` 和 `sin` 的 VJP 注册在约 166、170 行。 | | NumPy forward rules | `numpy/numpy_jvps.py`。例如当前 `log` 和 `sin` 的 JVP 注册在约 118、122 行。 | | 图的反向遍历 | `util.py :: toposort`,由 `core.py :: backward_pass` 使用。 | | tests | 顶层 `tests/`,不是 package 内隐藏测试。 | | examples | 顶层 `examples/`;`print_trace.py`、`dot_graph.py` 与 tracing 学习尤其相关。 | ## 一天内最值得看的 10 个 symbol | 顺序 | Symbol | 文件 | 为什么 | | --- | --- | --- | --- | | 1 | `grad` | `differential_operators.py` | 最常用用户入口;显示 scalar output 最终如何调用 VJP。 | | 2 | `make_vjp` | `core.py` | reverse-mode trace 的入口,并返回 VJP closure 与 forward value。 | | 3 | `trace` | `tracer.py` | 把输入装箱并执行用户函数,是 define-by-run 的起点。 | | 4 | `primitive` | `tracer.py` | 决定普通调用与带 Box 调用的不同路径,并创建节点。 | | 5 | `Box` | `tracer.py` | 把运行值、trace id、node 关联起来的基础包装。 | | 6 | `ArrayBox` | `numpy/numpy_boxes.py` | 把 Python/NumPy 运算符转发到 Autograd 包装的 NumPy primitive。 | | 7 | `VJPNode` | `core.py` | 保存 parents 与当前 primitive 的局部 VJP closure。 | | 8 | `backward_pass` | `core.py` | 沿反向拓扑顺序传播并累加 outgrads。 | | 9 | `defvjp` | `core.py` | 把 primitive 的局部 reverse rule 注册到 `primitive_vjps`。 | | 10 | `toposort` | `util.py` | 产生从输出向根节点的合法遍历顺序。 | 若还有时间,再读 `core.py :: make_jvp` / `JVPNode`,它们是理解 forward mode 的最短入口。 ## Mental model 仓库不是先把整个 Python 程序解析成一个 symbolic AST。当前 happy path 是:求导 operator 安排一次 trace,`Box` 随真实 Python 执行流穿过被包装的 primitives,各 primitive 调用产生带 parent 引用的 node;reverse mode 随后从输出节点反向消费这些链接。这个 mental model 只用于仓库导览,具体对象状态和逐行调用链留到后续课程验证。 ## 容易误解的地方 - `autograd/` 既是顶层仓库中的目录名,也是 Python package 名;引用时需区分 repository root 与 package root。 - 计算图不是单独的 `Graph` 类。当前实现通过 node 的 `parents` 关系形成图。 - `grad` 不是底层求导引擎;它是构建在 `_make_vjp` 上的 convenience operator。 - `numpy_vjps.py` 和 `numpy_jvps.py` 是两套方向不同的局部规则,不能混为一谈。 - 行号只对应当前 commit;长期引用优先使用“文件 + symbol”。 ## 学完后应该能回答的问题 1. 用户调用 `grad` 后,最先值得沿哪些文件继续追踪? 2. 为什么 NumPy wrapper、Box、node 和 VJP registry 是不同职责? 3. reverse mode 的图遍历和局部梯度规则分别位于哪里? 4. 为什么当前仓库地图支持“沿调用链读源码”,而不是逐目录通读?