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

daisytuner / docc / 28806128926

06 Jul 2026 04:16PM UTC coverage: 62.96%. First build
28806128926

push

github

web-flow
New LibNodeExpansion pass (#740)

* Switched expansion "pipeline" to new LibNodeExpansionPass that can recursively expand using a LibNodeExpander impl.
- removed access to analysis_manager from expansion methods, as those would currently not be appropriately maintained between nodes
+ New expansion API handles more of the boilerplate code (checking for standalone, creating boundary access nodes, removing old elements)
* Also migrated sdfg-json-to-c.cpp to not using the expansion pipeline anymore
* toStr() for ReduceNodes and giving PyStructuredSDFG access to the output_dir for additional dumping
~ DotVisualizer : Fix on nested sequences.
+ DotVisualizer: visualize empty sequences
* DotVisualizer default-enabled show block/loop ids
 * while MathNodes still have an expand-method, the new infrastructure is based upon "Expander" classes. The MathNodeExpander just redirects to the method for now
 ~ Broadcast node still used old ptr-output semantics
 * updated tensor & blas node expand to new expand API, that handles more of the boilerplate code (checking, removing old nodes, creating standalone-replacement nodes)
 - removed Transpose Node. Was unused and on old ptr-semantics
 + StructuredSDFGBuilder.add_sequence_at, add_for_at, add_map_at
 * switched StructuredSDFGBuilder internally to use ptr of Assignments to express using default assignments when needed
 * updated tests to use the new expand_single_math_node helper function, instead of the method directly
 + pass and expand-single helper functions are ready to use other expanders
 + EinsumExpansionPass is basically the legacy ExpansionPass, only restricted to EinsumNodes. It still needs to run in a pipeline to ensure finding multiple EinsumNodes per block. This is temporary, because Einsum is the only node that already supported splitting a block, which the new expansion disallows, as it should handle it itself when needed (but that part is not yet implemented)

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

40554 of 64412 relevant lines covered (62.96%)

963.11 hits per line

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

2.23
/sdfg/src/data_flow/library_nodes/math/tensor/reduce_ops/softmax_node.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/reduce_ops/softmax_node.h"
2
#include "sdfg/builder/structured_sdfg_builder.h"
3
#include "sdfg/data_flow/library_nodes/math/tensor/broadcast_node.h"
4
#include "sdfg/data_flow/library_nodes/math/tensor/elementwise_ops/div_node.h"
5
#include "sdfg/data_flow/library_nodes/math/tensor/elementwise_ops/exp_node.h"
6
#include "sdfg/data_flow/library_nodes/math/tensor/elementwise_ops/sub_node.h"
7
#include "sdfg/data_flow/library_nodes/math/tensor/reduce_ops/max_node.h"
8
#include "sdfg/data_flow/library_nodes/math/tensor/reduce_ops/sum_node.h"
9
#include "sdfg/data_flow/library_nodes/stdlib/malloc.h"
10
#include "sdfg/structured_control_flow/block.h"
11
#include "sdfg/structured_control_flow/for.h"
12
#include "sdfg/types/pointer.h"
13
#include "sdfg/types/scalar.h"
14
#include "sdfg/types/utils.h"
15

16
namespace sdfg {
17
namespace math {
18
namespace tensor {
19

20
SoftmaxNode::SoftmaxNode(
21
    size_t element_id,
22
    const DebugInfo& debug_info,
23
    const graph::Vertex vertex,
24
    data_flow::DataFlowGraph& parent,
25
    const std::vector<symbolic::Expression>& shape,
26
    const std::vector<int64_t>& axes,
27
    bool keepdims
28
)
29
    : ReduceNode(element_id, debug_info, vertex, parent, LibraryNodeType_Softmax, shape, axes, keepdims) {
8✔
30
    if (keepdims) {
8✔
31
        throw InvalidSDFGException("Unsupported attribute on library node: softmax");
×
32
    }
×
33
}
8✔
34

35
void SoftmaxNode::validate(const Function& function) const {}
12✔
36

NEW
37
passes::LibNodeExpander::ExpandOutcome SoftmaxNode::expand(passes::LibNodeExpander::ExpandContext& context, Block& block) {
×
38
    auto& dataflow = this->get_parent();
×
39

40
    if (dataflow.in_degree(*this) != 2) {
×
NEW
41
        return context.unable();
×
42
    }
×
43

44
    auto* in_edge = dataflow.in_edge_for_connector(*this, "X");
×
45
    auto* out_edge = dataflow.in_edge_for_connector(*this, "Y");
×
46
    if (!in_edge || !out_edge) {
×
NEW
47
        return context.unable();
×
48
    }
×
49

50
    // Calculate reduced shape (for Max and Sum)
51
    std::vector<symbolic::Expression> reduced_shape;
×
52
    std::vector<int64_t> sorted_axes = axes_;
×
53
    // Normalize negative axes
54
    for (auto& axis : sorted_axes) {
×
55
        if (axis < 0) {
×
56
            axis = static_cast<int64_t>(shape_.size()) + axis;
×
57
        }
×
58
        // Validate axis is in bounds
59
        if (axis < 0 || axis >= static_cast<int64_t>(shape_.size())) {
×
60
            throw InvalidSDFGException(
×
61
                "Library Node: Axis value out of bounds. Axis: " + std::to_string(axis) +
×
62
                " Shape size: " + std::to_string(shape_.size())
×
63
            );
×
64
        }
×
65
    }
×
66
    std::sort(sorted_axes.begin(), sorted_axes.end());
×
67

68
    for (size_t i = 0; i < shape_.size(); ++i) {
×
69
        bool is_axis = false;
×
70
        for (auto axis : sorted_axes) {
×
71
            if (axis == (int64_t) i) {
×
72
                is_axis = true;
×
73
                break;
×
74
            }
×
75
        }
×
76

77
        if (is_axis) {
×
78
            reduced_shape.push_back(symbolic::one());
×
79
        } else {
×
80
            reduced_shape.push_back(shape_[i]);
×
81
        }
×
82
    }
×
83

NEW
84
    auto expansion = context.replacement_requires_access_nodes(
×
NEW
85
        {passes::LibNodeExpander::InputUse::IndirectReadWrite, passes::LibNodeExpander::InputUse::IndirectRead}
×
NEW
86
    );
×
87

NEW
88
    if (expansion) {
×
NEW
89
        auto& seq = expansion->replace_with_sequence();
×
NEW
90
        auto& builder = expansion->builder();
×
91

NEW
92
        types::Scalar element_type(this->primitive_type(dataflow));
×
NEW
93
        types::Pointer pointer_type(element_type);
×
94

95
        // Type to store reduced results (e.g., max and sum)
NEW
96
        types::Tensor reduced_tensor_type(element_type, reduced_shape);
×
97
        // Type for broadcasted tensors (e.g., max and sum after broadcasting)
98
        // Compute broadcast strides: use strides from reduced_tensor_type, but set to zero for reduced dimensions
NEW
99
        symbolic::MultiExpression reduced_strides = reduced_tensor_type.strides();
×
NEW
100
        symbolic::MultiExpression broadcast_strides;
×
NEW
101
        for (size_t i = 0; i < shape_.size(); ++i) {
×
NEW
102
            bool is_reduced = std::find(sorted_axes.begin(), sorted_axes.end(), static_cast<int64_t>(i)) !=
×
NEW
103
                              sorted_axes.end();
×
NEW
104
            if (is_reduced) {
×
NEW
105
                broadcast_strides.push_back(symbolic::zero());
×
NEW
106
            } else {
×
NEW
107
                broadcast_strides.push_back(reduced_strides[i]);
×
NEW
108
            }
×
109
        }
×
NEW
110
        types::Tensor broadcast_tensor_type(element_type, shape_, broadcast_strides);
×
111

112
        // Temporary buffers
NEW
113
        std::string tmp_max_name = builder.find_new_name("_softmax_max");
×
NEW
114
        builder.add_container(tmp_max_name, pointer_type);
×
115

NEW
116
        std::string tmp_sub_name = builder.find_new_name("_softmax_sub");
×
NEW
117
        builder.add_container(tmp_sub_name, pointer_type);
×
118

NEW
119
        std::string tmp_exp_name = builder.find_new_name("_softmax_exp");
×
NEW
120
        builder.add_container(tmp_exp_name, pointer_type);
×
121

NEW
122
        std::string tmp_sum_name = builder.find_new_name("_softmax_sum");
×
NEW
123
        builder.add_container(tmp_sum_name, pointer_type);
×
124

125
        // Mallocs
NEW
126
        {
×
NEW
127
            symbolic::Expression bytes_elem = types::get_type_size(element_type, false);
×
128

NEW
129
            symbolic::Expression bytes_full = bytes_elem;
×
NEW
130
            for (auto& dim : this->shape_) {
×
NEW
131
                bytes_full = symbolic::mul(dim, bytes_full);
×
NEW
132
            }
×
133

NEW
134
            symbolic::Expression bytes_reduced = bytes_elem;
×
NEW
135
            for (auto& dim : reduced_shape) {
×
NEW
136
                bytes_reduced = symbolic::mul(dim, bytes_reduced);
×
NEW
137
            }
×
138

NEW
139
            auto& alloc_block = builder.add_block(seq, {}, this->debug_info());
×
140

NEW
141
            auto malloc_helper = [&](const std::string& name, const symbolic::Expression& size) {
×
NEW
142
                auto& access = builder.add_access(alloc_block, name);
×
NEW
143
                auto& malloc_node = builder.add_library_node<stdlib::MallocNode>(alloc_block, this->debug_info(), size);
×
NEW
144
                builder.add_computational_memlet(
×
NEW
145
                    alloc_block, malloc_node, "_ret", access, {}, pointer_type, this->debug_info()
×
NEW
146
                );
×
NEW
147
            };
×
148

NEW
149
            malloc_helper(tmp_max_name, bytes_reduced);
×
NEW
150
            malloc_helper(tmp_sub_name, bytes_full);
×
NEW
151
            malloc_helper(tmp_exp_name, bytes_full);
×
NEW
152
            malloc_helper(tmp_sum_name, bytes_reduced);
×
153
        }
×
154

155
        // 1. Max(X) -> TmpMax
NEW
156
        {
×
NEW
157
            auto& max_block = builder.add_block(seq, {}, this->debug_info());
×
NEW
158
            auto& max_node =
×
NEW
159
                builder.add_library_node<MaxNode>(max_block, this->debug_info(), this->shape_, this->axes_, true);
×
160

NEW
161
            auto& in_access = expansion->add_scalar_input_access(max_block, X_INPUT_IDX);
×
NEW
162
            auto& out_access = builder.add_access(max_block, tmp_max_name);
×
163

NEW
164
            builder.add_computational_memlet(
×
NEW
165
                max_block, in_access, max_node, "X", {}, in_edge->base_type(), this->debug_info()
×
NEW
166
            );
×
NEW
167
            builder.add_computational_memlet(
×
NEW
168
                max_block, out_access, max_node, "Y", {}, reduced_tensor_type, this->debug_info()
×
NEW
169
            );
×
NEW
170
        }
×
171

172
        // 2. Sub(X, TmpMaxBcast) -> TmpSub
NEW
173
        {
×
NEW
174
            auto& sub_block = builder.add_block(seq, {}, this->debug_info());
×
NEW
175
            auto& sub_node = builder.add_library_node<SubNode>(sub_block, this->debug_info(), this->shape_);
×
176

NEW
177
            auto& in1_access = expansion->add_scalar_input_access(sub_block, X_INPUT_IDX);
×
NEW
178
            auto& in2_access = builder.add_access(sub_block, tmp_max_name);
×
NEW
179
            auto& out_access = builder.add_access(sub_block, tmp_sub_name);
×
180

NEW
181
            builder.add_computational_memlet(
×
NEW
182
                sub_block, in1_access, sub_node, "A", {}, in_edge->base_type(), this->debug_info()
×
NEW
183
            );
×
NEW
184
            builder.add_computational_memlet(
×
NEW
185
                sub_block, in2_access, sub_node, "B", {}, broadcast_tensor_type, this->debug_info()
×
NEW
186
            );
×
NEW
187
            builder.add_computational_memlet(
×
NEW
188
                sub_block, out_access, sub_node, "C", {}, in_edge->base_type(), this->debug_info()
×
NEW
189
            );
×
NEW
190
        }
×
191

192
        // 3. Exp(TmpSub) -> TmpExp
NEW
193
        {
×
NEW
194
            auto& exp_block = builder.add_block(seq, {}, this->debug_info());
×
NEW
195
            auto& exp_node = builder.add_library_node<ExpNode>(exp_block, this->debug_info(), this->shape_);
×
196

NEW
197
            auto& in_access = builder.add_access(exp_block, tmp_sub_name);
×
NEW
198
            auto& out_access = builder.add_access(exp_block, tmp_exp_name);
×
199

NEW
200
            builder.add_computational_memlet(
×
NEW
201
                exp_block, in_access, exp_node, "X", {}, in_edge->base_type(), this->debug_info()
×
NEW
202
            );
×
NEW
203
            builder.add_computational_memlet(
×
NEW
204
                exp_block, out_access, exp_node, "Y", {}, in_edge->base_type(), this->debug_info()
×
NEW
205
            );
×
NEW
206
        }
×
207

208
        // 4. Sum(TmpExp) -> TmpSum
NEW
209
        {
×
NEW
210
            auto& sum_block = builder.add_block(seq, {}, this->debug_info());
×
NEW
211
            auto& sum_node =
×
NEW
212
                builder.add_library_node<SumNode>(sum_block, this->debug_info(), this->shape_, this->axes_, true);
×
213

NEW
214
            auto& in_access = builder.add_access(sum_block, tmp_exp_name);
×
NEW
215
            auto& out_access = builder.add_access(sum_block, tmp_sum_name);
×
216

NEW
217
            builder.add_computational_memlet(
×
NEW
218
                sum_block, in_access, sum_node, "X", {}, in_edge->base_type(), this->debug_info()
×
NEW
219
            );
×
NEW
220
            builder.add_computational_memlet(
×
NEW
221
                sum_block, out_access, sum_node, "Y", {}, reduced_tensor_type, this->debug_info()
×
NEW
222
            );
×
NEW
223
        }
×
224

225
        // 5. Div(TmpExp, TmpSum) -> Output
NEW
226
        {
×
NEW
227
            auto& div_block = builder.add_block(seq, {}, this->debug_info());
×
NEW
228
            auto& div_node = builder.add_library_node<DivNode>(div_block, this->debug_info(), this->shape_);
×
229

NEW
230
            auto& in1_access = builder.add_access(div_block, tmp_exp_name);
×
NEW
231
            auto& in2_access = builder.add_access(div_block, tmp_sum_name);
×
NEW
232
            auto& out_access = expansion->add_scalar_input_access(div_block, RESULT_PTR_IDX);
×
233

NEW
234
            builder.add_computational_memlet(
×
NEW
235
                div_block, in1_access, div_node, "A", {}, in_edge->base_type(), this->debug_info()
×
NEW
236
            );
×
NEW
237
            builder.add_computational_memlet(
×
NEW
238
                div_block, in2_access, div_node, "B", {}, broadcast_tensor_type, this->debug_info()
×
NEW
239
            );
×
NEW
240
            builder.add_computational_memlet(
×
NEW
241
                div_block, out_access, div_node, "C", {}, out_edge->base_type(), this->debug_info()
×
NEW
242
            );
×
NEW
243
        }
×
244

NEW
245
        return expansion->successfully_expanded();
×
NEW
246
    } else {
×
NEW
247
        return context.unable();
×
NEW
248
    }
×
NEW
249
}
×
250

251
bool SoftmaxNode::expand_reduction(
252
    passes::LibNodeExpander::AccessNodeExpand& expansion,
253
    builder::StructuredSDFGBuilder& builder,
254
    structured_control_flow::Sequence& body,
255
    const types::Tensor& input_type,
256
    const types::Tensor& output_type,
257
    const data_flow::Subset& input_subset,
258
    const data_flow::Subset& output_subset
NEW
259
) {
×
NEW
260
    throw std::runtime_error("StdNode::expand_reduction should not be called");
×
261
}
×
262

263
std::unique_ptr<data_flow::DataFlowNode> SoftmaxNode::
264
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
265
    return std::unique_ptr<
×
266
        data_flow::DataFlowNode>(new SoftmaxNode(element_id, this->debug_info(), vertex, parent, this->shape_, this->axes_)
×
267
    );
×
268
}
×
269

270
} // namespace tensor
271
} // namespace math
272
} // 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