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

daisytuner / sdfglib / 17651658650

11 Sep 2025 04:58PM UTC coverage: 61.012% (+1.3%) from 59.755%
17651658650

Pull #219

github

web-flow
Merge 742a12367 into f744ac9f5
Pull Request #219: stdlib Library Nodes and ConstantNodes

499 of 1681 new or added lines in 81 files covered. (29.68%)

95 existing lines in 36 files now uncovered.

9718 of 15928 relevant lines covered (61.01%)

108.0 hits per line

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

65.49
/src/data_flow/library_nodes/math/ml/maxpool.cpp
1
#include "sdfg/data_flow/library_nodes/math/ml/maxpool.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/debug_info.h"
7

8
namespace sdfg {
9
namespace math {
10
namespace ml {
11

12
/*************** Constructor ***************/
13
MaxPoolNode::MaxPoolNode(
1✔
14
    size_t element_id,
15
    const DebugInfoRegion &debug_info,
16
    const graph::Vertex vertex,
17
    data_flow::DataFlowGraph &parent,
18
    std::vector<symbolic::Expression> shape,
19
    std::vector<size_t> kernel_shape,
20
    std::vector<size_t> pads,
21
    std::vector<size_t> strides
22
)
23
    : MathNode(
1✔
24
          element_id, debug_info, vertex, parent, LibraryNodeType_MaxPool, {"Y"}, {"X"}, data_flow::ImplementationType_NONE
1✔
25
      ),
26
      shape_(shape), kernel_shape_(std::move(kernel_shape)), pads_(std::move(pads)), strides_(std::move(strides)) {}
1✔
27

28
/*************** Accessors ***************/
NEW
29
const std::vector<symbolic::Expression> &MaxPoolNode::shape() const { return shape_; }
×
30
std::vector<size_t> MaxPoolNode::kernel_shape() const { return kernel_shape_; }
×
31
std::vector<size_t> MaxPoolNode::pads() const { return pads_; }
×
32
std::vector<size_t> MaxPoolNode::strides() const { return strides_; }
×
33

NEW
34
symbolic::SymbolSet MaxPoolNode::symbols() const {
×
NEW
35
    symbolic::SymbolSet syms;
×
NEW
36
    for (const auto &dim : shape_) {
×
NEW
37
        for (auto &atom : symbolic::atoms(dim)) {
×
NEW
38
            syms.insert(atom);
×
39
        }
40
    }
NEW
41
    return syms;
×
NEW
42
}
×
43

NEW
44
void MaxPoolNode::replace(const symbolic::Expression &old_expression, const symbolic::Expression &new_expression) {
×
NEW
45
    for (auto &dim : shape_) {
×
NEW
46
        dim = symbolic::subs(dim, old_expression, new_expression);
×
47
    }
NEW
48
}
×
49

NEW
50
void MaxPoolNode::validate(const Function &) const {}
×
51

52
/*************** Expand ***************/
53
bool MaxPoolNode::expand(builder::StructuredSDFGBuilder &builder, analysis::AnalysisManager &analysis_manager) {
1✔
54
    auto &dataflow = this->get_parent();
1✔
55
    auto &block = static_cast<structured_control_flow::Block &>(*dataflow.get_parent());
1✔
56

57
    auto &scope_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
1✔
58
    auto &parent = static_cast<structured_control_flow::Sequence &>(*scope_analysis.parent_scope(&block));
1✔
59
    int index = parent.index(block);
1✔
60
    auto &transition = parent.at(index).second;
1✔
61

62
    // Locate edges
63
    const data_flow::Memlet *iedge_X = nullptr;
1✔
64
    const data_flow::Memlet *oedge_Y = nullptr;
1✔
65
    for (const auto &edge : dataflow.in_edges(*this)) {
2✔
66
        if (edge.dst_conn() == "X") {
1✔
67
            iedge_X = &edge;
1✔
68
        }
1✔
69
    }
70
    for (const auto &edge : dataflow.out_edges(*this)) {
2✔
71
        if (edge.src_conn() == "Y") {
1✔
72
            oedge_Y = &edge;
1✔
73
        }
1✔
74
    }
75
    if (!iedge_X || !oedge_Y) return false;
1✔
76

77
    std::string X_name = static_cast<const data_flow::AccessNode &>(iedge_X->src()).data();
1✔
78
    std::string Y_name = static_cast<const data_flow::AccessNode &>(oedge_Y->dst()).data();
1✔
79

80
    // Create new sequence before
81
    auto &new_sequence = builder.add_sequence_before(
2✔
82
        parent, block, transition.assignments(), builder.debug_info().get_region(block.debug_info().indices())
1✔
83
    );
84
    structured_control_flow::Sequence *last_scope = &new_sequence;
1✔
85

86
    // Create maps over output subset dims (parallel dims)
87
    data_flow::Subset out_subset;
1✔
88
    std::vector<symbolic::Expression> out_syms;
1✔
89
    structured_control_flow::Map *last_map = nullptr;
1✔
90
    for (size_t d = 0; d < this->shape_.size(); ++d) {
5✔
91
        std::string indvar_str = builder.find_new_name("_i");
4✔
92
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
4✔
93
        auto indvar = symbolic::symbol(indvar_str);
4✔
94
        auto init = symbolic::zero();
4✔
95
        auto update = symbolic::add(indvar, symbolic::one());
4✔
96
        auto cond = symbolic::Lt(indvar, this->shape_[d]);
4✔
97
        last_map = &builder.add_map(
8✔
98
            *last_scope,
4✔
99
            indvar,
100
            cond,
101
            init,
4✔
102
            update,
103
            structured_control_flow::ScheduleType_Sequential::create(),
4✔
104
            {},
4✔
105
            builder.subject().debug_info().get_region(block.debug_info().indices())
4✔
106
        );
107
        last_scope = &last_map->root();
4✔
108
        out_subset.push_back(indvar);
4✔
109
        out_syms.push_back(indvar);
4✔
110
    }
4✔
111

112
    // Kernel reduction loops
113
    structured_control_flow::For *last_for = nullptr;
1✔
114
    std::vector<symbolic::Expression> kernel_syms;
1✔
115
    for (size_t d = 0; d < kernel_shape_.size(); ++d) {
3✔
116
        std::string indvar_str = builder.find_new_name("_k");
2✔
117
        builder.add_container(indvar_str, types::Scalar(types::PrimitiveType::UInt64));
2✔
118
        auto indvar = symbolic::symbol(indvar_str);
2✔
119
        auto init = symbolic::integer(0);
2✔
120
        auto update = symbolic::add(indvar, symbolic::one());
2✔
121
        auto cond = symbolic::Lt(indvar, symbolic::integer(static_cast<int64_t>(kernel_shape_[d])));
2✔
122
        last_for = &builder.add_for(
4✔
123
            *last_scope,
2✔
124
            indvar,
125
            cond,
126
            init,
2✔
127
            update,
128
            {},
2✔
129
            builder.subject().debug_info().get_region(block.debug_info().indices())
2✔
130
        );
131
        last_scope = &last_for->root();
2✔
132
        kernel_syms.push_back(indvar);
2✔
133
    }
2✔
134

135
    // Build subsets
136
    // infer: X dims N,C,H,W => assume same dims as Y except spatial dims with stride/pad
137

138
    auto int_expr = [](size_t v) { return symbolic::integer(static_cast<int64_t>(v)); };
5✔
139
    auto get_stride = [&](size_t idx) -> symbolic::Expression {
3✔
140
        return idx < strides_.size() ? int_expr(strides_[idx]) : symbolic::one();
2✔
141
    };
142
    auto get_pad = [&](size_t idx) -> symbolic::Expression {
3✔
143
        return idx < pads_.size() ? int_expr(pads_[idx]) : symbolic::zero();
2✔
144
    };
145

146
    // Create innermost block
147
    auto &code_block =
1✔
148
        builder.add_block(*last_scope, {}, builder.subject().debug_info().get_region(block.debug_info().indices()));
1✔
149

150
    // Access nodes
151
    const DebugInfos dbg = builder.subject().debug_info().get_region(block.debug_info().indices());
1✔
152
    auto &X_acc = builder.add_access(code_block, X_name, dbg);
1✔
153
    auto &Y_acc_in = builder.add_access(code_block, Y_name, dbg);
1✔
154
    auto &Y_acc_out = builder.add_access(code_block, Y_name, dbg);
1✔
155

156
    // Build X subset using output coords * stride - pad + kernel_idx
157
    data_flow::Subset subset_X;
1✔
158
    // Assume dims: N, C, spatial...
159
    subset_X.push_back(out_syms[0]); // N
1✔
160
    subset_X.push_back(out_syms[1]); // C
1✔
161
    for (size_t d = 0; d < kernel_syms.size(); ++d) {
3✔
162
        auto expr =
163
            symbolic::sub(symbolic::add(symbolic::mul(get_stride(d), out_syms[2 + d]), kernel_syms[d]), get_pad(d));
2✔
164
        subset_X.push_back(expr);
2✔
165
    }
2✔
166

167
    // Y subset is just out_subset
168
    data_flow::Subset subset_Y = out_subset;
1✔
169

170
    // Tasklet max
171
    auto &tasklet = builder.add_tasklet(code_block, data_flow::TaskletCode::max, "_out", {"_in1", "_in2"}, dbg);
1✔
172

173
    builder.add_computational_memlet(code_block, Y_acc_in, tasklet, "_in1", subset_Y, oedge_Y->base_type(), dbg);
1✔
174
    builder.add_computational_memlet(code_block, X_acc, tasklet, "_in2", subset_X, iedge_X->base_type(), dbg);
1✔
175
    builder.add_computational_memlet(code_block, tasklet, "_out", Y_acc_out, subset_Y, oedge_Y->base_type(), dbg);
1✔
176

177
    // Cleanup old block
178
    builder.remove_memlet(block, *iedge_X);
1✔
179
    builder.remove_memlet(block, *oedge_Y);
1✔
180
    builder.remove_node(block, *this);
1✔
181
    builder.remove_child(parent, index + 1);
1✔
182

183
    return true;
1✔
184
}
1✔
185

186
/*************** Clone ***************/
187
std::unique_ptr<data_flow::DataFlowNode> MaxPoolNode::
188
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph &parent) const {
×
189
    return std::unique_ptr<data_flow::DataFlowNode>(new MaxPoolNode(
×
NEW
190
        element_id, this->debug_info(), vertex, parent, this->shape_, this->kernel_shape_, this->pads_, this->strides_
×
191
    ));
192
}
×
193

194
/*************** Serializer ***************/
195
nlohmann::json MaxPoolNodeSerializer::serialize(const data_flow::LibraryNode &library_node) {
×
196
    const MaxPoolNode &node = static_cast<const MaxPoolNode &>(library_node);
×
197
    nlohmann::json j;
×
198
    j["code"] = node.code().value();
×
199
    j["outputs"] = node.outputs();
×
200
    j["inputs"] = node.inputs();
×
201
    j["kernel_shape"] = node.kernel_shape();
×
202
    j["pads"] = node.pads();
×
203
    j["strides"] = node.strides();
×
204

NEW
205
    serializer::JSONSerializer serializer;
×
NEW
206
    j["shape"] = nlohmann::json::array();
×
NEW
207
    for (auto &dim : node.shape()) {
×
NEW
208
        j["shape"].push_back(serializer.expression(dim));
×
209
    }
210

211
    return j;
×
212
}
×
213

214
data_flow::LibraryNode &MaxPoolNodeSerializer::deserialize(
×
215
    const nlohmann::json &j, builder::StructuredSDFGBuilder &builder, structured_control_flow::Block &parent
216
) {
217
    auto code = j["code"].get<std::string>();
×
218
    if (code != LibraryNodeType_MaxPool.value()) {
×
219
        throw std::runtime_error("Invalid library node code");
×
220
    }
221
    sdfg::serializer::JSONSerializer serializer;
×
222
    DebugInfoRegion debug_info = serializer.json_to_debug_info_region(j["debug_info"], builder.debug_info());
×
223

224
    auto kernel_shape = j["kernel_shape"].get<std::vector<size_t>>();
×
225
    auto pads = j["pads"].get<std::vector<size_t>>();
×
226
    auto strides = j["strides"].get<std::vector<size_t>>();
×
227

NEW
228
    std::vector<symbolic::Expression> shape;
×
NEW
229
    for (const auto &dim : j["shape"]) {
×
NEW
230
        shape.push_back(SymEngine::Expression(dim.get<std::string>()));
×
231
    }
232

NEW
233
    return builder.add_library_node<MaxPoolNode>(parent, debug_info, shape, kernel_shape, pads, strides);
×
UNCOV
234
}
×
235

236
} // namespace ml
237
} // namespace math
238
} // 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