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

daisytuner / sdfglib / 15777275738

20 Jun 2025 10:49AM UTC coverage: 64.573% (-0.02%) from 64.591%
15777275738

Pull #95

github

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

215 of 298 new or added lines in 8 files covered. (72.15%)

22 existing lines in 3 files now uncovered.

8213 of 12719 relevant lines covered (64.57%)

152.49 hits per line

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

83.83
/src/symbolic/sets.cpp
1
#include "sdfg/symbolic/sets.h"
2

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

8
#include <regex>
9

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

15
namespace sdfg {
16
namespace symbolic {
17

18
ExpressionSet generate_constraints(SymbolSet& syms, const Assumptions& assums, SymbolSet& seen) {
233✔
19
    ExpressionSet constraints;
233✔
20
    for (auto& sym : syms) {
561✔
21
        if (assums.find(sym) == assums.end()) {
328✔
22
            continue;
9✔
23
        }
24
        if (seen.find(sym) != seen.end()) {
319✔
25
            continue;
209✔
26
        }
27
        seen.insert(sym);
110✔
28

29
        auto ub = assums.at(sym).upper_bound();
110✔
30
        auto lb = assums.at(sym).lower_bound();
110✔
31
        if (!symbolic::eq(ub, symbolic::infty(1))) {
110✔
32
            if (SymEngine::is_a<SymEngine::Min>(*ub)) {
66✔
33
                auto min = SymEngine::rcp_static_cast<const SymEngine::Min>(ub);
3✔
34
                auto args = min->get_args();
3✔
35
                for (auto& arg : args) {
9✔
36
                    auto con = symbolic::Le(sym, arg);
6✔
37
                    auto con_syms = symbolic::atoms(con);
6✔
38
                    constraints.insert(con);
6✔
39

40
                    auto con_cons = generate_constraints(con_syms, assums, seen);
6✔
41
                    constraints.insert(con_cons.begin(), con_cons.end());
6✔
42
                }
6✔
43
            } else {
3✔
44
                auto con = symbolic::Le(sym, ub);
63✔
45
                auto con_syms = symbolic::atoms(con);
63✔
46
                constraints.insert(con);
63✔
47

48
                auto con_cons = generate_constraints(con_syms, assums, seen);
63✔
49
                constraints.insert(con_cons.begin(), con_cons.end());
63✔
50
            }
63✔
51
        }
66✔
52
        if (!symbolic::eq(lb, symbolic::infty(-1))) {
110✔
53
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
110✔
54
                auto max = SymEngine::rcp_static_cast<const SymEngine::Max>(lb);
2✔
55
                auto args = max->get_args();
2✔
56
                for (auto& arg : args) {
6✔
57
                    auto con = symbolic::Ge(sym, arg);
4✔
58
                    auto con_syms = symbolic::atoms(con);
4✔
59
                    constraints.insert(con);
4✔
60

61
                    auto con_cons = generate_constraints(con_syms, assums, seen);
4✔
62
                    constraints.insert(con_cons.begin(), con_cons.end());
4✔
63
                }
4✔
64
            } else {
2✔
65
                auto con = symbolic::Ge(sym, lb);
108✔
66
                auto con_syms = symbolic::atoms(con);
108✔
67
                constraints.insert(con);
108✔
68

69
                auto con_cons = generate_constraints(con_syms, assums, seen);
108✔
70
                constraints.insert(con_cons.begin(), con_cons.end());
108✔
71
            }
108✔
72
        }
110✔
73
    }
110✔
74
    return constraints;
233✔
75
}
233✔
76

77
std::string constraint_to_isl_str(const Expression& con) {
329✔
78
    codegen::CLanguageExtension language_extension;
329✔
79

80
    if (SymEngine::is_a<SymEngine::StrictLessThan>(*con)) {
329✔
81
        auto le = SymEngine::rcp_static_cast<const SymEngine::StrictLessThan>(con);
×
82
        auto lhs = le->get_arg1();
×
83
        auto rhs = le->get_arg2();
×
84
        return language_extension.expression(lhs) + " < " + language_extension.expression(rhs);
×
85
    } else if (SymEngine::is_a<SymEngine::LessThan>(*con)) {
329✔
86
        auto le = SymEngine::rcp_static_cast<const SymEngine::LessThan>(con);
328✔
87
        auto lhs = le->get_arg1();
328✔
88
        auto rhs = le->get_arg2();
328✔
89
        return language_extension.expression(lhs) + " <= " + language_extension.expression(rhs);
328✔
90
    } else if (SymEngine::is_a<SymEngine::Equality>(*con)) {
329✔
91
        auto eq = SymEngine::rcp_static_cast<const SymEngine::Equality>(con);
×
92
        auto lhs = eq->get_arg1();
×
93
        auto rhs = eq->get_arg2();
×
94
        return language_extension.expression(lhs) + " == " + language_extension.expression(rhs);
×
95
    } else if (SymEngine::is_a<SymEngine::Unequality>(*con)) {
1✔
96
        auto ne = SymEngine::rcp_static_cast<const SymEngine::Unequality>(con);
×
97
        auto lhs = ne->get_arg1();
×
98
        auto rhs = ne->get_arg2();
×
99
        return language_extension.expression(lhs) + " != " + language_extension.expression(rhs);
×
100
    }
×
101

102
    return "";
1✔
103
}
329✔
104

105
std::tuple<std::string, std::string, std::string> expressions_to_intersection_map_str(
40✔
106
    const MultiExpression& expr1, const MultiExpression& expr2, const SymbolSet& params,
107
    const SymbolSet& monotonics, const Assumptions& assums) {
108
    codegen::CLanguageExtension language_extension;
40✔
109

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

121
    // Distinguish between dimensions and parameters
122
    std::vector<std::string> dimensions;
40✔
123
    SymbolSet dimensions_syms;
40✔
124
    std::vector<std::string> parameters;
40✔
125
    SymbolSet parameters_syms;
40✔
126
    for (auto& sym : syms) {
95✔
127
        if (params.find(sym) != params.end()) {
55✔
128
            if (parameters_syms.find(sym) != parameters_syms.end()) {
10✔
129
                continue;
×
130
            }
131
            parameters.push_back(sym->get_name());
10✔
132
            parameters_syms.insert(sym);
10✔
133
        } else {
10✔
134
            if (dimensions_syms.find(sym) != dimensions_syms.end()) {
45✔
135
                continue;
×
136
            }
137
            dimensions.push_back(sym->get_name());
45✔
138
            dimensions_syms.insert(sym);
45✔
139
        }
140
    }
141

142
    // Generate constraints
143
    SymbolSet seen;
40✔
144
    auto constraints_syms = generate_constraints(syms, assums, seen);
40✔
145

146
    // Extend parameters with additional symbols from constraints
147
    for (auto& con : constraints_syms) {
193✔
148
        auto con_syms = symbolic::atoms(con);
153✔
149
        for (auto& con_sym : con_syms) {
368✔
150
            if (dimensions_syms.find(con_sym) == dimensions_syms.end()) {
215✔
151
                if (parameters_syms.find(con_sym) != parameters_syms.end()) {
121✔
152
                    continue;
78✔
153
                }
154
                parameters.push_back(con_sym->get_name());
43✔
155
                parameters_syms.insert(con_sym);
43✔
156
            }
43✔
157
        }
158
    }
153✔
159

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

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

232
    if (!constraints_2.empty()) {
40✔
233
        map_2 += " : ";
38✔
234
        map_2 += helpers::join(constraints_2, " and ");
38✔
235
    }
38✔
236
    map_2 += " }";
40✔
237

238
    std::string map_3 = "{ [";
40✔
239
    for (size_t i = 0; i < dimensions.size(); i++) {
85✔
240
        map_3 += dimensions[i] + "_2";
45✔
241
        if (i < dimensions.size() - 1) {
45✔
242
            map_3 += ", ";
8✔
243
        }
8✔
244
    }
45✔
245
    map_3 += "] -> [";
40✔
246
    for (size_t i = 0; i < dimensions.size(); i++) {
85✔
247
        map_3 += dimensions[i] + "_1";
45✔
248
        if (i < dimensions.size() - 1) {
45✔
249
            map_3 += ", ";
8✔
250
        }
8✔
251
    }
45✔
252
    map_3 += "]";
40✔
253
    std::vector<std::string> monotonicity_constraints;
40✔
254
    // Monotonicity constraints
255
    for (size_t i = 0; i < dimensions.size(); i++) {
85✔
256
        if (monotonics.find(symbolic::symbol(dimensions[i])) == monotonics.end()) {
45✔
257
            continue;
10✔
258
        }
259
        monotonicity_constraints.push_back(dimensions[i] + "_1 != " + dimensions[i] + "_2");
35✔
260
    }
35✔
261
    if (!monotonics.empty()) {
40✔
262
        map_3 += " : ";
38✔
263
        map_3 += helpers::join(monotonicity_constraints, " and ");
38✔
264
    }
38✔
265
    map_3 += " }";
40✔
266

267
    map_1 = std::regex_replace(map_1, std::regex("\\."), "_");
40✔
268
    map_2 = std::regex_replace(map_2, std::regex("\\."), "_");
40✔
269
    map_3 = std::regex_replace(map_3, std::regex("\\."), "_");
40✔
270

271
    return {map_1, map_2, map_3};
40✔
272
}
40✔
273

274
std::string expressions_to_diagonal_map_str(const MultiExpression& expr1,
12✔
275
                                            const MultiExpression& expr2, const SymbolSet& params,
276
                                            const Assumptions& assums) {
277
    codegen::CLanguageExtension language_extension;
12✔
278

279
    // Get all symbols
280
    symbolic::SymbolSet syms;
12✔
281
    for (auto& expr : expr1) {
24✔
282
        auto syms_expr = symbolic::atoms(expr);
12✔
283
        syms.insert(syms_expr.begin(), syms_expr.end());
12✔
284
    }
12✔
285
    for (auto& expr : expr2) {
24✔
286
        auto syms_expr = symbolic::atoms(expr);
12✔
287
        syms.insert(syms_expr.begin(), syms_expr.end());
12✔
288
    }
12✔
289

290
    // Distinguish between dimensions and parameters
291
    std::vector<std::string> dimensions;
12✔
292
    SymbolSet dimensions_syms;
12✔
293
    std::vector<std::string> parameters;
12✔
294
    SymbolSet parameters_syms;
12✔
295
    for (auto& sym : syms) {
24✔
296
        if (params.find(sym) != params.end()) {
12✔
297
            if (parameters_syms.find(sym) != parameters_syms.end()) {
×
298
                continue;
×
299
            }
300
            parameters.push_back(sym->get_name());
×
301
            parameters_syms.insert(sym);
×
302
        } else {
×
303
            if (dimensions_syms.find(sym) != dimensions_syms.end()) {
12✔
304
                continue;
×
305
            }
306
            dimensions.push_back(sym->get_name());
12✔
307
            dimensions_syms.insert(sym);
12✔
308
        }
309
    }
310

311
    // Generate constraints
312
    SymbolSet seen;
12✔
313
    auto constraints_syms = generate_constraints(syms, assums, seen);
12✔
314

315
    // Extend parameters with additional symbols from constraints
316
    for (auto& con : constraints_syms) {
35✔
317
        auto con_syms = symbolic::atoms(con);
23✔
318
        for (auto& con_sym : con_syms) {
61✔
319
            if (dimensions_syms.find(con_sym) == dimensions_syms.end()) {
38✔
320
                if (parameters_syms.find(con_sym) != parameters_syms.end()) {
12✔
321
                    continue;
8✔
322
                }
323
                parameters.push_back(con_sym->get_name());
4✔
324
                parameters_syms.insert(con_sym);
4✔
325
            }
4✔
326
        }
327
    }
23✔
328

329
    // Define map
330
    std::string map;
12✔
331
    if (!parameters.empty()) {
12✔
332
        map += "[";
2✔
333
        map += helpers::join(parameters, ", ");
2✔
334
        map += "] -> ";
2✔
335
    }
2✔
336
    map += "{ [" + helpers::join(dimensions, ", ") + "] -> [";
12✔
337
    for (size_t i = 0; i < expr1.size(); i++) {
24✔
338
        auto dim = expr1[i];
12✔
339
        map += language_extension.expression(dim);
12✔
340
        map += ", ";
12✔
341
    }
12✔
342
    for (size_t i = 0; i < expr2.size(); i++) {
24✔
343
        auto dim = expr2[i];
12✔
344
        map += language_extension.expression(dim);
12✔
345
        if (i < expr2.size() - 1) {
12✔
346
            map += ", ";
×
347
        }
×
348
    }
12✔
349
    map += "] ";
12✔
350

351
    std::vector<std::string> constraints;
12✔
352
    for (auto& con : constraints_syms) {
35✔
353
        auto con_str = constraint_to_isl_str(con);
23✔
354
        if (!con_str.empty()) {
23✔
355
            constraints.push_back(con_str);
22✔
356
        }
22✔
357
    }
23✔
358
    if (!constraints.empty()) {
12✔
359
        map += " : ";
7✔
360
        map += helpers::join(constraints, " and ");
7✔
361
    }
7✔
362

363
    map += " }";
12✔
364

365
    map = std::regex_replace(map, std::regex("\\."), "_");
12✔
366
    return map;
12✔
367
}
12✔
368

369
bool is_disjoint_isl(const MultiExpression& expr1, const MultiExpression& expr2,
52✔
370
                     const SymbolSet& params, const SymbolSet& monotonics,
371
                     const Assumptions& assums) {
372
    if (expr1.size() != expr2.size()) {
52✔
373
        return false;
×
374
    }
375
    if (expr1.empty()) {
52✔
376
        return false;
12✔
377
    }
378

379
    isl_ctx* ctx = isl_ctx_alloc();
40✔
380

381
    // Transform both expressions into two maps with separate dimensions
382
    auto expr1_delinearized = delinearize(expr1, params, assums);
40✔
383
    auto expr2_delinearized = delinearize(expr2, params, assums);
40✔
384
    auto maps = expressions_to_intersection_map_str(expr1, expr2, params, monotonics, assums);
40✔
385
    isl_map* map_1 = isl_map_read_from_str(ctx, std::get<0>(maps).c_str());
40✔
386
    isl_map* map_2 = isl_map_read_from_str(ctx, std::get<1>(maps).c_str());
40✔
387
    isl_map* map_3 = isl_map_read_from_str(ctx, std::get<2>(maps).c_str());
40✔
388
    if (!map_1 || !map_2 || !map_3) {
40✔
389
        if (map_1) {
×
390
            isl_map_free(map_1);
×
391
        }
×
392
        if (map_2) {
×
393
            isl_map_free(map_2);
×
394
        }
×
395
        if (map_3) {
×
396
            isl_map_free(map_3);
×
397
        }
×
398
        isl_ctx_free(ctx);
×
399
        return false;
×
400
    }
401

402
    // Find aliasing pairs under the constraint that dimensions are different
403

404
    isl_map* composed = isl_map_apply_domain(map_2, map_3);
40✔
405
    if (!composed) {
40✔
406
        isl_map_free(map_1);
×
407
        if (map_2) {
×
408
            isl_map_free(map_2);
×
409
        }
×
410
        if (map_3) {
×
411
            isl_map_free(map_3);
×
412
        }
×
413
        isl_ctx_free(ctx);
×
414
        return false;
×
415
    }
416
    isl_map* alias_pairs = isl_map_intersect(composed, map_1);
40✔
417
    if (!alias_pairs) {
40✔
418
        if (composed) {
×
419
            isl_map_free(composed);
×
420
        }
×
421
        if (map_1) {
×
422
            isl_map_free(map_1);
×
423
        }
×
424
        isl_ctx_free(ctx);
×
425
        return false;
×
426
    }
427

428
    bool disjoint = isl_map_is_empty(alias_pairs);
40✔
429
    isl_map_free(alias_pairs);
40✔
430
    isl_ctx_free(ctx);
40✔
431

432
    return disjoint;
40✔
433
}
52✔
434

435
bool is_disjoint_monotonic(const MultiExpression& expr1, const MultiExpression& expr2,
56✔
436
                           const SymbolSet& params, const SymbolSet& monotonics,
437
                           const Assumptions& assums) {
438
    for (size_t i = 0; i < expr1.size(); i++) {
113✔
439
        auto& dim1 = expr1[i];
61✔
440
        if (expr2.size() <= i) {
61✔
441
            continue;
×
442
        }
443
        auto& dim2 = expr2[i];
61✔
444
        if (!symbolic::eq(dim1, dim2)) {
61✔
445
            continue;
16✔
446
        }
447

448
        // Collect all symbols
449
        symbolic::SymbolSet syms;
45✔
450
        for (auto& sym : symbolic::atoms(dim1)) {
93✔
451
            syms.insert(sym);
48✔
452
        }
453

454
        // Collect all non-constant symbols
455
        symbolic::SymbolSet generators;
45✔
456
        for (auto& sym : syms) {
93✔
457
            if (params.find(sym) == params.end()) {
48✔
458
                generators.insert(sym);
37✔
459
            }
37✔
460
        }
461
        if (generators.empty()) {
45✔
462
            continue;
8✔
463
        }
464

465
        // Check if all non-constant symbols are monotonics
466
        bool can_analyze = true;
37✔
467
        for (auto& sym : generators) {
67✔
468
            if (monotonics.find(sym) == monotonics.end()) {
37✔
469
                can_analyze = false;
7✔
470
                break;
7✔
471
            }
472
        }
473
        if (!can_analyze) {
37✔
474
            continue;
7✔
475
        }
476

477
        // Check if both dimensions are monotonic in non-constant symbols
478
        bool monotonic_dimension = true;
30✔
479
        for (auto& sym : generators) {
34✔
480
            if (!symbolic::is_monotonic(dim1, sym, assums)) {
30✔
481
                monotonic_dimension = false;
26✔
482
                break;
26✔
483
            }
484
        }
485
        if (!monotonic_dimension) {
30✔
486
            continue;
26✔
487
        }
488

489
        return true;
4✔
490
    }
45✔
491

492
    return false;
52✔
493
}
56✔
494

495
bool is_disjoint(const MultiExpression& expr1, const MultiExpression& expr2,
56✔
496
                 const SymbolSet& params, const SymbolSet& monotonics, const Assumptions& assums) {
497
    if (is_disjoint_monotonic(expr1, expr2, params, monotonics, assums)) {
56✔
498
        return true;
4✔
499
    }
500
    return is_disjoint_isl(expr1, expr2, params, monotonics, assums);
52✔
501
}
56✔
502

503
bool is_equivalent(const MultiExpression& expr1, const MultiExpression& expr2,
57✔
504
                   const SymbolSet& params, const Assumptions& assums) {
505
    if (expr1.size() != expr2.size()) {
57✔
506
        return false;
×
507
    }
508

509
    // Simple check for equality
510
    bool equals = true;
57✔
511
    for (size_t i = 0; i < expr1.size(); i++) {
65✔
512
        if (!symbolic::eq(expr1[i], expr2[i])) {
20✔
513
            equals = false;
12✔
514
            break;
12✔
515
        }
516
    }
8✔
517
    if (equals) {
57✔
518
        return true;
45✔
519
    }
520

521
    // Check for set equivalence
522
    isl_ctx* ctx = isl_ctx_alloc();
12✔
523
    // Builds { [params] -> [expr1..., expr2...] : assumptions }
524
    auto expr1_delinearized = delinearize(expr1, params, assums);
12✔
525
    auto expr2_delinearized = delinearize(expr2, params, assums);
12✔
526
    std::string map_str =
527
        expressions_to_diagonal_map_str(expr1_delinearized, expr2_delinearized, params, assums);
12✔
528
    isl_map* pair_map = isl_map_read_from_str(ctx, map_str.c_str());
12✔
529
    if (!pair_map) {
12✔
530
        isl_ctx_free(ctx);
×
531
        return false;
×
532
    }
533

534
    isl_set* range = isl_map_range(isl_map_copy(pair_map));
12✔
535

536
    // Build diagonal set: { [x₀, ..., xₙ, y₀, ..., yₙ] : x₀ = y₀ and ... and xₙ = yₙ }
537
    std::string diag_str = "{ [";
12✔
538
    for (size_t i = 0; i < expr1.size() * 2; ++i) {
36✔
539
        if (i > 0) diag_str += ", ";
24✔
540
        diag_str += "v" + std::to_string(i);
24✔
541
    }
24✔
542
    diag_str += "] : ";
12✔
543
    for (size_t i = 0; i < expr1.size(); ++i) {
24✔
544
        if (i > 0) diag_str += " and ";
12✔
545
        diag_str += "v" + std::to_string(i) + " = v" + std::to_string(i + expr1.size());
12✔
546
    }
12✔
547
    diag_str += " }";
12✔
548

549
    isl_set* diagonal = isl_set_read_from_str(ctx, diag_str.c_str());
12✔
550

551
    bool equivalent = false;
12✔
552
    if (range && diagonal) {
12✔
553
        equivalent = isl_set_is_subset(range, diagonal) == 1;
12✔
554
    }
12✔
555

556
    isl_set_free(range);
12✔
557
    isl_set_free(diagonal);
12✔
558
    isl_map_free(pair_map);
12✔
559
    isl_ctx_free(ctx);
12✔
560

561
    return equivalent;
12✔
562
}
57✔
563

564
MultiExpression delinearize(const MultiExpression& expr, const SymbolSet& params,
104✔
565
                            const Assumptions& assums) {
566
    MultiExpression delinearized;
104✔
567
    for (auto& dim : expr) {
242✔
568
        // Step 1: Convert expression into an affine polynomial
569
        SymbolVec symbols;
138✔
570
        for (auto& sym : atoms(dim)) {
261✔
571
            if (params.find(sym) == params.end()) {
123✔
572
                symbols.push_back(sym);
103✔
573
            }
103✔
574
        }
575
        if (symbols.empty()) {
138✔
576
            delinearized.push_back(dim);
35✔
577
            continue;
35✔
578
        }
579

580
        auto poly = polynomial(dim, symbols);
103✔
581
        if (poly == SymEngine::null) {
103✔
582
            delinearized.push_back(dim);
×
583
            continue;
×
584
        }
585

586
        auto aff_coeffs = affine_coefficients(poly, symbols);
103✔
587
        if (aff_coeffs.empty()) {
103✔
588
            delinearized.push_back(dim);
×
589
            continue;
×
590
        }
591

592
        // Step 2: Peel-off dimensions
593
        bool success = true;
103✔
594
        Expression remaining = dim;
103✔
595
        std::vector<Expression> peeled_dims;
103✔
596
        while (aff_coeffs.size() > 1) {
173✔
597
            // Find the symbol with largest stride (= largest atom count)
598
            Symbol new_dim = symbolic::symbol("");
103✔
599
            size_t max_atom_count = 0;
103✔
600
            for (const auto& [sym, coeff] : aff_coeffs) {
515✔
601
                if (sym->get_name() == "__daisy_constant__") {
206✔
602
                    continue;
103✔
603
                }
604
                size_t atom_count = symbolic::atoms(coeff).size();
103✔
605
                if (atom_count > max_atom_count || new_dim->get_name() == "") {
103✔
606
                    max_atom_count = atom_count;
103✔
607
                    new_dim = sym;
103✔
608
                }
103✔
609
            }
610
            if (new_dim->get_name() == "") {
103✔
611
                break;
×
612
            }
613

614
            // Symbol must be nonnegative
615
            auto sym_lb = minimum(new_dim, {}, assums);
103✔
616
            if (sym_lb == SymEngine::null) {
103✔
617
                success = false;
1✔
618
                break;
1✔
619
            }
620
            auto sym_cond = symbolic::Ge(sym_lb, symbolic::zero());
102✔
621
            if (!symbolic::is_true(sym_cond)) {
102✔
622
                success = false;
26✔
623
                break;
26✔
624
            }
625

626
            // Stride must be positive
627
            Expression stride = aff_coeffs.at(new_dim);
76✔
628
            auto stride_lb = minimum(stride, {}, assums);
76✔
629
            if (stride_lb == SymEngine::null) {
76✔
630
                success = false;
×
631
                break;
×
632
            }
633
            auto stride_cond = symbolic::Ge(stride_lb, symbolic::one());
76✔
634
            if (!symbolic::is_true(stride_cond)) {
76✔
635
                success = false;
×
636
                break;
×
637
            }
638

639
            // Peel off the dimension
640
            remaining = symbolic::sub(remaining, symbolic::mul(stride, new_dim));
76✔
641

642
            // Check if remainder is within bounds
643

644
            // remaining must be nonnegative
645
            auto rem_lb = minimum(remaining, {}, assums);
76✔
646
            if (rem_lb == SymEngine::null) {
76✔
647
                success = false;
×
648
                break;
×
649
            }
650
            auto cond_zero = symbolic::Ge(rem_lb, symbolic::zero());
76✔
651
            if (!symbolic::is_true(cond_zero)) {
76✔
652
                success = false;
2✔
653
                break;
2✔
654
            }
655

656
            // remaining must be less than stride
657
            auto rem = symbolic::sub(stride, remaining);
74✔
658
            rem = minimum(rem, {}, assums);
74✔
659
            if (rem == SymEngine::null) {
74✔
660
                success = false;
×
661
                break;
×
662
            }
663

664
            auto cond_stride = symbolic::Ge(rem, symbolic::one());
74✔
665
            if (!symbolic::is_true(cond_stride)) {
74✔
666
                success = false;
4✔
667
                break;
4✔
668
            }
669

670
            // Add the peeled dimension to the list
671
            peeled_dims.push_back(new_dim);
70✔
672
            aff_coeffs.erase(new_dim);
70✔
673
        }
103✔
674
        if (!success) {
103✔
675
            delinearized.push_back(dim);
33✔
676
            continue;
33✔
677
        }
678

679
        for (auto& peeled_dim : peeled_dims) {
140✔
680
            delinearized.push_back(peeled_dim);
70✔
681
        }
682

683
        // If remaining is not zero, then add the constant term
684
        if (!symbolic::eq(remaining, symbolic::zero()) && success) {
70✔
685
            delinearized.push_back(remaining);
×
686
        }
×
687
    }
138✔
688

689
    return delinearized;
104✔
690
}
104✔
691

692
bool is_subset(const MultiExpression& expr1, const MultiExpression& expr2,
13✔
693
               const Assumptions& assums) {
694
    if (expr1.size() == 0 && expr2.size() == 0) {
13✔
695
        return true;
1✔
696
    }
697

698
    SymbolSet params;
12✔
699
    SymbolSet monotonics;
12✔
700
    for (auto& entry : assums) {
14✔
701
        auto& ass = entry.second;
2✔
702

703
        // No knowledge about the symbol's changes
704
        if (ass.map() == SymEngine::null) {
2✔
705
            continue;
2✔
706
        }
707

708
        // The symbol is constant
NEW
709
        if (symbolic::eq(ass.map(), symbolic::zero())) {
×
NEW
710
            params.insert(entry.first);
×
NEW
711
        }
×
712

713
        // The symbol is monotonic
NEW
714
        if (symbolic::is_monotonic(ass.map(), entry.first, assums)) {
×
NEW
715
            monotonics.insert(entry.first);
×
NEW
716
        }
×
717
    }
718
    return is_equivalent(expr1, expr2, params, assums);
12✔
719
}
13✔
720

721
bool is_disjoint(const MultiExpression& expr1, const MultiExpression& expr2,
5✔
722
                 const Assumptions& assums) {
723
    if (expr1.size() == 0 && expr2.size() == 0) {
5✔
724
        return false;
3✔
725
    }
726

727
    SymbolSet params;
2✔
728
    SymbolSet monotonics;
2✔
729
    for (auto& entry : assums) {
2✔
NEW
730
        auto& ass = entry.second;
×
731

732
        // No knowledge about the symbol's changes
NEW
733
        if (ass.map() == SymEngine::null) {
×
NEW
734
            continue;
×
735
        }
736

737
        // The symbol is constant
NEW
738
        if (symbolic::eq(ass.map(), symbolic::zero())) {
×
NEW
739
            params.insert(entry.first);
×
NEW
740
        }
×
741

742
        // The symbol is monotonic
NEW
743
        if (symbolic::is_monotonic(ass.map(), entry.first, assums)) {
×
NEW
744
            monotonics.insert(entry.first);
×
NEW
745
        }
×
746
    }
747

748
    return is_disjoint(expr1, expr2, params, monotonics, assums);
2✔
749
}
5✔
750

751
}  // namespace symbolic
752
}  // 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