18 throw std::runtime_error(
"IR_SPEC 6.10 (rule " + std::to_string(
rule) +
"): " +
msg);
21const std::set<std::string>
kFunctions = {
"exp",
"log",
"sqrt",
"tanh",
"erf",
"abs",
"sigmoid",
"relu",
22 "max",
"min",
"where"};
23const std::set<std::string>
kRowReductions = {
"rowsum",
"rowmean",
"rowmax"};
26 if (
s.empty() || !(std::isalpha(
static_cast<unsigned char>(
s[0])) ||
s[0] ==
'_'))
return false;
38enum class Tok { Ident, Number, LParen, RParen, LBracket, RBracket, Comma, Plus, Minus, Star, Slash, Caret, Eq, End };
47std::vector<Token>
lex(
const std::string&
s,
const std::string&
where) {
48 std::vector<Token>
out;
50 while (
i <
s.size()) {
52 if (
c ==
' ' ||
c ==
'\t') {
58 if (std::isalpha(
static_cast<unsigned char>(
c)) ||
c ==
'_') {
60 while (
j <
s.size() && (std::isalnum(
static_cast<unsigned char>(
s[
j])) ||
s[
j] ==
'_')) ++
j;
62 t.text =
s.substr(
i,
j -
i);
64 }
else if (std::isdigit(
static_cast<unsigned char>(
c)) ||
c ==
'.') {
66 while (
j <
s.size() && std::isdigit(
static_cast<unsigned char>(
s[
j]))) ++
j, ++
mant;
68 if (
j <
s.size() &&
s[
j] ==
'.') {
71 while (
j <
s.size() && std::isdigit(
static_cast<unsigned char>(
s[
j]))) ++
j, ++
mant;
73 if (
mant == 0) fail(31,
where +
" does not parse: a malformed number at column " + std::to_string(
t.column));
74 if (
j <
s.size() && (
s[
j] ==
'e' ||
s[
j] ==
'E')) {
77 if (
k <
s.size() && (
s[
k] ==
'+' ||
s[
k] ==
'-')) ++
k;
79 while (
k <
s.size() && std::isdigit(
static_cast<unsigned char>(
s[
k]))) ++
k;
81 fail(31,
where +
" does not parse: a malformed number (an exponent without digits) at column " +
82 std::to_string(
t.column));
86 t.text =
s.substr(
i,
j -
i);
90 static const std::map<char, Tok>
kPunct = {{
'(', Tok::LParen}, {
')', Tok::RParen}, {
'[', Tok::LBracket},
91 {
']', Tok::RBracket}, {
',', Tok::Comma}, {
'+', Tok::Plus},
92 {
'-', Tok::Minus}, {
'*', Tok::Star}, {
'/', Tok::Slash},
93 {
'^', Tok::Caret}, {
'=', Tok::Eq}};
96 fail(31,
where +
" does not parse: unexpected character '" + std::string(1,
c) +
"' at column " +
97 std::to_string(
t.column));
99 t.text = std::string(1,
c);
102 out.push_back(std::move(
t));
106 end.column =
s.size() + 1;
114 Num, Idx, Read, Neg, Add, Sub, Mul, Div, Pow,
115 Exp, Log, Sqrt, Tanh, Erf, Abs, Sigmoid, Relu, Max, Min, Where, Sum
131enum class TKind { Input, Weight, State, Let, Out };
135 TKind
kind = TKind::Input;
170 mutable std::vector<int>
iv;
171 mutable std::vector<const float*>
data;
174 float eval(
const E&
e)
const;
175 void assign(
const Assign&
a,
float*
out)
const;
188 BodyParser(Program&
p,
const std::vector<Token>&
toks,
size_t pos,
const std::string&
where,
189 const ReadRules&
rules,
const std::vector<int>&
scope)
201 const Token&
peek()
const {
return t_[
i_]; }
202 const Token& next() {
return t_[
i_++]; }
204 fail(31, where_ +
" does not parse: " +
what +
" at column " + std::to_string(
peek().
column));
211 if (
depth > 256)
syntax(
"nesting deeper than the limit");
218 const K
k = next().kind == Tok::Plus ? K::Add : K::Sub;
222 n.a.push_back(std::move(left));
223 n.a.push_back(std::move(right));
232 const K
k = next().kind == Tok::Star ? K::Mul : K::Div;
236 n.a.push_back(std::move(left));
237 n.a.push_back(std::move(right));
261 n.a.push_back(std::move(
base));
269 const auto it =
p_.index_of.find(
t.text);
270 if (
it ==
p_.index_of.end())
271 fail(32, where_ +
": index '" +
t.text +
"' at column " + std::to_string(
t.column) +
" is not declared");
277 const Token&
t =
peek();
278 if (
t.kind == Tok::Number) {
279 if (!
t.integer)
syntax(
"an index offset or position must be an integer");
281 r.off = std::strtol(
t.text.c_str(),
nullptr, 10);
284 if (
t.kind != Tok::Ident)
syntax(
"expected an index");
287 if (!in_scope_[
r.index])
288 fail(32, where_ +
": index '" +
t.text +
"' at column " + std::to_string(
t.column) +
" is not in scope");
290 const bool minus = next().kind == Tok::Minus;
291 const Token&
n =
peek();
292 if (
n.kind != Tok::Number || !
n.integer)
syntax(
"expected an integer offset");
294 const long v = std::strtol(
n.text.c_str(),
nullptr, 10);
302 const Token&
t =
peek();
303 if (
t.kind == Tok::Number) {
307 n.v = std::strtof(
t.text.c_str(),
nullptr);
308 if (!std::isfinite(
n.v))
syntax(
"the number " +
t.text +
" is not finite in f32");
311 if (
t.kind == Tok::LParen) {
314 expect(Tok::RParen,
"')'");
317 if (
t.kind != Tok::Ident)
syntax(
"expected an operand");
318 const std::string
name =
t.text;
319 const size_t col =
t.column;
324 n.v =
static_cast<float>(3.14159265358979323846);
328 fail(31, where_ +
" uses " +
name +
" at column " + std::to_string(
col) +
329 " (the row reductions are not part of a recurrence body; sum replaces them)");
331 expect(Tok::LParen,
"'(' after sum");
333 if (
it.kind != Tok::Ident)
syntax(
"expected sum's index");
337 fail(32, where_ +
": sum over index '" +
it.text +
"' at column " + std::to_string(
it.column) +
338 ", which is already in scope");
339 expect(Tok::Comma,
"',' after sum's index");
343 expect(Tok::RParen,
"')'");
347 n.a.push_back(std::move(
arg));
351 static const std::map<std::string, std::pair<K, size_t>>
kFn = {
352 {
"exp", {K::Exp, 1}}, {
"log", {K::Log, 1}}, {
"sqrt", {K::Sqrt, 1}}, {
"tanh", {K::Tanh, 1}},
353 {
"erf", {K::Erf, 1}}, {
"abs", {K::Abs, 1}}, {
"sigmoid", {K::Sigmoid, 1}},
354 {
"relu", {K::Relu, 1}}, {
"max", {K::Max, 2}}, {
"min", {K::Min, 2}}, {
"where", {K::Where, 3}}};
356 if (
f ==
kFn.end()) fail(31, where_ +
": unknown function '" +
name +
"' at column " + std::to_string(
col));
359 n.k =
f->second.first;
360 if (
n.k == K::Exp ||
n.k == K::Log ||
n.k == K::Tanh ||
n.k == K::Erf ||
n.k == K::Sigmoid)
p_.exact =
false;
366 expect(Tok::RParen,
"')'");
367 if (
n.a.size() !=
f->second.second)
368 fail(31, where_ +
": " +
name +
" at column " + std::to_string(
col) +
" takes " +
369 std::to_string(
f->second.second) +
" argument(s), not " + std::to_string(
n.a.size()));
375 const auto it =
p_.tensor_of.find(
name);
376 if (
it ==
p_.tensor_of.end())
377 fail(32, where_ +
" reads '" +
name +
"' at column " + std::to_string(
col) +
", which is not declared");
378 const Tensor&
tn =
p_.tensors[
it->second];
379 const bool ok = (
tn.kind == TKind::Input &&
rules_.inputs) || (
tn.kind == TKind::Weight &&
rules_.weights) ||
380 (
tn.kind == TKind::State &&
rules_.state) || (
tn.kind == TKind::Let &&
tn.pos <
rules_.lets);
382 fail(32, where_ +
" reads '" +
name +
"' at column " + std::to_string(
col) +
", which its section cannot read");
386 n.ix.push_back(
ix());
389 n.ix.push_back(
ix());
391 expect(Tok::RBracket,
"']'");
392 if (
n.ix.size() !=
tn.sizes.size())
393 fail(32, where_ +
" reads '" +
name +
"' at column " + std::to_string(
col) +
" with " +
394 std::to_string(
n.ix.size()) +
" index(es); it has " + std::to_string(
tn.sizes.size()) +
399 const auto ii =
p_.index_of.find(
name);
400 if (
ii !=
p_.index_of.end()) {
401 if (!in_scope_[
ii->second])
402 fail(32, where_ +
": index '" +
name +
"' at column " + std::to_string(
col) +
" is not in scope");
408 if (
p_.tensor_of.count(
name))
409 fail(32, where_ +
" reads '" +
name +
"' at column " + std::to_string(
col) +
410 " without indices; it has " + std::to_string(
p_.tensors[
p_.tensor_of.at(
name)].sizes.size()) +
412 fail(32, where_ +
": '" +
name +
"' at column " + std::to_string(
col) +
" is not declared");
416 const std::vector<Token>&
t_;
424std::pair<std::string, std::vector<int>>
head(
const Program&
p,
const std::vector<Token>&
toks,
size_t&
pos,
433 std::vector<int> ids;
437 const auto it =
p.index_of.find(
t.text);
438 if (
it ==
p.index_of.end())
439 fail(32,
where +
": index '" +
t.text +
"' at column " + std::to_string(
t.column) +
" is not declared");
440 if (
distinct && std::find(ids.begin(), ids.end(),
it->second) != ids.end())
441 fail(32,
where +
": the assignment repeats index '" +
t.text +
"' at column " + std::to_string(
t.column));
442 ids.push_back(
it->second);
455 std::vector<std::string>
out;
456 if (!
node.contains(key) ||
node.at(key).is_null()) {
457 if (
required) fail(31, std::string(
"'") + key +
"' is missing");
461 if (!
v.is_array()) fail(31, std::string(
"'") + key +
"' is not a list of strings");
462 for (
const auto&
x :
v) {
463 if (!
x.is_string()) fail(31, std::string(
"'") + key +
"' holds a value that is not a string");
464 out.push_back(
x.get<std::string>());
466 if (
nonempty &&
out.empty()) fail(31, std::string(
"'") + key +
"' is empty");
474 case K::Num:
return e.v;
475 case K::Idx:
return static_cast<float>(
iv[
e.id]);
479 for (
size_t d = 0; d <
e.ix.size(); ++d) {
480 const long c = (
e.ix[d].index >= 0 ?
iv[
e.ix[d].index] : 0) +
e.ix[d].off;
482 flat +=
static_cast<size_t>(
c) *
t.strides[d];
486 case K::Neg:
return -
eval(
e.a[0]);
487 case K::Add: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x +
y; }
488 case K::Sub: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x -
y; }
489 case K::Mul: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x *
y; }
490 case K::Div: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x /
y; }
491 case K::Pow: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return std::pow(
x,
y); }
492 case K::Exp:
return std::exp(
eval(
e.a[0]));
493 case K::Log:
return std::log(
eval(
e.a[0]));
494 case K::Sqrt:
return std::sqrt(
eval(
e.a[0]));
495 case K::Tanh:
return std::tanh(
eval(
e.a[0]));
496 case K::Erf:
return std::erf(
eval(
e.a[0]));
497 case K::Abs:
return std::fabs(
eval(
e.a[0]));
499 const float x =
eval(
e.a[0]);
500 const float ex = std::exp(-
x);
501 const float den = 1.0f +
ex;
504 case K::Relu: {
const float x =
eval(
e.a[0]);
return x > 0.0f ?
x : 0.0f; }
505 case K::Max: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x >=
y ?
x :
y; }
506 case K::Min: {
const float x =
eval(
e.a[0]);
const float y =
eval(
e.a[1]);
return x <=
y ?
x :
y; }
507 case K::Where:
return eval(
e.a[0]) > 0.0f ?
eval(
e.a[1]) :
eval(
e.a[2]);
511 for (
int j = 0;
j <
n; ++
j) {
513 const float x =
eval(
e.a[0]);
524 const size_t k =
a.lhs.size();
525 for (
size_t d = 0; d <
k; ++d)
iv[
a.lhs[d]] = 0;
526 const size_t n =
tensors[
a.tensor].numel;
529 for (
size_t d =
k; d-- > 0;) {
548 auto P = std::make_unique<Program>();
550 std::set<std::string>
names;
554 if (!
names.insert(
name).second) fail(31,
"the name '" +
name +
"' is declared twice");
557 if (!
node.contains(
"index") || !
node.at(
"index").is_object() ||
node.at(
"index").empty())
558 fail(31,
"'index' is not a map of names to positive integers");
559 for (
const auto&
kv :
node.at(
"index").items()) {
560 if (!
kv.value().is_number_integer() ||
kv.value().get<
int64_t>() <= 0)
561 fail(31,
"index '" +
kv.key() +
"' has size " +
kv.value().dump() +
", not a positive integer");
562 if (
kv.value().get<
int64_t>() > (1 << 24))
563 throw std::runtime_error(
"IR recurrence: index '" +
kv.key() +
"' of size " +
kv.value().dump() +
564 " is larger than ember's interpreter takes (2^24)");
566 p.index_of[
kv.key()] =
static_cast<int>(
p.index_names.size());
567 p.index_names.push_back(
kv.key());
568 p.index_sizes.push_back(
static_cast<int>(
kv.value().get<
int64_t>()));
570 p.iv.assign(
p.index_names.size(), 0);
571 auto add_tensor = [&](
const std::string&
name, TKind
kind,
const std::vector<int>& ids, std::vector<int>&
list) {
575 for (
int id : ids)
t.sizes.push_back(
p.index_sizes[
id]);
576 t.strides.assign(
t.sizes.size(), 1);
577 for (
size_t d =
t.sizes.size(); d-- > 1;)
t.strides[d - 1] =
t.strides[d] *
static_cast<size_t>(
t.sizes[d]);
578 for (
int s :
t.sizes) {
579 t.numel *=
static_cast<size_t>(
s);
580 if (
t.numel > (
size_t(1) << 28))
581 throw std::runtime_error(
"IR recurrence: tensor '" +
name +
"' holds more than 2^28 elements, more than ember's interpreter takes");
583 t.pos =
static_cast<int>(
list.size());
584 const int id =
static_cast<int>(
p.tensors.size());
586 p.tensors.push_back(std::move(
t));
592 if (
node.contains(
"in"))
n_in =
node.at(
"in").is_array() ?
node.at(
"in").size() : 1;
595 fail(31,
"'inputs' declares " + std::to_string(
inputs.size()) +
" value(s) for the " + std::to_string(
n_in) +
597 for (
size_t v = 0;
v <
inputs.size(); ++
v) {
598 const std::string
where =
"inputs[" + std::to_string(
v) +
"]";
606 p.in_widths.push_back(
p.tensors[
id].numel);
609 if (
node.contains(
"weights") && !
node.at(
"weights").is_null()) {
610 if (!
node.at(
"weights").is_object()) fail(31,
"'weights' is not a map of declarations to tensor names");
611 for (
const auto&
kv :
node.at(
"weights").items()) {
612 if (!
kv.value().is_string() ||
kv.value().get<std::string>().
empty())
613 fail(31,
"weight '" +
kv.key() +
"' does not name a tensor");
614 const std::string
where =
"weights '" +
kv.key() +
"'";
628 for (
size_t k = 0;
k <
src.size(); ++
k) {
630 a.where = std::string(
section) +
"[" + std::to_string(
k) +
"]";
634 if (
a.toks[
pos].kind != Tok::Eq)
635 fail(31,
a.where +
" does not parse: expected '=' at column " + std::to_string(
a.toks[
pos].column));
636 a.body_tok =
pos + 1;
639 dst.push_back(std::move(
a));
646 if (!
node.contains(
"readout") || !
node.at(
"readout").is_string()) fail(31,
"'readout' is not one assignment");
651 std::vector<Assign>
ro;
653 ro[0].where =
"readout";
654 p.readout = std::move(
ro[0]);
656 for (
size_t k = 0;
k <
p.update.size(); ++
k) {
660 for (
size_t k = 0;
k <
p.let.size(); ++
k) {
666 std::vector<int>
outs;
668 p.out =
p.readout.tensor;
669 p.out_width =
p.tensors[
p.out].numel;
671 p.init_of_state.assign(
p.states.size(), -1);
672 for (
size_t k = 0;
k <
p.init.size(); ++
k) {
674 if (
it ==
p.tensor_of.end() ||
p.tensors[
it->second].kind != TKind::State)
675 fail(33,
p.init[
k].where +
" assigns '" +
init_names[
k] +
"', which is not a state tensor (an update's)");
676 const Tensor&
st =
p.tensors[
it->second];
677 if (
p.init_of_state[
st.pos] >= 0) fail(33,
p.init[
k].where +
" assigns '" +
init_names[
k] +
"' a second time");
678 std::vector<int>
sizes;
679 for (
int id :
p.init[
k].lhs)
sizes.push_back(
p.index_sizes[
id]);
680 if (
sizes !=
st.sizes) fail(33,
p.init[
k].where +
" assigns '" +
init_names[
k] +
"' with other sizes than its update");
681 p.init_of_state[
st.pos] =
static_cast<int>(
k);
682 p.init[
k].tensor =
it->second;
685 auto body = [&](Assign&
a,
const ReadRules&
r) {
a.body = BodyParser(
p,
a.toks,
a.body_tok,
a.where,
r,
a.lhs).parse(); };
686 for (
auto&
a :
p.init)
body(
a, ReadRules{
false,
true,
false, 0});
687 for (
size_t k = 0;
k <
p.let.size(); ++
k)
body(
p.let[
k], ReadRules{
true,
true,
true,
static_cast<int>(
k)});
689 body(
p.readout, ReadRules{true, true, true, static_cast<int>(p.let.size())});
690 for (
auto*
v : {&
p.init, &
p.let, &
p.update})
692 std::vector<Token>().swap(
p.readout.toks);
694 for (
size_t s = 0;
s <
p.states.size(); ++
s) {
695 const Tensor&
t =
p.tensors[
p.states[
s]];
700 st.init_body =
p.init_of_state[
s] >= 0;
701 p.state_info.push_back(std::move(
st));
702 p.new_state.emplace_back(
t.numel);
705 p.data.assign(
p.tensors.size(),
nullptr);
711std::string Recurrence::slot_errors(
const json& slot)
const {
712 const std::string
name = slot.contains(
"name") ? slot.at(
"name").dump() :
"?";
713 if (slot.value(
"op",
json()) !=
"recurrence")
return "the slot " +
name +
" of a recurrence is not of op recurrence";
714 if (!slot.contains(
"tensors") || !slot.at(
"tensors").is_array())
return "the slot " +
name +
" declares no tensors";
715 std::map<std::string, const json*> declared;
716 for (
const auto&
t : slot.at(
"tensors")) {
717 if (!
t.is_object() || !
t.contains(
"name") || !
t.at(
"name").is_string())
718 return "the slot " +
name +
" holds a tensor without a name";
719 if (!declared.emplace(
t.at(
"name").get<std::string>(), &
t).second)
720 return "the slot " +
name +
" declares tensor '" +
t.at(
"name").get<std::string>() +
"' twice";
722 for (
const auto&
st :
p_->state_info) {
723 const auto it = declared.find(
st.name);
724 if (
it == declared.end())
return "the slot " +
name +
" has no tensor '" +
st.name +
"' (the update assigns it)";
728 return "the slot " +
name +
"'s tensor '" +
st.name +
"' has shape " +
t.value(
"shape",
json()).dump() +
730 const json init =
t.value(
"init",
json());
731 if (
st.init_body ? init !=
"body" : init !=
json(0))
732 return "the slot " +
name +
"'s tensor '" +
st.name +
"' has init " + init.dump() +
", not " +
733 (
st.init_body ?
"\"body\" (the node's init assigns it)" :
"0 (the node's init does not assign it)");
735 if (declared.size() !=
p_->state_info.size()) {
736 for (
const auto&
kv : declared) {
739 if (!
found)
return "the slot " +
name +
"'s tensor '" +
kv.first +
"' is not one the update assigns";
745void Recurrence::init(
const std::vector<const float*>&
weights,
const std::vector<float*>&
state)
const {
748 for (
size_t s = 0;
s <
p.states.size(); ++
s) {
749 const int k =
p.init_of_state[
s];
757void Recurrence::step(
const std::vector<const float*>&
in,
const std::vector<const float*>&
weights,
758 const std::vector<float*>&
state,
float*
out)
const {
760 for (
size_t v = 0;
v <
p.inputs.size(); ++
v)
p.
data[
p.inputs[
v]] =
in[
v];
761 for (
size_t w = 0;
w <
p.weights.size(); ++
w)
p.data[
p.weights[
w]] =
weights[
w];
762 for (
size_t s = 0;
s <
p.states.size(); ++
s)
p.data[
p.states[
s]] =
state[
s];
763 for (
size_t l = 0;
l <
p.lets.size(); ++
l) {
764 p.assign(
p.let[
l],
p.let_buf[
l].data());
765 p.data[
p.lets[
l]] =
p.let_buf[
l].data();
768 for (
size_t s = 0;
s <
p.states.size(); ++
s)
p.assign(
p.update[
s],
p.new_state[
s].data());
769 for (
size_t s = 0;
s <
p.states.size(); ++
s) std::copy(
p.new_state[
s].begin(),
p.new_state[
s].end(),
state[
s]);
770 p.assign(
p.readout,
out);
static Recurrence compile(const nlohmann::json &node)
std::unique_ptr< Program > p_
const std::vector< size_t > & input_widths() const
const std::vector< WeightDecl > & weights() const
const float * in[expr::Expr::kMaxInputs]
const float * p[kIrDepthSourcesMax]
const float * col[expr::kMaxColumns]
enum ember_ir::expr::@33::Value::Shape shape
const std::vector< Token > & t_
std::vector< Token > toks
std::vector< size_t > strides
std::vector< bool > in_scope_
std::vector< std::vector< float > > let_buf
std::vector< int > inputs
std::vector< Recurrence::StateTensor > state_info
std::map< std::string, int > index_of
std::vector< int > weights
std::vector< Assign > let
std::map< std::string, int > tensor_of
std::vector< Assign > init
std::vector< size_t > in_widths
std::vector< std::string > index_names
std::vector< Recurrence::WeightDecl > weight_info
std::vector< std::vector< float > > new_state
std::vector< int > states
std::vector< int > index_sizes
std::vector< int > init_of_state
float eval(const E &e) const
std::vector< const float * > data
std::vector< Assign > update
std::vector< Tensor > tensors
void assign(const Assign &a, float *out) const