• 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

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

UNCOV
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");
×
UNCOV
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);
×
UNCOV
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");
×
UNCOV
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