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

daisytuner / sdfglib / 15682840168

16 Jun 2025 01:56PM UTC coverage: 64.969% (+0.08%) from 64.887%
15682840168

push

github

web-flow
Merge pull request #82 from daisytuner/assumptions2

parametrize extreme value analysis

147 of 165 new or added lines in 8 files covered. (89.09%)

7 existing lines in 2 files now uncovered.

8062 of 12409 relevant lines covered (64.97%)

119.02 hits per line

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

85.66
/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) {
215✔
19
    ExpressionSet constraints;
215✔
20
    for (auto& sym : syms) {
514✔
21
        if (assums.find(sym) == assums.end()) {
299✔
22
            continue;
9✔
23
        }
24
        if (seen.find(sym) != seen.end()) {
290✔
25
            continue;
191✔
26
        }
27
        seen.insert(sym);
99✔
28

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

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

48
                auto con_cons = generate_constraints(con_syms, assums, seen);
58✔
49
                constraints.insert(con_cons.begin(), con_cons.end());
58✔
50
            }
58✔
51
        }
60✔
52
        if (!symbolic::eq(lb, symbolic::infty(-1))) {
99✔
53
            if (SymEngine::is_a<SymEngine::Max>(*lb)) {
99✔
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);
97✔
66
                auto con_syms = symbolic::atoms(con);
97✔
67
                constraints.insert(con);
97✔
68

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

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

80
    if (SymEngine::is_a<SymEngine::StrictLessThan>(*con)) {
297✔
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)) {
297✔
86
        auto le = SymEngine::rcp_static_cast<const SymEngine::LessThan>(con);
296✔
87
        auto lhs = le->get_arg1();
296✔
88
        auto rhs = le->get_arg2();
296✔
89
        return language_extension.expression(lhs) + " <= " + language_extension.expression(rhs);
296✔
90
    } else if (SymEngine::is_a<SymEngine::Equality>(*con)) {
297✔
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
}
297✔
104

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

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

121
    // Distinguish between dimensions and parameters
122
    std::vector<std::string> dimensions;
46✔
123
    SymbolSet dimensions_syms;
46✔
124
    std::vector<std::string> parameters;
46✔
125
    SymbolSet parameters_syms;
46✔
126
    for (auto& sym : syms) {
97✔
127
        if (params.find(sym) != params.end()) {
51✔
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()) {
41✔
135
                continue;
×
136
            }
137
            dimensions.push_back(sym->get_name());
41✔
138
            dimensions_syms.insert(sym);
41✔
139
        }
140
    }
141

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

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

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

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

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

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

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

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

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

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

290
    // Distinguish between dimensions and parameters
291
    std::vector<std::string> dimensions;
6✔
292
    SymbolSet dimensions_syms;
6✔
293
    std::vector<std::string> parameters;
6✔
294
    SymbolSet parameters_syms;
6✔
295
    for (auto& sym : syms) {
16✔
296
        if (params.find(sym) != params.end()) {
10✔
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()) {
10✔
304
                continue;
×
305
            }
306
            dimensions.push_back(sym->get_name());
10✔
307
            dimensions_syms.insert(sym);
10✔
308
        }
309
    }
310

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

315
    // Extend parameters with additional symbols from constraints
316
    for (auto& con : constraints_syms) {
25✔
317
        auto con_syms = symbolic::atoms(con);
19✔
318
        for (auto& con_sym : con_syms) {
53✔
319
            if (dimensions_syms.find(con_sym) == dimensions_syms.end()) {
34✔
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
    }
19✔
328

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

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

363
    map += " }";
6✔
364

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

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

376
    isl_ctx* ctx = isl_ctx_alloc();
46✔
377

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

399
    // Find aliasing pairs under the constraint that dimensions are different
400

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

425
    bool disjoint = isl_map_is_empty(alias_pairs);
46✔
426
    isl_map_free(alias_pairs);
46✔
427
    isl_ctx_free(ctx);
46✔
428

429
    return disjoint;
46✔
430
}
46✔
431

432
bool is_disjoint_monotonic(const MultiExpression& expr1, const MultiExpression& expr2,
50✔
433
                           const SymbolSet& params, const SymbolSet& monotonics,
434
                           const Assumptions& assums) {
435
    for (size_t i = 0; i < expr1.size(); i++) {
101✔
436
        auto& dim1 = expr1[i];
55✔
437
        if (expr2.size() <= i) {
55✔
438
            continue;
×
439
        }
440
        auto& dim2 = expr2[i];
55✔
441
        if (!symbolic::eq(dim1, dim2)) {
55✔
442
            continue;
15✔
443
        }
444

445
        // Collect all symbols
446
        symbolic::SymbolSet syms;
40✔
447
        for (auto& sym : symbolic::atoms(dim1)) {
84✔
448
            syms.insert(sym);
44✔
449
        }
450

451
        // Collect all non-constant symbols
452
        symbolic::SymbolSet generators;
40✔
453
        for (auto& sym : syms) {
84✔
454
            if (params.find(sym) == params.end()) {
44✔
455
                generators.insert(sym);
33✔
456
            }
33✔
457
        }
458
        if (generators.empty()) {
40✔
459
            continue;
7✔
460
        }
461

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

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

486
        return true;
4✔
487
    }
40✔
488

489
    return false;
46✔
490
}
50✔
491

492
bool is_disjoint(const MultiExpression& expr1, const MultiExpression& expr2,
50✔
493
                 const SymbolSet& params, const SymbolSet& monotonics, const Assumptions& assums) {
494
    if (is_disjoint_monotonic(expr1, expr2, params, monotonics, assums)) {
50✔
495
        return true;
4✔
496
    }
497
    return is_disjoint_isl(expr1, expr2, params, monotonics, assums);
46✔
498
}
50✔
499

500
bool is_equivalent(const MultiExpression& expr1, const MultiExpression& expr2,
45✔
501
                   const SymbolSet& params, const Assumptions& assums) {
502
    if (expr1.size() != expr2.size()) {
45✔
503
        return false;
×
504
    }
505

506
    // Simple check for equality
507
    bool equals = true;
45✔
508
    for (size_t i = 0; i < expr1.size(); i++) {
47✔
509
        if (!symbolic::eq(expr1[i], expr2[i])) {
8✔
510
            equals = false;
6✔
511
            break;
6✔
512
        }
513
    }
2✔
514
    if (equals) {
45✔
515
        return true;
39✔
516
    }
517

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

531
    isl_set* range = isl_map_range(isl_map_copy(pair_map));
6✔
532

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

546
    isl_set* diagonal = isl_set_read_from_str(ctx, diag_str.c_str());
6✔
547

548
    bool equivalent = false;
6✔
549
    if (range && diagonal) {
6✔
550
        equivalent = isl_set_is_subset(range, diagonal) == 1;
6✔
551
    }
6✔
552

553
    isl_set_free(range);
6✔
554
    isl_set_free(diagonal);
6✔
555
    isl_map_free(pair_map);
6✔
556
    isl_ctx_free(ctx);
6✔
557

558
    return equivalent;
6✔
559
}
45✔
560

561
MultiExpression delinearize(const MultiExpression& expr, const SymbolSet& params,
104✔
562
                            const Assumptions& assums) {
563
    MultiExpression delinearized;
104✔
564
    for (auto& dim : expr) {
218✔
565
        // Step 1: Convert expression into an affine polynomial
566
        SymbolVec symbols;
114✔
567
        for (auto& sym : atoms(dim)) {
227✔
568
            if (params.find(sym) == params.end()) {
113✔
569
                symbols.push_back(sym);
93✔
570
            }
93✔
571
        }
572
        if (symbols.empty()) {
114✔
573
            delinearized.push_back(dim);
21✔
574
            continue;
21✔
575
        }
576

577
        auto poly = polynomial(dim, symbols);
93✔
578
        if (poly == SymEngine::null) {
93✔
579
            delinearized.push_back(dim);
×
580
            continue;
×
581
        }
582

583
        auto aff_coeffs = affine_coefficients(poly, symbols);
93✔
584
        if (aff_coeffs.empty()) {
93✔
585
            delinearized.push_back(dim);
×
586
            continue;
×
587
        }
588

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

611
            // Symbol must be nonnegative
612
            auto sym_lb = minimum(new_dim, {}, assums);
93✔
613
            if (sym_lb == SymEngine::null) {
93✔
614
                success = false;
1✔
615
                break;
1✔
616
            }
617
            auto sym_cond = symbolic::Ge(sym_lb, symbolic::zero());
92✔
618
            if (!symbolic::is_true(sym_cond)) {
92✔
619
                success = false;
24✔
620
                break;
24✔
621
            }
622

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

636
            // Peel off the dimension
637
            remaining = symbolic::sub(remaining, symbolic::mul(stride, new_dim));
68✔
638

639
            // Check if remainder is within bounds
640

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

653
            // remaining must be less than stride
654
            auto rem = symbolic::sub(stride, remaining);
66✔
655
            rem = minimum(rem, {}, assums);
66✔
656
            if (rem == SymEngine::null) {
66✔
NEW
657
                success = false;
×
NEW
658
                break;
×
659
            }
660

661
            auto cond_stride = symbolic::Ge(rem, symbolic::one());
66✔
662
            if (!symbolic::is_true(cond_stride)) {
66✔
663
                success = false;
4✔
664
                break;
4✔
665
            }
666

667
            // Add the peeled dimension to the list
668
            peeled_dims.push_back(new_dim);
62✔
669
            aff_coeffs.erase(new_dim);
62✔
670
        }
93✔
671
        if (!success) {
93✔
672
            delinearized.push_back(dim);
31✔
673
            continue;
31✔
674
        }
675

676
        for (auto& peeled_dim : peeled_dims) {
124✔
677
            delinearized.push_back(peeled_dim);
62✔
678
        }
679

680
        // If remaining is not zero, then add the constant term
681
        if (!symbolic::eq(remaining, symbolic::zero()) && success) {
62✔
682
            delinearized.push_back(remaining);
×
683
        }
×
684
    }
114✔
685

686
    return delinearized;
104✔
687
}
104✔
688

689
}  // namespace symbolic
690
}  // 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