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

daisytuner / docc / 26831349290

02 Jun 2026 03:50PM UTC coverage: 61.29% (-0.01%) from 61.302%
26831349290

Pull #725

github

web-flow
Merge a7e2175c0 into 887730e20
Pull Request #725: Tensor node backport

932 of 1642 new or added lines in 52 files covered. (56.76%)

92 existing lines in 33 files now uncovered.

35584 of 58058 relevant lines covered (61.29%)

11020.18 hits per line

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

75.31
/sdfg/src/data_flow/library_nodes/math/tensor/pooling_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/pooling_node.h"
2

3
#include "sdfg/analysis/analysis.h"
4
#include "sdfg/analysis/scope_analysis.h"
5
#include "sdfg/builder/structured_sdfg_builder.h"
6
#include "sdfg/data_flow/library_nodes/math/cmath/cmath_node.h"
7
#include "sdfg/data_flow/library_nodes/math/tensor/spatial_tensor_node.h"
8
#include "sdfg/data_flow/library_nodes/math/tensor/tensor_node.h"
9
#include "sdfg/symbolic/symbolic.h"
10
#include "sdfg/types/type.h"
11

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

16
PoolingNode::PoolingNode(
17
    size_t element_id,
18
    const DebugInfo& debug_info,
19
    const graph::Vertex vertex,
20
    data_flow::DataFlowGraph& parent,
21
    PoolingMode mode,
22
    const std::vector<symbolic::Expression>& shape,
23
    const std::vector<symbolic::Expression>& kernel_shape,
24
    const std::vector<symbolic::Expression>& strides,
25
    const std::vector<symbolic::Expression>& pads,
26
    const std::vector<symbolic::Expression>& dilations,
27
    QuantizationType quantization,
28
    const data_flow::ImplementationType& impl_type
29
)
30
    : SpatialTensorNode(
15✔
31
          element_id,
15✔
32
          debug_info,
15✔
33
          vertex,
15✔
34
          parent,
15✔
35
          LibraryNodeType_Pooling,
15✔
36
          {},
15✔
37
          {"Y", "X"},
15✔
38
          impl_type,
15✔
39
          quantization,
15✔
40
          shape,
15✔
41
          kernel_shape,
15✔
42
          strides,
15✔
43
          pads,
15✔
44
          dilations
15✔
45
      ),
15✔
46
      mode_(mode) {}
15✔
47

48
void PoolingNode::validate(const Function& function) const {
12✔
49
    TensorNode::validate(function);
12✔
50

51
    if (kernel_shape_.empty()) {
12✔
52
        throw InvalidSDFGException("PoolingNode kernel_shape cannot be empty");
×
53
    }
×
54

55
    size_t spatial_dims = kernel_shape_.size();
12✔
56

57
    if (!strides_.empty() && strides_.size() != spatial_dims) {
12✔
58
        throw InvalidSDFGException("PoolingNode strides must match kernel spatial dimensions");
×
59
    }
×
60

61
    if (!pads_.empty() && pads_.size() != 2 * spatial_dims) {
12✔
62
        throw InvalidSDFGException("PoolingNode pads must have 2 * spatial dimensions");
×
63
    }
×
64

65
    if (!dilations_.empty() && dilations_.size() != spatial_dims) {
12✔
66
        throw InvalidSDFGException("PoolingNode dilations must match kernel spatial dimensions");
×
67
    }
×
68
}
12✔
69

70
bool PoolingNode::expand(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
9✔
71
    auto& scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
9✔
72

73
    auto& dataflow = this->get_parent();
9✔
74
    auto& block = static_cast<structured_control_flow::Block&>(*dataflow.get_parent());
9✔
75
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&block));
9✔
76
    int index = parent.index(block);
9✔
77
    auto& transition = parent.at(index).second;
9✔
78

79
    auto primitive_type = this->primitive_type(dataflow);
9✔
80
    types::Scalar scalar_type(primitive_type);
9✔
81

82
    auto x_edge = dataflow.in_edge_for_connector(*this, "X");
9✔
83
    if (!x_edge) {
9✔
84
        return false;
×
85
    }
×
86

87
    auto y_edge = dataflow.in_edge_for_connector(*this, "Y");
9✔
88
    if (!y_edge) {
9✔
NEW
89
        return false;
×
NEW
90
    }
×
91

92
    auto* x_node = static_cast<const data_flow::AccessNode*>(&x_edge->src());
9✔
93
    auto* y_node = static_cast<const data_flow::AccessNode*>(&y_edge->src());
9✔
94

95
    if (!x_node || !y_node) {
9✔
96
        return false;
×
97
    }
×
98

99
    size_t spatial_dims = kernel_shape_.size();
9✔
100
    if (spatial_dims == 0) {
9✔
101
        return false;
×
102
    }
×
103

104
    // Get strides (default to 1)
105
    std::vector<symbolic::Expression> strides_vec;
9✔
106
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
107
        if (i < strides_.size()) {
17✔
108
            strides_vec.push_back(strides_[i]);
17✔
109
        } else {
17✔
110
            strides_vec.push_back(symbolic::one());
×
111
        }
×
112
    }
17✔
113

114
    // Get padding (default to 0)
115
    std::vector<symbolic::Expression> pads_begin_vec, pads_end_vec;
9✔
116
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
117
        if (i < pads_.size()) {
17✔
118
            pads_begin_vec.push_back(pads_[i]);
2✔
119
        } else {
15✔
120
            pads_begin_vec.push_back(symbolic::zero());
15✔
121
        }
15✔
122
        if (spatial_dims + i < pads_.size()) {
17✔
123
            pads_end_vec.push_back(pads_[spatial_dims + i]);
2✔
124
        } else {
15✔
125
            pads_end_vec.push_back(symbolic::zero());
15✔
126
        }
15✔
127
    }
17✔
128

129
    // Get dilations (default to 1)
130
    std::vector<symbolic::Expression> dilations_vec;
9✔
131
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
132
        if (i < dilations_.size()) {
17✔
133
            dilations_vec.push_back(dilations_[i]);
2✔
134
        } else {
15✔
135
            dilations_vec.push_back(symbolic::one());
15✔
136
        }
15✔
137
    }
17✔
138

139
    auto& X_var = x_node->data();
9✔
140
    auto& Y_var = y_node->data();
9✔
141

142
    // Input shape: [N, C, D0, D1, ..., Dn]
143
    symbolic::Expression N = shape_[0];
9✔
144
    symbolic::Expression C = shape_[1];
9✔
145
    std::vector<symbolic::Expression> input_spatial_dims;
9✔
146
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
147
        input_spatial_dims.push_back(shape_[2 + i]);
17✔
148
    }
17✔
149

150
    // Output spatial dimensions
151
    std::vector<symbolic::Expression> output_spatial_dims;
9✔
152
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
153
        auto d_in = input_spatial_dims[i];
17✔
154
        auto pad = symbolic::add(pads_begin_vec[i], pads_end_vec[i]);
17✔
155
        auto dk = symbolic::mul(dilations_vec[i], symbolic::sub(kernel_shape_[i], symbolic::one()));
17✔
156
        auto num = symbolic::sub(symbolic::add(d_in, pad), symbolic::add(dk, symbolic::one()));
17✔
157
        auto d_out = symbolic::add(symbolic::div(num, strides_vec[i]), symbolic::one());
17✔
158
        output_spatial_dims.push_back(d_out);
17✔
159
    }
17✔
160

161
    auto& new_sequence = builder.add_sequence_before(parent, block, transition.assignments(), block.debug_info());
9✔
162

163
    structured_control_flow::Sequence* current_scope = &new_sequence;
9✔
164
    std::vector<symbolic::Expression> output_indices;
9✔
165
    std::vector<symbolic::Expression> output_spatial_vars;
9✔
166

167
    // Map over batch
168
    std::string n_str = builder.find_new_name("n");
9✔
169
    builder.add_container(n_str, types::Scalar(types::PrimitiveType::UInt64));
9✔
170
    auto n_var = symbolic::symbol(n_str);
9✔
171
    auto& map_n = builder.add_map(
9✔
172
        *current_scope,
9✔
173
        n_var,
9✔
174
        symbolic::Lt(n_var, N),
9✔
175
        symbolic::zero(),
9✔
176
        symbolic::add(n_var, symbolic::one()),
9✔
177
        structured_control_flow::ScheduleType_Sequential::create(),
9✔
178
        {},
9✔
179
        block.debug_info()
9✔
180
    );
9✔
181
    current_scope = &map_n.root();
9✔
182
    output_indices.push_back(n_var);
9✔
183

184
    // Map over channel
185
    std::string c_str = builder.find_new_name("c");
9✔
186
    builder.add_container(c_str, types::Scalar(types::PrimitiveType::UInt64));
9✔
187
    auto c_var = symbolic::symbol(c_str);
9✔
188
    auto& map_c = builder.add_map(
9✔
189
        *current_scope,
9✔
190
        c_var,
9✔
191
        symbolic::Lt(c_var, C),
9✔
192
        symbolic::zero(),
9✔
193
        symbolic::add(c_var, symbolic::one()),
9✔
194
        structured_control_flow::ScheduleType_Sequential::create(),
9✔
195
        {},
9✔
196
        block.debug_info()
9✔
197
    );
9✔
198
    current_scope = &map_c.root();
9✔
199
    output_indices.push_back(c_var);
9✔
200

201
    // Map over each output spatial dimension
202
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
203
        std::string od_str = builder.find_new_name("od" + std::to_string(i));
17✔
204
        builder.add_container(od_str, types::Scalar(types::PrimitiveType::UInt64));
17✔
205
        auto od_var = symbolic::symbol(od_str);
17✔
206
        auto& map_od = builder.add_map(
17✔
207
            *current_scope,
17✔
208
            od_var,
17✔
209
            symbolic::Lt(od_var, output_spatial_dims[i]),
17✔
210
            symbolic::zero(),
17✔
211
            symbolic::add(od_var, symbolic::one()),
17✔
212
            structured_control_flow::ScheduleType_Sequential::create(),
17✔
213
            {},
17✔
214
            block.debug_info()
17✔
215
        );
17✔
216
        current_scope = &map_od.root();
17✔
217
        output_indices.push_back(od_var);
17✔
218
        output_spatial_vars.push_back(od_var);
17✔
219
    }
17✔
220

221
    // Create accumulator
222
    std::string accum_var = builder.find_new_name("_pool_accum");
9✔
223
    builder.add_container(accum_var, scalar_type);
9✔
224

225
    // Initialize accumulator
226
    std::string init_value;
9✔
227
    if (mode_ == PoolingMode::Max) {
9✔
228
        // Use -INFINITY for float, type-min for integers
229
        if (types::is_integer(primitive_type)) {
7✔
230
            switch (primitive_type) {
1✔
231
                case types::PrimitiveType::Int8:
×
232
                    init_value = "INT8_MIN";
×
233
                    break;
×
234
                case types::PrimitiveType::Int16:
×
235
                    init_value = "INT16_MIN";
×
236
                    break;
×
237
                case types::PrimitiveType::Int32:
1✔
238
                    init_value = "INT32_MIN";
1✔
239
                    break;
1✔
240
                case types::PrimitiveType::Int64:
×
241
                    init_value = "INT64_MIN";
×
242
                    break;
×
243
                default:
×
244
                    init_value = "0";
×
245
                    break;
×
246
            }
1✔
247
        } else {
6✔
248
            init_value = "-INFINITY";
6✔
249
        }
6✔
250
    } else {
7✔
251
        // Sum / Avg: init to 0
252
        init_value = types::is_integer(primitive_type) ? "0" : "0.0";
2✔
253
    }
2✔
254

255
    auto& init_block = builder.add_block(*current_scope, {}, block.debug_info());
9✔
256
    auto& accum_init = builder.add_access(init_block, accum_var, block.debug_info());
9✔
257
    auto& zero_const = builder.add_constant(init_block, init_value, scalar_type, block.debug_info());
9✔
258
    auto& init_tasklet = builder.add_tasklet(init_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
9✔
259
    builder.add_computational_memlet(init_block, zero_const, init_tasklet, "_in", {}, scalar_type, block.debug_info());
9✔
260
    builder.add_computational_memlet(init_block, init_tasklet, "_out", accum_init, {}, scalar_type, block.debug_info());
9✔
261

262
    // For loops over kernel spatial dimensions
263
    auto* loop_scope = current_scope;
9✔
264
    std::vector<symbolic::Expression> kernel_vars;
9✔
265
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
266
        std::string k_str = builder.find_new_name("k" + std::to_string(i));
17✔
267
        builder.add_container(k_str, types::Scalar(types::PrimitiveType::UInt64));
17✔
268
        auto k_var = symbolic::symbol(k_str);
17✔
269
        auto& for_k = builder.add_for(
17✔
270
            *loop_scope,
17✔
271
            k_var,
17✔
272
            symbolic::Lt(k_var, kernel_shape_[i]),
17✔
273
            symbolic::zero(),
17✔
274
            symbolic::add(k_var, symbolic::one()),
17✔
275
            {},
17✔
276
            block.debug_info()
17✔
277
        );
17✔
278
        loop_scope = &for_k.root();
17✔
279
        kernel_vars.push_back(k_var);
17✔
280
    }
17✔
281

282
    // Compute input spatial indices
283
    std::vector<symbolic::Expression> input_spatial_indices;
9✔
284
    for (size_t i = 0; i < spatial_dims; ++i) {
26✔
285
        auto k_dilated = symbolic::mul(kernel_vars[i], dilations_vec[i]);
17✔
286
        auto input_idx = symbolic::
17✔
287
            add(symbolic::sub(symbolic::mul(output_spatial_vars[i], strides_vec[i]), pads_begin_vec[i]), k_dilated);
17✔
288
        input_spatial_indices.push_back(input_idx);
17✔
289
    }
17✔
290

291
    // Add branching if padding is non-zero
292
    bool has_padding = false;
9✔
293
    for (auto padding : this->pads_) {
9✔
294
        if (!symbolic::eq(padding, symbolic::zero())) {
1✔
295
            has_padding = true;
1✔
296
            break;
1✔
297
        }
1✔
298
    }
1✔
299
    if (has_padding) {
9✔
300
        symbolic::Condition comp_condition = symbolic::__true__();
1✔
301
        for (size_t i = 0; i < spatial_dims; ++i) {
3✔
302
            comp_condition = symbolic::
2✔
303
                And(comp_condition,
2✔
304
                    symbolic::
2✔
305
                        And(symbolic::Lt(input_spatial_indices[i], input_spatial_dims[i]),
2✔
306
                            symbolic::Ge(input_spatial_indices[i], symbolic::zero())));
2✔
307
        }
2✔
308
        auto& branch = builder.add_if_else(*loop_scope, {}, block.debug_info());
1✔
309
        auto& comp_case = builder.add_case(branch, comp_condition, block.debug_info());
1✔
310
        loop_scope = &comp_case;
1✔
311
    }
1✔
312

313
    // Build X indices: [n, c, input_spatial...]
314
    std::vector<symbolic::Expression> x_indices_vec = {n_var, c_var};
9✔
315
    x_indices_vec.insert(x_indices_vec.end(), input_spatial_indices.begin(), input_spatial_indices.end());
9✔
316
    data_flow::Subset x_subset(x_indices_vec);
9✔
317

318
    // Computation block: accumulate
319
    auto& comp_block = builder.add_block(*loop_scope, {}, block.debug_info());
9✔
320
    auto& x_access = builder.add_access(comp_block, X_var, x_node->debug_info());
9✔
321
    auto& accum_read = builder.add_access(comp_block, accum_var, block.debug_info());
9✔
322
    auto& accum_write = builder.add_access(comp_block, accum_var, block.debug_info());
9✔
323

324
    if (mode_ == PoolingMode::Max) {
9✔
325
        bool is_int = types::is_integer(primitive_type);
7✔
326
        if (is_int) {
7✔
327
            auto tasklet_code = TensorNode::get_integer_minmax_tasklet(primitive_type, true);
1✔
328
            auto& tasklet = builder.add_tasklet(comp_block, tasklet_code, "_out", {"_in1", "_in2"}, block.debug_info());
1✔
329
            builder.add_computational_memlet(
1✔
330
                comp_block, x_access, tasklet, "_in1", x_subset, x_edge->base_type(), block.debug_info()
1✔
331
            );
1✔
332
            builder
1✔
333
                .add_computational_memlet(comp_block, accum_read, tasklet, "_in2", {}, scalar_type, block.debug_info());
1✔
334
            builder
1✔
335
                .add_computational_memlet(comp_block, tasklet, "_out", accum_write, {}, scalar_type, block.debug_info());
1✔
336
        } else {
6✔
337
            auto& libnode = builder.add_library_node<
6✔
338
                math::cmath::CMathNode>(comp_block, block.debug_info(), cmath::CMathFunction::fmax, primitive_type);
6✔
339
            builder.add_computational_memlet(
6✔
340
                comp_block, x_access, libnode, "_in1", x_subset, x_edge->base_type(), block.debug_info()
6✔
341
            );
6✔
342
            builder
6✔
343
                .add_computational_memlet(comp_block, accum_read, libnode, "_in2", {}, scalar_type, block.debug_info());
6✔
344
            builder
6✔
345
                .add_computational_memlet(comp_block, libnode, "_out", accum_write, {}, scalar_type, block.debug_info());
6✔
346
        }
6✔
347
    } else {
7✔
348
        // Sum or Avg: accumulate with addition
349
        bool is_int = types::is_integer(primitive_type);
2✔
350
        data_flow::TaskletCode opcode = is_int ? data_flow::TaskletCode::int_add : data_flow::TaskletCode::fp_add;
2✔
351
        auto& tasklet = builder.add_tasklet(comp_block, opcode, "_out", {"_in1", "_in2"}, block.debug_info());
2✔
352
        builder.add_computational_memlet(
2✔
353
            comp_block, x_access, tasklet, "_in1", x_subset, x_edge->base_type(), block.debug_info()
2✔
354
        );
2✔
355
        builder.add_computational_memlet(comp_block, accum_read, tasklet, "_in2", {}, scalar_type, block.debug_info());
2✔
356
        builder.add_computational_memlet(comp_block, tasklet, "_out", accum_write, {}, scalar_type, block.debug_info());
2✔
357
    }
2✔
358

359
    // After kernel loops: write result to output
360
    data_flow::Subset y_subset(output_indices);
9✔
361

362
    auto& output_block = builder.add_block(*current_scope, {}, block.debug_info());
9✔
363
    auto& accum_final = builder.add_access(output_block, accum_var, block.debug_info());
9✔
364
    auto& y_access = builder.add_access(output_block, Y_var, y_node->debug_info());
9✔
365

366
    if (mode_ == PoolingMode::Avg) {
9✔
367
        // Divide by window size: product of kernel_shape dimensions
368
        // Create a temporary for the divisor
369
        std::string divisor_var = builder.find_new_name("_pool_divisor");
1✔
370
        builder.add_container(divisor_var, scalar_type);
1✔
371

372
        // Compute window size as product of kernel dimensions
373
        symbolic::Expression window_size = kernel_shape_[0];
1✔
374
        for (size_t i = 1; i < spatial_dims; ++i) {
2✔
375
            window_size = symbolic::mul(window_size, kernel_shape_[i]);
1✔
376
        }
1✔
377

378
        auto& divisor_const =
1✔
379
            builder.add_constant(output_block, window_size->__str__(), scalar_type, block.debug_info());
1✔
380
        auto& divisor_access = builder.add_access(output_block, divisor_var, block.debug_info());
1✔
381
        auto& divisor_assign =
1✔
382
            builder.add_tasklet(output_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
1✔
383
        builder.add_computational_memlet(
1✔
384
            output_block, divisor_const, divisor_assign, "_in", {}, scalar_type, block.debug_info()
1✔
385
        );
1✔
386
        builder.add_computational_memlet(
1✔
387
            output_block, divisor_assign, "_out", divisor_access, {}, scalar_type, block.debug_info()
1✔
388
        );
1✔
389

390
        bool is_int = types::is_integer(primitive_type);
1✔
391
        data_flow::TaskletCode div_opcode = is_int ? data_flow::TaskletCode::int_sdiv : data_flow::TaskletCode::fp_div;
1✔
392
        auto& div_tasklet = builder.add_tasklet(output_block, div_opcode, "_out", {"_in1", "_in2"}, block.debug_info());
1✔
393
        builder
1✔
394
            .add_computational_memlet(output_block, accum_final, div_tasklet, "_in1", {}, scalar_type, block.debug_info());
1✔
395
        builder.add_computational_memlet(
1✔
396
            output_block, divisor_access, div_tasklet, "_in2", {}, scalar_type, block.debug_info()
1✔
397
        );
1✔
398
        builder.add_computational_memlet(
1✔
399
            output_block, div_tasklet, "_out", y_access, y_subset, y_edge->base_type(), y_edge->debug_info()
1✔
400
        );
1✔
401
    } else {
8✔
402
        // Max or Sum: just assign
403
        auto& assign_tasklet =
8✔
404
            builder.add_tasklet(output_block, data_flow::assign, "_out", {"_in"}, block.debug_info());
8✔
405
        builder.add_computational_memlet(
8✔
406
            output_block, accum_final, assign_tasklet, "_in", {}, scalar_type, block.debug_info()
8✔
407
        );
8✔
408
        builder.add_computational_memlet(
8✔
409
            output_block, assign_tasklet, "_out", y_access, y_subset, y_edge->base_type(), y_edge->debug_info()
8✔
410
        );
8✔
411
    }
8✔
412

413
    // Clean up original block
414
    builder.clear_code_node_legacy(block, *this);
9✔
415
    // WARNING: this has been deallocated at this point!!
416

417
    builder.remove_child(parent, index + 1);
9✔
418

419
    return true;
9✔
420
}
9✔
421

422
std::unique_ptr<data_flow::DataFlowNode> PoolingNode::
UNCOV
423
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
424
    return std::unique_ptr<data_flow::DataFlowNode>(new PoolingNode(
×
NEW
425
        element_id,
×
NEW
426
        this->debug_info(),
×
NEW
427
        vertex,
×
NEW
428
        parent,
×
NEW
429
        mode_,
×
NEW
430
        shape_,
×
NEW
431
        kernel_shape_,
×
NEW
432
        strides_,
×
NEW
433
        pads_,
×
NEW
434
        dilations_,
×
NEW
435
        fixed_quantization_,
×
NEW
436
        implementation_type_
×
437
    ));
×
438
}
×
439

440
std::string PoolingNode::mode_to_string(PoolingMode mode) {
3✔
441
    switch (mode) {
3✔
442
        case PoolingMode::Max:
1✔
443
            return "max";
1✔
444
        case PoolingMode::Sum:
1✔
445
            return "sum";
1✔
446
        case PoolingMode::Avg:
1✔
447
            return "avg";
1✔
448
    }
3✔
449
    return "unknown";
×
450
}
3✔
451

452
PoolingMode PoolingNode::string_to_mode(const std::string& str) {
3✔
453
    if (str == "max") return PoolingMode::Max;
3✔
454
    if (str == "sum") return PoolingMode::Sum;
2✔
455
    if (str == "avg") return PoolingMode::Avg;
1✔
456
    throw InvalidSDFGException("Unknown pooling mode: " + str);
×
457
}
1✔
458

NEW
459
symbolic::Expression PoolingNode::flop() const {
×
460
    // Total output elements: N * C * prod(output_spatial_dim(i))
NEW
461
    auto output_elems = symbolic::mul(symbolic::mul(shape_[0], shape_[1]), output_spatial_volume());
×
462

463
    // Each output element reduces a full kernel window.
NEW
464
    auto kv = kernel_volume();
×
465

NEW
466
    switch (mode_) {
×
NEW
467
        case PoolingMode::Max:
×
468
            // max pooling: (kv - 1) comparisons per output element
NEW
469
            return symbolic::mul(output_elems, symbolic::sub(kv, symbolic::one()));
×
NEW
470
        case PoolingMode::Sum:
×
471
            // sum pooling: (kv - 1) additions per output element
NEW
472
            return symbolic::mul(output_elems, symbolic::sub(kv, symbolic::one()));
×
NEW
473
        case PoolingMode::Avg:
×
474
            // avg pooling: (kv - 1) additions + 1 division per output element
NEW
475
            return symbolic::mul(output_elems, kv);
×
NEW
476
        default:
×
NEW
477
            return symbolic::symbol("UnknownFlops_Pool_n" + std::to_string(element_id_));
×
478
    }
×
NEW
479
}
×
480

NEW
481
data_flow::PointerAccessType PoolingNode::pointer_access_type(int input_idx) const {
×
NEW
482
    if (input_idx == 0) {
×
NEW
483
        return data_flow::PointerAccessMeta::create_full_write_only(symbolic::__nullptr__(), true);
×
NEW
484
    } else if (input_idx == 1) {
×
NEW
485
        return data_flow::PointerAccessMeta::create_read_only(symbolic::__nullptr__(), true);
×
NEW
486
    } else {
×
NEW
487
        return TensorNode::pointer_access_type(input_idx);
×
488
    }
×
NEW
489
}
×
490

NEW
491
std::string PoolingNode::toStr() const {
×
NEW
492
    std::stringstream ss;
×
NEW
493
    ss << "Pooling(mode=" << mode_to_string(mode_) << ", ";
×
NEW
494
    SpatialTensorNode::operator<<(ss);
×
NEW
495
    ss << ")";
×
NEW
496
    return ss.str();
×
UNCOV
497
}
×
498

499
nlohmann::json PoolingNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
×
500
    const PoolingNode& node = static_cast<const PoolingNode&>(library_node);
×
501
    nlohmann::json j;
×
502

503
    j["mode"] = PoolingNode::mode_to_string(node.mode());
×
504

NEW
505
    fill_base_values(node, j);
×
506

507
    return j;
×
508
}
×
509

510
data_flow::LibraryNode& PoolingNodeSerializer::deserialize(
511
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
512
) {
×
513
    assert(j.contains("mode"));
×
514

NEW
515
    auto base = deserialize_base_values(j);
×
516

NEW
517
    auto mode = PoolingNode::string_to_mode(j["mode"].get<std::string>());
×
518

NEW
519
    return builder.add_library_node<PoolingNode>(
×
NEW
520
        parent,
×
NEW
521
        base.debug_info,
×
NEW
522
        mode,
×
NEW
523
        base.shape,
×
NEW
524
        base.kernel_shape,
×
NEW
525
        base.strides,
×
NEW
526
        base.pads,
×
NEW
527
        base.dilations,
×
NEW
528
        base.quantization
×
NEW
529
    );
×
UNCOV
530
}
×
531

532
} // namespace tensor
533
} // namespace math
534
} // 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