• 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

32.9
/sdfg/src/data_flow/library_nodes/math/tensor/tensor_layout.cpp
1
#include "sdfg/data_flow/library_nodes/math/tensor/tensor_layout.h"
2

3
#include "sdfg/serializer/json_serializer.h"
4

5
namespace sdfg::math::tensor {
6

7
TensorLayout::TensorLayout(
8
    const symbolic::MultiExpression& shape, const symbolic::MultiExpression& strides, const symbolic::Expression offset
9
)
10
    : shape_(shape), strides_(strides), offset_(offset) {
9,742✔
11
    if (strides.empty()) {
9,742✔
12
        strides_ = linear_strides();
8,212✔
13
    }
8,212✔
14
}
9,742✔
15

16
void TensorLayout::serialize_to_json(nlohmann::json& j) const {
×
17
    nlohmann::json shape_arr = nlohmann::json::array();
×
18
    sdfg::serializer::JSONSerializer serializer;
×
19

20
    for (auto& dim : shape_) {
×
21
        shape_arr.push_back(serializer.expression(dim));
×
22
    }
×
23
    j["shape"] = shape_arr;
×
24

25
    nlohmann::json stride_arr = nlohmann::json::array();
×
26
    for (auto& stride : strides_) {
×
27
        stride_arr.push_back(serializer.expression(stride));
×
28
    }
×
29
    j["strides"] = stride_arr;
×
30

31
    j["offset"] = serializer.expression(offset_);
×
32
}
×
33

34
std::string TensorLayout::toStr() const {
×
35
    std::stringstream ss;
×
36
    ss << "TLayout(shape=[";
×
37
    for (auto& s : shape_) {
×
38
        ss << s->__str__() << ",";
×
39
    }
×
40
    ss << "], strides=[";
×
41
    for (auto& s : strides_) {
×
42
        ss << s->__str__() << ",";
×
43
    }
×
44
    ss << "])";
×
45
    return ss.str();
×
46
}
×
47

48
symbolic::MultiExpression TensorLayout::linear_strides(const symbolic::MultiExpression& shape) {
8,213✔
49
    symbolic::MultiExpression lin_strides;
8,213✔
50
    if (shape.empty()) {
8,213✔
51
        return lin_strides; // no shape -> no strides. Just a wrapper hiding a scalar
25✔
52
    }
25✔
53
    std::size_t dims = shape.size();
8,188✔
54
    lin_strides.resize(dims);
8,188✔
55
    lin_strides[dims - 1] = symbolic::integer(1);
8,188✔
56
    for (int i = static_cast<int>(dims) - 2; i >= 0; --i) {
10,793✔
57
        lin_strides[i] = symbolic::mul(lin_strides.at(i + 1), shape.at(i + 1));
2,605✔
58
    }
2,605✔
59

60
    return std::move(lin_strides);
8,188✔
61
}
8,213✔
62
void TensorLayout::collect_symbols(symbolic::SymbolSet& syms) const {
26✔
63
    for (const auto& dim : shape_) {
100✔
64
        for (auto& atom : symbolic::atoms(dim)) {
100✔
65
            syms.insert(atom);
4✔
66
        }
4✔
67
    }
100✔
68
    for (const auto& dim : strides_) {
100✔
69
        for (auto& atom : symbolic::atoms(dim)) {
100✔
70
            syms.insert(atom);
2✔
71
        }
2✔
72
    }
100✔
73
    for (auto& atom : symbolic::atoms(offset_)) {
26✔
74
        syms.insert(atom);
×
75
    }
×
76
}
26✔
77

78
void TensorLayout::replace_symbols(const symbolic::Expression& old, const symbolic::Expression& new_expr) {
×
79
    for (auto& dim : shape_) {
×
80
        dim = symbolic::subs(dim, old, new_expr);
×
81
    }
×
82
    for (auto& stride : strides_) {
×
83
        stride = symbolic::subs(stride, old, new_expr);
×
84
    }
×
85
    offset_ = symbolic::subs(offset_, old, new_expr);
×
86
}
×
87

88
void TensorLayout::replace_symbols(const symbolic::ExpressionMapping& replacements) {
×
89
    for (auto& dim : shape_) {
×
90
        dim = symbolic::subs(dim, replacements);
×
91
    }
×
92
    for (auto& stride : strides_) {
×
93
        stride = symbolic::subs(stride, replacements);
×
94
    }
×
95
    offset_ = symbolic::subs(offset_, replacements);
×
96
}
×
97

98
symbolic::Expression TensorLayout::total_elements() const { return SymEngine::mul(shape_); }
1✔
99

100
symbolic::MultiExpression TensorLayout::linear_strides() const { return std::move(linear_strides(shape_)); }
8,212✔
101

102
bool TensorLayout::is_scalar() const { return shape_.empty(); }
8,551✔
103

104
TensorLayout TensorLayout::deserialize_from_json(const nlohmann::json& j) {
×
105
    symbolic::MultiExpression shape;
×
106
    for (const auto& dim : j["shape"]) {
×
107
        shape.push_back(symbolic::parse(dim.get<std::string>()));
×
108
    }
×
109

110
    symbolic::MultiExpression strides;
×
111
    for (const auto& stride : j["strides"]) {
×
112
        strides.push_back(symbolic::parse(stride.get<std::string>()));
×
113
    }
×
114

115
    symbolic::Expression offset = symbolic::parse(j["offset"].get<std::string>());
×
116

117
    return std::move(TensorLayout(shape, strides, offset));
×
118
}
×
119

NEW
120
std::ostream& TensorLayout::emit_symbolic_list(std::ostream& stream, const symbolic::MultiExpression& list) {
×
NEW
121
    stream << "[";
×
NEW
122
    for (size_t i = 0; i < list.size(); ++i) {
×
123
        if (i > 0) stream << ", ";
×
NEW
124
        stream << list.at(i)->__str__();
×
125
    }
×
126
    stream << "]";
×
NEW
127
    return stream;
×
NEW
128
}
×
129

NEW
130
std::ostream& operator<<(std::ostream& stream, const TensorLayout& layout) {
×
NEW
131
    stream << "{shape=";
×
NEW
132
    TensorLayout::emit_symbolic_list(stream, layout.shape());
×
NEW
133
    stream << ", strides=";
×
NEW
134
    TensorLayout::emit_symbolic_list(stream, layout.strides());
×
135
    if (SymEngine::neq(*layout.offset(), *symbolic::integer(0))) {
×
136
        stream << ", off=" << layout.offset()->__str__();
×
137
    }
×
138
    stream << "}";
×
139

140
    return stream;
×
141
}
×
142

143
bool TensorLayout::has_linear_accesses_no_padding(symbolic::MultiExpression shape, symbolic::MultiExpression strides) {
8✔
144
    auto basic_strides = types::Tensor::strides_from_shape(shape);
8✔
145
    if (basic_strides.size() != strides.size()) {
8✔
146
        return false;
×
147
    }
×
148
    for (size_t i = 0; i < strides.size(); i++) {
26✔
149
        if (!symbolic::eq(basic_strides.at(i), strides.at(i))) {
18✔
150
            return false;
×
151
        }
×
152
    }
18✔
153
    return true;
8✔
154
}
8✔
155

156
bool TensorLayout::has_linear_accesses_no_padding() const { return has_linear_accesses_no_padding(shape_, strides_); }
8✔
157

158
bool TensorLayout::has_transposed_strides_no_padding() const {
×
159
    if (shape_.size() < 2) {
×
160
        return false;
×
161
    }
×
162
    symbolic::MultiExpression new_shape;
×
163
    new_shape.reserve(shape_.size());
×
164
    for (size_t i = 0; i < shape_.size() - 2; i++) {
×
165
        new_shape.push_back(shape_.at(i));
×
166
    }
×
167
    new_shape.push_back(shape_.at(shape_.size() - 1));
×
168
    new_shape.push_back(shape_.at(shape_.size() - 2));
×
169
    symbolic::MultiExpression transposed_strides(strides_);
×
170
    transposed_strides[strides_.size() - 2] = strides_.at(strides_.size() - 1);
×
171
    transposed_strides[strides_.size() - 1] = strides_.at(strides_.size() - 2);
×
172
    return TensorLayout::has_linear_accesses_no_padding(new_shape, transposed_strides);
×
173
}
×
174

175
bool TensorLayout::operator==(const TensorLayout& other) const {
7✔
176
    if (!symbolic::eq(this->offset_, other.offset_)) {
7✔
177
        return false;
×
178
    }
×
179

180
    if (this->shape_.size() != other.shape_.size()) {
7✔
181
        return false;
×
182
    }
×
183
    for (size_t i = 0; i < this->shape_.size(); ++i) {
11✔
184
        if (!symbolic::eq(this->get_dim(i), other.get_dim(i))) {
5✔
185
            return false;
1✔
186
        }
1✔
187
    }
5✔
188

189
    if (this->strides_.size() != other.strides_.size()) {
6✔
190
        return false;
×
191
    }
×
192
    for (size_t i = 0; i < this->strides_.size(); ++i) {
9✔
193
        if (!symbolic::eq(this->get_stride(i), other.get_stride(i))) {
3✔
194
            return false;
×
195
        }
×
196
    }
3✔
197

198
    return true;
6✔
199
};
6✔
200

201
std::unique_ptr<TensorLayout> TensorLayout::newaxis(size_t axis) const {
×
202
    if (axis > this->shape_.size()) {
×
203
        throw std::out_of_range("axis out of range for newaxis");
×
204
    }
×
205

206
    symbolic::MultiExpression new_shape = this->shape_;
×
207
    symbolic::MultiExpression new_strides = this->strides_;
×
208

209
    new_shape.insert(new_shape.begin() + axis, SymEngine::integer(1));
×
210
    new_strides.insert(new_strides.begin() + axis, SymEngine::integer(0));
×
211

212
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
213
}
×
214

215
std::unique_ptr<TensorLayout> TensorLayout::flip(size_t axis) const {
1✔
216
    if (axis >= shape_.size()) {
1✔
217
        throw std::out_of_range("axis out of range for flip");
×
218
    }
×
219

220
    symbolic::MultiExpression new_strides = this->strides_;
1✔
221

222
    // Negate the stride for the specified axis
223
    new_strides[axis] = SymEngine::neg(this->strides_[axis]);
1✔
224

225
    // Compute new offset: offset += stride * (shape - 1)
226
    auto shape_minus_one = SymEngine::sub(this->shape_[axis], SymEngine::integer(1));
1✔
227
    auto offset_adjustment = SymEngine::mul(this->strides_[axis], shape_minus_one);
1✔
228

229
    symbolic::Expression new_offset = SymEngine::add(this->offset_, offset_adjustment);
1✔
230

231
    return std::make_unique<TensorLayout>(this->shape_, new_strides, new_offset);
1✔
232
}
1✔
233

234
std::unique_ptr<TensorLayout> TensorLayout::unsqueeze(size_t axis) const { return this->newaxis(axis); }
×
235

236
std::unique_ptr<TensorLayout> TensorLayout::squeeze(size_t axis) const {
×
237
    if (axis >= this->shape_.size()) {
×
238
        throw std::out_of_range("axis out of range for squeeze");
×
239
    }
×
240

241
    if (!SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(axis))) {
×
242
        throw std::invalid_argument("cannot squeeze axis with symbolic size");
×
243
    }
×
244
    auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(axis))->as_int();
×
245
    if (dim_size != 1) {
×
246
        throw std::invalid_argument("cannot squeeze axis with size != 1");
×
247
    }
×
248

249
    symbolic::MultiExpression new_shape = this->shape_;
×
250
    symbolic::MultiExpression new_strides = this->strides_;
×
251

252
    new_shape.erase(new_shape.begin() + axis);
×
253
    new_strides.erase(new_strides.begin() + axis);
×
254

255
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
256
}
×
257

258
std::unique_ptr<TensorLayout> TensorLayout::squeeze() const {
×
259
    symbolic::MultiExpression new_shape;
×
260
    symbolic::MultiExpression new_strides;
×
261

262
    for (size_t i = 0; i < this->shape_.size(); ++i) {
×
263
        bool is_size_one = false;
×
264
        if (SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(i))) {
×
265
            auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(i))->as_int();
×
266
            is_size_one = (dim_size == 1);
×
267
        }
×
268

269
        if (!is_size_one) {
×
270
            new_shape.push_back(this->shape_.at(i));
×
271
            new_strides.push_back(this->strides_.at(i));
×
272
        }
×
273
    }
×
274

275
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
276
}
×
277

278
std::unique_ptr<TensorLayout> TensorLayout::reshape(const symbolic::MultiExpression& new_shape) const {
1✔
279
    // Compute the total number of elements in the current shape
280
    symbolic::Expression total_elements = this->total_elements();
1✔
281

282
    // Compute the total number of elements in the new shape
283
    symbolic::Expression new_total_elements = symbolic::one();
1✔
284
    for (const auto& dim : new_shape) {
2✔
285
        new_total_elements = symbolic::mul(new_total_elements, dim);
2✔
286
    }
2✔
287

288
    // Check if the total number of elements matches
289
    if (!symbolic::eq(total_elements, new_total_elements)) {
1✔
290
        throw std::invalid_argument("total number of elements must match for reshape");
×
291
    }
×
292

293
    // Compute new strides based on the new shape
294
    symbolic::MultiExpression new_strides = linear_strides(new_shape);
1✔
295

296
    return std::make_unique<TensorLayout>(new_shape, new_strides, offset_);
1✔
297
}
1✔
298

299
} // namespace sdfg::math::tensor
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