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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: MIT OR Apache-2.0
//! ReduceMaxToBackward — fused backward helper for the `Op::ReduceMaxTo`
//! primitive. Phase 7.6 step 4; migrated to a portable `PatternNode` DATA
//! recipe in Increment C carriers (A2 — the first recipe to carry a
//! `MaskedFill`, via the A2 fill-Scalar re-emit carrier).
//!
//! Routes the upstream gradient to position(s) where x equals its
//! per-window max; tied maxes share equally (fair-share subgradient).
//! Two inputs `[x, upstream]`, parameterless.
//!
//! Unique among the four backward-helper entries: this one is the
//! backward of a **primitive** (`Op::ReduceMaxTo`), not of a fused
//! forward. There is no `BackwardKind::Fused(REDUCE_MAX_TO_BACKWARD)`
//! edge from any forward entry — autograd reaches this helper
//! directly from `Op::ReduceMaxTo`'s arm in `NodeHandle::backward`. The
//! registry entry still exists so the executor's dispatch stays
//! consistent (every fused op routes through `Op::Fused(id, _)`) and
//! so step 5 can drop the legacy `Op::ReduceMaxToBackward` variant.
//!
//! Provides:
//! - [`entry`] — the metadata-side `FusedOpEntry`.
//! - `recipe` — the fair-share max subgradient as portable, shape-polymorphic
//! `PatternNode` data (Increment C carriers, A2).
//! - [`decompose`] — re-emits `recipe` through the
//! [`crate::registry::decompose_via_recipe`] bridge.
//!
//! See `softmax_last_dim_backward.rs` for the shared architectural
//! rationale around backward-helper entries (identity + future `BackendImpl`
//! host; the matcher is always stubbed — autograd emits the fused node).
use crate;
use crate::;
use ;
use ShapeExpr;
use ;
use OnceLock;
/// Metadata-side registry entry for ReduceMaxToBackward.
/// ReduceMaxToBackward's primitive recipe as **portable data** (Increment C
/// carriers, A2). Shape-polymorphic: the reduce/count targets are `SameAs {
/// operand: 1 }` (upstream's shape = the forward reduce target) and the two
/// broadcasts target `SameAs { operand: 0 }` (x's full shape) — all D2, nothing
/// baked. Binds: `0 = x`, `1 = up` (the upstream gradient) — the order autograd
/// emits (`inputs = [x, up_id]`).
///
/// Routes `upstream` to the position(s) where `x` equals its per-window max;
/// tied maxes share equally. The U8 `mask = (x == max)` becomes a float mask via
/// `MaskedFill(value=1, zeros, mask)` — the A2 carrier (this avoids an integer→
/// float `Cast` the CPU cast matrix doesn't carry). The `MaskedFill` fill Scalar
/// is authored dtype-polymorphically (baked value `1.0`, no `cast_dtype`), so
/// emit re-resolves it to x's dtype — bit-identical to the imperative
/// `Scalar::one(dtype)`.
///
/// `mask_f` is a SLOT-FREE subtree (every scalar baked), so emit's identity-share
/// dedups its TWO use sites (`ReduceSumTo` and the final `Mul`) to ONE emitted
/// node — the same single-compute DAG the imperative builder had. This is a
/// DIRECT structural mirror: no D3 keepdim swap and no D4 pad fire (the reduces
/// are `ReduceMaxTo`/`ReduceSumTo` which carry the kept shape, and every
/// broadcast is equal-rank), so the recipe base map matches the legacy body
/// exactly.
///
/// Emitted form (`bcast_x(v) = BroadcastTo(SameAs 0)(v)`,
/// `to_up(tag, v) = tag(SameAs 1)(v)`):
///
/// ```text
/// y = to_up(ReduceMaxTo, x) # per-window max (upstream shape)
/// mask_u8 = Equal(x, bcast_x(y)) # U8: 1 where x == max
/// mask_f = MaskedFill[1.0](MulScalar[0](x), mask_u8) # float mask (x-dtype)
/// ties = to_up(ReduceSumTo, mask_f) # count per window
/// share = Div(up, ties) # fair share for ties
/// grad_x = Mul(mask_f, bcast_x(share))
/// ```
/// Per-entry scalar projection: ReduceMaxToBackward is parameterless (the fill
/// `1.0` and the `MulScalar(0.0)` are BAKED pattern constants, not open slots),
/// so the right payload projects to ZERO open-slot scalars and any other payload
/// is a typed decline (`None` ⇒ the bridge returns the node unchanged — G2).
/// Decompose to the fair-share max subgradient — since A2 a re-emit of `recipe`'s
/// data through the [`decompose_via_recipe`] bridge (the fused node's two inputs
/// `[x, up]` are the binds; the resolving emit derives every interior shape/dtype
/// and re-resolves the `MaskedFill` fill Scalar to x's dtype). Any failure —
/// wrong params payload, a resolution decline at these shapes (symbolic extent,
/// …) — returns `id` (fixpoint, surfaced gap, never a panic): exactly the G2
/// posture the imperative body had.