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

daisytuner / docc / 28793626242

06 Jul 2026 01:04PM UTC coverage: 62.962% (+0.2%) from 62.801%
28793626242

Pull #740

github

web-flow
Merge b7e03d389 into 23e67a4ab
Pull Request #740: Expand pass

594 of 962 new or added lines in 31 files covered. (61.75%)

36 existing lines in 10 files now uncovered.

40557 of 64415 relevant lines covered (62.96%)

972.09 hits per line

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

46.67
/sdfg/src/data_flow/library_nodes/math/blas/batched_gemm_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/blas/batched_gemm_node.h"
2
#include "sdfg/data_flow/library_nodes/math/blas/gemm_node.h"
3

4
#include "sdfg/analysis/analysis.h"
5
#include "sdfg/builder/structured_sdfg_builder.h"
6

7
namespace sdfg {
8
namespace math {
9
namespace blas {
10

11
BatchedGEMMNode::BatchedGEMMNode(
12
    size_t element_id,
13
    const DebugInfo& debug_info,
14
    const graph::Vertex vertex,
15
    data_flow::DataFlowGraph& parent,
16
    const data_flow::ImplementationType& implementation_type,
17
    const BLAS_Precision& precision,
18
    const BLAS_Layout& layout,
19
    const BLAS_Transpose& trans_a,
20
    const BLAS_Transpose& trans_b,
21
    symbolic::Expression batch_count,
22
    symbolic::Expression m,
23
    symbolic::Expression n,
24
    symbolic::Expression k,
25
    symbolic::Expression lda,
26
    symbolic::Expression ldb,
27
    symbolic::Expression ldc,
28
    symbolic::Expression stride_a,
29
    symbolic::Expression stride_b,
30
    symbolic::Expression stride_c
31
)
32
    : BLASNode(
9✔
33
          element_id,
9✔
34
          debug_info,
9✔
35
          vertex,
9✔
36
          parent,
9✔
37
          LibraryNodeType_BatchedGEMM,
9✔
38
          {},
9✔
39
          {"__A", "__B", "__C", "__alpha", "__beta"},
9✔
40
          implementation_type,
9✔
41
          precision
9✔
42
      ),
9✔
43
      layout_(layout), trans_a_(trans_a), trans_b_(trans_b), batch_count_(batch_count), m_(m), n_(n), k_(k), lda_(lda),
9✔
44
      ldb_(ldb), ldc_(ldc), stride_a_(stride_a), stride_b_(stride_b), stride_c_(stride_c) {}
9✔
45

46
BLAS_Layout BatchedGEMMNode::layout() const { return this->layout_; }
×
47

48
BLAS_Transpose BatchedGEMMNode::trans_a() const { return this->trans_a_; }
×
49

50
BLAS_Transpose BatchedGEMMNode::trans_b() const { return this->trans_b_; }
×
51

52
symbolic::Expression BatchedGEMMNode::batch_count() const { return this->batch_count_; }
8✔
53

54
symbolic::Expression BatchedGEMMNode::m() const { return this->m_; }
2✔
55

56
symbolic::Expression BatchedGEMMNode::n() const { return this->n_; }
2✔
57

58
symbolic::Expression BatchedGEMMNode::k() const { return this->k_; }
2✔
59

60
symbolic::Expression BatchedGEMMNode::lda() const { return this->lda_; }
2✔
61

62
symbolic::Expression BatchedGEMMNode::ldb() const { return this->ldb_; }
2✔
63

64
symbolic::Expression BatchedGEMMNode::ldc() const { return this->ldc_; }
2✔
65

66
symbolic::Expression BatchedGEMMNode::stride_a() const { return this->stride_a_; }
2✔
67

68
symbolic::Expression BatchedGEMMNode::stride_b() const { return this->stride_b_; }
2✔
69

70
symbolic::Expression BatchedGEMMNode::stride_c() const { return this->stride_c_; }
2✔
71

72
symbolic::SymbolSet BatchedGEMMNode::symbols() const {
×
73
    symbolic::SymbolSet syms;
×
74

75
    for (auto& atom : symbolic::atoms(this->batch_count_)) {
×
76
        syms.insert(atom);
×
77
    }
×
78
    for (auto& atom : symbolic::atoms(this->m_)) {
×
79
        syms.insert(atom);
×
80
    }
×
81
    for (auto& atom : symbolic::atoms(this->n_)) {
×
82
        syms.insert(atom);
×
83
    }
×
84
    for (auto& atom : symbolic::atoms(this->k_)) {
×
85
        syms.insert(atom);
×
86
    }
×
87
    for (auto& atom : symbolic::atoms(this->lda_)) {
×
88
        syms.insert(atom);
×
89
    }
×
90
    for (auto& atom : symbolic::atoms(this->ldb_)) {
×
91
        syms.insert(atom);
×
92
    }
×
93
    for (auto& atom : symbolic::atoms(this->ldc_)) {
×
94
        syms.insert(atom);
×
95
    }
×
96
    for (auto& atom : symbolic::atoms(this->stride_a_)) {
×
97
        syms.insert(atom);
×
98
    }
×
99
    for (auto& atom : symbolic::atoms(this->stride_b_)) {
×
100
        syms.insert(atom);
×
101
    }
×
102
    for (auto& atom : symbolic::atoms(this->stride_c_)) {
×
103
        syms.insert(atom);
×
104
    }
×
105

106
    return syms;
×
107
}
×
108

109
void BatchedGEMMNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
110
    this->batch_count_ = symbolic::subs(this->batch_count_, old_expression, new_expression);
×
111
    this->m_ = symbolic::subs(this->m_, old_expression, new_expression);
×
112
    this->n_ = symbolic::subs(this->n_, old_expression, new_expression);
×
113
    this->k_ = symbolic::subs(this->k_, old_expression, new_expression);
×
114
    this->lda_ = symbolic::subs(this->lda_, old_expression, new_expression);
×
115
    this->ldb_ = symbolic::subs(this->ldb_, old_expression, new_expression);
×
116
    this->ldc_ = symbolic::subs(this->ldc_, old_expression, new_expression);
×
117
    this->stride_a_ = symbolic::subs(this->stride_a_, old_expression, new_expression);
×
118
    this->stride_b_ = symbolic::subs(this->stride_b_, old_expression, new_expression);
×
119
    this->stride_c_ = symbolic::subs(this->stride_c_, old_expression, new_expression);
×
120
}
×
121

122
void BatchedGEMMNode::replace(const symbolic::ExpressionMapping& replacements) {
×
123
    this->batch_count_ = symbolic::subs(this->batch_count_, replacements);
×
124
    this->m_ = symbolic::subs(this->m_, replacements);
×
125
    this->n_ = symbolic::subs(this->n_, replacements);
×
126
    this->k_ = symbolic::subs(this->k_, replacements);
×
127
    this->lda_ = symbolic::subs(this->lda_, replacements);
×
128
    this->ldb_ = symbolic::subs(this->ldb_, replacements);
×
129
    this->ldc_ = symbolic::subs(this->ldc_, replacements);
×
130
    this->stride_a_ = symbolic::subs(this->stride_a_, replacements);
×
131
    this->stride_b_ = symbolic::subs(this->stride_b_, replacements);
×
132
    this->stride_c_ = symbolic::subs(this->stride_c_, replacements);
×
133
}
×
134

135
void BatchedGEMMNode::validate(const Function& function) const { BLASNode::validate(function); }
5✔
136

137
symbolic::Expression BatchedGEMMNode::flop() const {
×
138
    // batch_count * (2*m*n*k) approximately
139
    auto res_elems = symbolic::mul(this->m_, this->n_);
×
140
    auto mm_mul_ops = symbolic::mul(res_elems, this->k_);
×
141
    auto mm_sum_ops = symbolic::mul(res_elems, symbolic::sub(this->k_, symbolic::one()));
×
142
    auto per_batch = symbolic::add(mm_mul_ops, mm_sum_ops);
×
143
    return symbolic::mul(this->batch_count_, per_batch);
×
144
}
×
145

146
std::unique_ptr<data_flow::DataFlowNode> BatchedGEMMNode::
147
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
148
    auto node_clone = std::unique_ptr<BatchedGEMMNode>(new BatchedGEMMNode(
×
149
        element_id,
×
150
        this->debug_info(),
×
151
        vertex,
×
152
        parent,
×
153
        this->implementation_type_,
×
154
        this->precision_,
×
155
        this->layout_,
×
156
        this->trans_a_,
×
157
        this->trans_b_,
×
158
        this->batch_count_,
×
159
        this->m_,
×
160
        this->n_,
×
161
        this->k_,
×
162
        this->lda_,
×
163
        this->ldb_,
×
164
        this->ldc_,
×
165
        this->stride_a_,
×
166
        this->stride_b_,
×
167
        this->stride_c_
×
168
    ));
×
169
    return std::move(node_clone);
×
170
}
×
171

172
std::string BatchedGEMMNode::toStr() const {
×
173
    return LibraryNode::toStr() + "(" + static_cast<char>(precision_) + ", " +
×
174
           std::string(BLAS_Layout_to_short_string(layout_)) + ", " + BLAS_Transpose_to_char(trans_a_) +
×
175
           BLAS_Transpose_to_char(trans_b_) + ", batch=" + batch_count_->__str__() + ", " + m_->__str__() + ", " +
×
176
           n_->__str__() + ", " + k_->__str__() + ", lda=" + lda_->__str__() + ", ldb=" + ldb_->__str__() +
×
177
           ", ldc=" + ldc_->__str__() + ", strA=" + stride_a_->__str__() + ", strB=" + stride_b_->__str__() +
×
178
           ", strC=" + stride_c_->__str__() + ")";
×
179
}
×
180

181
data_flow::PointerAccessType BatchedGEMMNode::pointer_access_type(int input_idx) const {
×
182
    if (input_idx == 0) { // A: batched m x k
×
183
        auto per_batch_range = GEMMNode::calc_matrix_access_range(m_, k_, lda_, trans_a_, layout_);
×
184
        return data_flow::PointerAccessMeta::create_read_only(symbolic::mul(batch_count_, per_batch_range), true);
×
185
    } else if (input_idx == 1) { // B: batched k x n
×
186
        auto per_batch_range = GEMMNode::calc_matrix_access_range(k_, n_, ldb_, trans_b_, layout_);
×
187
        return data_flow::PointerAccessMeta::create_read_only(symbolic::mul(batch_count_, per_batch_range), true);
×
188
    } else if (input_idx == 2) {
×
189
        auto per_batch_range = GEMMNode::calc_matrix_access_range(m_, n_, ldc_, BLAS_Transpose::No, layout_);
×
190
        auto range = symbolic::mul(batch_count_, per_batch_range);
×
191

192
        // Match GEMM handling: use full-write for dense layout and generic access otherwise.
193
        if (symbolic::eq(ldc_, n_)) {
×
194
            return data_flow::PointerAccessMeta::create_full_write_only(range, true);
×
195
        } else {
×
196
            auto pattern = data_flow::ConvexAccessPattern::create(range);
×
197
            return data_flow::PointerAccessMeta::create_generic(pattern->ref(), std::move(pattern), true);
×
198
        }
×
199
    } else {
×
200
        return LibraryNode::pointer_access_type(input_idx);
×
201
    }
×
202
}
×
203

204
passes::LibNodeExpander::ExpandOutcome BatchedGEMMNode::
205
    expand(passes::LibNodeExpander::ExpandContext& context, structured_control_flow::Block& block) {
2✔
206
    auto& dataflow = this->get_parent();
2✔
207

208
    if (trans_a_ == BLAS_Transpose::ConjTrans || trans_b_ == BLAS_Transpose::ConjTrans) {
2✔
NEW
209
        return context.unable();
×
210
    }
×
211

212
    auto primitive_type = scalar_primitive();
2✔
213
    if (primitive_type == types::PrimitiveType::Void) {
2✔
NEW
214
        return context.unable();
×
215
    }
×
216

217
    types::Scalar scalar_type(primitive_type);
2✔
218

219
    auto in_edges = dataflow.in_edges(*this);
2✔
220

221
    data_flow::Memlet* iedge_a = dataflow.in_edges_by_connector(*this).at(0);
2✔
222
    data_flow::Memlet* iedge_b = dataflow.in_edges_by_connector(*this).at(1);
2✔
223
    data_flow::Memlet* iedge_c = dataflow.in_edges_by_connector(*this).at(2);
2✔
224
    data_flow::Memlet* alpha_edge = dataflow.in_edges_by_connector(*this).at(3);
2✔
225
    data_flow::Memlet* beta_edge = dataflow.in_edges_by_connector(*this).at(4);
2✔
226

227
    using Use = passes::LibNodeExpander::InputUse;
2✔
228
    auto standalone = context.replacement_requires_access_nodes(
2✔
229
        {Use::IndirectRead, Use::IndirectRead, Use::IndirectWrite, Use::Scalar, Use::Scalar}
2✔
230
    );
2✔
231

232
    if (!standalone) {
2✔
NEW
233
        return context.unable();
×
UNCOV
234
    }
×
235

236
    auto& new_sequence = standalone->replace_with_sequence();
2✔
237
    auto& builder = standalone->builder();
2✔
238

239
    // Batch loop (outermost)
240
    std::string batch_indvar_str = builder.find_new_name("_batch");
2✔
241
    builder.add_container(batch_indvar_str, types::Scalar(types::PrimitiveType::UInt64));
2✔
242
    auto batch_indvar = symbolic::symbol(batch_indvar_str);
2✔
243
    auto& batch_loop = builder.add_map(
2✔
244
        new_sequence,
2✔
245
        batch_indvar,
2✔
246
        symbolic::Lt(batch_indvar, this->batch_count()),
2✔
247
        symbolic::zero(),
2✔
248
        symbolic::add(batch_indvar, symbolic::one()),
2✔
249
        structured_control_flow::ScheduleType_Sequential::create(),
2✔
250
        {},
2✔
251
        block.debug_info()
2✔
252
    );
2✔
253

254
    // Inner loops: i (m), j (n), k (k)
255
    std::vector<symbolic::Expression> indvar_ends{this->m(), this->n(), this->k()};
2✔
256
    data_flow::Subset new_subset;
2✔
257
    structured_control_flow::Sequence* last_scope = &batch_loop.root();
2✔
258
    structured_control_flow::StructuredLoop* last_map = nullptr;
2✔
259
    structured_control_flow::StructuredLoop* output_loop = nullptr;
2✔
260
    std::vector<std::string> indvar_names{"_i", "_j", "_k"};
2✔
261

262
    std::string sum_var = builder.find_new_name("_sum");
2✔
263
    builder.add_container(sum_var, scalar_type);
2✔
264

265
    for (size_t i = 0; i < 3; i++) {
8✔
266
        auto dim_begin = symbolic::zero();
6✔
267
        auto& dim_end = indvar_ends[i];
6✔
268

269
        std::string indvar_str = builder.find_new_name(indvar_names[i]);
6✔
270
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
6✔
271

272
        auto indvar = symbolic::symbol(indvar_str);
6✔
273
        auto init = dim_begin;
6✔
274
        auto update = symbolic::add(indvar, symbolic::one());
6✔
275
        auto condition = symbolic::Lt(indvar, dim_end);
6✔
276
        if (i < 2) {
6✔
277
            last_map = &builder.add_map(
4✔
278
                *last_scope,
4✔
279
                indvar,
4✔
280
                condition,
4✔
281
                init,
4✔
282
                update,
4✔
283
                structured_control_flow::ScheduleType_Sequential::create(),
4✔
284
                {},
4✔
285
                block.debug_info()
4✔
286
            );
4✔
287
        } else {
4✔
288
            last_map = &builder.add_for(*last_scope, indvar, condition, init, update, {}, block.debug_info());
2✔
289
        }
2✔
290
        last_scope = &last_map->root();
6✔
291

292
        if (i == 1) {
6✔
293
            output_loop = last_map;
2✔
294
        }
2✔
295

296
        new_subset.push_back(indvar);
6✔
297
    }
6✔
298

299
    // Batch offsets for A, B, C
300
    auto a_batch_offset = symbolic::mul(batch_indvar, stride_a_);
2✔
301
    auto b_batch_offset = symbolic::mul(batch_indvar, stride_b_);
2✔
302
    auto c_batch_offset = symbolic::mul(batch_indvar, stride_c_);
2✔
303

304
    // Init sum = 0
305
    auto& init_block = builder.add_block_before(output_loop->root(), *last_map, {}, block.debug_info());
2✔
306
    auto& sum_init = builder.add_access(init_block, sum_var, block.debug_info());
2✔
307

308
    auto& zero_node = builder.add_constant(init_block, "0.0", alpha_edge->base_type(), block.debug_info());
2✔
309
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
2✔
310
    builder.add_computational_memlet(init_block, zero_node, init_tasklet, "_in", {}, block.debug_info());
2✔
311
    builder.add_computational_memlet(init_block, init_tasklet, "_out", sum_init, {}, block.debug_info());
2✔
312

313
    // FMA: sum += A[batch_offset + ...] * B[batch_offset + ...]
314
    auto& code_block = builder.add_block(*last_scope, {}, block.debug_info());
2✔
315
    auto& input_node_a_new = standalone->add_indirect_read_access(code_block, A_INPUT_IDX);
2✔
316
    auto& input_node_b_new = standalone->add_indirect_read_access(code_block, B_INPUT_IDX);
2✔
317

318
    auto& core_fma =
2✔
319
        builder.add_tasklet(code_block, data_flow::fp_fma, "_out", {"_in1", "_in2", "_in3"}, block.debug_info());
2✔
320
    auto& sum_in = builder.add_access(code_block, sum_var, block.debug_info());
2✔
321
    auto& sum_out = builder.add_access(code_block, sum_var, block.debug_info());
2✔
322
    builder.add_computational_memlet(code_block, sum_in, core_fma, "_in3", {}, block.debug_info());
2✔
323

324
    // Row-major indexing with batch offset
325
    // No transpose: A is m×k, access A[batch*stride_a + lda*i + k]
326
    // Transpose:    A is k×m stored, access A[batch*stride_a + lda*k + i]
327
    symbolic::Expression a_idx =
2✔
328
        (trans_a_ == BLAS_Transpose::Trans)
2✔
329
            ? symbolic::add(a_batch_offset, symbolic::add(symbolic::mul(lda(), new_subset[2]), new_subset[0]))
2✔
330
            : symbolic::add(a_batch_offset, symbolic::add(symbolic::mul(lda(), new_subset[0]), new_subset[2]));
2✔
331
    builder.add_computational_memlet(
2✔
332
        code_block, input_node_a_new, core_fma, "_in1", {a_idx}, iedge_a->base_type(), iedge_a->debug_info()
2✔
333
    );
2✔
334
    // No transpose: B is k×n, access B[batch*stride_b + ldb*k + j]
335
    // Transpose:    B is n×k stored, access B[batch*stride_b + ldb*j + k]
336
    symbolic::Expression b_idx =
2✔
337
        (trans_b_ == BLAS_Transpose::Trans)
2✔
338
            ? symbolic::add(b_batch_offset, symbolic::add(symbolic::mul(ldb(), new_subset[1]), new_subset[2]))
2✔
339
            : symbolic::add(b_batch_offset, symbolic::add(symbolic::mul(ldb(), new_subset[2]), new_subset[1]));
2✔
340
    builder.add_computational_memlet(
2✔
341
        code_block, input_node_b_new, core_fma, "_in2", {b_idx}, iedge_b->base_type(), iedge_b->debug_info()
2✔
342
    );
2✔
343
    builder.add_computational_memlet(code_block, core_fma, "_out", sum_out, {}, iedge_c->debug_info());
2✔
344

345
    // Flush: C[batch*stride_c + ldc*i + j] = alpha * sum + beta * C[...]
346
    auto& flush_block = builder.add_block_after(output_loop->root(), *last_map, {}, block.debug_info());
2✔
347
    auto& sum_final = builder.add_access(flush_block, sum_var, block.debug_info());
2✔
348
    auto& input_node_c_new = standalone->add_indirect_read_access(flush_block, C_INPUT_IDX);
2✔
349
    symbolic::Expression c_idx =
2✔
350
        symbolic::add(c_batch_offset, symbolic::add(symbolic::mul(ldc(), new_subset[0]), new_subset[1]));
2✔
351

352
    // alpha * sum
353
    auto& scale_sum_tasklet =
2✔
354
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
355
    builder.add_computational_memlet(flush_block, sum_final, scale_sum_tasklet, "_in1", {}, block.debug_info());
2✔
356
    auto& alpha_node = standalone->add_scalar_input_access(flush_block, ALPHA_INPUT_IDX);
2✔
357
    builder.add_computational_memlet(flush_block, alpha_node, scale_sum_tasklet, "_in2", {}, block.debug_info());
2✔
358

359
    std::string scaled_sum_temp = builder.find_new_name("scaled_sum_temp");
2✔
360
    builder.add_container(scaled_sum_temp, scalar_type);
2✔
361
    auto& scaled_sum_final = builder.add_access(flush_block, scaled_sum_temp, block.debug_info());
2✔
362
    builder.add_computational_memlet(
2✔
363
        flush_block, scale_sum_tasklet, "_out", scaled_sum_final, {}, scalar_type, block.debug_info()
2✔
364
    );
2✔
365

366
    // beta * C[...]
367
    auto& scale_input_tasklet =
2✔
368
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_mul, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
369
    builder.add_computational_memlet(
2✔
370
        flush_block, input_node_c_new, scale_input_tasklet, "_in1", {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
2✔
371
    );
2✔
372
    auto& beta_node = standalone->add_scalar_input_access(flush_block, BETA_INPUT_IDX);
2✔
373
    builder.add_computational_memlet(flush_block, beta_node, scale_input_tasklet, "_in2", {}, block.debug_info());
2✔
374

375
    std::string scaled_input_temp = builder.find_new_name("scaled_input_temp");
2✔
376
    builder.add_container(scaled_input_temp, scalar_type);
2✔
377
    auto& scaled_input_c = builder.add_access(flush_block, scaled_input_temp, block.debug_info());
2✔
378
    builder.add_computational_memlet(
2✔
379
        flush_block, scale_input_tasklet, "_out", scaled_input_c, {}, scalar_type, block.debug_info()
2✔
380
    );
2✔
381

382
    // alpha*sum + beta*C
383
    auto& flush_add_tasklet =
2✔
384
        builder.add_tasklet(flush_block, data_flow::TaskletCode::fp_add, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
385
    auto& output_node_new = standalone->add_indirect_write_access(flush_block, C_INPUT_IDX);
2✔
386
    builder.add_computational_memlet(
2✔
387
        flush_block, scaled_sum_final, flush_add_tasklet, "_in1", {}, scalar_type, block.debug_info()
2✔
388
    );
2✔
389
    builder.add_computational_memlet(
2✔
390
        flush_block, scaled_input_c, flush_add_tasklet, "_in2", {}, scalar_type, block.debug_info()
2✔
391
    );
2✔
392
    builder.add_computational_memlet(
2✔
393
        flush_block, flush_add_tasklet, "_out", output_node_new, {c_idx}, iedge_c->base_type(), iedge_c->debug_info()
2✔
394
    );
2✔
395

396
    return standalone->successfully_expanded();
2✔
397
}
2✔
398

399
nlohmann::json BatchedGEMMNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
×
400
    const BatchedGEMMNode& node = static_cast<const BatchedGEMMNode&>(library_node);
×
401
    nlohmann::json j;
×
402

403
    serializer::JSONSerializer serializer;
×
404
    j["code"] = node.code().value();
×
405
    j["precision"] = node.precision();
×
406
    j["layout"] = node.layout();
×
407
    j["trans_a"] = node.trans_a();
×
408
    j["trans_b"] = node.trans_b();
×
409
    j["batch_count"] = serializer.expression(node.batch_count());
×
410
    j["m"] = serializer.expression(node.m());
×
411
    j["n"] = serializer.expression(node.n());
×
412
    j["k"] = serializer.expression(node.k());
×
413
    j["lda"] = serializer.expression(node.lda());
×
414
    j["ldb"] = serializer.expression(node.ldb());
×
415
    j["ldc"] = serializer.expression(node.ldc());
×
416
    j["stride_a"] = serializer.expression(node.stride_a());
×
417
    j["stride_b"] = serializer.expression(node.stride_b());
×
418
    j["stride_c"] = serializer.expression(node.stride_c());
×
419

420
    return j;
×
421
}
×
422

423
data_flow::LibraryNode& BatchedGEMMNodeSerializer::deserialize(
424
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
425
) {
×
426
    assert(j.contains("element_id"));
×
427
    assert(j.contains("code"));
×
428
    assert(j.contains("debug_info"));
×
429

430
    auto code = j["code"].get<std::string>();
×
431
    if (code != LibraryNodeType_BatchedGEMM.value()) {
×
432
        throw std::runtime_error("Invalid library node code");
×
433
    }
×
434

435
    sdfg::serializer::JSONSerializer serializer;
×
436
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
×
437

438
    auto precision = j.at("precision").get<BLAS_Precision>();
×
439
    auto layout = j.at("layout").get<BLAS_Layout>();
×
440
    auto trans_a = j.at("trans_a").get<BLAS_Transpose>();
×
441
    auto trans_b = j.at("trans_b").get<BLAS_Transpose>();
×
442
    auto batch_count = symbolic::parse(j.at("batch_count"));
×
443
    auto m = symbolic::parse(j.at("m"));
×
444
    auto n = symbolic::parse(j.at("n"));
×
445
    auto k = symbolic::parse(j.at("k"));
×
446
    auto lda = symbolic::parse(j.at("lda"));
×
447
    auto ldb = symbolic::parse(j.at("ldb"));
×
448
    auto ldc = symbolic::parse(j.at("ldc"));
×
449
    auto stride_a = symbolic::parse(j.at("stride_a"));
×
450
    auto stride_b = symbolic::parse(j.at("stride_b"));
×
451
    auto stride_c = symbolic::parse(j.at("stride_c"));
×
452

453
    auto implementation_type = j.at("implementation_type").get<std::string>();
×
454

455
    return builder.add_library_node<BatchedGEMMNode>(
×
456
        parent,
×
457
        debug_info,
×
458
        implementation_type,
×
459
        precision,
×
460
        layout,
×
461
        trans_a,
×
462
        trans_b,
×
463
        batch_count,
×
464
        m,
×
465
        n,
×
466
        k,
×
467
        lda,
×
468
        ldb,
×
469
        ldc,
×
470
        stride_a,
×
471
        stride_b,
×
472
        stride_c
×
473
    );
×
474
}
×
475

476
} // namespace blas
477
} // namespace math
478
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc