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

daisytuner / sdfglib / 15798071638

21 Jun 2025 05:35PM UTC coverage: 63.84% (-0.2%) from 64.022%
15798071638

Pull #97

github

web-flow
Merge bc9208a8e into b1dccd749
Pull Request #97: adds constant assumptions to handle complex symbolic expressions

35 of 46 new or added lines in 6 files covered. (76.09%)

26 existing lines in 4 files now uncovered.

8054 of 12616 relevant lines covered (63.84%)

152.19 hits per line

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

88.47
/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) {
44✔
19
    codegen::CLanguageExtension language_extension;
44✔
20

21
    // Get all symbols
22
    symbolic::SymbolSet syms;
44✔
23
    for (auto& expr : expr) {
88✔
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;
44✔
30
    SymbolSet dimensions_syms;
44✔
31
    std::vector<std::string> parameters;
44✔
32
    SymbolSet parameters_syms;
44✔
33
    for (auto& sym : syms) {
62✔
34
        if (assums.find(sym) == assums.end() && assums.at(sym).constant()) {
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;
44✔
51
    auto constraints_syms = generate_constraints(syms, assums, seen);
44✔
52

53
    // Extend parameters with additional symbols from constraints
54
    for (auto& con : constraints_syms) {
90✔
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;
44✔
69
    if (!parameters.empty()) {
44✔
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, ", ") + "] -> [";
44✔
76
    for (size_t i = 0; i < expr.size(); i++) {
88✔
77
        auto dim = expr[i];
44✔
78
        map += language_extension.expression(dim);
44✔
79
        if (i < expr.size() - 1) {
44✔
80
            map += ", ";
×
81
        }
×
82
    }
44✔
83
    map += "] ";
44✔
84

85
    std::vector<std::string> constraints;
44✔
86
    for (auto& con : constraints_syms) {
90✔
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()) {
44✔
93
        map += " : ";
18✔
94
        map += helpers::join(constraints, " and ");
18✔
95
    }
18✔
96

97
    map += " }";
44✔
98

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

103
std::tuple<std::string, std::string, std::string> expressions_to_intersection_map_str(
46✔
104
    const MultiExpression& expr1, const MultiExpression& expr2, const Symbol& indvar,
105
    const Assumptions& assums1, const Assumptions& assums2) {
106
    codegen::CLanguageExtension language_extension;
46✔
107

108
    // Get all symbols
109
    symbolic::SymbolSet syms;
46✔
110
    for (auto& expr : expr1) {
109✔
111
        auto syms_expr = symbolic::atoms(expr);
63✔
112
        syms.insert(syms_expr.begin(), syms_expr.end());
63✔
113
    }
63✔
114
    for (auto& expr : expr2) {
109✔
115
        auto syms_expr = symbolic::atoms(expr);
63✔
116
        syms.insert(syms_expr.begin(), syms_expr.end());
63✔
117
    }
63✔
118

119
    // Distinguish between dimensions and parameters
120
    std::vector<std::string> dimensions;
46✔
121
    SymbolSet dimensions_syms;
46✔
122
    std::vector<std::string> parameters;
46✔
123
    SymbolSet parameters_syms;
46✔
124
    for (auto& sym : syms) {
107✔
125
        if (sym->get_name() != indvar->get_name() && assums1.at(sym).constant()) {
61✔
126
            if (parameters_syms.find(sym) != parameters_syms.end()) {
10✔
127
                continue;
×
128
            }
129
            parameters.push_back(sym->get_name());
10✔
130
            parameters_syms.insert(sym);
10✔
131
        } else {
10✔
132
            if (dimensions_syms.find(sym) != dimensions_syms.end()) {
51✔
133
                continue;
×
134
            }
135
            dimensions.push_back(sym->get_name());
51✔
136
            dimensions_syms.insert(sym);
51✔
137
        }
138
    }
139

140
    // Generate constraints
141
    SymbolSet seen;
46✔
142
    auto constraints_syms = generate_constraints(syms, assums1, seen);
46✔
143

144
    // Extend parameters with additional symbols from constraints
145
    for (auto& con : constraints_syms) {
214✔
146
        auto con_syms = symbolic::atoms(con);
168✔
147
        for (auto& con_sym : con_syms) {
401✔
148
            if (dimensions_syms.find(con_sym) == dimensions_syms.end()) {
233✔
149
                if (parameters_syms.find(con_sym) != parameters_syms.end()) {
127✔
150
                    continue;
81✔
151
                }
152
                parameters.push_back(con_sym->get_name());
46✔
153
                parameters_syms.insert(con_sym);
46✔
154
            }
46✔
155
        }
156
    }
168✔
157

158
    // Define two maps
159
    std::string map_1;
46✔
160
    std::string map_2;
46✔
161
    if (!parameters.empty()) {
46✔
162
        map_1 += "[";
39✔
163
        map_1 += helpers::join(parameters, ", ");
39✔
164
        map_1 += "] -> ";
39✔
165
        map_2 += "[";
39✔
166
        map_2 += helpers::join(parameters, ", ");
39✔
167
        map_2 += "] -> ";
39✔
168
    }
39✔
169
    map_1 += "{ [";
46✔
170
    map_2 += "{ [";
46✔
171
    for (size_t i = 0; i < dimensions.size(); i++) {
97✔
172
        map_1 += dimensions[i] + "_1";
51✔
173
        map_2 += dimensions[i] + "_2";
51✔
174
        if (i < dimensions.size() - 1) {
51✔
175
            map_1 += ", ";
8✔
176
            map_2 += ", ";
8✔
177
        }
8✔
178
    }
51✔
179
    map_1 += "] -> [";
46✔
180
    map_2 += "] -> [";
46✔
181
    for (size_t i = 0; i < expr1.size(); i++) {
109✔
182
        auto dim = expr1[i];
63✔
183
        for (auto& iter : dimensions) {
139✔
184
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_1"));
76✔
185
        }
186
        map_1 += language_extension.expression(dim);
63✔
187
        if (i < expr1.size() - 1) {
63✔
188
            map_1 += ", ";
17✔
189
        }
17✔
190
    }
63✔
191
    for (size_t i = 0; i < expr2.size(); i++) {
109✔
192
        auto dim = expr2[i];
63✔
193
        for (auto& iter : dimensions) {
139✔
194
            dim = symbolic::subs(dim, symbolic::symbol(iter), symbolic::symbol(iter + "_2"));
76✔
195
        }
196
        map_2 += language_extension.expression(dim);
63✔
197
        if (i < expr2.size() - 1) {
63✔
198
            map_2 += ", ";
17✔
199
        }
17✔
200
    }
63✔
201
    map_1 += "] ";
46✔
202
    map_2 += "] ";
46✔
203

204
    std::vector<std::string> constraints_1;
46✔
205
    std::vector<std::string> constraints_2;
46✔
206
    // Add bounds
207
    for (auto& con : constraints_syms) {
214✔
208
        auto con_1 = con;
168✔
209
        auto con_2 = con;
168✔
210
        for (auto& iter : dimensions) {
376✔
211
            con_1 = symbolic::subs(con_1, symbolic::symbol(iter), symbolic::symbol(iter + "_1"));
208✔
212
            con_2 = symbolic::subs(con_2, symbolic::symbol(iter), symbolic::symbol(iter + "_2"));
208✔
213
        }
214
        auto con_str_1 = constraint_to_isl_str(con_1);
168✔
215
        if (con_str_1.empty()) {
168✔
216
            continue;
×
217
        }
218
        auto con_str_2 = constraint_to_isl_str(con_2);
168✔
219
        if (!con_str_1.empty()) {
168✔
220
            constraints_1.push_back(con_str_1);
168✔
221
            constraints_2.push_back(con_str_2);
168✔
222
        }
168✔
223
    }
168✔
224
    if (!constraints_1.empty()) {
46✔
225
        map_1 += " : ";
44✔
226
        map_1 += helpers::join(constraints_1, " and ");
44✔
227
    }
44✔
228
    map_1 += " }";
46✔
229

230
    if (!constraints_2.empty()) {
46✔
231
        map_2 += " : ";
44✔
232
        map_2 += helpers::join(constraints_2, " and ");
44✔
233
    }
44✔
234
    map_2 += " }";
46✔
235

236
    std::string map_3 = "{ [";
46✔
237
    for (size_t i = 0; i < dimensions.size(); i++) {
97✔
238
        map_3 += dimensions[i] + "_2";
51✔
239
        if (i < dimensions.size() - 1) {
51✔
240
            map_3 += ", ";
8✔
241
        }
8✔
242
    }
51✔
243
    map_3 += "] -> [";
46✔
244
    for (size_t i = 0; i < dimensions.size(); i++) {
97✔
245
        map_3 += dimensions[i] + "_1";
51✔
246
        if (i < dimensions.size() - 1) {
51✔
247
            map_3 += ", ";
8✔
248
        }
8✔
249
    }
51✔
250
    map_3 += "]";
46✔
251
    std::vector<std::string> monotonicity_constraints;
46✔
252
    if (dimensions_syms.find(indvar) != dimensions_syms.end()) {
46✔
253
        monotonicity_constraints.push_back(indvar->get_name() + "_1 != " + indvar->get_name() +
41✔
254
                                           "_2");
255
    }
41✔
256
    if (!monotonicity_constraints.empty()) {
46✔
257
        map_3 += " : ";
41✔
258
        map_3 += helpers::join(monotonicity_constraints, " and ");
41✔
259
    }
41✔
260
    map_3 += " }";
46✔
261

262
    map_1 = std::regex_replace(map_1, std::regex("\\."), "_");
46✔
263
    map_2 = std::regex_replace(map_2, std::regex("\\."), "_");
46✔
264
    map_3 = std::regex_replace(map_3, std::regex("\\."), "_");
46✔
265

266
    return {map_1, map_2, map_3};
46✔
267
}
46✔
268

269
ExpressionSet generate_constraints(SymbolSet& syms, const Assumptions& assums, SymbolSet& seen) {
310✔
270
    ExpressionSet constraints;
310✔
271
    for (auto& sym : syms) {
694✔
272
        if (assums.find(sym) == assums.end()) {
384✔
273
            continue;
8✔
274
        }
275
        if (seen.find(sym) != seen.end()) {
376✔
276
            continue;
245✔
277
        }
278
        seen.insert(sym);
131✔
279

280
        auto ub = assums.at(sym).upper_bound();
131✔
281
        auto lb = assums.at(sym).lower_bound();
131✔
282
        if (!symbolic::eq(ub, symbolic::infty(1))) {
131✔
283
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
84✔
284
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
3✔
285
                auto args = min->get_args();
3✔
286
                for (auto& arg : args) {
9✔
287
                    auto con = symbolic::Le(sym, arg);
6✔
288
                    auto con_syms = symbolic::atoms(con);
6✔
289
                    constraints.insert(con);
6✔
290

291
                    auto con_cons = generate_constraints(con_syms, assums, seen);
6✔
292
                    constraints.insert(con_cons.begin(), con_cons.end());
6✔
293
                }
6✔
294
            } else {
3✔
295
                auto con = symbolic::Le(sym, ub);
81✔
296
                auto con_syms = symbolic::atoms(con);
81✔
297
                constraints.insert(con);
81✔
298

299
                auto con_cons = generate_constraints(con_syms, assums, seen);
81✔
300
                constraints.insert(con_cons.begin(), con_cons.end());
81✔
301
            }
81✔
302
        }
84✔
303
        if (!symbolic::eq(lb, symbolic::infty(-1))) {
131✔
304
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
131✔
305
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb);
2✔
306
                auto args = max->get_args();
2✔
307
                for (auto& arg : args) {
6✔
308
                    auto con = symbolic::Ge(sym, arg);
4✔
309
                    auto con_syms = symbolic::atoms(con);
4✔
310
                    constraints.insert(con);
4✔
311

312
                    auto con_cons = generate_constraints(con_syms, assums, seen);
4✔
313
                    constraints.insert(con_cons.begin(), con_cons.end());
4✔
314
                }
4✔
315
            } else {
2✔
316
                auto con = symbolic::Ge(sym, lb);
129✔
317
                auto con_syms = symbolic::atoms(con);
129✔
318
                constraints.insert(con);
129✔
319

320
                auto con_cons = generate_constraints(con_syms, assums, seen);
129✔
321
                constraints.insert(con_cons.begin(), con_cons.end());
129✔
322
            }
129✔
323
        }
131✔
324
    }
131✔
325
    return constraints;
310✔
326
}
310✔
327

328
std::string constraint_to_isl_str(const Expression& con) {
382✔
329
    codegen::CLanguageExtension language_extension;
382✔
330

331
    if (SymEngine::is_a<SymEngine::StrictLessThan>(*con)) {
382✔
332
        auto le = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(con);
×
333
        auto lhs = le->get_arg1();
×
334
        auto rhs = le->get_arg2();
×
335
        return language_extension.expression(lhs) + " < " + language_extension.expression(rhs);
×
336
    } else if (SymEngine::is_a<SymEngine::LessThan>(*con)) {
382✔
337
        auto le = SymEngine::rcp_static_cast<const SymEngine::LessThan>(con);
380✔
338
        auto lhs = le->get_arg1();
380✔
339
        auto rhs = le->get_arg2();
380✔
340
        return language_extension.expression(lhs) + " <= " + language_extension.expression(rhs);
380✔
341
    } else if (SymEngine::is_a<SymEngine::Equality>(*con)) {
382✔
342
        auto eq = SymEngine::rcp_static_cast<const SymEngine::Equality>(con);
×
343
        auto lhs = eq->get_arg1();
×
344
        auto rhs = eq->get_arg2();
×
345
        return language_extension.expression(lhs) + " == " + language_extension.expression(rhs);
×
346
    } else if (SymEngine::is_a<SymEngine::Unequality>(*con)) {
2✔
347
        auto ne = SymEngine::rcp_static_cast<const SymEngine::Unequality>(con);
×
348
        auto lhs = ne->get_arg1();
×
349
        auto rhs = ne->get_arg2();
×
350
        return language_extension.expression(lhs) + " != " + language_extension.expression(rhs);
×
351
    }
×
352

353
    return "";
2✔
354
}
382✔
355

356
void canonicalize_map_dims(isl_map* map, const std::string& in_prefix,
44✔
357
                           const std::string& out_prefix) {
358
    int n_in = isl_map_dim(map, isl_dim_in);
44✔
359
    int n_out = isl_map_dim(map, isl_dim_out);
44✔
360

361
    for (int i = 0; i < n_in; ++i) {
62✔
362
        std::string name = in_prefix + std::to_string(i);
18✔
363
        map = isl_map_set_dim_name(map, isl_dim_in, i, name.c_str());
18✔
364
    }
18✔
365

366
    for (int i = 0; i < n_out; ++i) {
88✔
367
        std::string name = out_prefix + std::to_string(i);
44✔
368
        map = isl_map_set_dim_name(map, isl_dim_out, i, name.c_str());
44✔
369
    }
44✔
370
}
44✔
371

372
MultiExpression delinearize(const MultiExpression& expr, const Assumptions& assums) {
136✔
373
    MultiExpression delinearized;
136✔
374
    for (auto& dim : expr) {
306✔
375
        // Step 1: Convert expression into an affine polynomial
376
        SymbolVec symbols;
170✔
377
        for (auto& sym : atoms(dim)) {
310✔
378
            if (!assums.at(sym).constant()) {
140✔
379
                symbols.push_back(sym);
34✔
380
            }
34✔
381
        }
382
        if (symbols.empty()) {
170✔
383
            delinearized.push_back(dim);
136✔
384
            continue;
136✔
385
        }
386

387
        auto poly = polynomial(dim, symbols);
34✔
388
        if (poly == SymEngine::null) {
34✔
389
            delinearized.push_back(dim);
×
390
            continue;
×
391
        }
392

393
        auto aff_coeffs = affine_coefficients(poly, symbols);
34✔
394
        if (aff_coeffs.empty()) {
34✔
395
            delinearized.push_back(dim);
×
396
            continue;
×
397
        }
398

399
        // Step 2: Peel-off dimensions
400
        bool success = true;
34✔
401
        Expression remaining = dim;
34✔
402
        std::vector<Expression> peeled_dims;
34✔
403
        while (aff_coeffs.size() > 1) {
46✔
404
            // Find the symbol with largest stride (= largest atom count)
405
            Symbol new_dim = symbolic::symbol("");
34✔
406
            size_t max_atom_count = 0;
34✔
407
            for (const auto& [sym, coeff] : aff_coeffs) {
170✔
408
                if (sym->get_name() == "__daisy_constant__") {
68✔
409
                    continue;
34✔
410
                }
411
                size_t atom_count = symbolic::atoms(coeff).size();
34✔
412
                if (atom_count > max_atom_count || new_dim->get_name() == "") {
34✔
413
                    max_atom_count = atom_count;
34✔
414
                    new_dim = sym;
34✔
415
                }
34✔
416
            }
417
            if (new_dim->get_name() == "") {
34✔
418
                break;
×
419
            }
420

421
            // Symbol must be nonnegative
422
            auto sym_lb = minimum(new_dim, {}, assums);
34✔
423
            if (sym_lb == SymEngine::null) {
34✔
424
                success = false;
×
425
                break;
×
426
            }
427
            auto sym_cond = symbolic::Ge(sym_lb, symbolic::zero());
34✔
428
            if (!symbolic::is_true(sym_cond)) {
34✔
429
                success = false;
14✔
430
                break;
14✔
431
            }
432

433
            // Stride must be positive
434
            Expression stride = aff_coeffs.at(new_dim);
20✔
435
            auto stride_lb = minimum(stride, {}, assums);
20✔
436
            if (stride_lb == SymEngine::null) {
20✔
437
                success = false;
×
438
                break;
×
439
            }
440
            auto stride_cond = symbolic::Ge(stride_lb, symbolic::one());
20✔
441
            if (!symbolic::is_true(stride_cond)) {
20✔
442
                success = false;
×
443
                break;
×
444
            }
445

446
            // Peel off the dimension
447
            remaining = symbolic::sub(remaining, symbolic::mul(stride, new_dim));
20✔
448

449
            // Check if remainder is within bounds
450

451
            // remaining must be nonnegative
452
            auto rem_lb = minimum(remaining, {}, assums);
20✔
453
            if (rem_lb == SymEngine::null) {
20✔
454
                success = false;
×
455
                break;
×
456
            }
457
            auto cond_zero = symbolic::Ge(rem_lb, symbolic::zero());
20✔
458
            if (!symbolic::is_true(cond_zero)) {
20✔
UNCOV
459
                success = false;
×
UNCOV
460
                break;
×
461
            }
462

463
            // remaining must be less than stride
464
            auto rem = symbolic::sub(stride, remaining);
20✔
465
            rem = minimum(rem, {}, assums);
20✔
466
            if (rem == SymEngine::null) {
20✔
467
                success = false;
×
468
                break;
×
469
            }
470

471
            auto cond_stride = symbolic::Ge(rem, symbolic::one());
20✔
472
            if (!symbolic::is_true(cond_stride)) {
20✔
473
                success = false;
8✔
474
                break;
8✔
475
            }
476

477
            // Add the peeled dimension to the list
478
            peeled_dims.push_back(new_dim);
12✔
479
            aff_coeffs.erase(new_dim);
12✔
480
        }
34✔
481
        if (!success) {
34✔
482
            delinearized.push_back(dim);
22✔
483
            continue;
22✔
484
        }
485

486
        for (auto& peeled_dim : peeled_dims) {
24✔
487
            delinearized.push_back(peeled_dim);
12✔
488
        }
489

490
        // If remaining is not zero, then add the constant term
491
        if (!symbolic::eq(remaining, symbolic::zero()) && success) {
12✔
492
            delinearized.push_back(remaining);
×
493
        }
×
494
    }
170✔
495

496
    return delinearized;
136✔
497
}
136✔
498

499
}  // namespace symbolic
500
}  // 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