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

daisytuner / docc / 26463753889

26 May 2026 05:18PM UTC coverage: 60.864% (-0.02%) from 60.886%
26463753889

Pull #719

github

web-flow
Merge 0b90ddd88 into 707dadcf8
Pull Request #719: Libnode ptr edges

961 of 1749 new or added lines in 52 files covered. (54.95%)

90 existing lines in 29 files now uncovered.

35222 of 57870 relevant lines covered (60.86%)

11043.61 hits per line

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

34.25
/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) {
4,446✔
11
    if (strides.empty()) {
4,446✔
12
        strides_ = linear_strides();
2,977✔
13
    }
2,977✔
14
}
4,446✔
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) {
2,978✔
49
    symbolic::MultiExpression lin_strides;
2,978✔
50
    if (shape.empty()) {
2,978✔
51
        return lin_strides; // no shape -> no strides. Just a wrapper hiding a scalar
30✔
52
    }
30✔
53
    std::size_t dims = shape.size();
2,948✔
54
    lin_strides.resize(dims);
2,948✔
55
    lin_strides[dims - 1] = symbolic::integer(1);
2,948✔
56
    for (int i = static_cast<int>(dims) - 2; i >= 0; --i) {
4,743✔
57
        lin_strides[i] = symbolic::mul(lin_strides.at(i + 1), shape.at(i + 1));
1,795✔
58
    }
1,795✔
59

60
    return std::move(lin_strides);
2,948✔
61
}
2,978✔
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
symbolic::Expression TensorLayout::total_elements() const { return SymEngine::mul(shape_); }
1✔
89

90
symbolic::MultiExpression TensorLayout::linear_strides() const { return std::move(linear_strides(shape_)); }
2,977✔
91

92
TensorLayout TensorLayout::deserialize_from_json(const nlohmann::json& j) {
×
93
    symbolic::MultiExpression shape;
×
94
    for (const auto& dim : j["shape"]) {
×
95
        shape.push_back(symbolic::parse(dim.get<std::string>()));
×
96
    }
×
97

98
    symbolic::MultiExpression strides;
×
99
    for (const auto& stride : j["strides"]) {
×
100
        strides.push_back(symbolic::parse(stride.get<std::string>()));
×
101
    }
×
102

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

105
    return std::move(TensorLayout(shape, strides, offset));
×
106
}
×
107

108
std::ostream& operator<<(std::ostream& stream, const TensorLayout& layout) {
×
109
    stream << "{shape[";
×
110
    for (size_t i = 0; i < layout.shape().size(); ++i) {
×
111
        if (i > 0) stream << ", ";
×
112
        stream << layout.shape().at(i)->__str__();
×
113
    }
×
114
    stream << "], strides=[";
×
115
    for (size_t i = 0; i < layout.strides().size(); ++i) {
×
116
        if (i > 0) stream << ", ";
×
117
        stream << layout.strides().at(i)->__str__();
×
118
    }
×
119
    stream << "]";
×
120
    if (SymEngine::neq(*layout.offset(), *symbolic::integer(0))) {
×
121
        stream << ", off=" << layout.offset()->__str__();
×
122
    }
×
123
    stream << "}";
×
124

125
    return stream;
×
126
}
×
127

128
bool TensorLayout::has_linear_accesses_no_padding(symbolic::MultiExpression shape, symbolic::MultiExpression strides) {
8✔
129
    auto basic_strides = types::Tensor::strides_from_shape(shape);
8✔
130
    if (basic_strides.size() != strides.size()) {
8✔
131
        return false;
×
132
    }
×
133
    for (size_t i = 0; i < strides.size(); i++) {
26✔
134
        if (!symbolic::eq(basic_strides.at(i), strides.at(i))) {
18✔
135
            return false;
×
136
        }
×
137
    }
18✔
138
    return true;
8✔
139
}
8✔
140

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

143
bool TensorLayout::has_transposed_strides_no_padding() const {
×
144
    if (shape_.size() < 2) {
×
145
        return false;
×
146
    }
×
147
    symbolic::MultiExpression new_shape;
×
148
    new_shape.reserve(shape_.size());
×
149
    for (size_t i = 0; i < shape_.size() - 2; i++) {
×
150
        new_shape.push_back(shape_.at(i));
×
151
    }
×
152
    new_shape.push_back(shape_.at(shape_.size() - 1));
×
153
    new_shape.push_back(shape_.at(shape_.size() - 2));
×
154
    symbolic::MultiExpression transposed_strides(strides_);
×
155
    transposed_strides[strides_.size() - 2] = strides_.at(strides_.size() - 1);
×
156
    transposed_strides[strides_.size() - 1] = strides_.at(strides_.size() - 2);
×
157
    return TensorLayout::has_linear_accesses_no_padding(new_shape, transposed_strides);
×
158
}
×
159

160
bool TensorLayout::operator==(const TensorLayout& other) const {
7✔
161
    if (!symbolic::eq(this->offset_, other.offset_)) {
7✔
NEW
162
        return false;
×
NEW
163
    }
×
164

165
    if (this->shape_.size() != other.shape_.size()) {
7✔
NEW
166
        return false;
×
NEW
167
    }
×
168
    for (size_t i = 0; i < this->shape_.size(); ++i) {
11✔
169
        if (!symbolic::eq(this->get_dim(i), other.get_dim(i))) {
5✔
170
            return false;
1✔
171
        }
1✔
172
    }
5✔
173

174
    if (this->strides_.size() != other.strides_.size()) {
6✔
NEW
175
        return false;
×
NEW
176
    }
×
177
    for (size_t i = 0; i < this->strides_.size(); ++i) {
9✔
178
        if (!symbolic::eq(this->get_stride(i), other.get_stride(i))) {
3✔
NEW
179
            return false;
×
NEW
180
        }
×
181
    }
3✔
182

183
    return true;
6✔
184
};
6✔
185

NEW
186
std::unique_ptr<TensorLayout> TensorLayout::newaxis(size_t axis) const {
×
NEW
187
    if (axis > this->shape_.size()) {
×
NEW
188
        throw std::out_of_range("axis out of range for newaxis");
×
NEW
189
    }
×
190

NEW
191
    symbolic::MultiExpression new_shape = this->shape_;
×
NEW
192
    symbolic::MultiExpression new_strides = this->strides_;
×
193

NEW
194
    new_shape.insert(new_shape.begin() + axis, SymEngine::integer(1));
×
NEW
195
    new_strides.insert(new_strides.begin() + axis, SymEngine::integer(0));
×
196

NEW
197
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
198
}
×
199

200
std::unique_ptr<TensorLayout> TensorLayout::flip(size_t axis) const {
1✔
201
    if (axis >= shape_.size()) {
1✔
NEW
202
        throw std::out_of_range("axis out of range for flip");
×
NEW
203
    }
×
204

205
    symbolic::MultiExpression new_strides = this->strides_;
1✔
206

207
    // Negate the stride for the specified axis
208
    new_strides[axis] = SymEngine::neg(this->strides_[axis]);
1✔
209

210
    // Compute new offset: offset += stride * (shape - 1)
211
    auto shape_minus_one = SymEngine::sub(this->shape_[axis], SymEngine::integer(1));
1✔
212
    auto offset_adjustment = SymEngine::mul(this->strides_[axis], shape_minus_one);
1✔
213

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

216
    return std::make_unique<TensorLayout>(this->shape_, new_strides, new_offset);
1✔
217
}
1✔
218

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

NEW
221
std::unique_ptr<TensorLayout> TensorLayout::squeeze(size_t axis) const {
×
NEW
222
    if (axis >= this->shape_.size()) {
×
NEW
223
        throw std::out_of_range("axis out of range for squeeze");
×
NEW
224
    }
×
225

NEW
226
    if (!SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(axis))) {
×
NEW
227
        throw std::invalid_argument("cannot squeeze axis with symbolic size");
×
NEW
228
    }
×
NEW
229
    auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(axis))->as_int();
×
NEW
230
    if (dim_size != 1) {
×
NEW
231
        throw std::invalid_argument("cannot squeeze axis with size != 1");
×
NEW
232
    }
×
233

NEW
234
    symbolic::MultiExpression new_shape = this->shape_;
×
NEW
235
    symbolic::MultiExpression new_strides = this->strides_;
×
236

NEW
237
    new_shape.erase(new_shape.begin() + axis);
×
NEW
238
    new_strides.erase(new_strides.begin() + axis);
×
239

NEW
240
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
241
}
×
242

NEW
243
std::unique_ptr<TensorLayout> TensorLayout::squeeze() const {
×
NEW
244
    symbolic::MultiExpression new_shape;
×
NEW
245
    symbolic::MultiExpression new_strides;
×
246

NEW
247
    for (size_t i = 0; i < this->shape_.size(); ++i) {
×
NEW
248
        bool is_size_one = false;
×
NEW
249
        if (SymEngine::is_a<SymEngine::Integer>(*this->shape_.at(i))) {
×
NEW
250
            auto dim_size = SymEngine::rcp_dynamic_cast<const SymEngine::Integer>(this->shape_.at(i))->as_int();
×
NEW
251
            is_size_one = (dim_size == 1);
×
NEW
252
        }
×
253

NEW
254
        if (!is_size_one) {
×
NEW
255
            new_shape.push_back(this->shape_.at(i));
×
NEW
256
            new_strides.push_back(this->strides_.at(i));
×
NEW
257
        }
×
NEW
258
    }
×
259

NEW
260
    return std::make_unique<TensorLayout>(new_shape, new_strides, this->offset_);
×
NEW
261
}
×
262

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

267
    // Compute the total number of elements in the new shape
268
    symbolic::Expression new_total_elements = symbolic::one();
1✔
269
    for (const auto& dim : new_shape) {
2✔
270
        new_total_elements = symbolic::mul(new_total_elements, dim);
2✔
271
    }
2✔
272

273
    // Check if the total number of elements matches
274
    if (!symbolic::eq(total_elements, new_total_elements)) {
1✔
NEW
275
        throw std::invalid_argument("total number of elements must match for reshape");
×
NEW
276
    }
×
277

278
    // Compute new strides based on the new shape
279
    symbolic::MultiExpression new_strides = linear_strides(new_shape);
1✔
280

281
    return std::make_unique<TensorLayout>(new_shape, new_strides, offset_);
1✔
282
}
1✔
283

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