• 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

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