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

daisytuner / docc / 28158800507

25 Jun 2026 08:57AM UTC coverage: 61.644% (+0.06%) from 61.582%
28158800507

push

github

web-flow
MapFusionByDomain (#771)

 + New Map fusion caches data about iteration domain and map candidates
 + only matches up iteration domain exactly, per loop level.
 + Can support fusing non-leaf stacks of loops (stack ends where the shallower stack stops being perfectly nested & parallel)
 + new Element::replace for bulk replacements
 + New PatternMatcher visitor supports descending into replaced or modified nodes to allow for single-pass nested loop fusings
 + LoopAnalysis can now be kept up-to-date with changes done by Map-fusion
 + unit tests for the updating of LoopAnalysis
 * updated LoopAnalysis to be easier to keep up-to-date with changes. LoopTree is no longer ordered, if you want to iterate in pre-order, use the specific method for that
 + convenience StructuredSDFGBuilder.remove_from_parent()
 + RedundantLoadElim pass to skip reading from memory locations that have just been written (same block). Fusing no longer needs to do this
     RedundantLoadElimination does a simple check for other writes to the same structure. Can skip writes if redundant or not modify, if their are writes to different indices
* Updated verifiers to match new fusion
~ moved verifier checks behind correctness checks in npbench harness. Its more critical if we do not even get the expected results
* Added MapFusionByDomain also to loop-norm stage (currently inactive, causes more kernels that currently cannot be safely offloaded to CUDA.
---------

Co-authored-by: Lukas Truemper <lukas.truemper@outlook.de>

771 of 1186 new or added lines in 55 files covered. (65.01%)

6 existing lines in 6 files now uncovered.

38302 of 62134 relevant lines covered (61.64%)

987.24 hits per line

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

33.19
/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,995✔
11
    if (strides.empty()) {
9,995✔
12
        strides_ = linear_strides();
8,466✔
13
    }
8,466✔
14
}
9,995✔
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,467✔
49
    symbolic::MultiExpression lin_strides;
8,467✔
50
    if (shape.empty()) {
8,467✔
51
        return lin_strides; // no shape -> no strides. Just a wrapper hiding a scalar
14✔
52
    }
14✔
53
    std::size_t dims = shape.size();
8,453✔
54
    lin_strides.resize(dims);
8,453✔
55
    lin_strides[dims - 1] = symbolic::integer(1);
8,453✔
56
    for (int i = static_cast<int>(dims) - 2; i >= 0; --i) {
11,067✔
57
        lin_strides[i] = symbolic::mul(lin_strides.at(i + 1), shape.at(i + 1));
2,614✔
58
    }
2,614✔
59

60
    return std::move(lin_strides);
8,453✔
61
}
8,467✔
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

NEW
88
void TensorLayout::replace_symbols(const symbolic::ExpressionMapping& replacements) {
×
NEW
89
    for (auto& dim : shape_) {
×
NEW
90
        dim = symbolic::subs(dim, replacements);
×
NEW
91
    }
×
NEW
92
    for (auto& stride : strides_) {
×
NEW
93
        stride = symbolic::subs(stride, replacements);
×
NEW
94
    }
×
NEW
95
    offset_ = symbolic::subs(offset_, replacements);
×
NEW
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,466✔
101

102
bool TensorLayout::is_scalar() const { return shape_.empty(); }
8,416✔
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

120
std::ostream& operator<<(std::ostream& stream, const TensorLayout& layout) {
×
121
    stream << "{shape[";
×
122
    for (size_t i = 0; i < layout.shape().size(); ++i) {
×
123
        if (i > 0) stream << ", ";
×
124
        stream << layout.shape().at(i)->__str__();
×
125
    }
×
126
    stream << "], strides=[";
×
127
    for (size_t i = 0; i < layout.strides().size(); ++i) {
×
128
        if (i > 0) stream << ", ";
×
129
        stream << layout.strides().at(i)->__str__();
×
130
    }
×
131
    stream << "]";
×
132
    if (SymEngine::neq(*layout.offset(), *symbolic::integer(0))) {
×
133
        stream << ", off=" << layout.offset()->__str__();
×
134
    }
×
135
    stream << "}";
×
136

137
    return stream;
×
138
}
×
139

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

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

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

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

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

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

195
    return true;
6✔
196
};
6✔
197

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

203
    symbolic::MultiExpression new_shape = this->shape_;
×
204
    symbolic::MultiExpression new_strides = this->strides_;
×
205

206
    new_shape.insert(new_shape.begin() + axis, SymEngine::integer(1));
×
207
    new_strides.insert(new_strides.begin() + axis, SymEngine::integer(0));
×
208

209
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
210
}
×
211

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

217
    symbolic::MultiExpression new_strides = this->strides_;
1✔
218

219
    // Negate the stride for the specified axis
220
    new_strides[axis] = SymEngine::neg(this->strides_[axis]);
1✔
221

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

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

228
    return std::make_unique<TensorLayout>(this->shape_, new_strides, new_offset);
1✔
229
}
1✔
230

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

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

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

246
    symbolic::MultiExpression new_shape = this->shape_;
×
247
    symbolic::MultiExpression new_strides = this->strides_;
×
248

249
    new_shape.erase(new_shape.begin() + axis);
×
250
    new_strides.erase(new_strides.begin() + axis);
×
251

252
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
253
}
×
254

255
std::unique_ptr<TensorLayout> TensorLayout::squeeze() const {
×
256
    symbolic::MultiExpression new_shape;
×
257
    symbolic::MultiExpression new_strides;
×
258

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

266
        if (!is_size_one) {
×
267
            new_shape.push_back(this->shape_.at(i));
×
268
            new_strides.push_back(this->strides_.at(i));
×
269
        }
×
270
    }
×
271

272
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
273
}
×
274

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

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

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

290
    // Compute new strides based on the new shape
291
    symbolic::MultiExpression new_strides = linear_strides(new_shape);
1✔
292

293
    return std::make_unique<TensorLayout>(new_shape, new_strides, offset_);
1✔
294
}
1✔
295

296
} // 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