Blog Edition 文本附件：保留技术内容，仅适配路径/链接与运行命令。

# HIPS/autograd 源码口试

请不要边看答案边回答。仓库基线为 commit `f53a21734fdfae636f448744d9097d8d35a643a0`。

[TEACHING SIMPLIFICATION]
以下不是 HIPS/autograd verbatim source。这是保留的课程口试题，使用真实公开 API，但不是某个核心函数或现有实验文件的节选。省略测试夹具，只保留需解释的 loss 程序。

```python
import autograd.numpy as np
from autograd import grad

def loss(x, y):
    a = np.sin(x)
    b = x * y
    c = a + b
    return np.sum(c ** 2)

g = grad(loss, 0)
```

## 口头解释

1. `grad(loss,0)` 创建时做什么，真正 trace 何时开始？
2. `unary_to_nary` 如何只替换参数 `x`？
3. root node 与 start Box 分别是什么？
4. `sin` 如何通过 `autograd.numpy` 成为 primitive operation？
5. `multiply` 如何确定 boxed argument positions 与 parents？
6. `a`, `b`, `c` 对应的 Python objects 和 nodes 有何区别？
7. 当前实现中 computational graph 存在哪里？
8. 每个 `VJPNode` 保存什么，不保存什么？
9. forward evaluation 的哪些 state 会被 VJP closure 捕获？
10. `sum(c**2)` 的 output adjoint 为什么是 scalar 1？
11. `backward_pass` 使用怎样的遍历顺序？
12. power 与 sum 的 VJP 各如何传播 shape 和 cotangent？
13. add node 如何把 upstream 分给 `a` 与 `b`？
14. `x` 从 `sin(x)` 和 `x*y` 收到的两条 contribution 在哪里汇合？
15. `add_outgrads` 的 `(gradient,mutable_flag)` 表示什么？
16. `unbroadcast` 可能在哪些 operation rules 中介入？
17. 最终 gradient 的解析表达式是什么？
18. 若 `loss` 返回 `c**2` 而不 `sum`，`grad` 会怎样？
19. `grad(grad(...))` 时 TraceStack 如何避免层次混乱？
20. 如何用 `check_grads` 独立验证该函数？

## 手绘任务

画出两张图：

1. forward computational graph，标出 `sin`, multiply, add, power, sum 与 parents。
2. backward flow，标出 output seed、每个 local VJP、`x` 的两条 pathway 与 accumulation。

## 源码定位任务

不使用全文总结，只通过 `rg` 找出并说明：

```text
differential_operators.py :: grad
wrap_util.py :: unary_to_nary
core.py :: make_vjp / VJPNode / backward_pass / add_outgrads
tracer.py :: trace / primitive / Box / find_top_boxed_args
util.py :: toposort
numpy_boxes.py :: ArrayBox
numpy_vjps.py :: sin / multiply / add / power / sum rules
test_util.py :: check_grads
```

验收标准：回答必须始终区分数学导数、AD 抽象算法与 Python 实现对象；能指出具体 symbol，而不是只复述“Autograd 会自动反向传播”。
