ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
graph_ir_recurrence.h
Go to the documentation of this file.
1#pragma once
2// The IR's `recurrence` op (paperkiln spec/IR_SPEC.md section 6.10, the recurrence profile, revision 1.1) on
3// the CPU, written for ember from the specification text (no code taken from paperkiln): a recurrent op
4// defined in the document by an index map, declarations of its inputs and weights, and bodies in a closed
5// grammar (expr's, section 5.19, plus indexed reads with integer offsets, bare index values and an ordered
6// sum) for the initial state, intermediate lets, the per-token update and the readout.
7//
8// Per token (section 6.10.3): every input reads row t; the lets are evaluated in order; every state tensor's
9// update is evaluated from the state before the token, and the new state replaces the old only when all of
10// it is formed; the readout is evaluated with the state after the update. A body is evaluated at every point
11// of its assignment's index space, row-major. A read outside a tensor's shape is +0; sum(j, e) is
12// ((+0 + e|0) + e|1) + ..., in ascending j; every primitive is f32 and rounded after it (this file is
13// compiled with -ffp-contract=off, CMakeLists.txt). The functions are expr's: where(c, a, b) is a for c > 0
14// (a NaN condition selects b), max(a, b) a for a >= b, min(a, b) a for a <= b, relu(e) e for e > 0 else +0,
15// sigmoid(e) 1 / (1 + exp(-e)), ^ is powf; numbers are strtof's nearest float, pi the nearest float to pi.
16//
17// Class (section 6.10.4): EXACT when no body uses exp, log, tanh, erf, sigmoid or ^; ROUNDING otherwise.
18//
19// compile() checks rules 31 and 32 of section 10.1 and names them: "IR_SPEC 6.10 (rule N): ...", naming the
20// body (for example update[1]) and its column for a body that does not parse. The slot (rule 33), the weights'
21// element counts (rule 34, container) and the inputs' widths (rule 34, when the rows arrive) are checked by
22// the callers through slot_errors(), weights() and input_widths().
23#include <cstddef>
24#include <memory>
25#include <string>
26#include <vector>
27
28#include <nlohmann/json.hpp>
29
30namespace ember_ir {
31namespace rec {
32
33struct Program;
34
36 public:
37 // Throws std::runtime_error "IR_SPEC 6.10 (rule 31|32): ..." for a node the grammar or the read rules refuse.
38 static Recurrence compile(const nlohmann::json& node);
39
40 struct StateTensor {
41 std::string name;
42 std::vector<int64_t> shape;
43 size_t numel = 0;
44 bool init_body = false; // the node's init assigns it
45 };
46 struct WeightDecl {
47 std::string decl; // the declared name (local to the node)
48 std::string tensor; // the tensor name ({i} unresolved)
49 size_t numel = 0;
50 };
51 const std::vector<StateTensor>& state() const; // the update's tensors, in update order
52 const std::vector<WeightDecl>& weights() const; // in the weights map's order
53 const std::vector<size_t>& input_widths() const; // per value of `in`
54 size_t out_width() const;
55 bool exact() const; // no exp, log, tanh, erf, sigmoid or ^ in any body
56
57 // Rule 33 against the node's slot declaration (block.state entry): empty when the slot's tensors are the
58 // update's (names, shapes, init "body" exactly for those init assigns), else the message.
59 std::string slot_errors(const nlohmann::json& slot) const;
60
61 // The initial state: state[s] (state()[s].numel floats) set to its init body over weights (weights()[w]
62 // .numel floats each), or +0.
63 void init(const std::vector<const float*>& weights, const std::vector<float*>& state) const;
64
65 // One token: in[v] is input v's row (input_widths()[v] floats), state[s] is read and replaced, out gets
66 // out_width() floats. Not thread-safe (scratch is per object).
67 void step(const std::vector<const float*>& in, const std::vector<const float*>& weights,
68 const std::vector<float*>& state, float* out) const;
69
74
75 private:
77};
78
79} // namespace rec
80} // namespace ember_ir
static Recurrence compile(const nlohmann::json &node)
void step(const std::vector< const float * > &in, const std::vector< const float * > &weights, const std::vector< float * > &state, float *out) const
void init(const std::vector< const float * > &weights, const std::vector< float * > &state) const
std::string slot_errors(const nlohmann::json &slot) const
std::unique_ptr< Program > p_
const std::vector< StateTensor > & state() const
const std::vector< size_t > & input_widths() const
Recurrence(Recurrence &&) noexcept
const std::vector< WeightDecl > & weights() const
const float * in[expr::Expr::kMaxInputs]
node out
size_t j
Definition api.h:20