@@ -773,20 +773,8 @@ std::tuple<Tensor&, Tensor&> channelwise_gated_delta_rule_out(
773773 const auto * initial_state_data = initial_state.const_data_ptr <float >();
774774 auto * state_data = final_state_out.mutable_data_ptr <float >();
775775 auto * output_data = out.mutable_data_ptr <float >();
776-
777- const int64_t scratch_numel = 2 * v_head_dim;
778- std::unique_ptr<float []> fallback_scratch;
779- float * scratch_data = nullptr ;
780- Result<void *> scratch = ctx.allocate_temp (
781- scratch_numel * sizeof (float ), /* alignment=*/ alignof (float ));
782- if (scratch.ok ()) {
783- scratch_data = reinterpret_cast <float *>(scratch.get ());
784- } else {
785- fallback_scratch = std::make_unique<float []>(scratch_numel);
786- scratch_data = fallback_scratch.get ();
787- }
788- float * v_pred = scratch_data;
789- float * delta = scratch_data + v_head_dim;
776+ std::vector<float > v_pred (v_head_dim);
777+ std::vector<float > delta (v_head_dim);
790778
791779 for (int64_t batch = 0 ; batch < batch_size; ++batch) {
792780 for (int64_t head = 0 ; head < num_heads; ++head) {
@@ -821,44 +809,47 @@ std::tuple<Tensor&, Tensor&> channelwise_gated_delta_rule_out(
821809 const float beta_t = beta_head[token];
822810 auto * output_t = output_head + token * value_seq_stride;
823811
824- // The recurrence needs only two passes over the K x V state S:
825- // pass 1 (read-only): v_pred = (Diag(decay) S)^T k
826- // pass 2 (read+write): S = Diag(decay) S + k (x) delta; o = S^T q
827- // Decay is folded into both passes (never materialized separately), and
828- // the rank-1 write and output readout share pass 2, so S is streamed
829- // twice per token instead of four times. Multiply groupings match the
830- // naive form, so results are identical up to floating-point contraction
831- // (e.g. compiler FMA).
832-
833- // Pass 1: predicted value off the decayed state (S left untouched).
834- std::fill (v_pred, v_pred + v_head_dim, 0 .0f );
812+ // 1. Per-key-channel decay: scale each state row by decay_t[k].
835813 for (int64_t k_idx = 0 ; k_idx < k_head_dim; ++k_idx) {
836814 const float decay_value = decay_t [k_idx];
815+ auto * state_row = state_head + k_idx * v_head_dim;
816+ for (int64_t v_idx = 0 ; v_idx < v_head_dim; ++v_idx) {
817+ state_row[v_idx] *= decay_value;
818+ }
819+ }
820+
821+ // 2. v_pred = S^T k: the memory's predicted value for this key, read
822+ // off the decayed state.
823+ std::fill (v_pred.begin (), v_pred.end (), 0 .0f );
824+ for (int64_t k_idx = 0 ; k_idx < k_head_dim; ++k_idx) {
837825 const float key_value = k_t [k_idx];
838826 const auto * state_row = state_head + k_idx * v_head_dim;
839827 for (int64_t v_idx = 0 ; v_idx < v_head_dim; ++v_idx) {
840- v_pred[v_idx] += ( state_row[v_idx] * decay_value) * key_value;
828+ v_pred[v_idx] += state_row[v_idx] * key_value;
841829 }
842830 }
843831
844- // delta = beta * (v - v_pred).
832+ // 3. delta = beta * (v - v_pred).
845833 for (int64_t v_idx = 0 ; v_idx < v_head_dim; ++v_idx) {
846834 delta[v_idx] = (v_t [v_idx] - v_pred[v_idx]) * beta_t ;
847835 }
848836
849- // Pass 2: apply decay + rank-1 write in place, and read back the
850- // updated row for the output projection in the same sweep.
851- std::fill (output_t , output_t + v_head_dim, 0 .0f );
837+ // 4. Rank-1 write: S += k (x) delta.
852838 for (int64_t k_idx = 0 ; k_idx < k_head_dim; ++k_idx) {
853- const float decay_value = decay_t [k_idx];
854839 const float key_value = k_t [k_idx];
855- const float query_value = q_t [k_idx];
856840 auto * state_row = state_head + k_idx * v_head_dim;
857841 for (int64_t v_idx = 0 ; v_idx < v_head_dim; ++v_idx) {
858- const float updated =
859- state_row[v_idx] * decay_value + key_value * delta[v_idx];
860- state_row[v_idx] = updated;
861- output_t [v_idx] += updated * query_value;
842+ state_row[v_idx] += key_value * delta[v_idx];
843+ }
844+ }
845+
846+ // 5. Output readout: o_t = S^T q.
847+ std::fill (output_t , output_t + v_head_dim, 0 .0f );
848+ for (int64_t k_idx = 0 ; k_idx < k_head_dim; ++k_idx) {
849+ const float query_value = q_t [k_idx];
850+ const auto * state_row = state_head + k_idx * v_head_dim;
851+ for (int64_t v_idx = 0 ; v_idx < v_head_dim; ++v_idx) {
852+ output_t [v_idx] += state_row[v_idx] * query_value;
862853 }
863854 }
864855 }
0 commit comments