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

daisytuner / sdfglib / 15787041637

20 Jun 2025 08:14PM UTC coverage: 63.076% (-1.5%) from 64.591%
15787041637

Pull #95

github

web-flow
Merge 8c012fc8f into 37b47b09d
Pull Request #95: Extends Data Dependency Analysis

717 of 900 new or added lines in 19 files covered. (79.67%)

95 existing lines in 7 files now uncovered.

7788 of 12347 relevant lines covered (63.08%)

134.28 hits per line

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

83.12
/src/symbolic/utils.cpp
1
#include "sdfg/symbolic/utils.h"
2

3
#include <isl/ctx.h>
4
#include <isl/set.h>
5
#include <isl/space.h>
6

7
#include <regex>
8

9
#include "sdfg/codegen/language_extensions/c_language_extension.h"
10
#include "sdfg/symbolic/assumptions.h"
11
#include "sdfg/symbolic/extreme_values.h"
12
#include "sdfg/symbolic/polynomials.h"
13
#include "sdfg/symbolic/symbolic.h"
14

15
namespace sdfg {
16
namespace symbolic {
17

18
std::string expression_to_map_str(const MultiExpression& expr, const Assumptions& assums) {
118✔
19
    codegen::CLanguageExtension language_extension;
118✔
20

21
    // Get all symbols
22
    symbolic::SymbolSet syms;
118✔
23
    for (auto& expr : expr) {
162✔
24
        auto syms_expr = symbolic::atoms(expr);
44✔
25
        syms.insert(syms_expr.begin(), syms_expr.end());
44✔
26
    }
44✔
27

28
    // Distinguish between dimensions and parameters
29
    std::vector<std::string> dimensions;
118✔
30
    SymbolSet dimensions_syms;
118✔
31
    std::vector<std::string> parameters;
118✔
32
    SymbolSet parameters_syms;
118✔
33
    for (auto& sym : syms) {
136✔
34
        if (is_parameter(sym, assums)) {
18✔
NEW
35
            if (parameters_syms.find(sym) != parameters_syms.end()) {
×
NEW
36
                continue;
×
37
            }
NEW
38
            parameters.push_back(sym->get_name());
×
NEW
39
            parameters_syms.insert(sym);
×
NEW
40
        } else {
×
41
            if (dimensions_syms.find(sym) != dimensions_syms.end()) {
18✔
NEW
42
                continue;
×
43
            }
44
            dimensions.push_back(sym->get_name());
18✔
45
            dimensions_syms.insert(sym);
18✔
46
        }
47
    }
48

49
    // Generate constraints
50
    SymbolSet seen;
118✔
51
    auto constraints_syms = generate_constraints(syms, assums, seen);
118✔
52

53
    // Extend parameters with additional symbols from constraints
54
    for (auto& con : constraints_syms) {
164✔
55
        auto con_syms = symbolic::atoms(con);
46✔
56
        for (auto& con_sym : con_syms) {
110✔
57
            if (dimensions_syms.find(con_sym) == dimensions_syms.end()) {
64✔
58
                if (parameters_syms.find(con_sym) != parameters_syms.end()) {
24✔
59
                    continue;
12✔
60
                }
61
                parameters.push_back(con_sym->get_name());
12✔
62
                parameters_syms.insert(con_sym);
12✔
63
            }
12✔
64
        }
65
    }
46✔
66

67
    // Define map
68
    std::string map;
118✔
69
    if (!parameters.empty()) {
118✔
70
        std::sort(parameters.begin(), parameters.end());
6✔
71
        map += "[";
6✔
72
        map += helpers::join(parameters, ", ");
6✔
73
        map += "] -> ";
6✔
74
    }
6✔
75
    map += "{ [" + helpers::join(dimensions, ", ") + "] -> [";
118✔
76
    for (size_t i = 0; i < expr.size(); i++) {
162✔
77
        auto dim = expr[i];
44✔
78
        map += language_extension.expression(dim);
44✔
79
        if (i < expr.size() - 1) {
44✔
NEW
80
            map += ", ";
×
NEW
81
        }
×
82
    }
44✔
83
    map += "] ";
118✔
84

85
    std::vector<std::string> constraints;
118✔
86
    for (auto& con : constraints_syms) {
164✔
87
        auto con_str = constraint_to_isl_str(con);
46✔
88
        if (!con_str.empty()) {
46✔
89
            constraints.push_back(con_str);
44✔
90
        }
44✔
91
    }
46✔
92
    if (!constraints.empty()) {
118✔
93
        map += " : ";
18✔
94
        map += helpers::join(constraints, " and ");
18✔
95
    }
18✔
96

97
    map += " }";
118✔
98

99
    map = std::regex_replace(map, std::regex("\\."), "_");
118✔
100
    return map;
118✔
101
}
118✔
102

103
ExpressionSet generate_constraints(SymbolSet& syms, const Assumptions& assums, SymbolSet& seen) {
170✔
104
    ExpressionSet constraints;
170✔
105
    for (auto& sym : syms) {
260✔
106
        if (assums.find(sym) == assums.end()) {
90✔
107
            continue;
8✔
108
        }
109
        if (seen.find(sym) != seen.end()) {
82✔
110
            continue;
58✔
111
        }
112
        seen.insert(sym);
24✔
113

114
        auto ub = assums.at(sym).upper_bound();
24✔
115
        auto lb = assums.at(sym).lower_bound();
24✔
116
        if (!symbolic::eq(ub, symbolic::infty(1))) {
24✔
117
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
24✔
118
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
2✔
119
                auto args = min->get_args();
2✔
120
                for (auto& arg : args) {
6✔
121
                    auto con = symbolic::Le(sym, arg);
4✔
122
                    auto con_syms = symbolic::atoms(con);
4✔
123
                    constraints.insert(con);
4✔
124

125
                    auto con_cons = generate_constraints(con_syms, assums, seen);
4✔
126
                    constraints.insert(con_cons.begin(), con_cons.end());
4✔
127
                }
4✔
128
            } else {
2✔
129
                auto con = symbolic::Le(sym, ub);
22✔
130
                auto con_syms = symbolic::atoms(con);
22✔
131
                constraints.insert(con);
22✔
132

133
                auto con_cons = generate_constraints(con_syms, assums, seen);
22✔
134
                constraints.insert(con_cons.begin(), con_cons.end());
22✔
135
            }
22✔
136
        }
24✔
137
        if (!symbolic::eq(lb, symbolic::infty(-1))) {
24✔
138
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
24✔
139
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb);
2✔
140
                auto args = max->get_args();
2✔
141
                for (auto& arg : args) {
6✔
142
                    auto con = symbolic::Ge(sym, arg);
4✔
143
                    auto con_syms = symbolic::atoms(con);
4✔
144
                    constraints.insert(con);
4✔
145

146
                    auto con_cons = generate_constraints(con_syms, assums, seen);
4✔
147
                    constraints.insert(con_cons.begin(), con_cons.end());
4✔
148
                }
4✔
149
            } else {
2✔
150
                auto con = symbolic::Ge(sym, lb);
22✔
151
                auto con_syms = symbolic::atoms(con);
22✔
152
                constraints.insert(con);
22✔
153

154
                auto con_cons = generate_constraints(con_syms, assums, seen);
22✔
155
                constraints.insert(con_cons.begin(), con_cons.end());
22✔
156
            }
22✔
157
        }
24✔
158
    }
24✔
159
    return constraints;
170✔
160
}
170✔
161

162
std::string constraint_to_isl_str(const Expression& con) {
46✔
163
    codegen::CLanguageExtension language_extension;
46✔
164

165
    if (SymEngine::is_a<SymEngine::StrictLessThan>(*con)) {
46✔
NEW
166
        auto le = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(con);
×
NEW
167
        auto lhs = le->get_arg1();
×
NEW
168
        auto rhs = le->get_arg2();
×
NEW
169
        return language_extension.expression(lhs) + " < " + language_extension.expression(rhs);
×
170
    } else if (SymEngine::is_a<SymEngine::LessThan>(*con)) {
46✔
171
        auto le = SymEngine::rcp_static_cast<const SymEngine::LessThan>(con);
44✔
172
        auto lhs = le->get_arg1();
44✔
173
        auto rhs = le->get_arg2();
44✔
174
        return language_extension.expression(lhs) + " <= " + language_extension.expression(rhs);
44✔
175
    } else if (SymEngine::is_a<SymEngine::Equality>(*con)) {
46✔
NEW
176
        auto eq = SymEngine::rcp_static_cast<const SymEngine::Equality>(con);
×
NEW
177
        auto lhs = eq->get_arg1();
×
NEW
178
        auto rhs = eq->get_arg2();
×
NEW
179
        return language_extension.expression(lhs) + " == " + language_extension.expression(rhs);
×
180
    } else if (SymEngine::is_a<SymEngine::Unequality>(*con)) {
2✔
NEW
181
        auto ne = SymEngine::rcp_static_cast<const SymEngine::Unequality>(con);
×
NEW
182
        auto lhs = ne->get_arg1();
×
NEW
183
        auto rhs = ne->get_arg2();
×
NEW
184
        return language_extension.expression(lhs) + " != " + language_extension.expression(rhs);
×
NEW
185
    }
×
186

187
    return "";
2✔
188
}
46✔
189

190
void canonicalize_map_dims(isl_map* map, const std::string& in_prefix,
118✔
191
                           const std::string& out_prefix) {
192
    int n_in = isl_map_dim(map, isl_dim_in);
118✔
193
    int n_out = isl_map_dim(map, isl_dim_out);
118✔
194

195
    for (int i = 0; i < n_in; ++i) {
136✔
196
        std::string name = in_prefix + std::to_string(i);
18✔
197
        map = isl_map_set_dim_name(map, isl_dim_in, i, name.c_str());
18✔
198
    }
18✔
199

200
    for (int i = 0; i < n_out; ++i) {
162✔
201
        std::string name = out_prefix + std::to_string(i);
44✔
202
        map = isl_map_set_dim_name(map, isl_dim_out, i, name.c_str());
44✔
203
    }
44✔
204
}
118✔
205

206
MultiExpression delinearize(const MultiExpression& expr, const Assumptions& assums) {
118✔
207
    MultiExpression delinearized;
118✔
208
    for (auto& dim : expr) {
162✔
209
        // Step 1: Convert expression into an affine polynomial
210
        SymbolVec symbols;
44✔
211
        for (auto& sym : atoms(dim)) {
62✔
212
            if (!is_parameter(sym, assums)) {
18✔
213
                symbols.push_back(sym);
18✔
214
            }
18✔
215
        }
216
        if (symbols.empty()) {
44✔
217
            delinearized.push_back(dim);
26✔
218
            continue;
26✔
219
        }
220

221
        auto poly = polynomial(dim, symbols);
18✔
222
        if (poly == SymEngine::null) {
18✔
NEW
223
            delinearized.push_back(dim);
×
NEW
224
            continue;
×
225
        }
226

227
        auto aff_coeffs = affine_coefficients(poly, symbols);
18✔
228
        if (aff_coeffs.empty()) {
18✔
NEW
229
            delinearized.push_back(dim);
×
NEW
230
            continue;
×
231
        }
232

233
        // Step 2: Peel-off dimensions
234
        bool success = true;
18✔
235
        Expression remaining = dim;
18✔
236
        std::vector<Expression> peeled_dims;
18✔
237
        while (aff_coeffs.size() > 1) {
22✔
238
            // Find the symbol with largest stride (= largest atom count)
239
            Symbol new_dim = symbolic::symbol("");
18✔
240
            size_t max_atom_count = 0;
18✔
241
            for (const auto& [sym, coeff] : aff_coeffs) {
90✔
242
                if (sym->get_name() == "__daisy_constant__") {
36✔
243
                    continue;
18✔
244
                }
245
                size_t atom_count = symbolic::atoms(coeff).size();
18✔
246
                if (atom_count > max_atom_count || new_dim->get_name() == "") {
18✔
247
                    max_atom_count = atom_count;
18✔
248
                    new_dim = sym;
18✔
249
                }
18✔
250
            }
251
            if (new_dim->get_name() == "") {
18✔
NEW
252
                break;
×
253
            }
254

255
            // Symbol must be nonnegative
256
            auto sym_lb = minimum(new_dim, {}, assums);
18✔
257
            if (sym_lb == SymEngine::null) {
18✔
NEW
258
                success = false;
×
NEW
259
                break;
×
260
            }
261
            auto sym_cond = symbolic::Ge(sym_lb, symbolic::zero());
18✔
262
            if (!symbolic::is_true(sym_cond)) {
18✔
263
                success = false;
6✔
264
                break;
6✔
265
            }
266

267
            // Stride must be positive
268
            Expression stride = aff_coeffs.at(new_dim);
12✔
269
            auto stride_lb = minimum(stride, {}, assums);
12✔
270
            if (stride_lb == SymEngine::null) {
12✔
NEW
271
                success = false;
×
NEW
272
                break;
×
273
            }
274
            auto stride_cond = symbolic::Ge(stride_lb, symbolic::one());
12✔
275
            if (!symbolic::is_true(stride_cond)) {
12✔
NEW
276
                success = false;
×
NEW
277
                break;
×
278
            }
279

280
            // Peel off the dimension
281
            remaining = symbolic::sub(remaining, symbolic::mul(stride, new_dim));
12✔
282

283
            // Check if remainder is within bounds
284

285
            // remaining must be nonnegative
286
            auto rem_lb = minimum(remaining, {}, assums);
12✔
287
            if (rem_lb == SymEngine::null) {
12✔
NEW
288
                success = false;
×
NEW
289
                break;
×
290
            }
291
            auto cond_zero = symbolic::Ge(rem_lb, symbolic::zero());
12✔
292
            if (!symbolic::is_true(cond_zero)) {
12✔
NEW
293
                success = false;
×
NEW
294
                break;
×
295
            }
296

297
            // remaining must be less than stride
298
            auto rem = symbolic::sub(stride, remaining);
12✔
299
            rem = minimum(rem, {}, assums);
12✔
300
            if (rem == SymEngine::null) {
12✔
NEW
301
                success = false;
×
NEW
302
                break;
×
303
            }
304

305
            auto cond_stride = symbolic::Ge(rem, symbolic::one());
12✔
306
            if (!symbolic::is_true(cond_stride)) {
12✔
307
                success = false;
8✔
308
                break;
8✔
309
            }
310

311
            // Add the peeled dimension to the list
312
            peeled_dims.push_back(new_dim);
4✔
313
            aff_coeffs.erase(new_dim);
4✔
314
        }
18✔
315
        if (!success) {
18✔
316
            delinearized.push_back(dim);
14✔
317
            continue;
14✔
318
        }
319

320
        for (auto& peeled_dim : peeled_dims) {
8✔
321
            delinearized.push_back(peeled_dim);
4✔
322
        }
323

324
        // If remaining is not zero, then add the constant term
325
        if (!symbolic::eq(remaining, symbolic::zero()) && success) {
4✔
NEW
326
            delinearized.push_back(remaining);
×
NEW
327
        }
×
328
    }
44✔
329

330
    return delinearized;
118✔
331
}
118✔
332

333
}  // namespace symbolic
334
}  // 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