1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use SmallVec;
use crateRecordable;
/// One cotangent per operand, in the operation's positional order.
///
/// `None` marks an operand that is data rather than a differentiable
/// value (a broadcast's reference, a gather's selection), so
/// non-differentiability is structural in the rule's signature rather
/// than implicit by omission.
pub type Cotangents<Data> = ;
/// Which values a derivative rule reads when it runs: the per-operand
/// payloads and the node's own output.
///
/// Shape-only reads (a `reshape` backward reading its operand's shape,
/// a reduction broadcasting over a reference) need no read set,
/// because a freed slot holds a shape-correct placeholder. Reads
/// therefore names exactly the payloads whose *values* a rule reads,
/// and a training plan may free everything else once its forward
/// consumers finish. Each `reads` sits beside the `backward` it
/// describes; keeping the two in step is part of changing a rule.
pub
/// A derivative rule: the cotangent an operation hands back to each
/// operand, written against the recordable vocabulary
/// ([`Recordable`]) so one body serves two interpretations — the
/// engine computes it over payload buffers, and `differentiate`
/// records it through `Trace` handles.
///
/// It is implemented by each computed `Op` variant and dispatched
/// through the enum with a plain `match`, so implementations stay
/// statically sized and the trait never needs to be object safe. Leaves
/// and parameters do not implement it: they are supplied, not computed,
/// and the enum's dispatch handles them directly. The rules are pure:
/// operands arrive as a positional slice of references gathered by the
/// engine, results are returned rather than written, and no rule ever
/// sees the tape, a `ValueId`, or a run buffer. Gradient
/// accumulation — the multivariate chain rule — is the engine's job,
/// stated once in `Run::backward`. Each operation's `forward` is a
/// sibling inherent method over `Tensor<E>`: computing a payload is
/// engine business, not part of the rule, which is why the recording
/// interpretation never needs it.
pub
/// Splits the positional operand list of a unary operation.
///
/// It is generic so the same helper serves operand payloads and operand
/// shapes.
///
/// # Panics
/// Panics if `operands` does not hold exactly one entry; recording
/// checks every node against its `arity`, so a mismatch is an engine
/// bug.
pub
/// Splits the positional operand list of a binary operation.
///
/// It is generic so the same helper serves operand payloads and operand
/// shapes.
///
/// # Panics
/// Panics if `operands` does not hold exactly two entries; recording
/// checks every node against its `arity`, so a mismatch is an engine
/// bug.
pub