ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
graph_ir_ops.h
Go to the documentation of this file.
1#pragma once
2// CPU kernels for the IR ops of the gated and attention-residual families
3// (paperkiln ir.hpp at 176b425: flex highway/plain, attnres, srd's router):
4// sub, relu, sigmoid, affine, mul_col, rms_row and depth_attend. Each one is
5// the named paperkiln CPU op (src/ops.cpp, nn::AttnResStack) in its order of
6// operations, every product and sum rounded to f32 on its own: this file is
7// compiled with -ffp-contract=off (CMakeLists.txt), so no a * b + c becomes a
8// fused multiply-add. Kept free of ember's other headers so the op-level gate
9// can build it alone. Rows are [T, width] row-major.
10#include <cstddef>
11#include <vector>
12
13namespace ember_ir {
14namespace cpu {
15
16// a - b elementwise. EXACT.
17void sub(const float* a, const float* b, float* out, size_t n);
18// max(0, x) as std::max(0.0f, x) (ops::relu: -0 and NaN give +0). EXACT.
19void relu(const float* x, float* out, size_t n);
20// 1 / (1 + exp(-x)) (ops::sigmoid). ROUNDING.
21void sigmoid(const float* x, float* out, size_t n);
22// (x * scale) + shift: the product rounded, then the sum (ops::scale, then
23// ops::add_scalar). EXACT.
24void affine(const float* x, float* out, size_t n, float scale, float shift);
25// x [T, w] times the column c [T, 1]: out[t, j] = x[t, j] * c[t]. EXACT.
26void mul_col(const float* x, const float* c, float* out, int T, int w);
27// Per feature: out[t, j] = (x[t, j] * g[j]) + b[j], the product rounded, then
28// the sum; b null: the product alone (ops `scale`, a gain without a [d, d]
29// diagonal linear). EXACT.
30void scale(const float* x, const float* g, const float* b, float* out, int T, int w);
31// [T, w] -> [T, 1]: sqrt(ss / w + eps), ss = sum of x[t, j]^2 in ascending j
32// (ops::rms_row). ROUNDING.
33void rms_row(const float* x, float* out, int T, int w, float eps);
34// Attention over depth (nn::AttnResStack::depth_attend, block form): sources
35// s_0..s_{S-1}, each [T, d]; w the pseudo-query [d]. S = 1: out = s_0 and w
36// is not read (may be null). S >= 2, per row: r_i = 1 / sqrt(ss_i / d + eps)
37// (unit-weight rmsnorm), l_i = sum_j (s_i[j] * r_i) * w[j] in ascending j,
38// a = max-shifted softmax over i (z summed in ascending i, a_i = e_i / z),
39// out = s_0 a_0 + s_1 a_1 + ... in ascending i. ROUNDING.
40void depth_attend(const std::vector<const float*>& sources, const float* w, float* out, int T, int d, float eps);
41// Mixture-of-Recursions token-choice routing (ir.hpp depth_route) from one token's router logits
42// l [R]: depth = 1 + the index of the largest logit (ties to the lower index); gate = p[depth - 1],
43// p the max-shifted softmax (e_j = exp(l_j - max), z summed in ascending j, p_j = e_j / z).
44// EXACT given the logits for the depth; ROUNDING for the gate.
45void mor_route(const float* l, int R, int& depth, float& gate);
46// MoR gate "score" after a recursion step: x = x_in + g (x - x_in) per element, the difference
47// rounded, then the product, then the sum. EXACT.
48void mor_gate(const float* x_in, float* x, size_t n, float g);
49// Multi-head latent attention (IR_SPEC 5.20, revision 1.5; DeepSeek-V2's MLA in the absorbed form
50// llama.cpp converts to). qn [T, H * dn] and qr [T, H * dr] (head h at columns h * dn and h * dr);
51// kv the slot's rows [c | kr], r + dr floats each (row index as in `rows`); rows[t] the kv rows query t
52// sees, in ascending position (section 5.15's masking); Kb [H, r, dn] (ne {dn, r, H}) and Vb [H, dv, r]
53// (ne {r, dv, H}); out [T, H * dv]. Per head, in the reference's order: qa_i = sum_k Kb[h][i][k] qn_k
54// (ascending k); s_j = ((qa . c_j) + (qr_h . kr_j)) * scale, each dot product accumulated in ascending
55// order and rounded, then the sum, then the product; a_j = e_j / z with e_j = exp(s_j - max) and z summed
56// in ascending j; u_i = sum_j a_j c_j[i] (ascending j); out_o = sum_i Vb[h][o][i] u_i. ROUNDING.
57void mla_attention(const float* qn, const float* qr, const float* kv, float* out, int T,
58 const std::vector<std::vector<int>>& rows, int H, int r, int dn, int dr, int dv, const float* Kb,
59 const float* Vb, float scale);
60// split with groups g (IR_SPEC 5.5, revision 1.5): x [T, g * sum(sizes)] is g consecutive groups of
61// sum(sizes) columns, and output k is the k-th part of every group, the groups in order ([T, g * sizes[k]]).
62// g = 1 is the plain split. Copies. EXACT.
63void split_groups(const float* x, int T, const std::vector<int>& sizes, int g, const std::vector<float*>& outs);
64
65} // namespace cpu
66} // namespace ember_ir
int n
float c
std::vector< int > sizes
std::vector< E > a
node depth_attend
node rms_row
node relu
node affine
node sub
node out
node mla_attention
node mul_col
node scale
node sigmoid
size_t j
void split_groups(const float *x, int T, const std::vector< int > &sizes, int g, const std::vector< float * > &outs)
void mor_gate(const float *x_in, float *x, size_t n, float g)
void mor_route(const float *l, int R, int &depth, float &gate)
Definition api.h:20