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
// SPDX-License-Identifier: MIT OR Apache-2.0
//! PowIBackward — fused backward helper for the `Op::PowI(exp)`
//! primitive. Phase 7.6 step 4; migrated to a portable `PatternNode`
//! DATA recipe in Increment C carriers (A3 — the first recipe to carry a
//! `PowI`, via the A3 i32-exponent re-emit carrier, and the first whose
//! STRUCTURE is param-derived).
//!
//! Closed-form: `grad_x = exp · x^(exp-1) · upstream`. Two inputs
//! `[x, upstream]` + `exp: i32`. Replaces the autograd primitive
//! decomposition `PowI(n-1) → MulScalar(n as f64) → Mul` (3 nodes,
//! 3 dispatches, 2 intermediate buffers) with a single launch of
//! baracuda alpha.31's `unary_powi_backward_*` kernel.
//!
//! Like `ReduceMaxToBackward`, this is the backward of a primitive
//! (`Op::PowI`), not of a fused forward — autograd reaches it directly
//! from `Op::PowI`'s arm in `NodeHandle::backward` rather than through a
//! `BackwardKind::Fused` edge.
//!
//! Provides:
//! - [`entry`] — the metadata-side `FusedOpEntry`.
//! - `recipe` — the op's closed-form backward as portable `PatternNode` data
//! (Increment C carriers, A3). Unlike the shape-polymorphic slice-1/-2
//! recipes, this datum is a pure function of `exp` (see its doc for why).
//! - [`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 ;
/// Metadata-side registry entry for PowIBackward.
/// PowIBackward's primitive recipe as portable `PatternNode` data (Increment C
/// carriers, A3) — the first recipe to carry a `PowI` (via the A3 i32-exponent
/// re-emit carrier). The closed-form gradient `grad_x = exp · x^(exp-1) ·
/// upstream` is a DIRECT three-node mirror of the pre-migration imperative body
/// (`PowI(exp-1) → MulScalar(exp) → Mul`), so no D3 keepdim swap and no D4 pad
/// fire and the recipe base map is bit-identical to the legacy body (a
/// structure-preserving migration — it inherits the slice-1 numeric proof).
/// Binds: `0 = x`, `1 = upstream` — the order autograd's `Op::PowI` arm emits
/// (`inputs = [id, up_id]`).
///
/// Unlike the shape-polymorphic slice-1/-2 recipes (a process-lifetime
/// `OnceLock` datum + open scalar slots filled from the params projection),
/// PowIBackward's recipe is a pure function of `exp`: the exponent is not a
/// shape and not an f64 slot — it is the structural `i32` of `Op::PowI`, which
/// only the A3 carrier can transport and which no open-slot mechanism can fill.
/// So both param-derived constants are CONSTANT-FOLDED into the datum at build
/// time (the minimal "param-derived const in the recipe" the Increment C plan
/// authorizes for C-4-class values, rather than restructuring to dodge the
/// missing thread):
/// * the `PowI` carries `scalars = [(exp-1) as f64]` — the A3 carrier
/// reconstructs `Op::PowI(exp-1)` (exact for every i32 through f64);
/// * the `MulScalar` carries `scalars = [exp as f64]` — a BAKED constant.
///
/// Both are baked (non-empty `scalars`), so the datum has ZERO open scalar slots
/// and [`decompose`] fills the projection with the empty vec. Edge cases fall
/// out exactly as the imperative body: `exp==1` → `PowI(0)·1·upstream =
/// upstream`; `exp==0` → `MulScalar(0) = 0`.
///
/// Emitted form:
///
/// ```text
/// pow = PowI(exp-1)(x)
/// scaled = MulScalar(exp)(pow)
/// grad_x = Mul(scaled, upstream)
/// ```
/// Decompose to the closed-form gradient `grad_x = exp · x^(exp-1) · upstream`,
/// where `x` (input 0) is the forward `PowI` input and `upstream` (input 1) is
/// the upstream gradient — since A3 a re-emit of `recipe`'s param-derived data
/// through the [`decompose_via_recipe`] bridge (the fused node's two inputs
/// `[x, upstream]` are the binds; every scalar is baked, so the projection is
/// the empty vec; the resolving emit derives every interior shape/dtype). A
/// wrong params payload declines HERE (before building the recipe) and any later
/// failure (a resolution decline at these shapes, …) returns `id` (fixpoint,
/// surfaced gap, never a panic): exactly the G2 posture the imperative body had.
/// Matcher stub — backward-helper nodes originate from autograd emitting
/// `Op::Fused(POWI_BACKWARD, _)`, not from user-decomposed forms.