ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
graph_ir_ops.cpp
Go to the documentation of this file.
1// Built with -ffp-contract=off (CMakeLists.txt): every product below is rounded
2// before it is added (graph_ir_ops.h). The WebAssembly build has no fused
3// multiply-add instruction, so it cannot contract either.
4#include "graph_ir_ops.h"
5
6#include <algorithm>
7#include <cmath>
8#include <cstring>
9#include <limits>
10
11namespace ember_ir {
12namespace cpu {
13
14void sub(const float* a, const float* b, float* out, size_t n) {
15 for (size_t i = 0; i < n; ++i) out[i] = a[i] - b[i];
16}
17
18void relu(const float* x, float* out, size_t n) {
19 for (size_t i = 0; i < n; ++i) out[i] = std::max(0.0f, x[i]);
20}
21
22void sigmoid(const float* x, float* out, size_t n) {
23 for (size_t i = 0; i < n; ++i) out[i] = 1.0f / (1.0f + std::exp(-x[i]));
24}
25
26void affine(const float* x, float* out, size_t n, float scale, float shift) {
27 for (size_t i = 0; i < n; ++i) {
28 const float p = x[i] * scale;
29 out[i] = p + shift;
30 }
31}
32
33void mor_route(const float* l, int R, int& depth, float& gate) {
34 int j_max = 0;
35 for (int j = 1; j < R; ++j)
36 if (l[j] > l[j_max]) j_max = j; // strict: a tie keeps the lower index
37 float z = 0.0f;
38 for (int j = 0; j < R; ++j) z += std::exp(l[j] - l[j_max]);
39 depth = j_max + 1;
40 gate = std::exp(l[j_max] - l[j_max]) / z;
41}
42
43void mor_gate(const float* x_in, float* x, size_t n, float g) {
44 for (size_t i = 0; i < n; ++i) {
45 const float diff = x[i] - x_in[i];
46 const float p = g * diff;
47 x[i] = x_in[i] + p;
48 }
49}
50
51void scale(const float* x, const float* g, const float* b, float* out, int T, int w) {
52 for (int t = 0; t < T; ++t) {
53 const size_t r = static_cast<size_t>(t) * w;
54 if (b)
55 for (int j = 0; j < w; ++j) out[r + j] = x[r + j] * g[j] + b[j];
56 else
57 for (int j = 0; j < w; ++j) out[r + j] = x[r + j] * g[j];
58 }
59}
60
61void mul_col(const float* x, const float* c, float* out, int T, int w) {
62 for (int t = 0; t < T; ++t) {
63 const float ct = c[t];
64 const size_t r = static_cast<size_t>(t) * w;
65 for (int j = 0; j < w; ++j) out[r + j] = x[r + j] * ct;
66 }
67}
68
69void rms_row(const float* x, float* out, int T, int w, float eps) {
70 for (int t = 0; t < T; ++t) {
71 const float* r = x + static_cast<size_t>(t) * w;
72 float ss = 0.0f;
73 for (int j = 0; j < w; ++j) {
74 const float sq = r[j] * r[j];
75 ss += sq;
76 }
77 out[t] = std::sqrt(ss / static_cast<float>(w) + eps);
78 }
79}
80
81void depth_attend(const std::vector<const float*>& sources, const float* w, float* out, int T, int d, float eps) {
82 const size_t S = sources.size();
83 const size_t n = static_cast<size_t>(T) * d;
84 if (S == 1) { // softmax over one source is the identity mixture: s_0 bit for bit
85 std::memcpy(out, sources[0], n * sizeof(float));
86 return;
87 }
88 std::vector<float> a(S);
89 for (int t = 0; t < T; ++t) {
90 const size_t row = static_cast<size_t>(t) * d;
91 // Logits: w . rmsnorm(s_i) (ops::rmsnorm with unit weight, ops::mul_row,
92 // then the matmul with a ones column).
93 for (size_t i = 0; i < S; ++i) {
94 const float* s = sources[i] + row;
95 float ss = 0.0f;
96 for (int j = 0; j < d; ++j) {
97 const float sq = s[j] * s[j];
98 ss += sq;
99 }
100 ss /= static_cast<float>(d);
101 const float r = 1.0f / std::sqrt(ss + eps);
102 float l = 0.0f;
103 for (int j = 0; j < d; ++j) {
104 const float normed = s[j] * r;
105 const float p = normed * w[j];
106 l += p;
107 }
108 a[i] = l;
109 }
110 // Softmax over the sources (Matrix::apply_softmax): max-shifted, z in
111 // ascending i, a_i = e_i / z.
112 float mx = -std::numeric_limits<float>::infinity();
113 for (size_t i = 0; i < S; ++i) mx = std::max(mx, a[i]);
114 float z = 0.0f;
115 for (size_t i = 0; i < S; ++i) {
116 a[i] = std::exp(a[i] - mx);
117 z += a[i];
118 }
119 for (size_t i = 0; i < S; ++i) a[i] /= z;
120 // out = s_0 a_0 + s_1 a_1 + ... (ops::mul_col per source, ops::add in order).
121 float* o = out + row;
122 const float* s0 = sources[0] + row;
123 for (int j = 0; j < d; ++j) o[j] = s0[j] * a[0];
124 for (size_t i = 1; i < S; ++i) {
125 const float* s = sources[i] + row;
126 for (int j = 0; j < d; ++j) {
127 const float p = s[j] * a[i];
128 o[j] = o[j] + p;
129 }
130 }
131 }
132}
133
134void mla_attention(const float* qn, const float* qr, const float* kv, float* out, int T,
135 const std::vector<std::vector<int>>& rows, int H, int r, int dn, int dr, int dv, const float* Kb,
136 const float* Vb, float scale) {
137 const size_t w = static_cast<size_t>(r) + dr;
138#pragma omp parallel for schedule(dynamic)
139 for (int t = 0; t < T; ++t) {
140 const std::vector<int>& vis = rows[t];
141 std::vector<float> qa(static_cast<size_t>(r)), u(static_cast<size_t>(r)), s(vis.size());
142 for (int h = 0; h < H; ++h) {
143 const float* qnh = qn + (static_cast<size_t>(t) * H + h) * dn;
144 const float* qrh = qr + (static_cast<size_t>(t) * H + h) * dr;
145 const float* K = Kb + static_cast<size_t>(h) * r * dn;
146 const float* V = Vb + static_cast<size_t>(h) * dv * r;
147 for (int i = 0; i < r; ++i) { // qa = K[h] qn_h
148 float acc = 0.0f;
149 for (int k = 0; k < dn; ++k) {
150 const float p = K[static_cast<size_t>(i) * dn + k] * qnh[k];
151 acc += p;
152 }
153 qa[i] = acc;
154 }
155 float mx = -std::numeric_limits<float>::infinity();
156 for (size_t j = 0; j < vis.size(); ++j) {
157 const float* c = kv + static_cast<size_t>(vis[j]) * w;
158 float a = 0.0f, b = 0.0f;
159 for (int i = 0; i < r; ++i) {
160 const float p = qa[i] * c[i];
161 a += p;
162 }
163 for (int e = 0; e < dr; ++e) {
164 const float p = qrh[e] * c[r + e];
165 b += p;
166 }
167 const float sum = a + b;
168 s[j] = sum * scale;
169 mx = std::max(mx, s[j]);
170 }
171 float z = 0.0f;
172 for (size_t j = 0; j < vis.size(); ++j) {
173 s[j] = std::exp(s[j] - mx);
174 z += s[j];
175 }
176 std::fill(u.begin(), u.end(), 0.0f);
177 for (size_t j = 0; j < vis.size(); ++j) {
178 const float a = s[j] / z;
179 const float* c = kv + static_cast<size_t>(vis[j]) * w;
180 for (int i = 0; i < r; ++i) {
181 const float p = a * c[i];
182 u[i] += p;
183 }
184 }
185 float* o = out + (static_cast<size_t>(t) * H + h) * dv;
186 for (int k = 0; k < dv; ++k) { // out = V[h] u
187 float acc = 0.0f;
188 for (int i = 0; i < r; ++i) {
189 const float p = V[static_cast<size_t>(k) * r + i] * u[i];
190 acc += p;
191 }
192 o[k] = acc;
193 }
194 }
195 }
196}
197
198void split_groups(const float* x, int T, const std::vector<int>& sizes, int g, const std::vector<float*>& outs) {
199 int group = 0;
200 for (int s : sizes) group += s;
201 const size_t width = static_cast<size_t>(group) * g;
202 for (int t = 0; t < T; ++t) {
203 const float* row = x + static_cast<size_t>(t) * width;
204 for (int q = 0; q < g; ++q) {
205 int col = 0;
206 for (size_t k = 0; k < sizes.size(); ++k) {
207 float* dst = outs[k] + (static_cast<size_t>(t) * g + q) * sizes[k];
208 std::memcpy(dst, row + static_cast<size_t>(q) * group + col, static_cast<size_t>(sizes[k]) * sizeof(float));
209 col += sizes[k];
210 }
211 }
212 }
213}
214
215} // namespace cpu
216} // namespace ember_ir
const float * p[kIrDepthSourcesMax]
const float * col[expr::kMaxColumns]
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