• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / docc / 30465625756

29 Jul 2026 02:53PM UTC coverage: 64.817%. First build
30465625756

Pull #910

github

web-flow
Merge 318d9aa81 into 3eb01880e
Pull Request #910: [PyTorch] Add Support for Embedding Layers

517 of 591 new or added lines in 6 files covered. (87.48%)

44583 of 68783 relevant lines covered (64.82%)

714.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.02
/sdfg/src/data_flow/library_nodes/math/tensor/embedding_renorm_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/embedding_renorm_node.h"
2

3
#include <cmath>
4
#include <iomanip>
5
#include <sstream>
6

7
#include "sdfg/builder/structured_sdfg_builder.h"
8
#include "sdfg/data_flow/library_nodes/math/cmath/cmath_node.h"
9
#include "sdfg/structured_control_flow/for.h"
10
#include "sdfg/structured_control_flow/map.h"
11
#include "sdfg/types/scalar.h"
12

13
namespace sdfg {
14
namespace math {
15
namespace tensor {
16

17
EmbeddingRenormNode::EmbeddingRenormNode(
18
    size_t element_id,
19
    const DebugInfo& debug_info,
20
    const graph::Vertex vertex,
21
    data_flow::DataFlowGraph& parent,
22
    const std::vector<symbolic::Expression>& weight_shape,
23
    const std::vector<symbolic::Expression>& index_shape,
24
    double max_norm,
25
    double norm_type,
26
    const data_flow::ImplementationType& impl_type
27
)
28
    : TensorNode(element_id, debug_info, vertex, parent, LibraryNodeType_EmbeddingRenorm, {}, {"W", "I"}, impl_type),
10✔
29
      weight_shape_(weight_shape), index_shape_(index_shape), max_norm_(max_norm), norm_type_(norm_type) {}
10✔
30

31
const std::vector<symbolic::Expression>& EmbeddingRenormNode::weight_shape() const { return weight_shape_; }
2✔
32

33
const std::vector<symbolic::Expression>& EmbeddingRenormNode::index_shape() const { return index_shape_; }
2✔
34

35
double EmbeddingRenormNode::max_norm() const { return max_norm_; }
2✔
36

37
double EmbeddingRenormNode::norm_type() const { return norm_type_; }
2✔
38

NEW
39
bool EmbeddingRenormNode::supports_integer_types() const { return false; }
×
40

41
void EmbeddingRenormNode::validate(const Function& function) const {
11✔
42
    // The index tensor is integer-typed while the weight is floating-point, so the
43
    // uniform-primitive-type check in TensorNode::validate does not apply. Validate at
44
    // the MathNode level and add our own structural checks (mirrors EmbeddingNode).
45
    MathNode::validate(function);
11✔
46

47
    if (weight_shape_.size() != 2) {
11✔
48
        throw InvalidSDFGException(
1✔
49
            "EmbeddingRenormNode: weight must be 2-dimensional but got rank " + std::to_string(weight_shape_.size())
1✔
50
        );
1✔
51
    }
1✔
52
    if (index_shape_.empty()) {
10✔
NEW
53
        throw InvalidSDFGException("EmbeddingRenormNode: index_shape must not be empty");
×
NEW
54
    }
×
55
}
10✔
56

57
symbolic::SymbolSet EmbeddingRenormNode::symbols() const {
2✔
58
    symbolic::SymbolSet syms;
2✔
59
    for (const auto& dim : weight_shape_) {
4✔
60
        for (auto& atom : symbolic::atoms(dim)) {
4✔
61
            syms.insert(atom);
4✔
62
        }
4✔
63
    }
4✔
64
    for (const auto& dim : index_shape_) {
2✔
65
        for (auto& atom : symbolic::atoms(dim)) {
2✔
66
            syms.insert(atom);
2✔
67
        }
2✔
68
    }
2✔
69
    return syms;
2✔
70
}
2✔
71

72
void EmbeddingRenormNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
1✔
73
    for (auto& dim : weight_shape_) {
2✔
74
        dim = symbolic::subs(dim, old_expression, new_expression);
2✔
75
    }
2✔
76
    for (auto& dim : index_shape_) {
1✔
77
        dim = symbolic::subs(dim, old_expression, new_expression);
1✔
78
    }
1✔
79
}
1✔
80

NEW
81
void EmbeddingRenormNode::replace(const symbolic::ExpressionMapping& replacements) {
×
NEW
82
    for (auto& dim : weight_shape_) {
×
NEW
83
        dim = symbolic::subs(dim, replacements);
×
NEW
84
    }
×
NEW
85
    for (auto& dim : index_shape_) {
×
NEW
86
        dim = symbolic::subs(dim, replacements);
×
NEW
87
    }
×
NEW
88
}
×
89

90
passes::LibNodeExpander::ExpandOutcome EmbeddingRenormNode::
91
    expand(passes::LibNodeExpander::ExpandContext& context, structured_control_flow::Block& block) {
6✔
92
    auto& dataflow = this->get_parent();
6✔
93

94
    if (dataflow.in_degree(*this) != 2 || dataflow.out_degree(*this) != 0) {
6✔
NEW
95
        return context.unable();
×
NEW
96
    }
×
97

98
    auto edges = dataflow.in_edges_by_connector(*this);
6✔
99
    auto& weight_edge = *edges.at(W_INPUT_IDX);
6✔
100
    auto& index_edge = *edges.at(INDEX_IDX);
6✔
101

102
    using Use = passes::LibNodeExpander::InputUse;
6✔
103
    std::vector<Use> uses = {Use::IndirectReadWrite, Use::IndirectRead};
6✔
104
    auto standalone = context.replacement_requires_access_nodes(uses);
6✔
105
    if (!standalone) {
6✔
NEW
106
        return context.unable();
×
NEW
107
    }
×
108

109
    auto& builder = standalone->builder();
6✔
110
    const types::PrimitiveType prim = weight_edge.base_type().primitive_type();
6✔
111
    const types::Scalar scalar_type(prim);
6✔
112
    const symbolic::Expression embedding_dim = weight_shape_[1];
6✔
113
    const bool p_is_inf = std::isinf(norm_type_);
6✔
114
    const bool p_is_one = (norm_type_ == 1.0);
6✔
115
    const bool p_is_two = (norm_type_ == 2.0);
6✔
116

117
    auto fmt = [](double v) {
10✔
118
        std::ostringstream ss;
10✔
119
        ss << std::setprecision(17) << v;
10✔
120
        return ss.str();
10✔
121
    };
10✔
122
    auto fresh_scalar = [&](const std::string& prefix) {
38✔
123
        std::string name = builder.find_new_name(prefix);
38✔
124
        builder.add_container(name, scalar_type);
38✔
125
        return name;
38✔
126
    };
38✔
127

128
    // Sequential loops over the index tensor. Sequential ordering makes the in-place
129
    // renormalization idempotent for duplicate indices (see class docs).
130
    size_t nB = index_shape_.size();
6✔
131
    symbolic::MultiExpression loop_vars;
6✔
132
    structured_control_flow::Sequence* inner_scope = nullptr;
6✔
133
    for (size_t i = 0; i < nB; ++i) {
13✔
134
        std::string var_name = builder.find_new_name("_i" + std::to_string(i));
7✔
135
        builder.add_container(var_name, types::Scalar(types::PrimitiveType::Int64));
7✔
136
        auto sym_var = symbolic::symbol(var_name);
7✔
137
        auto condition = symbolic::Lt(sym_var, index_shape_[i]);
7✔
138
        auto init = symbolic::zero();
7✔
139
        auto update = symbolic::add(sym_var, symbolic::one());
7✔
140
        if (i == 0) {
7✔
141
            auto& loop = standalone->replace_with_structured_loop(
6✔
142
                passes::LibNodeExpander::AccessNodeExpand::LoopType::Map,
6✔
143
                sym_var,
6✔
144
                condition,
6✔
145
                init,
6✔
146
                update,
6✔
147
                structured_control_flow::ScheduleType_Sequential::create()
6✔
148
            );
6✔
149
            inner_scope = &loop.root();
6✔
150
        } else {
6✔
151
            auto& loop = builder.add_map(
1✔
152
                *inner_scope,
1✔
153
                sym_var,
1✔
154
                condition,
1✔
155
                init,
1✔
156
                update,
1✔
157
                structured_control_flow::ScheduleType_Sequential::create(),
1✔
158
                this->debug_info()
1✔
159
            );
1✔
160
            inner_scope = &loop.root();
1✔
161
        }
1✔
162
        loop_vars.push_back(sym_var);
7✔
163
    }
7✔
164

165
    // Load the data-dependent row coordinate idx = I[loop_vars] into a scalar symbol.
166
    std::string idx_name = builder.find_new_name("_idx");
6✔
167
    builder.add_container(idx_name, types::Scalar(types::PrimitiveType::Int64));
6✔
168
    auto idx_sym = symbolic::symbol(idx_name);
6✔
169
    {
6✔
170
        auto& load_block = builder.add_block(*inner_scope, {}, this->debug_info());
6✔
171
        auto& idx_in_acc = standalone->add_indirect_read_access(load_block, INDEX_IDX);
6✔
172
        auto& idx_out_acc = builder.add_access(load_block, idx_name, this->debug_info());
6✔
173
        auto& load_tasklet =
6✔
174
            builder.add_tasklet(load_block, data_flow::TaskletCode::assign, "_out", {"_in"}, this->debug_info());
6✔
175
        builder.add_computational_memlet(
6✔
176
            load_block, idx_in_acc, load_tasklet, "_in", loop_vars, index_edge.base_type(), this->debug_info()
6✔
177
        );
6✔
178
        builder.add_computational_memlet(
6✔
179
            load_block,
6✔
180
            load_tasklet,
6✔
181
            "_out",
6✔
182
            idx_out_acc,
6✔
183
            {},
6✔
184
            types::Scalar(types::PrimitiveType::Int64),
6✔
185
            this->debug_info()
6✔
186
        );
6✔
187
    }
6✔
188

189
    // Accumulator for the row norm.
190
    std::string acc_name = fresh_scalar("_norm_acc");
6✔
191
    {
6✔
192
        auto& init_block = builder.add_block(*inner_scope, {}, this->debug_info());
6✔
193
        auto& zero_const = builder.add_constant(init_block, "0.0", scalar_type, this->debug_info());
6✔
194
        auto& acc_out = builder.add_access(init_block, acc_name, this->debug_info());
6✔
195
        auto& init_tasklet =
6✔
196
            builder.add_tasklet(init_block, data_flow::TaskletCode::assign, "_out", {"_in"}, this->debug_info());
6✔
197
        builder
6✔
198
            .add_computational_memlet(init_block, zero_const, init_tasklet, "_in", {}, scalar_type, this->debug_info());
6✔
199
        builder.add_computational_memlet(init_block, init_tasklet, "_out", acc_out, {}, scalar_type, this->debug_info());
6✔
200
    }
6✔
201

202
    // Sequential accumulation loop over the embedding dimension.
203
    {
6✔
204
        std::string j_name = builder.find_new_name("_j");
6✔
205
        builder.add_container(j_name, types::Scalar(types::PrimitiveType::Int64));
6✔
206
        auto j_sym = symbolic::symbol(j_name);
6✔
207
        auto& acc_loop = builder.add_for(
6✔
208
            *inner_scope,
6✔
209
            j_sym,
6✔
210
            symbolic::Lt(j_sym, embedding_dim),
6✔
211
            symbolic::zero(),
6✔
212
            symbolic::add(j_sym, symbolic::one()),
6✔
213
            this->debug_info()
6✔
214
        );
6✔
215
        auto& body = acc_loop.root();
6✔
216
        symbolic::MultiExpression w_subset = {idx_sym, j_sym};
6✔
217

218
        // aw = |W[idx, j]|
219
        auto& b = builder.add_block(body, {}, this->debug_info());
6✔
220
        auto& w_in = standalone->add_indirect_read_access(b, W_INPUT_IDX);
6✔
221
        std::string aw_name = fresh_scalar("_aw");
6✔
222
        auto& aw_acc = builder.add_access(b, aw_name, this->debug_info());
6✔
223
        auto& abs_op =
6✔
224
            builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::fabs, prim);
6✔
225
        builder.add_computational_memlet(b, w_in, abs_op, "_in1", w_subset, weight_edge.base_type(), this->debug_info());
6✔
226
        builder.add_computational_memlet(b, abs_op, "_out", aw_acc, {}, scalar_type, this->debug_info());
6✔
227

228
        // term = aw^p (or aw for p==1). For p==inf we take the running maximum instead.
229
        std::string term_name;
6✔
230
        if (p_is_one || p_is_inf) {
6✔
231
            term_name = aw_name;
2✔
232
        } else if (p_is_two) {
4✔
233
            term_name = fresh_scalar("_term");
2✔
234
            auto& term_acc = builder.add_access(b, term_name, this->debug_info());
2✔
235
            auto& sq_op = builder.add_tasklet(b, data_flow::fp_mul, "_out", {"_in1", "_in2"}, this->debug_info());
2✔
236
            builder.add_computational_memlet(b, aw_acc, sq_op, "_in1", {}, scalar_type, this->debug_info());
2✔
237
            builder.add_computational_memlet(b, aw_acc, sq_op, "_in2", {}, scalar_type, this->debug_info());
2✔
238
            builder.add_computational_memlet(b, sq_op, "_out", term_acc, {}, scalar_type, this->debug_info());
2✔
239
        } else {
2✔
240
            term_name = fresh_scalar("_term");
2✔
241
            auto& term_acc = builder.add_access(b, term_name, this->debug_info());
2✔
242
            auto& p_const = builder.add_constant(b, fmt(norm_type_), scalar_type, this->debug_info());
2✔
243
            auto& pow_op =
2✔
244
                builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::pow, prim);
2✔
245
            builder.add_computational_memlet(b, aw_acc, pow_op, "_in1", {}, scalar_type, this->debug_info());
2✔
246
            builder.add_computational_memlet(b, p_const, pow_op, "_in2", {}, scalar_type, this->debug_info());
2✔
247
            builder.add_computational_memlet(b, pow_op, "_out", term_acc, {}, scalar_type, this->debug_info());
2✔
248
        }
2✔
249

250
        // acc = acc <op> term, where op is fmax for the infinity norm and + otherwise.
251
        auto& acc_read = builder.add_access(b, acc_name, this->debug_info());
6✔
252
        // For p==1 and p==inf the term IS the abs value: read it from the exact access
253
        // node abs_op wrote to (aw_acc) so the read-after-write dependency is established
254
        // within the block. For the other norms the term was written to a fresh container
255
        // (term_name) by the sq/pow op above, which reads aw_acc and is therefore ordered.
256
        auto& term_read = (p_is_one || p_is_inf) ? aw_acc : builder.add_access(b, term_name, this->debug_info());
6✔
257
        auto& acc_write = builder.add_access(b, acc_name, this->debug_info());
6✔
258
        if (p_is_inf) {
6✔
259
            auto& max_op =
1✔
260
                builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::fmax, prim);
1✔
261
            builder.add_computational_memlet(b, acc_read, max_op, "_in1", {}, scalar_type, this->debug_info());
1✔
262
            builder.add_computational_memlet(b, term_read, max_op, "_in2", {}, scalar_type, this->debug_info());
1✔
263
            builder.add_computational_memlet(b, max_op, "_out", acc_write, {}, scalar_type, this->debug_info());
1✔
264
        } else {
5✔
265
            auto& add_op = builder.add_tasklet(b, data_flow::fp_add, "_out", {"_in1", "_in2"}, this->debug_info());
5✔
266
            builder.add_computational_memlet(b, acc_read, add_op, "_in1", {}, scalar_type, this->debug_info());
5✔
267
            builder.add_computational_memlet(b, term_read, add_op, "_in2", {}, scalar_type, this->debug_info());
5✔
268
            builder.add_computational_memlet(b, add_op, "_out", acc_write, {}, scalar_type, this->debug_info());
5✔
269
        }
5✔
270
    }
6✔
271

272
    // norm = acc^(1/p): identity for p in {1, inf}, sqrt for p==2, pow(acc, 1/p) otherwise.
273
    std::string norm_name;
6✔
274
    if (p_is_one || p_is_inf) {
6✔
275
        norm_name = acc_name;
2✔
276
    } else {
4✔
277
        norm_name = fresh_scalar("_norm");
4✔
278
        auto& b = builder.add_block(*inner_scope, {}, this->debug_info());
4✔
279
        auto& acc_acc = builder.add_access(b, acc_name, this->debug_info());
4✔
280
        auto& norm_acc = builder.add_access(b, norm_name, this->debug_info());
4✔
281
        if (p_is_two) {
4✔
282
            auto& sqrt_op =
2✔
283
                builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::sqrt, prim);
2✔
284
            builder.add_computational_memlet(b, acc_acc, sqrt_op, "_in1", {}, scalar_type, this->debug_info());
2✔
285
            builder.add_computational_memlet(b, sqrt_op, "_out", norm_acc, {}, scalar_type, this->debug_info());
2✔
286
        } else {
2✔
287
            auto& inv_p_const = builder.add_constant(b, fmt(1.0 / norm_type_), scalar_type, this->debug_info());
2✔
288
            auto& pow_op =
2✔
289
                builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::pow, prim);
2✔
290
            builder.add_computational_memlet(b, acc_acc, pow_op, "_in1", {}, scalar_type, this->debug_info());
2✔
291
            builder.add_computational_memlet(b, inv_p_const, pow_op, "_in2", {}, scalar_type, this->debug_info());
2✔
292
            builder.add_computational_memlet(b, pow_op, "_out", norm_acc, {}, scalar_type, this->debug_info());
2✔
293
        }
2✔
294
    }
4✔
295

296
    // scale = min(1, max_norm / (norm + 1e-7)). The clamp leaves within-norm rows unchanged.
297
    std::string scale_name = fresh_scalar("_scale");
6✔
298
    {
6✔
299
        auto& b = builder.add_block(*inner_scope, {}, this->debug_info());
6✔
300

301
        // denom = norm + 1e-7
302
        std::string denom_name = fresh_scalar("_denom");
6✔
303
        auto& norm_read = builder.add_access(b, norm_name, this->debug_info());
6✔
304
        auto& eps_const = builder.add_constant(b, "1e-7", scalar_type, this->debug_info());
6✔
305
        auto& denom_acc = builder.add_access(b, denom_name, this->debug_info());
6✔
306
        auto& denom_op = builder.add_tasklet(b, data_flow::fp_add, "_out", {"_in1", "_in2"}, this->debug_info());
6✔
307
        builder.add_computational_memlet(b, norm_read, denom_op, "_in1", {}, scalar_type, this->debug_info());
6✔
308
        builder.add_computational_memlet(b, eps_const, denom_op, "_in2", {}, scalar_type, this->debug_info());
6✔
309
        builder.add_computational_memlet(b, denom_op, "_out", denom_acc, {}, scalar_type, this->debug_info());
6✔
310

311
        // ratio = max_norm / denom
312
        std::string ratio_name = fresh_scalar("_ratio");
6✔
313
        auto& maxnorm_const = builder.add_constant(b, fmt(max_norm_), scalar_type, this->debug_info());
6✔
314
        auto& ratio_acc = builder.add_access(b, ratio_name, this->debug_info());
6✔
315
        auto& div_op = builder.add_tasklet(b, data_flow::fp_div, "_out", {"_in1", "_in2"}, this->debug_info());
6✔
316
        builder.add_computational_memlet(b, maxnorm_const, div_op, "_in1", {}, scalar_type, this->debug_info());
6✔
317
        builder.add_computational_memlet(b, denom_acc, div_op, "_in2", {}, scalar_type, this->debug_info());
6✔
318
        builder.add_computational_memlet(b, div_op, "_out", ratio_acc, {}, scalar_type, this->debug_info());
6✔
319

320
        // scale = min(1, ratio)
321
        auto& one_const = builder.add_constant(b, "1.0", scalar_type, this->debug_info());
6✔
322
        auto& scale_acc = builder.add_access(b, scale_name, this->debug_info());
6✔
323
        auto& min_op =
6✔
324
            builder.add_library_node<cmath::CMathNode>(b, this->debug_info(), cmath::CMathFunction::fmin, prim);
6✔
325
        builder.add_computational_memlet(b, one_const, min_op, "_in1", {}, scalar_type, this->debug_info());
6✔
326
        builder.add_computational_memlet(b, ratio_acc, min_op, "_in2", {}, scalar_type, this->debug_info());
6✔
327
        builder.add_computational_memlet(b, min_op, "_out", scale_acc, {}, scalar_type, this->debug_info());
6✔
328
    }
6✔
329

330
    // In-place scaling loop: W[idx, j] *= scale.
331
    {
6✔
332
        std::string j_name = builder.find_new_name("_j_scale");
6✔
333
        builder.add_container(j_name, types::Scalar(types::PrimitiveType::Int64));
6✔
334
        auto j_sym = symbolic::symbol(j_name);
6✔
335
        auto& scale_loop = builder.add_map(
6✔
336
            *inner_scope,
6✔
337
            j_sym,
6✔
338
            symbolic::Lt(j_sym, embedding_dim),
6✔
339
            symbolic::zero(),
6✔
340
            symbolic::add(j_sym, symbolic::one()),
6✔
341
            structured_control_flow::ScheduleType_Sequential::create(),
6✔
342
            this->debug_info()
6✔
343
        );
6✔
344
        auto& body = scale_loop.root();
6✔
345
        symbolic::MultiExpression w_subset = {idx_sym, j_sym};
6✔
346

347
        auto& b = builder.add_block(body, {}, this->debug_info());
6✔
348
        auto& w_in = standalone->add_indirect_read_access(b, W_INPUT_IDX);
6✔
349
        auto& scale_read = builder.add_access(b, scale_name, this->debug_info());
6✔
350
        auto& w_out = standalone->add_indirect_write_access(b, W_INPUT_IDX);
6✔
351
        auto& mul_op = builder.add_tasklet(b, data_flow::fp_mul, "_out", {"_in1", "_in2"}, this->debug_info());
6✔
352
        builder.add_computational_memlet(b, w_in, mul_op, "_in1", w_subset, weight_edge.base_type(), this->debug_info());
6✔
353
        builder.add_computational_memlet(b, scale_read, mul_op, "_in2", {}, scalar_type, this->debug_info());
6✔
354
        builder
6✔
355
            .add_computational_memlet(b, mul_op, "_out", w_out, w_subset, weight_edge.base_type(), this->debug_info());
6✔
356
    }
6✔
357

358
    return standalone->successfully_expanded();
6✔
359
}
6✔
360

361
std::unique_ptr<data_flow::DataFlowNode> EmbeddingRenormNode::
NEW
362
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
NEW
363
    return std::unique_ptr<data_flow::DataFlowNode>(new EmbeddingRenormNode(
×
NEW
364
        element_id, this->debug_info(), vertex, parent, weight_shape_, index_shape_, max_norm_, norm_type_
×
NEW
365
    ));
×
NEW
366
}
×
367

NEW
368
data_flow::PointerAccessType EmbeddingRenormNode::pointer_access_type(int input_idx) const {
×
NEW
369
    if (input_idx == W_INPUT_IDX) {
×
NEW
370
        return data_flow::PointerAccessMeta::
×
NEW
371
            create_generic(data_flow::MemoryAccessPatternType(), data_flow::MemoryAccessPatternType(), true);
×
NEW
372
    } else if (input_idx == INDEX_IDX) {
×
NEW
373
        return data_flow::PointerAccessMeta::create_read_only(symbolic::__nullptr__(), true);
×
NEW
374
    } else {
×
NEW
375
        return TensorNode::pointer_access_type(input_idx);
×
NEW
376
    }
×
NEW
377
}
×
378

379
nlohmann::json EmbeddingRenormNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
1✔
380
    const EmbeddingRenormNode& renorm_node = static_cast<const EmbeddingRenormNode&>(library_node);
1✔
381
    nlohmann::json j;
1✔
382

383
    j["code"] = renorm_node.code().value();
1✔
384

385
    serializer::JSONSerializer serializer;
1✔
386
    j["weight_shape"] = nlohmann::json::array();
1✔
387
    for (auto& dim : renorm_node.weight_shape()) {
2✔
388
        j["weight_shape"].push_back(serializer.expression(dim));
2✔
389
    }
2✔
390
    j["index_shape"] = nlohmann::json::array();
1✔
391
    for (auto& dim : renorm_node.index_shape()) {
1✔
392
        j["index_shape"].push_back(serializer.expression(dim));
1✔
393
    }
1✔
394
    j["max_norm"] = renorm_node.max_norm();
1✔
395
    j["norm_type"] = renorm_node.norm_type();
1✔
396

397
    return j;
1✔
398
}
1✔
399

400
data_flow::LibraryNode& EmbeddingRenormNodeSerializer::deserialize(
401
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
402
) {
1✔
403
    assert(j.contains("element_id"));
1✔
404
    assert(j.contains("code"));
1✔
405
    assert(j.contains("debug_info"));
1✔
406
    assert(j.contains("weight_shape"));
1✔
407
    assert(j.contains("index_shape"));
1✔
408
    assert(j.contains("max_norm"));
1✔
409
    assert(j.contains("norm_type"));
1✔
410

411
    std::vector<symbolic::Expression> weight_shape;
1✔
412
    for (const auto& dim : j["weight_shape"]) {
2✔
413
        weight_shape.push_back(symbolic::parse(dim.get<std::string>()));
2✔
414
    }
2✔
415
    std::vector<symbolic::Expression> index_shape;
1✔
416
    for (const auto& dim : j["index_shape"]) {
1✔
417
        index_shape.push_back(symbolic::parse(dim.get<std::string>()));
1✔
418
    }
1✔
419
    double max_norm = j["max_norm"].get<double>();
1✔
420
    double norm_type = j["norm_type"].get<double>();
1✔
421

422
    sdfg::serializer::JSONSerializer serializer;
1✔
423
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
1✔
424

425
    return builder
1✔
426
        .add_library_node<EmbeddingRenormNode>(parent, debug_info, weight_shape, index_shape, max_norm, norm_type);
1✔
427
}
1✔
428

429
} // namespace tensor
430
} // namespace math
431
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc