Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 58.4% 596 / 0 / 1020
Functions: -% 0 / 1 / 1
Branches: 48.9% 542 / 0 / 1108

OMCompiler/Compiler/NFFrontEnd/NFResizableConnections.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NFResizableConnections
37 "Connection sets of arrays of connectors whose sizes are resizable parameters
38 (--resizableArrays). Like NFArrayConnections, but all sizes, loop ranges and
39 indices stay symbolic, so that the equations are valid for every value of the
40 size parameters (changed with -override without translating again).
41
42 Numbers are piecewise linear expressions in the size parameters in the normal
43 form max_i(min_j(a_ij)) with affine a_ij. Comparisons are decided
44 symbolically (yes, no or maybe), assuming size parameters >= 1 and the facts
45 given by the valid indices of the connections (e.g. N <= P for a[1:N] connected
46 to b). Undecidable comparisons give pieces that may be empty: their equations
47 are for loops over possibly empty ranges and sums over possibly empty slices.
48
49 The connected connectors are split into pieces (boxes of indices), such that
50 every connection maps whole pieces to whole pieces. A union-find over the
51 pieces and their dimensions gives the connection sets: per set the free
52 dimensions (loops) and the collapsed ones (all elements in the same set).
53 Connections inside one array shifted by one (p[i] to p[i+1]) are handled in
54 closed form, connections that cover a whole array one to one merge it into
55 the other array."
56
57 import FlatModel = NFFlatModel;
58
59 protected
60 import Binding = NFBinding;
61 import Call = NFCall;
62 import ComponentRef = NFComponentRef;
63 import Connector = NFConnector;
64 import DAE;
65 import Dimension = NFDimension;
66 import Equation = NFEquation;
67 import ElementSource;
68 import MetaModelica.Dangerous.*;
69 import Expression = NFExpression;
70 import NFFunction.Function;
71 import NFInstNode.InstNode;
72 import NFInstNode;
73 import NFPrefixes.Purity;
74 import NFPrefixes.Variability;
75 import Operator = NFOperator;
76 import Op = NFOperator.Op;
77 import SimplifyExp = NFSimplifyExp;
78 import Subscript = NFSubscript;
79 import Type = NFType;
80 import Variable = NFVariable;
81 import Class = NFClass;
82 import ErrorExt;
83 import AbsynUtil;
84 import UnorderedMap;
85 import UnorderedSet;
86
87 constant Integer NO = 0;
88 constant Integer YES = 1;
89 constant Integer MAYBE = 2;
90 constant Integer PARAM_MIN = 1 "size parameters are at least this";
91 constant Integer MIN_SIZE = 2 "resizable dimensions and connect loops have at least this many elements";
92 constant Integer MAX_PIECES = 300 "pieces of one connector array before giving up";
93 constant Integer MAX_ROUNDS = 50;
94
95 // ---------------------------------------------------------------------------
96 // affine expressions c + sum k_p * p
97 // ---------------------------------------------------------------------------
98
99 uniontype Aff
100 record AFF
101 Integer c;
102 list<tuple<String, Integer>> k "sorted by name, no zero coefficients";
103 end AFF;
104 end Aff;
105
106 function affInt
107 input Integer c;
108 output Aff a = AFF(c, {});
109 end affInt;
110
111 function affParam
112 input String name;
113 output Aff a = AFF(0, {(name, 1)});
114 end affParam;
115
116 function affAdd
117 input Aff a;
118 input Aff b;
119 output Aff res;
120 protected
121 list<tuple<String, Integer>> k1, k2, k = {};
122 String n1, n2;
123 Integer c1, c2, ca, cb;
124 algorithm
125 8795 AFF(ca, k1) := a;
126 8795 AFF(cb, k2) := b;
127
4/4
✓ Branch 0 taken 6319 times.
✓ Branch 1 taken 7023 times.
✓ Branch 2 taken 4547 times.
✓ Branch 3 taken 1772 times.
13342 while not listEmpty(k1) and not listEmpty(k2) loop
128 4547 (n1, c1) := listHead(k1);
129 4547 (n2, c2) := listHead(k2);
130
3/4
✓ Branch 0 taken 4547 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4451 times.
✓ Branch 4 taken 96 times.
4547 if n1 == n2 then
131
2/2
✓ Branch 0 taken 2026 times.
✓ Branch 1 taken 2425 times.
4451 if c1 + c2 <> 0 then
132 2026 k := (n1, c1 + c2) :: k;
133 end if;
134 4451 k1 := listRest(k1);
135 4451 k2 := listRest(k2);
136 elseif stringCompare(n1, n2) < 0 then
137 24 k := (n1, c1) :: k;
138 24 k1 := listRest(k1);
139 else
140 72 k := (n2, c2) :: k;
141 72 k2 := listRest(k2);
142 end if;
143 end while;
144 8795 res := AFF(ca + cb, List.append_reverse(k, listAppend(k1, k2)));
145 end affAdd;
146
147 function affScale
148 input Aff a;
149 input Integer f;
150 output Aff res;
151 protected
152 Integer c;
153 list<tuple<String, Integer>> k;
154 algorithm
155 8162 AFF(c, k) := a;
156
1/2
✓ Branch 0 taken 8162 times.
✗ Branch 1 not taken.
8162 if f == 0 then
157 res := AFF(0, {});
158 else
159
4/4
✓ Branch 0 taken 6628 times.
✓ Branch 1 taken 8162 times.
✓ Branch 2 taken 6628 times.
✓ Branch 3 taken 8162 times.
14790 res := AFF(c * f, list((Util.tuple21(t), f * Util.tuple22(t)) for t in k));
160 end if;
161 end affScale;
162
163 function affNeg
164 input Aff a;
165 output Aff res = affScale(a, -1);
166 end affNeg;
167
168 function affSub
169 input Aff a;
170 input Aff b;
171 output Aff res = affAdd(a, affNeg(b));
172 end affSub;
173
174 function affIsConst
175 input Aff a;
176 output Boolean b;
177 protected
178 list<tuple<String, Integer>> k;
179 algorithm
180 2206 AFF(k = k) := a;
181 ✗ b := listEmpty(k);
182 end affIsConst;
183
184 function affString
185 input Aff a;
186 output String str;
187 protected
188 Integer c, v;
189 list<tuple<String, Integer>> k;
190 list<String> strl = {};
191 String n;
192 algorithm
193 2550 AFF(c, k) := a;
194
2/2
✓ Branch 0 taken 1190 times.
✓ Branch 1 taken 2550 times.
3740 for t in k loop
195 1190 (n, v) := t;
196
3/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 1149 times.
✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
1190 strl := (if v == 1 then n elseif v == -1 then "-" + n else intString(v) + "*" + n) :: strl;
197 end for;
198
4/4
✓ Branch 0 taken 691 times.
✓ Branch 1 taken 1859 times.
✓ Branch 2 taken 489 times.
✓ Branch 3 taken 202 times.
2550 if c <> 0 or listEmpty(strl) then
199 2348 strl := intString(c) :: strl;
200 end if;
201 2550 str := stringDelimitList(listReverseInPlace(strl), " + ");
202 end affString;
203
204 function affNonNeg
205 "a >= 0 for all parameter values >= PARAM_MIN"
206 input Aff a;
207 output Boolean b = true;
208 protected
209 Integer c, s, v;
210 list<tuple<String, Integer>> k;
211 algorithm
212 6650 AFF(c, k) := a;
213 s := c;
214
2/2
✓ Branch 0 taken 4998 times.
✓ Branch 1 taken 2784 times.
7782 for t in k loop
215 4998 (_, v) := t;
216
2/2
✓ Branch 0 taken 3866 times.
✓ Branch 1 taken 1132 times.
4998 if v < 0 then
217 b := false;
218 3866 return;
219 end if;
220 1132 s := s + v * PARAM_MIN;
221 end for;
222 2784 b := s >= 0;
223 end affNonNeg;
224
225 function affProvable
226 "a >= 0 from the parameter bounds and up to two facts"
227 input Aff a;
228 input list<Aff> facts;
229 output Boolean b = true;
230 protected
231 list<Aff> rest = facts;
232 Aff f, d;
233 algorithm
234
2/2
✓ Branch 1 taken 173 times.
✓ Branch 2 taken 1296 times.
1469 if affNonNeg(a) then
235 173 return;
236 end if;
237
2/2
✓ Branch 0 taken 2685 times.
✓ Branch 1 taken 337 times.
3022 while not listEmpty(rest) loop
238 2685 f :: rest := rest;
239 2685 d := affSub(a, f);
240
2/2
✓ Branch 1 taken 1726 times.
✓ Branch 2 taken 959 times.
2685 if affNonNeg(d) then
241 959 return;
242 end if;
243
2/2
✓ Branch 0 taken 2496 times.
✓ Branch 1 taken 1726 times.
4222 for h in rest loop
244
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2496 times.
2496 if affNonNeg(affSub(d, h)) then
245 ✗ return;
246 end if;
247 end for;
248 end while;
249 b := false;
250 end affProvable;
251
252 function affLe
253 "a <= b: YES, NO or MAYBE"
254 input Aff a;
255 input Aff b;
256 input list<Aff> facts;
257 output Integer res;
258 protected
259 Aff d = affSub(b, a);
260 Integer c;
261 algorithm
262
2/2
✓ Branch 0 taken 947 times.
✓ Branch 1 taken 1132 times.
2079 if affIsConst(d) then
263 947 AFF(c = c) := d;
264
2/2
✓ Branch 0 taken 537 times.
✓ Branch 1 taken 410 times.
947 res := if c >= 0 then YES else NO;
265 elseif affProvable(d, facts) then
266 res := YES;
267 elseif affProvable(affAdd(affNeg(d), affInt(-1)), facts) then
268 res := NO;
269 else
270 res := MAYBE;
271 end if;
272 end affLe;
273
274 // ---------------------------------------------------------------------------
275 // symbolic numbers max_i(min_j(a_ij))
276 // ---------------------------------------------------------------------------
277
278 type Term = list<Aff> "min over the elements";
279
280 uniontype Sym
281 record SYM
282 list<list<Aff>> terms "max over the terms, a term is the min over its elements";
283 String key "unique string of the normal form";
284 end SYM;
285 end Sym;
286
287 function symInt
288 input Integer c;
289 output Sym s = SYM({{affInt(c)}}, intString(c));
290 end symInt;
291
292 function symAff
293 input Aff a;
294 output Sym s = SYM({{a}}, affString(a));
295 end symAff;
296
297 function symString
298 input Sym s;
299 output String str;
300 algorithm
301
2/2
✓ Branch 1 taken 2295 times.
✓ Branch 2 taken 765 times.
10710 SYM(key = str) := s;
302 end symString;
303
304 function symEq
305 input Sym a;
306 input Sym b;
307 output Boolean eq = symString(a) == symString(b);
308 end symEq;
309
310 function symIsAff
311 input Sym s;
312 output Boolean b;
313 algorithm
314 b := match s
315 case SYM(terms = {{_}}) then true;
316 else false;
317 end match;
318 end symIsAff;
319
320 function symGetAff
321 input Sym s;
322 output Aff a;
323 algorithm
324
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 4567 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4567 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4567 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4567 times.
4567 SYM(terms = {{a}}) := s;
325 end symGetAff;
326
327 function termString
328 input list<Aff> t;
329 output String str;
330 algorithm
331 str := match t
332 810 case {_} then affString(listHead(t));
333 ✗ else "min(" + stringDelimitList(list(affString(a) for a in t), ", ") + ")";
334 end match;
335 end termString;
336
337 function termLe
338 "min(t) <= min(u) for certain: every element of u is >= some element of t"
339 input list<Aff> t;
340 input list<Aff> u;
341 input list<Aff> facts;
342 output Boolean res = true;
343 protected
344 Boolean found;
345 algorithm
346
2/2
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 419 times.
1101 for b in u loop
347 found := false;
348
2/2
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 263 times.
945 for a in t loop
349
2/2
✓ Branch 1 taken 263 times.
✓ Branch 2 taken 419 times.
682 if affLe(a, b, facts) == YES then
350 found := true;
351 break;
352 end if;
353 end for;
354
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 419 times.
682 if not found then
355 res := false;
356 263 return;
357 end if;
358 end for;
359 end termLe;
360
361 function termGt
362 "min(t) > min(u) for certain: some element of u is < every element of t"
363 input list<Aff> t;
364 input list<Aff> u;
365 input list<Aff> facts;
366 output Boolean res = false;
367 protected
368 Boolean all_gt;
369 algorithm
370 ✗ for b in u loop
371 all_gt := true;
372 ✗ for a in t loop
373 ✗ if affLe(a, b, facts) <> NO then
374 all_gt := false;
375 break;
376 end if;
377 end for;
378 ✗ if all_gt then
379 res := true;
380 ✗ return;
381 end if;
382 end for;
383 end termGt;
384
385 function pruneTerm
386 "min over affine expressions without the elements >= another one, sorted"
387 input list<Aff> t;
388 input list<Aff> facts;
389 output list<Aff> res = {};
390 protected
391 Boolean dominated;
392 algorithm
393
2/2
✓ Branch 0 taken 1620 times.
✓ Branch 1 taken 1229 times.
2849 for a in t loop
394 dominated := false;
395
2/2
✓ Branch 0 taken 391 times.
✓ Branch 1 taken 1420 times.
1811 for b in res loop
396
2/2
✓ Branch 1 taken 191 times.
✓ Branch 2 taken 200 times.
391 if affLe(b, a, facts) == YES then
397 dominated := true;
398 break;
399 end if;
400 end for;
401
2/2
✓ Branch 0 taken 1420 times.
✓ Branch 1 taken 200 times.
1620 if not dominated then
402
4/6
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 191 times.
✓ Branch 4 taken 1420 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1420 times.
1611 res := a :: list(b for b guard affLe(a, b, facts) <> YES in res);
403 end if;
404 end for;
405 1229 res := List.sort(res, affGreater);
406 end pruneTerm;
407
408 function affGreater
409 input Aff a;
410 input Aff b;
411 output Boolean gt = stringCompare(affString(a), affString(b)) > 0;
412 end affGreater;
413
414 function symMake
415 "the normal form of max over terms"
416 input list<list<Aff>> terms;
417 input list<Aff> facts;
418 output Sym s;
419 protected
420 list<list<Aff>> keep = {};
421 Boolean dominated;
422 list<String> strl;
423 UnorderedMap<String, Term> uniq;
424 algorithm
425
2/2
✓ Branch 0 taken 1229 times.
✓ Branch 1 taken 810 times.
2039 for t in terms loop
426 1229 t := pruneTerm(t, facts);
427 dominated := false;
428
2/2
✓ Branch 0 taken 419 times.
✓ Branch 1 taken 1073 times.
1492 for u in keep loop
429
2/2
✓ Branch 1 taken 263 times.
✓ Branch 2 taken 156 times.
419 if termLe(t, u, facts) then
430 dominated := true;
431 break;
432 end if;
433 end for;
434
2/2
✓ Branch 0 taken 1073 times.
✓ Branch 1 taken 156 times.
1229 if not dominated then
435
4/6
✓ Branch 1 taken 263 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 263 times.
✓ Branch 4 taken 1073 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1073 times.
1336 keep := t :: list(u for u guard not termLe(u, t, facts) in keep);
436 end if;
437 end for;
438 // unique terms, sorted by their strings (the normal form)
439 810 uniq := UnorderedMap.new<Term>(stringHashDjb2, stringEq);
440
2/2
✓ Branch 0 taken 810 times.
✓ Branch 1 taken 810 times.
1620 for t in keep loop
441 810 UnorderedMap.tryAdd(termString(t), t, uniq);
442 end for;
443 810 strl := List.sort(UnorderedMap.keyList(uniq), stringGreater);
444
4/4
✓ Branch 0 taken 810 times.
✓ Branch 1 taken 810 times.
✓ Branch 2 taken 810 times.
✓ Branch 3 taken 810 times.
1620 keep := list(UnorderedMap.getOrFail(str, uniq) for str in strl);
445
1/2
✓ Branch 1 taken 810 times.
✗ Branch 2 not taken.
810 s := SYM(keep, if listLength(strl) == 1 then listHead(strl) else "max(" + stringDelimitList(strl, ", ") + ")");
446 end symMake;
447
448 function stringGreater
449 input String a;
450 input String b;
451 output Boolean gt = stringCompare(a, b) > 0;
452 end stringGreater;
453
454 function symAdd
455 input Sym a;
456 input Sym b;
457 input list<Aff> facts;
458 output Sym s;
459 protected
460 list<list<Aff>> ta, tb, terms = {};
461 list<Aff> t;
462 algorithm
463 1140 SYM(terms = ta) := a;
464 1140 SYM(terms = tb) := b;
465
2/4
✓ Branch 1 taken 1140 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1140 times.
1140 if symIsAff(a) and symIsAff(b) then
466 1140 s := symAff(affAdd(symGetAff(a), symGetAff(b)));
467 1140 return;
468 end if;
469 ✗ for x in ta loop
470 ✗ for y in tb loop
471 t := {};
472 ✗ for ex in x loop
473 ✗ for ey in y loop
474 ✗ t := affAdd(ex, ey) :: t;
475 end for;
476 end for;
477 terms := t :: terms;
478 end for;
479 end for;
480 ✗ s := symMake(terms, facts);
481 end symAdd;
482
483 function symAddInt
484 input Sym a;
485 input Integer c;
486 input list<Aff> facts;
487 output Sym s = symAdd(a, symInt(c), facts);
488 end symAddInt;
489
490 function symMin
491 input Sym a;
492 input Sym b;
493 input list<Aff> facts;
494 output Sym s;
495 protected
496 list<list<Aff>> ta, tb, terms = {};
497 algorithm
498
2/2
✓ Branch 1 taken 319 times.
✓ Branch 2 taken 391 times.
710 if symEq(a, b) then
499 s := a;
500 319 return;
501 end if;
502 391 SYM(terms = ta) := a;
503 391 SYM(terms = tb) := b;
504
2/2
✓ Branch 0 taken 391 times.
✓ Branch 1 taken 391 times.
782 for x in ta loop
505
2/2
✓ Branch 0 taken 391 times.
✓ Branch 1 taken 391 times.
782 for y in tb loop
506 391 terms := listAppend(x, y) :: terms;
507 end for;
508 end for;
509 391 s := symMake(terms, facts);
510 end symMin;
511
512 function symMax
513 input Sym a;
514 input Sym b;
515 input list<Aff> facts;
516 output Sym s;
517 protected
518 list<list<Aff>> ta, tb;
519 algorithm
520
2/2
✓ Branch 1 taken 291 times.
✓ Branch 2 taken 419 times.
710 if symEq(a, b) then
521 s := a;
522 291 return;
523 end if;
524 419 SYM(terms = ta) := a;
525 419 SYM(terms = tb) := b;
526 419 s := symMake(listAppend(ta, tb), facts);
527 end symMax;
528
529 function symNeg
530 "-max_i min_j a_ij = min_i max_j (-a_ij)"
531 input Sym a;
532 input list<Aff> facts;
533 output Sym s;
534 protected
535 list<list<Aff>> ta;
536 Boolean first = true;
537 Sym m;
538 algorithm
539
1/2
✓ Branch 1 taken 512 times.
✗ Branch 2 not taken.
512 if symIsAff(a) then
540 512 s := symAff(affNeg(symGetAff(a)));
541 512 return;
542 end if;
543 ✗ SYM(terms = ta) := a;
544 ✗ for t in ta loop
545 ✗ m := symMake(list({affNeg(x)} for x in t), facts);
546 ✗ s := if first then m else symMin(s, m, facts);
547 first := false;
548 end for;
549 end symNeg;
550
551 function symSub
552 input Sym a;
553 input Sym b;
554 input list<Aff> facts;
555 output Sym s = symAdd(a, symNeg(b, facts), facts);
556 end symSub;
557
558 function symLe
559 "a <= b: YES, NO or MAYBE"
560 input Sym a;
561 input Sym b;
562 input list<Aff> facts;
563 output Integer res;
564 protected
565 Sym d;
566 list<list<Aff>> td, ta, tb;
567 Aff zero = affInt(0);
568 Boolean b1, b2;
569 algorithm
570
2/2
✓ Branch 1 taken 722 times.
✓ Branch 2 taken 815 times.
1537 if symEq(a, b) then
571 res := YES;
572 722 return;
573 end if;
574
2/4
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815 times.
✗ Branch 5 not taken.
815 if symIsAff(a) and symIsAff(b) then
575 815 res := affLe(symGetAff(a), symGetAff(b), facts);
576 815 return;
577 end if;
578 // the sign of the difference b - a = max_i min_j d_ij
579 ✗ d := symSub(b, a, facts);
580 ✗ SYM(terms = td) := d;
581 ✗ for t in td loop
582 ✗ if List.all(list(affLe(zero, x, facts) == YES for x in t), Util.id) then
583 res := YES;
584 ✗ return;
585 end if;
586 end for;
587 b1 := true;
588 ✗ for t in td loop
589 ✗ if not List.any(list(affLe(zero, x, facts) == NO for x in t), Util.id) then
590 b1 := false;
591 break;
592 end if;
593 end for;
594 ✗ if b1 then
595 res := NO;
596 ✗ return;
597 end if;
598 // term by term
599 ✗ SYM(terms = ta) := a;
600 ✗ SYM(terms = tb) := b;
601 b1 := true;
602 ✗ for t in ta loop
603 b2 := false;
604 ✗ for u in tb loop
605 ✗ if termLe(t, u, facts) then
606 b2 := true;
607 break;
608 end if;
609 end for;
610 ✗ if not b2 then
611 b1 := false;
612 break;
613 end if;
614 end for;
615 ✗ if b1 then
616 res := YES;
617 ✗ return;
618 end if;
619 ✗ for t in ta loop
620 b2 := true;
621 ✗ for u in tb loop
622 ✗ if not termGt(t, u, facts) then
623 b2 := false;
624 break;
625 end if;
626 end for;
627 ✗ if b2 then
628 res := NO;
629 ✗ return;
630 end if;
631 end for;
632 res := MAYBE;
633 end symLe;
634
635 function symToExp
636 input Sym s;
637 input UnorderedMap<String, Expression> params;
638 output Expression exp;
639 protected
640 list<list<Aff>> terms;
641 list<Expression> maxl, minl;
642 algorithm
643 80 SYM(terms = terms) := s;
644 maxl := {};
645
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 80 times.
160 for t in terms loop
646
4/4
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 80 times.
✓ Branch 3 taken 80 times.
160 minl := list(affToExp(a, params) for a in t);
647 80 maxl := foldBuiltin(NFBuiltinFuncs.MIN_INT, minl) :: maxl;
648 end for;
649 80 exp := foldBuiltin(NFBuiltinFuncs.MAX_INT, listReverseInPlace(maxl));
650 end symToExp;
651
652 function foldBuiltin
653 input Function fn;
654 input list<Expression> expl;
655 output Expression exp;
656 protected
657 list<Expression> rest;
658 algorithm
659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 exp :: rest := expl;
660
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
160 for e in rest loop
661 ✗ exp := Expression.CALL(Call.makeTypedCall(fn, {exp, e},
662 NFPrefixes.variabilityMax(Expression.variability(exp), Expression.variability(e)), Purity.PURE));
663 end for;
664 end foldBuiltin;
665
666 function affToExp
667 input Aff a;
668 input UnorderedMap<String, Expression> params;
669 output Expression exp;
670 protected
671 Integer c, v;
672 list<tuple<String, Integer>> k;
673 String n;
674 Expression e;
675 algorithm
676 80 AFF(c, k) := a;
677 80 exp := Expression.INTEGER(c);
678
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 80 times.
109 for t in k loop
679 29 (n, v) := t;
680 29 e := UnorderedMap.getOrFail(n, params);
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if v <> 1 then
682 ✗ e := Expression.BINARY(Expression.INTEGER(v), Operator.makeMul(Type.INTEGER()), e);
683 end if;
684 29 exp := Expression.BINARY(exp, Operator.makeAdd(Type.INTEGER()), e);
685 end for;
686 80 exp := SimplifyExp.simplify(exp);
687 end affToExp;
688
689 // ---------------------------------------------------------------------------
690 // intervals [lo, hi] (step 1, empty if hi < lo) and boxes of intervals
691 // ---------------------------------------------------------------------------
692
693 uniontype Iv
694 record IV
695 Sym lo;
696 Sym hi;
697 end IV;
698 end Iv;
699
700 type Box = list<Iv>;
701
702 function ivLo
703 input Iv iv;
704 output Sym lo;
705 algorithm
706 18 IV(lo = lo) := iv;
707 end ivLo;
708
709 function ivHi
710 input Iv iv;
711 output Sym hi;
712 algorithm
713 20 IV(hi = hi) := iv;
714 end ivHi;
715
716 function ivString
717 input Iv iv;
718 output String str;
719 protected
720 Sym lo, hi;
721 algorithm
722 528 IV(lo, hi) := iv;
723 1056 str := symString(lo) + ":" + symString(hi);
724 end ivString;
725
726 function boxString
727 input Box box;
728 output String str = "[" + stringDelimitList(list(ivString(iv) for iv in box), ", ") + "]";
729 end boxString;
730
731 function ivEmpty
732 input Iv iv;
733 input list<Aff> facts;
734 output Integer res;
735 protected
736 Sym lo, hi;
737 algorithm
738 801 IV(lo, hi) := iv;
739 res := match symLe(lo, hi, facts)
740 case 1 then NO; // YES
741 case 0 then YES; // NO
742 else MAYBE;
743 end match;
744 end ivEmpty;
745
746 function boxEmpty
747 input Box box;
748 input list<Aff> facts;
749 output Integer res = NO;
750 protected
751 Integer r;
752 algorithm
753
2/2
✓ Branch 0 taken 753 times.
✓ Branch 1 taken 519 times.
1272 for iv in box loop
754 753 r := ivEmpty(iv, facts);
755
2/2
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 457 times.
753 if r == YES then
756 res := YES;
757 296 return;
758 elseif r == MAYBE then
759 res := MAYBE;
760 end if;
761 end for;
762 end boxEmpty;
763
764 function ivInter
765 input Iv a;
766 input Iv b;
767 input list<Aff> facts;
768 output Iv res;
769 algorithm
770 686 res := IV(symMax(a.lo, b.lo, facts), symMin(a.hi, b.hi, facts));
771 end ivInter;
772
773 function ivMinus
774 "a without b: up to two pieces, possibly empty"
775 input Iv a;
776 input Iv b;
777 input list<Aff> facts;
778 output list<Iv> res = {};
779 protected
780 Iv left, right;
781 algorithm
782 24 left := IV(a.lo, symMin(a.hi, symAddInt(b.lo, -1, facts), facts));
783 24 right := IV(symMax(a.lo, symAddInt(b.hi, 1, facts), facts), a.hi);
784
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
24 if ivEmpty(right, facts) <> YES then
785 res := right :: res;
786 end if;
787
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
24 if ivEmpty(left, facts) <> YES then
788 res := left :: res;
789 end if;
790 end ivMinus;
791
792 function boxInter
793 input Box a;
794 input Box b;
795 input list<Aff> facts;
796 output Box res = List.threadMap(a, b, function ivInter(facts = facts));
797 end boxInter;
798
799 function boxMinus
800 "a without b as disjoint boxes"
801 input Box a;
802 input Box b;
803 input list<Aff> facts;
804 output list<Box> res = {};
805 protected
806 array<Iv> rest = listArray(a);
807 array<Iv> bb = listArray(b);
808 Box bx;
809 algorithm
810
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
46 for d in 1:arrayLength(rest) loop
811
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 24 times.
46 for p in ivMinus(rest[d], bb[d], facts) loop
812 bx := {};
813
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
46 for j in arrayLength(rest):-1:1 loop
814
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 22 times.
24 bx := (if j == d then p else rest[j]) :: bx;
815 end for;
816
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 if boxEmpty(bx, facts) <> YES then
817 res := bx :: res;
818 end if;
819 end for;
820 24 rest[d] := ivInter(rest[d], bb[d], facts);
821 end for;
822 22 res := listReverseInPlace(res);
823 end boxMinus;
824
825 function boxContains
826 "b is a subset of a for certain"
827 input Box a;
828 input Box b;
829 input list<Aff> facts;
830 output Boolean res = true;
831 protected
832 list<Iv> rb = b;
833 Iv y;
834 algorithm
835
2/2
✓ Branch 0 taken 291 times.
✓ Branch 1 taken 270 times.
561 for x in a loop
836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 291 times.
291 y :: rb := rb;
837
4/4
✓ Branch 1 taken 255 times.
✓ Branch 2 taken 36 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 249 times.
291 if symLe(x.lo, y.lo, facts) <> YES or symLe(y.hi, x.hi, facts) <> YES then
838 res := false;
839 42 return;
840 end if;
841 end for;
842 end boxContains;
843
844 function boxKey
845 input Box box;
846 output String key = boxString(box);
847 end boxKey;
848
849 // ---------------------------------------------------------------------------
850 // graph: connector arrays and connections (edges) between them
851 // ---------------------------------------------------------------------------
852
853 uniontype Side
854 "one side of a connection: per dimension of the connector the iterator of
855 the connection domain (0 for a constant index) and an offset:
856 index = iterator + offset, or index = offset"
857 record SIDE
858 Integer vertex;
859 list<Integer> iters;
860 list<Sym> offs;
861 end SIDE;
862 end Side;
863
864 function sideVertex
865 input Side side;
866 output Integer v;
867 algorithm
868
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 SIDE(vertex = v) := side;
869 end sideVertex;
870
871 uniontype Edge
872 record EDGE
873 Box dom "the ranges of the iterators";
874 Side a;
875 Side b;
876 DAE.ElementSource source;
877 end EDGE;
878 end Edge;
879
880 uniontype Vertex
881 record VERTEX
882 String name;
883 Option<Connector> conn "NONE for virtual hubs";
884 Box box;
885 end VERTEX;
886 end Vertex;
887
888 function vertexBox
889 input Vector<Vertex> vertices;
890 input Integer v;
891 output Box box;
892 algorithm
893 262 VERTEX(box = box) := Vector.get(vertices, v);
894 end vertexBox;
895
896 function vertexName
897 input Vector<Vertex> vertices;
898 input Integer v;
899 output String name;
900 algorithm
901 ✗ VERTEX(name = name) := Vector.get(vertices, v);
902 end vertexName;
903
904 function sideImage
905 input Side side;
906 input Box dom;
907 input list<Aff> facts;
908 output Box box = {};
909 protected
910 list<Sym> offs = side.offs;
911 Sym off;
912 Iv iv;
913 algorithm
914
2/2
✓ Branch 0 taken 301 times.
✓ Branch 1 taken 327 times.
628 for it in side.iters loop
915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301 times.
301 off :: offs := offs;
916
2/2
✓ Branch 0 taken 211 times.
✓ Branch 1 taken 90 times.
301 if it > 0 then
917 211 iv := listGet(dom, it);
918 211 box := IV(symAdd(iv.lo, off, facts), symAdd(iv.hi, off, facts)) :: box;
919 else
920 90 box := IV(off, off) :: box;
921 end if;
922 end for;
923 327 box := listReverseInPlace(box);
924 end sideImage;
925
926 function sidePreimage
927 "the part of the domain mapped into box"
928 input Side side;
929 input Box dom;
930 input Box box;
931 input list<Aff> facts;
932 output Box res;
933 protected
934 array<Iv> ivs = listArray(dom);
935 list<Sym> offs = side.offs;
936 list<Iv> bl = box;
937 Sym off;
938 Iv b;
939 algorithm
940
2/2
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 202 times.
401 for it in side.iters loop
941
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
199 off :: offs := offs;
942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
199 b :: bl := bl;
943
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 41 times.
199 if it > 0 then
944 158 ivs[it] := ivInter(ivs[it], IV(symSub(b.lo, off, facts), symSub(b.hi, off, facts)), facts);
945 end if;
946 end for;
947 202 res := arrayList(ivs);
948 end sidePreimage;
949
950 function sideConstIn
951 "every constant index of the side lies in box for certain"
952 input Side side;
953 input Box box;
954 input list<Aff> facts;
955 output Boolean res = true;
956 protected
957 list<Sym> offs = side.offs;
958 list<Iv> bl = box;
959 Sym off;
960 Iv b;
961 algorithm
962
2/2
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 202 times.
461 for it in side.iters loop
963
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 259 times.
259 off :: offs := offs;
964
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 259 times.
259 b :: bl := bl;
965
6/6
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 162 times.
✓ Branch 3 taken 93 times.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 52 times.
✓ Branch 7 taken 41 times.
259 if it == 0 and (symLe(b.lo, off, facts) <> YES or symLe(off, b.hi, facts) <> YES) then
966 res := false;
967 56 return;
968 end if;
969 end for;
970 end sideConstIn;
971
972 function addFact
973 input Sym f;
974 input UnorderedSet<String> keys;
975 input output list<Aff> facts;
976 algorithm
977
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 127 times.
✓ Branch 4 taken 73 times.
✓ Branch 5 taken 54 times.
✓ Branch 7 taken 36 times.
✓ Branch 8 taken 18 times.
308 if symIsAff(f) and not affIsConst(symGetAff(f)) and not UnorderedSet.contains(symString(f), keys) then
978 18 UnorderedSet.add(symString(f), keys);
979 18 facts := symGetAff(f) :: facts;
980 end if;
981 end addFact;
982
983 function indexFacts
984 "resizable connector arrays and connect loops have at least MIN_SIZE elements
985 (as the backend requires). Valid indices: the image of a certainly non-empty
986 connection domain lies in the connector array, which gives facts like N <= P"
987 input Vector<Vertex> vertices;
988 input list<Edge> edges;
989 output list<Aff> facts = {};
990 protected
991 list<Iv> vbox;
992 Iv viv;
993 list<Aff> size_facts;
994 UnorderedSet<String> keys = UnorderedSet.new(stringHashDjb2, stringEq);
995 algorithm
996
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 5 times.
48 for v in 1:Vector.size(vertices) loop
997
2/2
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 36 times.
61 for iv in vertexBox(vertices, v) loop
998 25 facts := addFact(symSub(iv.hi, symAddInt(iv.lo, MIN_SIZE - 1, {}), {}), keys, facts);
999 end for;
1000 end for;
1001
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
38 for e in edges loop
1002
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 26 times.
42 for iv in e.dom loop
1003 16 facts := addFact(symSub(iv.hi, symAddInt(iv.lo, MIN_SIZE - 1, {}), {}), keys, facts);
1004 end for;
1005 end for;
1006 size_facts := facts;
1007
1008
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
38 for e in edges loop
1009
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 if boxEmpty(e.dom, size_facts) == NO then
1010
2/2
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 26 times.
78 for side in {e.a, e.b} loop
1011 52 vbox := vertexBox(vertices, side.vertex);
1012
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 52 times.
95 for iv in sideImage(side, e.dom, {}) loop
1013
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 viv :: vbox := vbox;
1014 43 facts := addFact(symSub(viv.hi, iv.hi, {}), keys, facts);
1015 43 facts := addFact(symSub(iv.lo, viv.lo, {}), keys, facts);
1016 end for;
1017 end for;
1018 end if;
1019 end for;
1020 end indexFacts;
1021
1022 // ---------------------------------------------------------------------------
1023 // contraction of one to one connected arrays, shifts inside one array
1024 // ---------------------------------------------------------------------------
1025
1026 uniontype Merge
1027 "x merged into y: y[d'] = x[d] + c for (d, c) = tr[d']"
1028 record MERGE
1029 Integer x;
1030 Integer y;
1031 list<tuple<Integer, Sym>> tr;
1032 end MERGE;
1033 end Merge;
1034
1035 function substituteSide
1036 input Side side;
1037 input Integer x;
1038 input Integer y;
1039 input list<tuple<Integer, Sym>> tr;
1040 input list<Aff> facts;
1041 output Side res;
1042 protected
1043 array<Integer> iters;
1044 array<Sym> offs;
1045 list<Integer> it_l = {};
1046 list<Sym> off_l = {};
1047 Integer d;
1048 Sym c;
1049 algorithm
1050
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if side.vertex <> x then
1051 res := side;
1052 26 return;
1053 end if;
1054 ✗ iters := listArray(side.iters);
1055 ✗ offs := listArray(side.offs);
1056 ✗ for t in tr loop
1057 ✗ (d, c) := t;
1058 ✗ it_l := iters[d] :: it_l;
1059 ✗ off_l := symAdd(offs[d], c, facts) :: off_l;
1060 end for;
1061 ✗ res := SIDE(y, listReverseInPlace(it_l), listReverseInPlace(off_l));
1062 end substituteSide;
1063
1064 function fullBijection
1065 "the transformation x -> y if the edge maps all of sx.vertex one to one into
1066 sy.vertex (every dimension its own iterator)"
1067 input Vector<Vertex> vertices;
1068 input Edge e;
1069 input Side sx;
1070 input Side sy;
1071 input list<Aff> facts;
1072 output Option<list<tuple<Integer, Sym>>> otr = NONE();
1073 protected
1074 list<Integer> its_x = sx.iters, its_y = sy.iters;
1075 array<Sym> offx = listArray(sx.offs);
1076 list<Sym> offy = sy.offs;
1077 list<Iv> img, vbox;
1078 list<tuple<Integer, Sym>> tr = {};
1079 Integer d;
1080 Sym oy;
1081 UnorderedMap<Integer, Integer> pos_x "iterator -> dimension of x";
1082 UnorderedSet<Integer> set_y;
1083 algorithm
1084 // every dimension of both sides its own iterator, all iterators of the domain
1085 58 pos_x := UnorderedMap.new<Integer>(Util.id, intEq);
1086 d := 0;
1087
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 58 times.
104 for it in its_x loop
1088 46 d := d + 1;
1089 46 UnorderedMap.add(it, d, pos_x);
1090 end for;
1091 58 set_y := UnorderedSet.fromList(its_y, Util.id, intEq);
1092
11/14
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 45 times.
✓ Branch 4 taken 13 times.
✓ Branch 6 taken 38 times.
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 38 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 38 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 30 times.
✓ Branch 19 taken 8 times.
✓ Branch 22 taken 8 times.
✓ Branch 23 taken 22 times.
58 if sx.vertex == sy.vertex or UnorderedMap.contains(0, pos_x) or UnorderedSet.contains(0, set_y) or
1093 UnorderedMap.size(pos_x) <> listLength(its_x) or UnorderedSet.size(set_y) <> listLength(its_y) or
1094 listLength(its_x) <> listLength(e.dom) or listLength(its_y) <> listLength(e.dom) then
1095 36 return;
1096 end if;
1097 22 img := sideImage(sx, e.dom, facts);
1098 22 vbox := vertexBox(vertices, sx.vertex);
1099
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 6 times.
27 for iv in img loop
1100
4/4
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 5 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 5 times.
58 if not (symEq(iv.lo, ivLo(listHead(vbox))) and symEq(iv.hi, ivHi(listHead(vbox)))) then
1101 16 return;
1102 end if;
1103 5 vbox := listRest(vbox);
1104 end for;
1105
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 for it in its_y loop
1106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 oy :: offy := offy;
1107 3 d := UnorderedMap.getOrFail(it, pos_x);
1108 3 tr := (d, symSub(oy, offx[d], facts)) :: tr;
1109 end for;
1110 6 otr := SOME(listReverseInPlace(tr));
1111 end fullBijection;
1112
1113 function contract
1114 "merges arrays that are fully and one to one connected to another array"
1115 input Vector<Vertex> vertices;
1116 input array<Boolean> alive;
1117 input output list<Edge> edges;
1118 input list<Aff> facts;
1119 output list<Merge> merges = {};
1120 protected
1121 Boolean changed = true;
1122 Integer k, x, y, z, w;
1123 Option<list<tuple<Integer, Sym>>> otr;
1124 list<tuple<Integer, Sym>> tr, tz;
1125 array<Edge> ea;
1126 Edge ek, ej;
1127 algorithm
1128
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
30 while changed loop
1129 changed := false;
1130 18 ea := listArray(edges);
1131
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 13 times.
44 for k in 1:arrayLength(ea) loop
1132 32 ek := ea[k];
1133
2/2
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 26 times.
116 for sides in {(ek.a, ek.b), (ek.b, ek.a)} loop
1134 58 otr := fullBijection(vertices, ek, Util.tuple21(sides), Util.tuple22(sides), facts);
1135
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 6 times.
58 if isSome(otr) then
1136 6 SOME(tr) := otr;
1137 6 x := sideVertex(Util.tuple21(sides));
1138 6 y := sideVertex(Util.tuple22(sides));
1139 edges := {};
1140
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
25 for j in arrayLength(ea):-1:1 loop
1141
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 6 times.
19 if j <> k then
1142 13 ej := ea[j];
1143 13 edges := EDGE(ej.dom, substituteSide(ej.a, x, y, tr, facts),
1144 substituteSide(ej.b, x, y, tr, facts), ej.source) :: edges;
1145 end if;
1146 end for;
1147 6 arrayUpdate(alive, x, false);
1148 // z -> x -> y
1149
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 6 times.
13 merges := list(composeMerge(m, x, y, tr, facts) for m in merges);
1150 6 merges := MERGE(x, y, tr) :: merges;
1151 changed := true;
1152 6 break;
1153 end if;
1154 end for;
1155
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 26 times.
32 if changed then
1156 break;
1157 end if;
1158 end for;
1159 end while;
1160 end contract;
1161
1162 function composeMerge
1163 input Merge m;
1164 input Integer x;
1165 input Integer y;
1166 input list<tuple<Integer, Sym>> tr;
1167 input list<Aff> facts;
1168 output Merge res;
1169 protected
1170 array<tuple<Integer, Sym>> tz;
1171 Integer d;
1172 Sym c;
1173 list<tuple<Integer, Sym>> l = {};
1174 algorithm
1175
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if m.y <> x then
1176 res := m;
1177 7 return;
1178 end if;
1179 // y[d'] = x[d2] + c = z[tz[d2].d] + tz[d2].c + c
1180 ✗ tz := listArray(m.tr);
1181 ✗ for t in tr loop
1182 ✗ (d, c) := t;
1183 ✗ l := (Util.tuple21(tz[d]), symAdd(c, Util.tuple22(tz[d]), facts)) :: l;
1184 end for;
1185 ✗ res := MERGE(m.x, y, listReverseInPlace(l));
1186 end composeMerge;
1187
1188 function mergedImage
1189 "the image of the box of x in y"
1190 input Box box;
1191 input list<tuple<Integer, Sym>> tr;
1192 input list<Aff> facts;
1193 output Box res;
1194 protected
1195 array<Iv> b = listArray(box);
1196 Integer d;
1197 Sym c;
1198 algorithm
1199
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 12 times.
30 res := list(IV(symAdd(ivLo(b[Util.tuple21(t)]), Util.tuple22(t), facts),
1200 symAdd(ivHi(b[Util.tuple21(t)]), Util.tuple22(t), facts)) for t in tr);
1201 end mergedImage;
1202
1203 function reduceSelfShifts
1204 "connect(p[.., i, ..], p[.., i + s, ..]) for i in lo:hi with s = +-1 joins
1205 p[.., min(lo, lo+s):max(hi, hi+s), ..] into one set, for every size and also
1206 for an empty domain. Such an edge is replaced by a virtual hub vertex
1207 connected to the whole range."
1208 input Vector<Vertex> vertices;
1209 input array<Boolean> alive;
1210 input output list<Edge> edges;
1211 input list<Aff> facts;
1212 output array<Boolean> outAlive;
1213 protected
1214 list<Edge> res = {};
1215 list<Integer> diff;
1216 array<Integer> ia, ib;
1217 array<Sym> oa, ob;
1218 Integer d, it, n, hub, newit;
1219 Sym shift, lo, hi;
1220 array<Iv> dom;
1221 Box hub_box, new_dom;
1222 list<Integer> hub_iters, p_iters;
1223 list<Sym> hub_offs, p_offs;
1224 list<Boolean> alive_l;
1225 algorithm
1226 12 alive_l := listReverse(arrayList(alive));
1227
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12 times.
32 for e in edges loop
1228
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if e.a.vertex <> e.b.vertex then
1229 res := e :: res;
1230 20 continue;
1231 end if;
1232 ✗ ia := listArray(e.a.iters); ib := listArray(e.b.iters);
1233 ✗ oa := listArray(e.a.offs); ob := listArray(e.b.offs);
1234 ✗ diff := list(j for j guard not (ia[j] == ib[j] and symEq(oa[j], ob[j])) in 1:arrayLength(ia));
1235 ✗ if listEmpty(diff) then
1236 ✗ continue; // p[i] -- p[i]
1237 end if;
1238 ✗ if listLength(diff) > 1 then
1239 ✗ unsupported("a connection inside one array shifted in more than one dimension", e.source);
1240 end if;
1241 ✗ d := listHead(diff);
1242 ✗ if ia[d] == 0 or ia[d] <> ib[d] then
1243 ✗ unsupported("a connection inside one array with a constant or permuted index", e.source);
1244 end if;
1245 ✗ shift := symSub(ob[d], oa[d], facts);
1246 ✗ if not (symEq(shift, symInt(1)) or symEq(shift, symInt(-1))) then
1247 ✗ unsupported("a connection inside one array shifted by " + symString(shift), e.source);
1248 end if;
1249 ✗ it := ia[d];
1250 ✗ dom := listArray(e.dom);
1251 n := arrayLength(dom);
1252 ✗ lo := symMin(symAdd(ivLo(dom[it]), oa[d], facts), symAdd(ivLo(dom[it]), ob[d], facts), facts);
1253 ✗ hi := symMax(symAdd(ivHi(dom[it]), oa[d], facts), symAdd(ivHi(dom[it]), ob[d], facts), facts);
1254 // the hub: [1:1] and the other iterators of the domain
1255 ✗ hub_box := IV(symInt(1), symInt(1)) :: list(dom[j] for j guard j <> it in 1:n);
1256 ✗ hub := Vector.size(vertices) + 1;
1257 ✗ Vector.push(vertices, VERTEX("$hub" + intString(hub), NONE(), hub_box));
1258 alive_l := true :: alive_l;
1259 // new domain: the shifted iterator replaced by [1:1], the whole range as a new last iterator
1260 ✗ newit := n + 1;
1261 ✗ new_dom := listAppend(list(if j == it then IV(symInt(1), symInt(1)) else dom[j] for j in 1:n), {IV(lo, hi)});
1262 ✗ hub_iters := 0 :: list(j for j guard j <> it in 1:n);
1263 ✗ hub_offs := symInt(1) :: list(symInt(0) for j guard j <> it in 1:n);
1264 ✗ p_iters := list(if j == d then newit else ia[j] for j in 1:arrayLength(ia));
1265 ✗ p_offs := list(if j == d then symInt(0) else oa[j] for j in 1:arrayLength(ia));
1266 ✗ res := EDGE(new_dom, SIDE(hub, hub_iters, hub_offs), SIDE(e.a.vertex, p_iters, p_offs), e.source) :: res;
1267 end for;
1268 12 edges := listReverseInPlace(res);
1269 12 outAlive := listArray(listReverseInPlace(alive_l));
1270 end reduceSelfShifts;
1271
1272 function unsupported
1273 input String msg;
1274 input DAE.ElementSource source;
1275 algorithm
1276 ✗ Error.addSourceMessage(Error.INTERNAL_ERROR,
1277 {"--resizableArrays: " + msg + " is not supported in connections yet."}, ElementSource.getInfo(source));
1278 ✗ fail();
1279 end unsupported;
1280
1281 // ---------------------------------------------------------------------------
1282 // pieces
1283 // ---------------------------------------------------------------------------
1284
1285 function splitPieces
1286 "splits every piece by box into the part inside and the parts outside"
1287 input list<Box> pieces;
1288 input Box box;
1289 input list<Aff> facts;
1290 output list<Box> res = {};
1291 output Boolean changed = false;
1292 protected
1293 Box inside;
1294 list<Box> uniq;
1295 UnorderedSet<String> keys = UnorderedSet.new(stringHashDjb2, stringEq);
1296 String key;
1297 algorithm
1298
2/2
✓ Branch 0 taken 507 times.
✓ Branch 1 taken 233 times.
740 for p in pieces loop
1299 507 inside := boxInter(p, box, facts);
1300
4/4
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 247 times.
✓ Branch 4 taken 238 times.
✓ Branch 5 taken 22 times.
507 if boxEmpty(inside, facts) == YES or boxContains(box, p, facts) then
1301 485 res := p :: res;
1302 else
1303 changed := true;
1304 res := inside :: res;
1305 22 res := List.append_reverse(boxMinus(p, box, facts), res);
1306 end if;
1307 end for;
1308 // unique pieces
1309 uniq := {};
1310
2/2
✓ Branch 0 taken 529 times.
✓ Branch 1 taken 233 times.
762 for p in res loop
1311 529 key := boxKey(p);
1312
1/2
✓ Branch 1 taken 529 times.
✗ Branch 2 not taken.
529 if not UnorderedSet.contains(key, keys) then
1313 529 UnorderedSet.add(key, keys);
1314 uniq := p :: uniq;
1315 end if;
1316 end for;
1317 res := uniq;
1318
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 233 times.
233 if listLength(res) > MAX_PIECES then
1319 ✗ Error.addInternalError(getInstanceName() + ": --resizableArrays: the connection sets do not converge (shifted connections around a cycle?).", sourceInfo());
1320 ✗ fail();
1321 end if;
1322 end splitPieces;
1323
1324 function computePieces
1325 input Vector<Vertex> vertices;
1326 input array<Boolean> alive;
1327 input list<Edge> edges;
1328 input list<tuple<Integer, Box>> splits;
1329 input list<Aff> facts;
1330 output array<list<Box>> pieces;
1331 protected
1332 Boolean changed, c;
1333 Integer v;
1334 Box b, pre;
1335 list<Box> pl;
1336 Side sa, sb;
1337 algorithm
1338 12 pieces := arrayCreate(Vector.size(vertices), {});
1339
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 5 times.
48 for v in 1:Vector.size(vertices) loop
1340
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
36 if alive[v] then
1341 60 pieces[v] := {vertexBox(vertices, v)};
1342 end if;
1343 end for;
1344
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 for s in splits loop
1345 6 (v, b) := s;
1346 6 (pl, _) := splitPieces(pieces[v], b, facts);
1347 6 pieces[v] := pl;
1348 end for;
1349 8 for round in 1:MAX_ROUNDS loop
1350 changed := false;
1351
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 20 times.
70 for e in edges loop
1352
2/2
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 50 times.
200 for sides in {(e.a, e.b), (e.b, e.a)} loop
1353 100 (sa, sb) := sides;
1354 100 (pl, c) := splitPieces(pieces[sa.vertex], sideImage(sa, e.dom, facts), facts);
1355 100 pieces[sa.vertex] := pl;
1356
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 5 times.
100 changed := changed or c;
1357
2/2
✓ Branch 1 taken 212 times.
✓ Branch 2 taken 100 times.
312 for q in pieces[sb.vertex] loop
1358
2/2
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 44 times.
212 if sideConstIn(sb, q, facts) then
1359 168 pre := sidePreimage(sb, e.dom, q, facts);
1360
2/2
✓ Branch 1 taken 127 times.
✓ Branch 2 taken 41 times.
168 if boxEmpty(pre, facts) <> YES then
1361 127 (pl, c) := splitPieces(pieces[sa.vertex], sideImage(sa, pre, facts), facts);
1362 127 pieces[sa.vertex] := pl;
1363
4/4
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 3 times.
127 changed := changed or c;
1364 end if;
1365 end if;
1366 end for;
1367 end for;
1368 end for;
1369
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8 times.
20 if not changed then
1370 12 return;
1371 end if;
1372 end for;
1373 ✗ Error.addInternalError(getInstanceName() + ": --resizableArrays: the connection sets do not converge.", sourceInfo());
1374 ✗ fail();
1375 end computePieces;
1376
1377 // ---------------------------------------------------------------------------
1378 // connection sets: union-find over the pieces and their dimensions
1379 // ---------------------------------------------------------------------------
1380
1381 uniontype Sets
1382 record SETS
1383 Vector<Integer> pieceVertex;
1384 Vector<Box> pieceBox;
1385 Vector<Integer> pieceDim0 "the first dimension node of the piece";
1386 Vector<Integer> parent "of the pieces";
1387 Vector<Integer> dimParent;
1388 Vector<Sym> dimOff "coordinate of the parent - coordinate of the node";
1389 Vector<Boolean> collapsed;
1390 array<list<Integer>> vertexPieces;
1391 end SETS;
1392 end Sets;
1393
1394 function addPiece
1395 input Sets sets;
1396 input Integer v;
1397 input Box box;
1398 output Integer p;
1399 protected
1400 Integer n0;
1401 algorithm
1402 58 Vector.push(sets.pieceVertex, v);
1403 58 Vector.push(sets.pieceBox, box);
1404 58 p := Vector.size(sets.pieceVertex);
1405 58 Vector.push(sets.parent, p);
1406 58 n0 := Vector.size(sets.dimParent) + 1;
1407 58 Vector.push(sets.pieceDim0, n0);
1408
2/2
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 13 times.
107 for d in 1:listLength(box) loop
1409 49 Vector.push(sets.dimParent, n0 + d - 1);
1410 49 Vector.push(sets.dimOff, symInt(0));
1411 49 Vector.push(sets.collapsed, false);
1412 end for;
1413 58 arrayUpdate(sets.vertexPieces, v, List.appendElt(p, sets.vertexPieces[v]));
1414 end addPiece;
1415
1416 function dimNode
1417 input Sets sets;
1418 input Integer p;
1419 input Integer d;
1420 output Integer n = Vector.get(sets.pieceDim0, p) + d - 1;
1421 end dimNode;
1422
1423 function pieceFind
1424 input Sets sets;
1425 input output Integer p;
1426 algorithm
1427
2/2
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 122 times.
163 while Vector.get(sets.parent, p) <> p loop
1428 41 p := Vector.get(sets.parent, p);
1429 end while;
1430 end pieceFind;
1431
1432 function pieceUnion
1433 input Sets sets;
1434 input Integer p;
1435 input Integer q;
1436 protected
1437 Integer rp = pieceFind(sets, p), rq = pieceFind(sets, q);
1438 algorithm
1439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if rp <> rq then
1440 32 Vector.update(sets.parent, rq, rp);
1441 end if;
1442 end pieceUnion;
1443
1444 function dimFind
1445 input Sets sets;
1446 input Integer x;
1447 input list<Aff> facts;
1448 output Integer root = x;
1449 output Sym off = symInt(0);
1450 algorithm
1451
2/2
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 138 times.
184 while Vector.get(sets.dimParent, root) <> root loop
1452 46 off := symAdd(off, Vector.get(sets.dimOff, root), facts);
1453 46 root := Vector.get(sets.dimParent, root);
1454 end while;
1455 end dimFind;
1456
1457 function dimUnion
1458 "coordinate y = coordinate x + d. Returns the shift if x and y are already in
1459 the same class (0 if consistent)."
1460 input Sets sets;
1461 input Integer x;
1462 input Integer y;
1463 input Sym d;
1464 input list<Aff> facts;
1465 output Option<Sym> shift = NONE();
1466 protected
1467 Integer rx, ry;
1468 Sym ox, oy;
1469 algorithm
1470 20 (rx, ox) := dimFind(sets, x, facts);
1471 20 (ry, oy) := dimFind(sets, y, facts);
1472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if rx == ry then
1473 ✗ shift := SOME(symSub(ox, symAdd(d, oy, facts), facts));
1474 else
1475 20 Vector.update(sets.dimParent, ry, rx);
1476 20 Vector.update(sets.dimOff, ry, symSub(symSub(ox, d, facts), oy, facts));
1477 end if;
1478 end dimUnion;
1479
1480 function pieceOf
1481 input Sets sets;
1482 input Integer v;
1483 input Box box;
1484 input Vector<Vertex> vertices;
1485 input DAE.ElementSource source;
1486 input list<Aff> facts;
1487 output Integer p = 0;
1488 algorithm
1489
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 for q in sets.vertexPieces[v] loop
1490
2/2
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 20 times.
46 if boxContains(Vector.get(sets.pieceBox, q), box, facts) then
1491 p := q;
1492 26 return;
1493 end if;
1494 end for;
1495 ✗ unsupported("the connection of " + vertexName(vertices, v) + boxString(box) + " (no matching piece)", source);
1496 end pieceOf;
1497
1498 function addEdgeToSets
1499 input Sets sets;
1500 input Edge e;
1501 input Vector<Vertex> vertices;
1502 input list<Aff> facts;
1503 protected
1504 Box pre, qbox;
1505 Integer p, ndom, dp, dq;
1506 list<Integer> dps, dqs;
1507 array<Integer> ia, ib;
1508 array<Sym> oa, ob;
1509 Option<Sym> shift;
1510 Sym s;
1511 Boolean found = false;
1512 algorithm
1513 20 ia := listArray(e.a.iters); ib := listArray(e.b.iters);
1514 20 oa := listArray(e.a.offs); ob := listArray(e.b.offs);
1515 20 ndom := listLength(e.dom);
1516
2/2
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 20 times.
66 for q in sets.vertexPieces[e.b.vertex] loop
1517 46 qbox := Vector.get(sets.pieceBox, q);
1518
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 34 times.
46 if not sideConstIn(e.b, qbox, facts) then
1519 12 continue;
1520 end if;
1521 34 pre := sidePreimage(e.b, e.dom, qbox, facts);
1522
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 26 times.
34 if boxEmpty(pre, facts) == YES then
1523 8 continue;
1524 end if;
1525 found := true;
1526 26 p := pieceOf(sets, e.a.vertex, sideImage(e.a, pre, facts), vertices, e.source, facts);
1527 26 pieceUnion(sets, p, q);
1528
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
45 for j in 1:ndom loop
1529
7/8
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 19 times.
40 dps := list(d for d guard ia[d] == j in 1:arrayLength(ia));
1530
6/6
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 19 times.
✓ Branch 6 taken 19 times.
40 dqs := list(d for d guard ib[d] == j in 1:arrayLength(ib));
1531
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 if listLength(dps) > 1 or listLength(dqs) > 1 then
1532 ✗ unsupported("an iterator in two dimensions of a connector", e.source);
1533 end if;
1534
3/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
19 if not listEmpty(dps) and not listEmpty(dqs) then
1535 17 dp := listHead(dps);
1536 17 dq := listHead(dqs);
1537 17 shift := dimUnion(sets, dimNode(sets, p, dp), dimNode(sets, q, dq), symSub(ob[dq], oa[dp], facts), facts);
1538
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 if isSome(shift) then
1539 ✗ SOME(s) := shift;
1540 ✗ if not symEq(s, symInt(0)) then
1541 ✗ if symEq(s, symInt(1)) or symEq(s, symInt(-1)) then
1542 ✗ Vector.update(sets.collapsed, dimNode(sets, p, dp), true);
1543 else
1544 ✗ unsupported("connections shifted by " + symString(s) + " around a cycle", e.source);
1545 end if;
1546 end if;
1547 end if;
1548 elseif not listEmpty(dps) then
1549 ✗ Vector.update(sets.collapsed, dimNode(sets, p, listHead(dps)), true);
1550 elseif not listEmpty(dqs) then
1551 2 Vector.update(sets.collapsed, dimNode(sets, q, listHead(dqs)), true);
1552 end if;
1553 end for;
1554 end for;
1555 // a constant index that lies in no piece for certain would lose the connection
1556
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
20 if not found and boxEmpty(e.dom, facts) <> YES then
1557 ✗ unsupported("the connection of " + vertexName(vertices, e.b.vertex) + boxString(sideImage(e.b, e.dom, facts)) + " (no matching piece)", e.source);
1558 end if;
1559 end addEdgeToSets;
1560
1561 function readdMerged
1562 "the merged arrays back as members: a piece of x for every piece of y in its image"
1563 input Sets sets;
1564 input list<Merge> merges;
1565 input Vector<Vertex> vertices;
1566 input list<Aff> facts;
1567 protected
1568 Box img, qbox;
1569 array<Iv> ivs;
1570 Integer px, d, dq;
1571 Sym c;
1572 algorithm
1573
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 for m in merges loop
1574 6 img := mergedImage(vertexBox(vertices, m.x), m.tr, facts);
1575
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
12 for q in sets.vertexPieces[m.y] loop
1576 6 qbox := Vector.get(sets.pieceBox, q);
1577
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if boxContains(img, qbox, facts) then
1578 6 ivs := arrayCreate(listLength(m.tr), IV(symInt(1), symInt(1)));
1579 dq := 0;
1580
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 for t in m.tr loop
1581 3 (d, c) := t;
1582 3 dq := dq + 1;
1583 9 ivs[d] := IV(symSub(ivLo(listGet(qbox, dq)), c, facts), symSub(ivHi(listGet(qbox, dq)), c, facts));
1584 end for;
1585 6 px := addPiece(sets, m.x, arrayList(ivs));
1586 6 pieceUnion(sets, q, px);
1587 dq := 0;
1588
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 for t in m.tr loop
1589 3 (d, c) := t;
1590 3 dq := dq + 1;
1591 // coordinate px = coordinate q - c
1592 3 _ := dimUnion(sets, dimNode(sets, q, dq), dimNode(sets, px, d), symNeg(c, facts), facts);
1593 end for;
1594 elseif boxEmpty(boxInter(img, qbox, facts), facts) <> YES then
1595 ✗ Error.addInternalError(getInstanceName() + ": --resizableArrays: piece " + boxString(qbox) + " of " +
1596 vertexName(vertices, m.y) + " is partly in the image of " + vertexName(vertices, m.x), sourceInfo());
1597 ✗ fail();
1598 end if;
1599 end for;
1600 end for;
1601 end readdMerged;
1602
1603 function connectionSets
1604 input Vector<Vertex> vertices;
1605 input list<Edge> edges;
1606 input list<tuple<Integer, Box>> extraSplits = {} "boxes of vertices the pieces have to respect";
1607 output Sets sets;
1608 output list<Aff> facts;
1609 protected
1610 array<Boolean> alive;
1611 list<Merge> merges;
1612 list<Edge> el = edges;
1613 array<list<Box>> pieces;
1614 UnorderedMap<Integer, Merge> merged_by_x;
1615 list<tuple<Integer, Box>> splits;
1616 Integer v;
1617 Box b;
1618 Merge m;
1619 algorithm
1620 12 facts := indexFacts(vertices, el);
1621 12 alive := arrayCreate(Vector.size(vertices), true);
1622 12 (el, merges) := contract(vertices, alive, el, facts);
1623 12 (el, alive) := reduceSelfShifts(vertices, alive, el, facts);
1624 // the extra boxes of merged vertices are boxes of the vertices they are merged into
1625 12 merged_by_x := UnorderedMap.new<Merge>(Util.id, intEq);
1626
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 for mg in merges loop
1627 6 UnorderedMap.add(mg.x, mg, merged_by_x);
1628 end for;
1629
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 12 times.
18 splits := list((mg.y, mergedImage(vertexBox(vertices, mg.x), mg.tr, facts)) for mg in merges);
1630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 for sp in extraSplits loop
1631 ✗ (v, b) := sp;
1632 ✗ if UnorderedMap.contains(v, merged_by_x) then
1633 ✗ m := UnorderedMap.getOrFail(v, merged_by_x);
1634 ✗ splits := (m.y, mergedImage(b, m.tr, facts)) :: splits;
1635 else
1636 ✗ splits := (v, b) :: splits;
1637 end if;
1638 end for;
1639 12 pieces := computePieces(vertices, alive, el, splits, facts);
1640 12 sets := SETS(Vector.new<Integer>(), Vector.new<Box>(), Vector.new<Integer>(), Vector.new<Integer>(),
1641 Vector.new<Integer>(), Vector.new<Sym>(), Vector.new<Boolean>(),
1642 arrayCreate(Vector.size(vertices), {}));
1643
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
48 for v in 1:arrayLength(pieces) loop
1644
2/2
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 36 times.
88 for b in pieces[v] loop
1645 52 addPiece(sets, v, b);
1646 end for;
1647 end for;
1648
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12 times.
32 for e in el loop
1649 20 addEdgeToSets(sets, e, vertices, facts);
1650 end for;
1651 12 readdMerged(sets, merges, vertices, facts);
1652 end connectionSets;
1653
1654 // ---------------------------------------------------------------------------
1655 // from the flat model to the graph
1656 // ---------------------------------------------------------------------------
1657
1658 uniontype SubEntry
1659 "a subscript of one side of a connect: an index (iterator + offset or a
1660 constant), or a slice that is paired with a slice of the other side"
1661 record FIXED
1662 Integer it;
1663 Sym off;
1664 end FIXED;
1665 record SLICED
1666 Sym lo;
1667 Sym hi;
1668 end SLICED;
1669 end SubEntry;
1670
1671 uniontype Context
1672 record CONTEXT
1673 Vector<Vertex> vertices;
1674 UnorderedMap<String, Integer> vertexIndex;
1675 UnorderedMap<String, Expression> params "size parameters by name";
1676 UnorderedMap<String, Variable> variables;
1677 end CONTEXT;
1678 end Context;
1679
1680 function isConnection
1681 input Equation eq;
1682 output Boolean isConn;
1683 algorithm
1684 isConn := match eq
1685 local
1686 Equation e;
1687 case Equation.CONNECT() then true;
1688 41 case Equation.FOR(body = e :: _) then isConnection(e);
1689 ✗ case Equation.IF() then List.any(eq.branches, isConnectionBranch);
1690 else false;
1691 end match;
1692 end isConnection;
1693
1694 function isConnectionBranch
1695 input Equation.Branch branch;
1696 output Boolean isConn;
1697 algorithm
1698 isConn := match branch
1699 local
1700 Equation e;
1701 ✗ case Equation.Branch.BRANCH(body = e :: _) then isConnection(e);
1702 else false;
1703 end match;
1704 end isConnectionBranch;
1705
1706 function crefDims
1707 input ComponentRef cr;
1708 output list<Dimension> dims = {};
1709 protected
1710 ComponentRef c = cr;
1711 algorithm
1712
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 36 times.
108 while not ComponentRef.isEmpty(c) loop
1713 72 dims := listAppend(Type.arrayDims(ComponentRef.nodeType(c)), dims);
1714 72 c := ComponentRef.rest(c);
1715 end while;
1716 end crefDims;
1717
1718 function isInComponentArray
1719 "true if a cref is a component of an array of components, e.g. s.N for s[M]"
1720 input ComponentRef cref;
1721 output Boolean b;
1722 protected
1723 ComponentRef rest;
1724 algorithm
1725 46 rest := ComponentRef.rest(cref);
1726
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
46 b := not ComponentRef.isEmpty(rest) and Type.isArray(ComponentRef.nodeType(ComponentRef.stripSubscripts(rest)));
1727 end isInComponentArray;
1728
1729 function uniformArrayElement
1730 "The value of all elements of an array expression if they are all the same."
1731 input Expression exp;
1732 output Option<Expression> elem;
1733 algorithm
1734 elem := match exp
1735 local
1736 Expression body;
1737 list<tuple<InstNode, Expression>> iters;
1738 case Expression.CALL(call = Call.TYPED_ARRAY_CONSTRUCTOR(exp = body, iters = iters))
1739 guard not List.any(iters, function iteratorOccursIn(exp = body))
1740 ✗ then uniformArrayElement(body);
1741 case Expression.CALL() guard Call.isNamed(exp.call, "fill")
1742 ✗ then uniformArrayElement(listHead(Call.arguments(exp.call)));
1743 case _ guard not Type.isArray(Expression.typeOf(exp)) then SOME(exp);
1744 else NONE();
1745 end match;
1746 end uniformArrayElement;
1747
1748 function iteratorOccursIn
1749 input tuple<InstNode, Expression> iter;
1750 input Expression exp;
1751 output Boolean b = Expression.containsIterator(exp, Util.tuple21(iter));
1752 end iteratorOccursIn;
1753
1754 function resolveAlias
1755 "a parameter of an array of components with the same value for all elements
1756 (e.g. s[i].N for s[M](each N = K)) is that value"
1757 input ComponentRef cref;
1758 input Context ctx;
1759 output Option<Expression> alias = NONE();
1760 protected
1761 Option<Variable> ovar;
1762 Variable var;
1763 Option<Expression> obexp;
1764 Expression bexp;
1765 algorithm
1766 // an integer parameter bound to an affine expression of other parameters
1767 // (e.g. s.N = N for s(N = N)) is that expression
1768 46 ovar := UnorderedMap.get(ComponentRef.toString(cref), ctx.variables);
1769
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 if isSome(ovar) then
1770 46 SOME(var) := ovar;
1771 46 obexp := Binding.getExpOpt(var.binding);
1772
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 46 times.
✗ Branch 6 not taken.
46 if isSome(obexp) and Type.isInteger(var.ty) then
1773
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 46 times.
46 SOME(bexp) := obexp;
1774 // only an expression of resizable parameters, like NFFlatten.resizableDimensionAlias
1775
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
46 if not Expression.isInteger(bexp) and isAffineExp(bexp) and onlyResizableCrefs(bexp) then
1776 alias := SOME(bexp);
1777 ✗ return;
1778 end if;
1779 end if;
1780 end if;
1781
1782
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 if isInComponentArray(cref) then
1783 ✗ ovar := UnorderedMap.get(ComponentRef.toString(ComponentRef.stripSubscriptsAll(cref)), ctx.variables);
1784 ✗ if isSome(ovar) then
1785 ✗ SOME(var) := ovar;
1786 ✗ obexp := Binding.getExpOpt(var.binding);
1787 ✗ if isSome(obexp) then
1788 ✗ SOME(bexp) := obexp;
1789 ✗ alias := uniformArrayElement(bexp);
1790 end if;
1791 end if;
1792 end if;
1793 end resolveAlias;
1794
1795 function onlyResizableCrefs
1796 input Expression exp;
1797 output Boolean b = not Expression.contains(exp, isNonResizableCref);
1798 end onlyResizableCrefs;
1799
1800 function isNonResizableCref
1801 input Expression exp;
1802 output Boolean b;
1803 algorithm
1804 b := match exp
1805 ✗ case Expression.CREF() then not ComponentRef.isResizable(exp.cref);
1806 else false;
1807 end match;
1808 end isNonResizableCref;
1809
1810 function isAffineExp
1811 "an integer expression of crefs, +, - and multiplication with integers"
1812 input Expression exp;
1813 output Boolean b;
1814 algorithm
1815 b := match exp
1816 case Expression.INTEGER() then true;
1817 ✗ case Expression.CREF() then not ComponentRef.isIterator(exp.cref) and Type.isInteger(exp.ty);
1818 case Expression.BINARY() guard exp.operator.op == Op.ADD or exp.operator.op == Op.SUB
1819 ✗ then isAffineExp(exp.exp1) and isAffineExp(exp.exp2);
1820 case Expression.BINARY() guard exp.operator.op == Op.MUL
1821 ✗ then isAffineExp(exp.exp1) and isAffineExp(exp.exp2) and
1822 (Expression.isInteger(exp.exp1) or Expression.isInteger(exp.exp2));
1823 ✗ case Expression.UNARY() guard exp.operator.op == Op.UMINUS then isAffineExp(exp.exp);
1824 else false;
1825 end match;
1826 end isAffineExp;
1827
1828 function expToAff
1829 "an integer expression as affine expression; iterators become #<position in
1830 the domain>, other crefs size parameters"
1831 input Expression exp;
1832 input list<String> iterNames;
1833 input Context ctx;
1834 input DAE.ElementSource source;
1835 output Aff a;
1836 algorithm
1837 a := match exp
1838 local
1839 Option<Expression> alias;
1840 Expression e;
1841 Aff a1, a2;
1842 String name;
1843 Integer pos;
1844
1845 40 case Expression.INTEGER() then affInt(exp.value);
1846
1847 case Expression.CREF() guard ComponentRef.isIterator(exp.cref)
1848 algorithm
1849 30 name := ComponentRef.firstName(exp.cref);
1850 pos := 0;
1851
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
64 for i in 1:listLength(iterNames) loop
1852
3/4
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 4 times.
34 if listGet(iterNames, i) == name then
1853 pos := i;
1854 end if;
1855 end for;
1856
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if pos == 0 then
1857 ✗ unsupported("the iterator " + name + " in " + Expression.toString(exp), source);
1858 end if;
1859 30 then affParam("#" + intString(pos));
1860
1861 case Expression.CREF()
1862 algorithm
1863 46 alias := resolveAlias(exp.cref, ctx);
1864
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 if isSome(alias) then
1865 ✗ SOME(e) := alias;
1866 a1 := expToAff(e, iterNames, ctx, source);
1867 else
1868 46 name := ComponentRef.toString(exp.cref);
1869 46 UnorderedMap.tryAdd(name, exp, ctx.params);
1870 46 a1 := affParam(name);
1871 end if;
1872 then a1;
1873
1874 case Expression.BINARY()
1875 algorithm
1876 28 a1 := expToAff(exp.exp1, iterNames, ctx, source);
1877 28 a2 := expToAff(exp.exp2, iterNames, ctx, source);
1878 a1 := match exp.operator.op
1879 5 case Op.ADD then affAdd(a1, a2);
1880 23 case Op.SUB then affSub(a1, a2);
1881 ✗ case Op.MUL guard affIsConst(a1) then affScale(a2, a1.c);
1882 ✗ case Op.MUL guard affIsConst(a2) then affScale(a1, a2.c);
1883 else algorithm
1884 ✗ unsupported("the expression " + Expression.toString(exp) + " (not affine)", source);
1885 then fail();
1886 end match;
1887 then a1;
1888
1889 case Expression.UNARY() guard exp.operator.op == Op.UMINUS
1890 ✗ then affNeg(expToAff(exp.exp, iterNames, ctx, source));
1891
1892 else algorithm
1893 ✗ unsupported("the expression " + Expression.toString(exp) + " in a connection index or range", source);
1894 then fail();
1895 end match;
1896 end expToAff;
1897
1898 function expToSym
1899 "an expression without iterators"
1900 input Expression exp;
1901 input Context ctx;
1902 input DAE.ElementSource source;
1903 output Sym s = symAff(expToAff(exp, {}, ctx, source));
1904 end expToSym;
1905
1906 function dimToSym
1907 input Dimension dim;
1908 input Context ctx;
1909 input DAE.ElementSource source;
1910 output Sym s;
1911 algorithm
1912 s := match dim
1913 25 case Dimension.RESIZABLE() then expToSym(dim.exp, ctx, source);
1914 ✗ case Dimension.INTEGER() then symInt(dim.size);
1915 ✗ case Dimension.EXP() then expToSym(dim.exp, ctx, source);
1916 ✗ case _ guard Dimension.isKnown(dim) then symInt(Dimension.size(dim));
1917 else algorithm
1918 ✗ unsupported("the dimension " + Dimension.toString(dim) + " of a connector", source);
1919 then fail();
1920 end match;
1921 end dimToSym;
1922
1923 function vertexOf
1924 "the vertex of a connector (unsubscripted), created if it does not exist"
1925 input ComponentRef connCref;
1926 input DAE.ElementSource source;
1927 input Context ctx;
1928 output Integer v;
1929 protected
1930 Connector conn = Connector.fromCref(connCref, ComponentRef.nodeType(connCref), source);
1931 String key = vertexKey(ComponentRef.toString(connCref), Connector.isOutside(conn));
1932 Option<Integer> ov;
1933 algorithm
1934 84 ov := UnorderedMap.get(key, ctx.vertexIndex);
1935
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 48 times.
84 if isSome(ov) then
1936 48 SOME(v) := ov;
1937 else
1938
4/4
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 36 times.
61 Vector.push(ctx.vertices, VERTEX(key, SOME(conn),
1939 list(IV(symInt(1), dimToSym(d, ctx, source)) for d in crefDims(connCref))));
1940 36 v := Vector.size(ctx.vertices);
1941 36 UnorderedMap.add(key, v, ctx.vertexIndex);
1942 end if;
1943 end vertexOf;
1944
1945 function vertexKey
1946 "a vertex for each face of a connector: inside and outside elements of a
1947 connector are in different connection sets"
1948 input String name;
1949 input Boolean outside;
1950 output String key = name + (if outside then "$outside" else "$inside");
1951 end vertexKey;
1952
1953 function connectSide
1954 input Expression exp;
1955 input list<String> iterNames;
1956 input DAE.ElementSource source;
1957 input Context ctx;
1958 output Integer v;
1959 output list<SubEntry> entries = {};
1960 protected
1961 ComponentRef cr;
1962 list<Subscript> subs;
1963 Box box;
1964 Iv viv;
1965 Aff a, rest;
1966 Integer it, c;
1967 String n;
1968 Expression range;
1969 algorithm
1970 52 cr := ComponentRef.fillSubscripts(Expression.toCref(exp));
1971 52 subs := ComponentRef.subscriptsAllFlat(cr);
1972 52 v := vertexOf(ComponentRef.stripSubscriptsAll(cr), source, ctx);
1973 52 box := vertexBox(ctx.vertices, v);
1974
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 if listLength(subs) <> listLength(box) then
1975 ✗ unsupported("the subscripts of " + Expression.toString(exp), source);
1976 end if;
1977
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 52 times.
95 for s in subs loop
1978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 viv :: box := box;
1979 entries := match s
1980 case Subscript.INDEX()
1981 algorithm
1982 43 a := expToAff(s.index, iterNames, ctx, source);
1983 it := 0;
1984 rest := a;
1985
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 43 times.
84 for t in a.k loop
1986 41 (n, c) := t;
1987
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 11 times.
41 if stringGet(n, 1) == 35 /* # */ then
1988
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if it <> 0 or c <> 1 then
1989 ✗ unsupported("the index " + Expression.toString(s.index), source);
1990 end if;
1991 30 it := stringInt(substring(n, 2, stringLength(n)));
1992 30 rest := affSub(a, affParam(n));
1993 end if;
1994 end for;
1995 43 then FIXED(it, symAff(rest)) :: entries;
1996 case Subscript.SLICE(slice = range as Expression.RANGE())
1997 algorithm
1998 ✗ checkStep(range.step, source);
1999 ✗ then SLICED(expToSym(range.start, ctx, source), expToSym(range.stop, ctx, source)) :: entries;
2000 ✗ case Subscript.WHOLE() then SLICED(viv.lo, viv.hi) :: entries;
2001 else algorithm
2002 ✗ unsupported("the subscript " + Subscript.toString(s) + " in " + Expression.toString(exp), source);
2003 then fail();
2004 end match;
2005 end for;
2006 52 entries := listReverseInPlace(entries);
2007 end connectSide;
2008
2009 function checkStep
2010 input Option<Expression> step;
2011 input DAE.ElementSource source;
2012 algorithm
2013 _ := match step
2014 case NONE() then ();
2015 case SOME(Expression.INTEGER(1)) then ();
2016 else algorithm
2017 ✗ unsupported("a range with a step", source);
2018 then fail();
2019 end match;
2020 end checkStep;
2021
2022 function collectEdges
2023 input Equation eq;
2024 input list<String> iterNames;
2025 input Box dom;
2026 input Context ctx;
2027 input output list<Edge> edges;
2028 protected
2029 Sym lo, hi;
2030 list<Box> doms;
2031 list<Equation> body;
2032 algorithm
2033 edges := match eq
2034 local
2035 Expression range;
2036 case Equation.CONNECT()
2037 26 then makeEdge(eq.lhs, eq.rhs, iterNames, dom, eq.source, ctx) :: edges;
2038
2039 case Equation.FOR(range = SOME(range as Expression.RANGE()))
2040 algorithm
2041 10 checkStep(range.step, eq.source);
2042 10 lo := expToSym(range.start, ctx, eq.source);
2043 10 hi := expToSym(range.stop, ctx, eq.source);
2044
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 10 times.
26 for e in eq.body loop
2045 48 edges := collectEdges(e, listAppend(iterNames, {InstNode.name(eq.iterator)}),
2046 listAppend(dom, {IV(lo, hi)}), ctx, edges);
2047 end for;
2048 then edges;
2049
2050 case Equation.IF()
2051 algorithm
2052 ✗ for b in ifDomains(eq.branches, eq.source, iterNames, dom, ctx) loop
2053 ✗ (doms, body) := b;
2054 ✗ for d in doms loop
2055 ✗ for e in body loop
2056 ✗ edges := collectEdges(e, iterNames, d, ctx, edges);
2057 end for;
2058 end for;
2059 end for;
2060 then edges;
2061
2062 else algorithm
2063 ✗ unsupported("the connection equation " + Equation.toString(eq), Equation.source(eq));
2064 then fail();
2065 end match;
2066 end collectEdges;
2067
2068 function ifDomains
2069 "the domains of the branches of an if equation in the surrounding loops: a
2070 branch holds where its condition holds and the ones before do not"
2071 input list<Equation.Branch> branches;
2072 input DAE.ElementSource source;
2073 input list<String> iterNames;
2074 input Box dom;
2075 input Context ctx;
2076 output list<tuple<list<Box>, list<Equation>>> res = {};
2077 protected
2078 list<Box> rest = {dom};
2079 Expression cond;
2080 list<Equation> body;
2081 algorithm
2082 ✗ for branch in branches loop
2083 ✗ if listEmpty(rest) then
2084 break;
2085 end if;
2086 (cond, body) := match branch
2087 ✗ case Equation.Branch.BRANCH() then (branch.condition, branch.body);
2088 else algorithm
2089 ✗ unsupported("an invalid branch of an if equation", source);
2090 then fail();
2091 end match;
2092 ✗ res := (List.flatten(list(restrictBox(d, cond, true, iterNames, ctx, source) for d in rest)), body) :: res;
2093 ✗ rest := List.flatten(list(restrictBox(d, cond, false, iterNames, ctx, source) for d in rest));
2094 end for;
2095 ✗ res := listReverseInPlace(res);
2096 end ifDomains;
2097
2098 function restrictBox
2099 "the parts of the box where the condition holds (or not): true, false or a
2100 comparison of one iterator with an affine expression in the size parameters"
2101 input Box box;
2102 input Expression cond;
2103 input Boolean holds;
2104 input list<String> iterNames;
2105 input Context ctx;
2106 input DAE.ElementSource source;
2107 output list<Box> res;
2108 protected
2109 Aff a, rest;
2110 Integer pos = 0, sign = 0, c, decided;
2111 String n;
2112 Op op;
2113 Operator operator;
2114 Sym bound;
2115 Iv iv;
2116 list<Iv> ivs;
2117 algorithm
2118 ✗ if Expression.isBoolean(cond) then
2119 ✗ res := if Expression.isTrue(cond) == holds then {box} else {};
2120 ✗ return;
2121 end if;
2122
2123 (a, operator) := match cond
2124 case Expression.RELATION() guard Operator.isRelational(cond.operator)
2125 ✗ then (affSub(expToAff(cond.exp1, iterNames, ctx, source), expToAff(cond.exp2, iterNames, ctx, source)), cond.operator);
2126 else algorithm
2127 ✗ unsupported("the condition " + Expression.toString(cond) + " of an if equation", source);
2128 then fail();
2129 end match;
2130
2131 ✗ if not holds then
2132 ✗ operator := Operator.negate(operator);
2133 end if;
2134 ✗ op := operator.op;
2135
2136 // a op 0 with a = sign * iterator + rest
2137 rest := a;
2138 ✗ for t in a.k loop
2139 ✗ (n, c) := t;
2140 ✗ if stringGet(n, 1) == 35 /* # */ then
2141 ✗ if pos <> 0 or abs(c) <> 1 then
2142 ✗ unsupported("the condition " + Expression.toString(cond) + " of an if equation", source);
2143 end if;
2144 ✗ pos := stringInt(substring(n, 2, stringLength(n)));
2145 sign := c;
2146 ✗ rest := affSub(a, affScale(affParam(n), c));
2147 end if;
2148 end for;
2149
2150 ✗ if pos == 0 then
2151 // no iterator: decided for all values of the size parameters or unsupported
2152 decided := match op
2153 ✗ case Op.LESS then affLe(a, affInt(-1), {});
2154 ✗ case Op.LESSEQ then affLe(a, affInt(0), {});
2155 ✗ case Op.GREATER then affLe(affInt(1), a, {});
2156 ✗ case Op.GREATEREQ then affLe(affInt(0), a, {});
2157 ✗ case Op.EQUAL then if affIsConst(a) then (if a.c == 0 then YES else NO) else MAYBE;
2158 ✗ case Op.NEQUAL then if affIsConst(a) then (if a.c == 0 then NO else YES) else MAYBE;
2159 else MAYBE;
2160 end match;
2161 ✗ if decided == MAYBE then
2162 ✗ unsupported("the condition " + Expression.toString(cond) + " of an if equation", source);
2163 end if;
2164 ✗ res := if decided == YES then {box} else {};
2165 ✗ return;
2166 end if;
2167
2168 // iterator op bound
2169 ✗ if sign < 0 then
2170 op := match op
2171 case Op.LESS then Op.GREATER;
2172 case Op.LESSEQ then Op.GREATEREQ;
2173 case Op.GREATER then Op.LESS;
2174 case Op.GREATEREQ then Op.LESSEQ;
2175 else op;
2176 end match;
2177 ✗ bound := symAff(rest);
2178 else
2179 ✗ bound := symAff(affNeg(rest));
2180 end if;
2181
2182 ✗ iv := listGet(box, pos);
2183 ivs := match op
2184 ✗ case Op.LESS then {IV(iv.lo, symMin(iv.hi, symAddInt(bound, -1, {}), {}))};
2185 ✗ case Op.LESSEQ then {IV(iv.lo, symMin(iv.hi, bound, {}))};
2186 ✗ case Op.GREATER then {IV(symMax(iv.lo, symAddInt(bound, 1, {}), {}), iv.hi)};
2187 ✗ case Op.GREATEREQ then {IV(symMax(iv.lo, bound, {}), iv.hi)};
2188 ✗ case Op.EQUAL then {IV(symMax(iv.lo, bound, {}), symMin(iv.hi, bound, {}))};
2189 ✗ case Op.NEQUAL then {IV(iv.lo, symMin(iv.hi, symAddInt(bound, -1, {}), {})),
2190 IV(symMax(iv.lo, symAddInt(bound, 1, {}), {}), iv.hi)};
2191 end match;
2192 ✗ res := list(List.set(box, pos, i) for i guard ivEmpty(i, {}) <> YES in ivs);
2193 end restrictBox;
2194
2195 function makeEdge
2196 "the edge between two connectors (or connector arrays, paired slice by slice)
2197 in the domain of the surrounding loops"
2198 input Expression lhs;
2199 input Expression rhs;
2200 input list<String> iterNames;
2201 input Box dom;
2202 input DAE.ElementSource source;
2203 input Context ctx;
2204 output Edge edge;
2205 protected
2206 Integer va, vb, it;
2207 list<SubEntry> ea, eb, sa, sb;
2208 list<Integer> ia, ib;
2209 list<Sym> oa, ob;
2210 Box d, slice_ivs;
2211 Sym len_a, len_b;
2212 list<Aff> facts = {};
2213 algorithm
2214 26 (va, ea) := connectSide(lhs, iterNames, source, ctx);
2215 26 (vb, eb) := connectSide(rhs, iterNames, source, ctx);
2216
4/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
47 sa := list(e for e guard isSliced(e) in ea);
2217
4/6
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
48 sb := list(e for e guard isSliced(e) in eb);
2218
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
26 if listLength(sa) <> listLength(sb) then
2219 ✗ unsupported("connecting arrays with different numbers of dimensions", source);
2220 end if;
2221 // every pair of slices gets a new iterator 1:n
2222 slice_ivs := {};
2223 26 it := listLength(dom);
2224
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 for p in List.zip(sa, sb) loop
2225 ✗ len_a := symSub(sliceHi(Util.tuple21(p)), sliceLo(Util.tuple21(p)), facts);
2226 ✗ len_b := symSub(sliceHi(Util.tuple22(p)), sliceLo(Util.tuple22(p)), facts);
2227 ✗ if not symEq(len_a, len_b) then
2228 ✗ unsupported("connecting slices whose sizes " + symString(len_a) + " + 1 and " + symString(len_b) +
2229 " + 1 are not equal symbolically", source);
2230 end if;
2231 ✗ slice_ivs := IV(symInt(1), symAddInt(len_a, 1, facts)) :: slice_ivs;
2232 end for;
2233 26 d := listAppend(dom, listReverse(slice_ivs));
2234 26 (ia, oa) := sideDims(ea, it, facts);
2235 26 (ib, ob) := sideDims(eb, it, facts);
2236 26 edge := EDGE(d, SIDE(va, ia, oa), SIDE(vb, ib, ob), source);
2237 end makeEdge;
2238
2239 function isSliced
2240 input SubEntry e;
2241 output Boolean b;
2242 algorithm
2243 b := match e case SLICED() then true; else false; end match;
2244 end isSliced;
2245
2246 function sliceLo
2247 input SubEntry e;
2248 output Sym s;
2249 algorithm
2250 ✗ SLICED(lo = s) := e;
2251 end sliceLo;
2252
2253 function sliceHi
2254 input SubEntry e;
2255 output Sym s;
2256 algorithm
2257 ✗ SLICED(hi = s) := e;
2258 end sliceHi;
2259
2260 function sideDims
2261 "iterators and offsets of a side, the k-th slice gets iterator n + k with
2262 the offset lo - 1"
2263 input list<SubEntry> entries;
2264 input Integer n;
2265 input list<Aff> facts;
2266 output list<Integer> iters = {};
2267 output list<Sym> offs = {};
2268 protected
2269 Integer k = n;
2270 algorithm
2271
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 52 times.
95 for e in entries loop
2272 _ := match e
2273 case FIXED()
2274 algorithm
2275 43 iters := e.it :: iters;
2276 43 offs := e.off :: offs;
2277 then ();
2278 case SLICED()
2279 algorithm
2280 ✗ k := k + 1;
2281 iters := k :: iters;
2282 ✗ offs := symAddInt(e.lo, -1, facts) :: offs;
2283 then ();
2284 end match;
2285 end for;
2286 52 iters := listReverseInPlace(iters);
2287 52 offs := listReverseInPlace(offs);
2288 end sideDims;
2289
2290 // ---------------------------------------------------------------------------
2291 // equations
2292 // ---------------------------------------------------------------------------
2293
2294 uniontype ConnVar
2295 "a variable of a connector: its cref, the number of dimensions of the
2296 connector and the path inside the connector"
2297 record CONN_VAR
2298 ComponentRef cref;
2299 Integer connDims;
2300 String member;
2301 end CONN_VAR;
2302 end ConnVar;
2303
2304 uniontype Idx
2305 record IDX_EXP
2306 Expression exp;
2307 end IDX_EXP;
2308 record IDX_LOOP
2309 Integer dim;
2310 end IDX_LOOP;
2311 end Idx;
2312
2313 function memberName
2314 "the path of a connector variable inside its connector, e.g. v for line.a.v"
2315 input ComponentRef var;
2316 input ComponentRef conn;
2317 output String name;
2318 protected
2319 list<String> var_names = crefNames(var);
2320 algorithm
2321
1/2
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
216 for i in 1:listLength(crefNames(conn)) loop
2322 144 var_names := List.restOrEmpty(var_names);
2323 end for;
2324 72 name := stringDelimitList(var_names, ".");
2325 end memberName;
2326
2327 function crefNames
2328 input ComponentRef cref;
2329 output list<String> names = {};
2330 protected
2331 ComponentRef c = cref;
2332 algorithm
2333
2/2
✓ Branch 1 taken 360 times.
✓ Branch 2 taken 144 times.
504 while not ComponentRef.isEmpty(c) loop
2334 360 names := ComponentRef.firstName(c) :: names;
2335 360 c := ComponentRef.rest(c);
2336 end while;
2337 end crefNames;
2338
2339 function connectorVariables
2340 "the potential and flow variables of every connector array"
2341 input list<Variable> variables;
2342 input Vector<Vertex> vertices;
2343 input UnorderedMap<String, Integer> vertexIndex;
2344 output array<list<ConnVar>> potentials;
2345 output array<list<ConnVar>> flows;
2346 protected
2347 ComponentRef c;
2348 Option<Integer> ov;
2349 Integer v;
2350 Connector conn;
2351 Box box;
2352 algorithm
2353 12 potentials := arrayCreate(Vector.size(vertices), {});
2354 12 flows := arrayCreate(Vector.size(vertices), {});
2355
2/2
✓ Branch 1 taken 111 times.
✓ Branch 2 taken 12 times.
123 for var in listReverse(variables) loop
2356
4/4
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 32 times.
✓ Branch 4 taken 32 times.
✓ Branch 5 taken 47 times.
111 if Variable.isPotential(var) or Variable.isFlow(var) then
2357 // the connectors containing the variable, or the variable itself
2358 // (connector RealInput = input Real)
2359 64 c := var.name;
2360
2/2
✓ Branch 1 taken 192 times.
✓ Branch 2 taken 64 times.
256 while not ComponentRef.isEmpty(c) loop
2361
2/2
✓ Branch 0 taken 384 times.
✓ Branch 1 taken 192 times.
576 for outside in {false, true} loop
2362 384 ov := UnorderedMap.get(vertexKey(ComponentRef.toString(c), outside), vertexIndex);
2363
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 384 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 312 times.
384 if isSome(ov) then
2364 72 SOME(v) := ov;
2365
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 72 times.
72 VERTEX(conn = SOME(conn), box = box) := Vector.get(vertices, v);
2366
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 36 times.
72 if Variable.isFlow(var) then
2367 72 flows[v] := CONN_VAR(var.name, listLength(box), memberName(var.name, Connector.name(conn))) :: flows[v];
2368 else
2369 72 potentials[v] := CONN_VAR(var.name, listLength(box), memberName(var.name, Connector.name(conn))) :: potentials[v];
2370 end if;
2371 end if;
2372 end for;
2373 192 c := ComponentRef.rest(c);
2374 end while;
2375 end if;
2376 end for;
2377 end connectorVariables;
2378
2379 function applyConnSubs
2380 "the variable of a connector subscripted with the indices of the connector,
2381 the dimensions of the variable inside the connector stay whole"
2382 input ComponentRef cr;
2383 input Integer connDims;
2384 input list<Subscript> indices;
2385 output Expression outExp;
2386 protected
2387 list<Subscript> subs;
2388 algorithm
2389 126 outExp := Expression.fromCref(cr);
2390
4/4
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 107 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 98 times.
126 if Type.isArray(Expression.typeOf(outExp)) and connDims > 0 then
2391 98 subs := List.firstN(indices, intMin(connDims, Type.dimensionCount(Expression.typeOf(outExp))));
2392 98 outExp := Expression.applySubscripts(subs, outExp);
2393 end if;
2394 end applyConnSubs;
2395
2396 function makeEqualityAssert
2397 "assert(abs(l - r) <= 0) for Reals, assert(l == r) else"
2398 input Expression l;
2399 input Expression r;
2400 input Type ty;
2401 output Equation eq;
2402 protected
2403 Type elem_ty = Type.arrayElementType(ty);
2404 Expression exp;
2405 algorithm
2406 ✗ if Type.isArray(ty) then
2407 ✗ Error.addInternalError(getInstanceName() + ": connected parameters of array type are not supported with --resizableArrays yet: "
2408 + Expression.toString(l) + ", " + Expression.toString(r), sourceInfo());
2409 ✗ fail();
2410 end if;
2411 ✗ if Type.isReal(elem_ty) then
2412 ✗ exp := Expression.BINARY(l, Operator.makeSub(elem_ty), r);
2413 ✗ exp := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.ABS_REAL, {exp}, Expression.variability(exp), Purity.PURE));
2414 ✗ exp := Expression.RELATION(exp, Operator.makeLessEq(elem_ty), Expression.REAL(0.0), -1);
2415 else
2416 ✗ exp := Expression.RELATION(l, Operator.makeEqual(elem_ty), r, -1);
2417 end if;
2418 ✗ eq := Equation.ASSERT(exp, Expression.STRING("Connected constants/parameters must be equal"),
2419 NFBuiltin.ASSERTIONLEVEL_ERROR, NFInstNode.NO_SCOPE, DAE.emptyElementSource);
2420 end makeEqualityAssert;
2421
2422 function wrapLoops
2423 "the equations in for loops, the first loop outermost"
2424 input list<Equation> body;
2425 input list<tuple<InstNode, Expression>> loops;
2426 output list<Equation> eqs = body;
2427 algorithm
2428
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 if listEmpty(body) then
2429 ✗ return;
2430 end if;
2431
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 90 times.
117 for l in listReverse(loops) loop
2432 54 eqs := {Equation.FOR(Util.tuple21(l), SOME(Util.tuple22(l)), eqs, NFInstNode.NO_SCOPE, DAE.emptyElementSource)};
2433 end for;
2434 end wrapLoops;
2435
2436 function iterExp
2437 input InstNode iter;
2438 output Expression exp = Expression.fromCref(ComponentRef.makeIterator(iter, Type.INTEGER()));
2439 end iterExp;
2440
2441 function rangeExp
2442 input Sym lo;
2443 input Sym hi;
2444 input UnorderedMap<String, Expression> params;
2445 output Expression exp = Expression.makeRange(symToExp(lo, params), NONE(), symToExp(hi, params));
2446 end rangeExp;
2447
2448 function generateEquations
2449 input Sets sets;
2450 input Vector<Vertex> vertices;
2451 input array<list<ConnVar>> potentials;
2452 input array<list<ConnVar>> flows;
2453 input UnorderedMap<String, Expression> params;
2454 input list<Aff> facts;
2455 output list<Equation> equations = {};
2456 protected
2457 Integer n = Vector.size(sets.pieceVertex);
2458 array<list<Integer>> groups = arrayCreate(n, {});
2459 Integer r;
2460 algorithm
2461
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
70 for p in n:-1:1 loop
2462 58 r := pieceFind(sets, p);
2463 116 groups[r] := p :: groups[r];
2464 end for;
2465
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
70 for p in 1:n loop
2466
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 32 times.
58 if not listEmpty(groups[p]) then
2467 26 equations := setEquations(groups[p], sets, vertices, potentials, flows, params, facts, equations);
2468 end if;
2469 end for;
2470 12 equations := listReverseInPlace(equations);
2471 end generateEquations;
2472
2473 function setEquations
2474 input list<Integer> members;
2475 input Sets sets;
2476 input Vector<Vertex> vertices;
2477 input array<list<ConnVar>> potentials;
2478 input array<list<ConnVar>> flows;
2479 input UnorderedMap<String, Expression> params;
2480 input list<Aff> facts;
2481 input output list<Equation> equations;
2482 protected
2483 UnorderedMap<Integer, ClassNodes> cls "class -> (piece, dim, off)";
2484 list<Integer> cls_order = {}, free = {}, real = {}, refs, nonempty;
2485 UnorderedSet<Integer> nonempty_set;
2486 Boolean collapsed, elementwise;
2487 Integer c, cnt, ref, v, d;
2488 Sym off, lo, hi;
2489 Box box;
2490 list<tuple<Integer, Integer, Sym>> nodes;
2491 UnorderedMap<Integer, Sym> tlo, thi "the range of a free class";
2492 UnorderedMap<Integer, Expression> tval "the iterator (or constant) of a free class";
2493 list<tuple<InstNode, Expression>> loops = {}, ploops;
2494 UnorderedMap<Integer, InstNode> loop_iters "loop dimension -> its iterator";
2495 UnorderedMap<Integer, IdxList> idx;
2496 list<Idx> pidx, ridx;
2497 list<Subscript> pexps, rexps;
2498 InstNode iter;
2499 Expression e;
2500 list<Equation> body;
2501 Option<Sym> trange_lo, trange_hi;
2502 algorithm
2503 26 (cls, free) := setFreeClasses(members, sets, facts);
2504
2505 // the members that may be non-empty
2506
5/6
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
✓ Branch 4 taken 58 times.
✓ Branch 5 taken 26 times.
✓ Branch 6 taken 58 times.
✓ Branch 7 taken 26 times.
84 nonempty := list(p for p guard boxEmpty(Vector.get(sets.pieceBox, p), facts) <> YES in members);
2507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if listEmpty(nonempty) then
2508 ✗ return;
2509 end if;
2510 26 nonempty_set := UnorderedSet.fromList(nonempty, Util.id, intEq);
2511
2512 // the ranges of the free classes are the same for all nonempty
2513 26 tlo := UnorderedMap.new<Sym>(Util.id, intEq);
2514 26 thi := UnorderedMap.new<Sym>(Util.id, intEq);
2515 26 tval := UnorderedMap.new<Expression>(Util.id, intEq);
2516
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 26 times.
40 for c in free loop
2517
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 14 times.
45 for nd in UnorderedMap.getOrFail(c, cls) loop
2518 31 (ref, d, off) := nd;
2519
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if UnorderedSet.contains(ref, nonempty_set) then
2520 31 box := Vector.get(sets.pieceBox, ref);
2521 62 lo := symAdd(ivLo(listGet(box, d)), off, facts);
2522 62 hi := symAdd(ivHi(listGet(box, d)), off, facts);
2523
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 14 times.
31 if UnorderedMap.contains(c, tlo) then
2524
2/4
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 17 times.
17 if not (symEq(lo, UnorderedMap.getOrFail(c, tlo)) and symEq(hi, UnorderedMap.getOrFail(c, thi))) then
2525 ✗ Error.addInternalError(getInstanceName() + ": --resizableArrays: different ranges in one connection set: " +
2526 setString(nonempty, sets, vertices), sourceInfo());
2527 ✗ fail();
2528 end if;
2529 else
2530 14 UnorderedMap.add(c, lo, tlo);
2531 14 UnorderedMap.add(c, hi, thi);
2532 end if;
2533 end if;
2534 end for;
2535 14 lo := UnorderedMap.getOrFail(c, tlo);
2536 14 hi := UnorderedMap.getOrFail(c, thi);
2537
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
14 if symEq(lo, hi) then
2538 3 UnorderedMap.add(c, symToExp(lo, params), tval);
2539 else
2540 11 iter := InstNode.newUniqueIterator();
2541 11 loops := (iter, rangeExp(lo, hi, params)) :: loops;
2542 11 UnorderedMap.add(c, iterExp(iter), tval);
2543 end if;
2544 end for;
2545 26 loops := listReverseInPlace(loops);
2546
2547 // the indices of the nonempty
2548 26 idx := UnorderedMap.new<IdxList>(Util.id, intEq);
2549
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
84 for p in nonempty loop
2550 58 box := Vector.get(sets.pieceBox, p);
2551 pidx := {};
2552
2/2
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 13 times.
107 for d in 1:listLength(box) loop
2553 49 (c, off) := dimFind(sets, dimNode(sets, p, d), facts);
2554
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 18 times.
49 if UnorderedMap.contains(c, tval) then
2555 // t - off
2556 31 e := Expression.BINARY(UnorderedMap.getOrFail(c, tval), Operator.makeSub(Type.INTEGER()), symToExp(off, params));
2557 31 pidx := IDX_EXP(SimplifyExp.simplify(e)) :: pidx;
2558 elseif symEq(ivLo(listGet(box, d)), ivHi(listGet(box, d))) then
2559 32 pidx := IDX_EXP(symToExp(ivLo(listGet(box, d)), params)) :: pidx;
2560 else
2561 2 pidx := IDX_LOOP(d) :: pidx;
2562 end if;
2563 end for;
2564 58 UnorderedMap.add(p, listReverseInPlace(pidx), idx);
2565 end for;
2566
2567
6/8
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 58 times.
✓ Branch 10 taken 58 times.
✓ Branch 11 taken 26 times.
✓ Branch 12 taken 58 times.
✓ Branch 13 taken 26 times.
84 real := list(p for p guard isSome(vertexConn(vertices, Vector.get(sets.pieceVertex, p))) in nonempty);
2568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if listEmpty(real) then
2569 ✗ return;
2570 end if;
2571
2572 // the reference member: no loops, else certainly non-empty with one loop
2573
6/6
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 56 times.
✓ Branch 4 taken 58 times.
✓ Branch 5 taken 26 times.
✓ Branch 6 taken 56 times.
✓ Branch 7 taken 26 times.
84 refs := list(p for p guard listEmpty(loopDims(UnorderedMap.getOrFail(p, idx))) in real);
2574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if listEmpty(refs) then
2575 ✗ refs := list(p for p guard boxEmpty(Vector.get(sets.pieceBox, p), facts) == NO and
2576 listLength(loopDims(UnorderedMap.getOrFail(p, idx))) == 1 in real);
2577 end if;
2578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if listEmpty(refs) then
2579 ✗ Error.addInternalError(getInstanceName() + ": --resizableArrays: no reference element in the connection set " +
2580 setString(nonempty, sets, vertices), sourceInfo());
2581 ✗ fail();
2582 end if;
2583 26 ref := listHead(refs);
2584 26 box := Vector.get(sets.pieceBox, ref);
2585 26 ridx := UnorderedMap.getOrFail(ref, idx);
2586
4/4
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 26 times.
47 rexps := list(Subscript.INDEX(idxValue(i, box, NONE(), params)) for i in ridx);
2587
2588 // potential equations: every member equal to the reference
2589
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
84 for p in real loop
2590 58 v := Vector.get(sets.pieceVertex, p);
2591 58 box := Vector.get(sets.pieceBox, p);
2592 58 pidx := UnorderedMap.getOrFail(p, idx);
2593 ploops := {};
2594 58 loop_iters := UnorderedMap.new<InstNode>(Util.id, intEq);
2595
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 58 times.
60 for d in loopDims(pidx) loop
2596 2 iter := InstNode.newUniqueIterator();
2597 2 UnorderedMap.add(d, iter, loop_iters);
2598 2 lo := ivLo(listGet(box, d));
2599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if p == ref then
2600 ✗ lo := symAddInt(lo, 1, facts);
2601 end if;
2602 4 ploops := (iter, rangeExp(lo, ivHi(listGet(box, d)), params)) :: ploops;
2603 end for;
2604 58 ploops := listReverseInPlace(ploops);
2605
3/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
58 if p == ref and listEmpty(ploops) then
2606 26 continue;
2607 end if;
2608
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 32 times.
88 pexps := list(Subscript.INDEX(idxValue(i, box, SOME(loop_iters), params)) for i in pidx);
2609 32 body := potentialEquations(potentials[v], potentials[Vector.get(sets.pieceVertex, ref)], pexps, rexps);
2610 32 equations := List.append_reverse(wrapLoops(wrapLoops(body, ploops), loops), equations);
2611 end for;
2612
2613 // the flow equation: the sum over all nonempty, the loop dimensions summed
2614
6/6
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 26 times.
✓ Branch 6 taken 56 times.
✓ Branch 7 taken 2 times.
84 elementwise := List.any(list(not listEmpty(loopDims(UnorderedMap.getOrFail(p, idx))) for p in real), Util.id);
2615 26 body := flowEquations(real, sets, vertices, flows, idx, elementwise, params);
2616 26 equations := List.append_reverse(wrapLoops(body, loops), equations);
2617 end setEquations;
2618
2619 function prependNode
2620 input Option<list<tuple<Integer, Integer, Sym>>> old;
2621 input tuple<Integer, Integer, Sym> node;
2622 output list<tuple<Integer, Integer, Sym>> res;
2623 algorithm
2624 res := match old
2625 local list<tuple<Integer, Integer, Sym>> l;
2626 case SOME(l) then node :: l;
2627 else {node};
2628 end match;
2629 end prependNode;
2630
2631 function setFreeClasses
2632 "the dimension classes of the members of a set (class -> its nodes: piece,
2633 dimension, offset) and the free ones, in the order of the members: no
2634 collapsed node, every member exactly once"
2635 input list<Integer> members;
2636 input Sets sets;
2637 input list<Aff> facts;
2638 output UnorderedMap<Integer, ClassNodes> cls;
2639 output list<Integer> free = {};
2640 protected
2641 list<Integer> cls_order = {};
2642 Integer c;
2643 Sym off;
2644 Boolean collapsed;
2645 UnorderedMap<Integer, Integer> counts;
2646 algorithm
2647 26 cls := UnorderedMap.new<ClassNodes>(Util.id, intEq);
2648
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
84 for p in members loop
2649
2/2
✓ Branch 2 taken 45 times.
✓ Branch 3 taken 13 times.
107 for d in 1:listLength(Vector.get(sets.pieceBox, p)) loop
2650 49 (c, off) := dimFind(sets, dimNode(sets, p, d), facts);
2651
2/2
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 20 times.
49 if not UnorderedMap.contains(c, cls) then
2652 cls_order := c :: cls_order;
2653 end if;
2654 49 UnorderedMap.addUpdate(c, function prependNode(node = (p, d, off)), cls);
2655 end for;
2656 end for;
2657
2/2
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 26 times.
55 for c in listReverse(cls_order) loop
2658 collapsed := false;
2659 29 counts := UnorderedMap.new<Integer>(Util.id, intEq);
2660
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 29 times.
78 for nd in UnorderedMap.getOrFail(c, cls) loop
2661
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 47 times.
49 if Vector.get(sets.collapsed, dimNode(sets, Util.tuple31(nd), Util.tuple32(nd))) then
2662 collapsed := true;
2663 end if;
2664 49 UnorderedMap.addUpdate(Util.tuple31(nd), function incCount(dummy = 0), counts);
2665 end for;
2666
8/10
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 49 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 14 times.
✓ Branch 10 taken 15 times.
✓ Branch 12 taken 14 times.
✗ Branch 13 not taken.
78 if UnorderedMap.size(counts) <> listLength(members) or
2667 List.any(list(n <> 1 for n in UnorderedMap.valueList(counts)), Util.id) then
2668 collapsed := true;
2669 end if;
2670
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if not collapsed then
2671 free := c :: free;
2672 end if;
2673 end for;
2674 26 free := listReverseInPlace(free);
2675 end setFreeClasses;
2676
2677 function incCount
2678 input Option<Integer> old;
2679 input Integer dummy;
2680 output Integer n = Util.getOptionOrDefault(old, 0) + 1;
2681 end incCount;
2682
2683 function vertexConn
2684 input Vector<Vertex> vertices;
2685 input Integer v;
2686 output Option<Connector> conn;
2687 algorithm
2688 232 VERTEX(conn = conn) := Vector.get(vertices, v);
2689 end vertexConn;
2690
2691 type ClassNodes = list<tuple<Integer, Integer, Sym>> "the nodes of a dimension class: piece, dimension, offset";
2692 type IdxList = list<Idx>;
2693
2694 function loopDims
2695 input list<Idx> idx;
2696 output list<Integer> dims = {};
2697 algorithm
2698
2/2
✓ Branch 1 taken 196 times.
✓ Branch 2 taken 232 times.
428 for i in listReverse(idx) loop
2699 _ := match i
2700 8 case IDX_LOOP() algorithm dims := i.dim :: dims; then ();
2701 else ();
2702 end match;
2703 end for;
2704 end loopDims;
2705
2706 function idxValue
2707 "the index expression of a dimension: loops get their iterators in order,
2708 without loops the first element"
2709 input Idx i;
2710 input Box box;
2711 input Option<UnorderedMap<Integer, InstNode>> loopIters "the iterators of the loop dimensions, NONE for the first element";
2712 input UnorderedMap<String, Expression> params;
2713 output Expression exp;
2714 algorithm
2715 exp := match (i, loopIters)
2716 local
2717 UnorderedMap<Integer, InstNode> iters;
2718 47 case (IDX_EXP(), _) then i.exp;
2719 ✗ case (IDX_LOOP(), NONE()) then symToExp(ivLo(listGet(box, i.dim)), params);
2720 2 case (IDX_LOOP(), SOME(iters)) then iterExp(UnorderedMap.getOrFail(i.dim, iters));
2721 end match;
2722 end idxValue;
2723
2724 function potentialEquations
2725 input list<ConnVar> vars1;
2726 input list<ConnVar> vars2;
2727 input list<Subscript> inds1;
2728 input list<Subscript> inds2;
2729 output list<Equation> equations = {};
2730 protected
2731 Expression l, r;
2732 Type ty;
2733 algorithm
2734
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 for v1 in vars1 loop
2735
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 for v2 in vars2 loop
2736
3/6
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 32 times.
✗ Branch 11 not taken.
32 if v1.member == v2.member and
2737 Type.isEqual(Type.arrayElementType(ComponentRef.nodeType(v1.cref)), Type.arrayElementType(ComponentRef.nodeType(v2.cref))) then
2738 32 l := applyConnSubs(v1.cref, v1.connDims, inds1);
2739 32 r := applyConnSubs(v2.cref, v2.connDims, inds2);
2740 32 ty := Expression.typeOf(l);
2741
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
32 if ComponentRef.variability(v1.cref) > Variability.PARAMETER and ComponentRef.variability(v2.cref) > Variability.PARAMETER then
2742 64 equations := Equation.makeEquality(l, r, ty, scalarizeMode = NFEquation.ScalarizeMode.DONT_SCALARIZE) :: equations;
2743 else
2744 ✗ equations := makeEqualityAssert(l, r, ty) :: equations;
2745 end if;
2746 end if;
2747 end for;
2748 end for;
2749 32 equations := listReverseInPlace(equations);
2750 end potentialEquations;
2751
2752 function flowEquations
2753 input list<Integer> members;
2754 input Sets sets;
2755 input Vector<Vertex> vertices;
2756 input array<list<ConnVar>> flows;
2757 input UnorderedMap<Integer, IdxList> idx;
2758 input Boolean elementwise;
2759 input UnorderedMap<String, Expression> params;
2760 output list<Equation> equations = {};
2761 protected
2762 UnorderedMap<String, ExpList> terms = UnorderedMap.new<ExpList>(stringHashDjb2, stringEq) "the terms of each sum, in reverse order";
2763 Integer v, sz;
2764 Box box;
2765 list<Idx> pidx;
2766 list<Subscript> inds;
2767 Box vbox;
2768 Boolean is_sum, outside, all_outside;
2769 Connector conn;
2770 Expression e, sum_exp;
2771 list<Expression> expl;
2772 Type ty;
2773 list<Dimension> mdims;
2774 algorithm
2775 // the outside connectors are subtracted, if all are outside nothing is
2776 all_outside := true;
2777
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
84 for p in members loop
2778
2/4
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 58 times.
58 SOME(conn) := vertexConn(vertices, Vector.get(sets.pieceVertex, p));
2779
2/2
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 4 times.
58 if not Connector.isOutside(conn) then
2780 all_outside := false;
2781 end if;
2782 end for;
2783
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 26 times.
84 for p in members loop
2784 58 v := Vector.get(sets.pieceVertex, p);
2785
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 58 times.
58 SOME(conn) := vertexConn(vertices, v);
2786
3/4
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
58 outside := Connector.isOutside(conn) and not all_outside;
2787 58 box := Vector.get(sets.pieceBox, p);
2788 58 pidx := UnorderedMap.getOrFail(p, idx);
2789 58 is_sum := not listEmpty(loopDims(pidx));
2790 58 vbox := vertexBox(vertices, v);
2791
4/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 58 times.
107 inds := list(flowSubscript(i, box, vbox, params) for i in pidx);
2792
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 58 times.
116 for fv in flows[v] loop
2793 58 mdims := Type.arrayDims(ComponentRef.nodeType(fv.cref));
2794
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
58 if elementwise and not listEmpty(mdims) then
2795
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 if listLength(mdims) <> 1 or not Dimension.isKnown(listHead(mdims)) then
2796 ✗ Error.addInternalError(getInstanceName() + ": the flow variable " + ComponentRef.toString(fv.cref) +
2797 " has to be a vector of known size inside its connector to sum it over a range of connectors.", sourceInfo());
2798 ✗ fail();
2799 end if;
2800 2 sz := Dimension.size(listHead(mdims));
2801
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
8 for k in 1:sz loop
2802 12 e := flowTerm(ComponentRef.setSubscripts({Subscript.INDEX(Expression.INTEGER(k))}, fv.cref), fv.connDims, inds, is_sum, outside);
2803 6 addNamed(fv.member + "[" + intString(k) + "]", e, terms);
2804 end for;
2805 else
2806 56 e := flowTerm(fv.cref, fv.connDims, inds, is_sum, outside);
2807 56 addNamed(fv.member, e, terms);
2808 end if;
2809 end for;
2810 end for;
2811
2812 // one sum for each flow variable, in the order of their first terms
2813
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 26 times.
54 for name in UnorderedMap.keyList(terms) loop
2814 28 expl := listReverse(UnorderedMap.getOrFail(name, terms));
2815
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 sum_exp :: expl := expl;
2816
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 28 times.
62 for e in expl loop
2817 34 sum_exp := Expression.BINARY(sum_exp, Operator.makeAdd(Expression.typeOf(e)), e);
2818 end for;
2819 28 ty := Expression.typeOf(sum_exp);
2820 28 equations := Equation.makeEquality(sum_exp, Expression.makeZero(ty), ty) :: equations;
2821 end for;
2822 26 equations := listReverseInPlace(equations);
2823 end flowEquations;
2824
2825 function flowSubscript
2826 "an index or a slice of the summed elements (no : for whole dimensions, the
2827 backend needs the ranges of resizable dimensions)"
2828 input Idx i;
2829 input Box box;
2830 input Box vertexBox;
2831 input UnorderedMap<String, Expression> params;
2832 output Subscript sub;
2833 protected
2834 Iv iv, viv;
2835 algorithm
2836 sub := match i
2837 47 case IDX_EXP() then Subscript.INDEX(i.exp);
2838 case IDX_LOOP()
2839 algorithm
2840 2 iv := listGet(box, i.dim);
2841 2 viv := listGet(vertexBox, i.dim);
2842 2 then Subscript.SLICE(rangeExp(ivLo(iv), ivHi(iv), params));
2843 end match;
2844 end flowSubscript;
2845
2846 function flowTerm
2847 input ComponentRef cr;
2848 input Integer connDims;
2849 input list<Subscript> inds;
2850 input Boolean isSum;
2851 input Boolean outside;
2852 output Expression e;
2853 algorithm
2854 62 e := applyConnSubs(cr, connDims, inds);
2855
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 58 times.
62 if isSum then
2856 // the sum over all elements of the summed dimensions
2857 8 e := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.SUM,
2858 {e}, Expression.variability(e), Purity.PURE, Type.arrayElementType(Expression.typeOf(e))));
2859 end if;
2860
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 if outside then
2861 ✗ e := Expression.negate(e);
2862 end if;
2863 end flowTerm;
2864
2865 type ExpList = list<Expression>;
2866
2867 function addNamed
2868 "adds a term to the sum of the flow variable name"
2869 input String name;
2870 input Expression e;
2871 input UnorderedMap<String, ExpList> terms;
2872 algorithm
2873 62 UnorderedMap.addUpdate(name, function prependTerm(e = e), terms);
2874 end addNamed;
2875
2876 function prependTerm
2877 input Option<list<Expression>> old;
2878 input Expression e;
2879 output list<Expression> res = e :: Util.getOptionOrDefault(old, {});
2880 end prependTerm;
2881
2882 function setString
2883 input list<Integer> members;
2884 input Sets sets;
2885 input Vector<Vertex> vertices;
2886 output String str = stringDelimitList(list(vertexName(vertices, Vector.get(sets.pieceVertex, p)) +
2887 boxString(Vector.get(sets.pieceBox, p)) for p in members), ", ");
2888 end setString;
2889
2890 function dumpSets
2891 input Sets sets;
2892 input Vector<Vertex> vertices;
2893 input list<Aff> facts;
2894 protected
2895 Integer n = Vector.size(sets.pieceVertex);
2896 array<list<Integer>> groups = arrayCreate(n, {});
2897 Integer r;
2898 algorithm
2899 ✗ print("Resizable connection sets (facts: " + stringDelimitList(list(affString(f) + " >= 0" for f in facts), ", ") + "):\n");
2900 ✗ for p in n:-1:1 loop
2901 ✗ r := pieceFind(sets, p);
2902 ✗ groups[r] := p :: groups[r];
2903 end for;
2904 ✗ for p in 1:n loop
2905 ✗ if not listEmpty(groups[p]) then
2906 ✗ print(" {" + setString(groups[p], sets, vertices) + "}\n");
2907 end if;
2908 end for;
2909 end dumpSets;
2910
2911 // ---------------------------------------------------------------------------
2912 // the overconstrained connection graph with symbolic sizes
2913 //
2914 // Like NFOCConnectionGraph, but on the symbolic connection sets instead of
2915 // the elements: G1 is the graph of the connections of the overconstrained
2916 // connectors (redundant connections are ignored), G2 adds the branches. G2 is
2917 // a forest (no broken connections) iff components(G1) - branches =
2918 // components(G2), checked as an identity of polynomials in the size
2919 // parameters. Every component of G2 gets its definite root, or the potential
2920 // root of the smallest priority (both have to be unique). Everything else
2921 // (rooted, uniqueRoot, broken connections, ...) is not supported: the caller
2922 // uses the classic graph then.
2923 // ---------------------------------------------------------------------------
2924
2925 uniontype Poly
2926 "a polynomial in the size parameters"
2927 record POLY
2928 list<tuple<String, Integer>> terms "monomial (names joined by *, sorted) -> coefficient, sorted, no zeros";
2929 end POLY;
2930 end Poly;
2931
2932 function polyConst
2933 input Integer c;
2934 output Poly p = POLY(if c == 0 then {} else {("", c)});
2935 end polyConst;
2936
2937 function polyFromAff
2938 input Aff a;
2939 output Poly p;
2940 protected
2941 Integer c;
2942 list<tuple<String, Integer>> k;
2943 algorithm
2944 ✗ AFF(c, k) := a;
2945 ✗ p := polyNormalize((if c == 0 then {} else {("", c)}) :: {k});
2946 end polyFromAff;
2947
2948 function polyNormalize
2949 "the sum of lists of terms"
2950 input list<list<tuple<String, Integer>>> termLists;
2951 output Poly p;
2952 protected
2953 UnorderedMap<String, Integer> acc = UnorderedMap.new<Integer>(stringHashDjb2, stringEq);
2954 String m;
2955 Integer c;
2956 algorithm
2957 ✗ for l in termLists loop
2958 ✗ for t in l loop
2959 ✗ (m, c) := t;
2960 ✗ UnorderedMap.addUpdate(m, function addInt(c = c), acc);
2961 end for;
2962 end for;
2963 ✗ p := POLY(list((m, UnorderedMap.getOrFail(m, acc)) for m guard UnorderedMap.getOrFail(m, acc) <> 0
2964 in List.sort(UnorderedMap.keyList(acc), stringGreater)));
2965 end polyNormalize;
2966
2967 function addInt
2968 input Option<Integer> old;
2969 input Integer c;
2970 output Integer n = Util.getOptionOrDefault(old, 0) + c;
2971 end addInt;
2972
2973 function polyAdd
2974 input Poly a;
2975 input Poly b;
2976 output Poly p = polyNormalize({a.terms, b.terms});
2977 end polyAdd;
2978
2979 function polySub
2980 input Poly a;
2981 input Poly b;
2982 output Poly p = polyNormalize({a.terms, list((Util.tuple21(t), -Util.tuple22(t)) for t in b.terms)});
2983 end polySub;
2984
2985 function polyMul
2986 input Poly a;
2987 input Poly b;
2988 output Poly p;
2989 protected
2990 list<tuple<String, Integer>> terms = {};
2991 algorithm
2992 ✗ for ta in a.terms loop
2993 ✗ for tb in b.terms loop
2994 ✗ terms := (monomialMul(Util.tuple21(ta), Util.tuple21(tb)), Util.tuple22(ta) * Util.tuple22(tb)) :: terms;
2995 end for;
2996 end for;
2997 ✗ p := polyNormalize({terms});
2998 end polyMul;
2999
3000 function monomialMul
3001 input String m1;
3002 input String m2;
3003 output String m;
3004 protected
3005 list<String> n1 = {}, n2 = {};
3006 algorithm
3007 ✗ if m1 <> "" then
3008 ✗ n1 := Util.stringSplitAtChar(m1, "*");
3009 end if;
3010 ✗ if m2 <> "" then
3011 ✗ n2 := Util.stringSplitAtChar(m2, "*");
3012 end if;
3013 ✗ m := stringDelimitList(List.sort(listAppend(n1, n2), stringGreater), "*");
3014 end monomialMul;
3015
3016 function polyString
3017 input Poly p;
3018 output String str = if listEmpty(p.terms) then "0" else
3019 stringDelimitList(list(intString(Util.tuple22(t)) + (if Util.tuple21(t) == "" then "" else "*" + Util.tuple21(t)) for t in p.terms), " + ");
3020 end polyString;
3021
3022 function polyEq
3023 input Poly a;
3024 input Poly b;
3025 output Boolean eq = polyString(a) == polyString(b);
3026 end polyEq;
3027
3028 function ocUnsupported
3029 "the symbolic overconstrained graph does not support this, the classic one is used"
3030 input String msg;
3031 algorithm
3032 ✗ if Flags.isSet(Flags.CGRAPH) then
3033 ✗ print("NFResizableConnections: symbolic overconstrained connection graph not used: " + msg + "\n");
3034 end if;
3035 ✗ fail();
3036 end ocUnsupported;
3037
3038 function symLength
3039 "the number of elements of lo:hi as polynomial, lo:hi certainly not empty"
3040 input Sym lo;
3041 input Sym hi;
3042 input list<Aff> facts;
3043 output Poly p;
3044 protected
3045 Sym d = symSub(hi, lo, facts);
3046 algorithm
3047 ✗ if not symIsAff(d) or symLe(lo, hi, facts) <> YES then
3048 ✗ ocUnsupported("the size of " + symString(lo) + ":" + symString(hi) + " is not polynomial");
3049 end if;
3050 ✗ p := polyFromAff(affAdd(symGetAff(d), affInt(1)));
3051 end symLength;
3052
3053 function boxCount
3054 input Box box;
3055 input list<Aff> facts;
3056 output Poly p = polyConst(1);
3057 algorithm
3058 ✗ for iv in box loop
3059 ✗ p := polyMul(p, symLength(ivLo(iv), ivHi(iv), facts));
3060 end for;
3061 end boxCount;
3062
3063 function setGroups
3064 "the members of each set"
3065 input Sets sets;
3066 output list<list<Integer>> groups = {};
3067 protected
3068 Integer n = Vector.size(sets.pieceVertex);
3069 array<list<Integer>> g = arrayCreate(n, {});
3070 Integer r;
3071 algorithm
3072 ✗ for p in n:-1:1 loop
3073 ✗ r := pieceFind(sets, p);
3074 ✗ arrayUpdate(g, r, p :: g[r]);
3075 end for;
3076 ✗ for p in n:-1:1 loop
3077 ✗ if not listEmpty(g[p]) then
3078 groups := g[p] :: groups;
3079 end if;
3080 end for;
3081 end setGroups;
3082
3083 function setComponents
3084 "the number of components of a set: the product of the sizes of its free
3085 classes; NONE for a set of virtual hubs only"
3086 input list<Integer> members;
3087 input Sets sets;
3088 input Vector<Vertex> vertices;
3089 input list<Aff> facts;
3090 output Option<Poly> count;
3091 protected
3092 UnorderedMap<Integer, ClassNodes> cls;
3093 list<Integer> free;
3094 Integer p, d;
3095 Sym off, lo, hi;
3096 Box box;
3097 Poly c;
3098 algorithm
3099 ✗ for m in members loop
3100 ✗ if boxEmpty(Vector.get(sets.pieceBox, m), facts) <> NO then
3101 ✗ ocUnsupported("a piece of " + vertexName(vertices, Vector.get(sets.pieceVertex, m)) + " may be empty");
3102 end if;
3103 end for;
3104 ✗ if not List.any(list(isSome(vertexConn(vertices, Vector.get(sets.pieceVertex, m))) for m in members), Util.id) then
3105 count := NONE();
3106 ✗ return;
3107 end if;
3108 ✗ (cls, free) := setFreeClasses(members, sets, facts);
3109 ✗ c := polyConst(1);
3110 ✗ for fc in free loop
3111 ✗ (p, d, off) := listHead(UnorderedMap.getOrFail(fc, cls));
3112 ✗ box := Vector.get(sets.pieceBox, p);
3113 ✗ lo := symAdd(ivLo(listGet(box, d)), off, facts);
3114 ✗ hi := symAdd(ivHi(listGet(box, d)), off, facts);
3115 ✗ for nd in UnorderedMap.getOrFail(fc, cls) loop
3116 ✗ (p, d, off) := nd;
3117 ✗ box := Vector.get(sets.pieceBox, p);
3118 ✗ if not (symEq(symAdd(ivLo(listGet(box, d)), off, facts), lo) and symEq(symAdd(ivHi(listGet(box, d)), off, facts), hi)) then
3119 ✗ ocUnsupported("different ranges in one set");
3120 end if;
3121 end for;
3122 ✗ c := polyMul(c, symLength(lo, hi, facts));
3123 end for;
3124 count := SOME(c);
3125 end setComponents;
3126
3127 function componentCount
3128 input Sets sets;
3129 input Vector<Vertex> vertices;
3130 input list<Aff> facts;
3131 output Poly count = polyConst(0);
3132 protected
3133 Option<Poly> oc;
3134 Poly c;
3135 algorithm
3136 ✗ for members in setGroups(sets) loop
3137 ✗ oc := setComponents(members, sets, vertices, facts);
3138 ✗ if isSome(oc) then
3139 ✗ SOME(c) := oc;
3140 ✗ count := polyAdd(count, c);
3141 end if;
3142 end for;
3143 end componentCount;
3144
3145 uniontype OcRoot
3146 "a Connections.root (priority -1) or potentialRoot call: the elements of a connector"
3147 record OC_ROOT
3148 Integer vertex;
3149 Box elements;
3150 Integer priority;
3151 end OC_ROOT;
3152 end OcRoot;
3153
3154 uniontype OcGraph
3155 record OC_GRAPH
3156 UnorderedMap<String, String> members "connector -> its overconstrained member";
3157 UnorderedSet<String> prefixes "the prefixes of the overconstrained connectors";
3158 list<Edge> connections;
3159 list<Edge> branches;
3160 list<OcRoot> roots;
3161 list<tuple<Integer, Box>> boxes "the elements of roots and isRoot arguments";
3162 end OC_GRAPH;
3163 end OcGraph;
3164
3165 function ocMembers
3166 "the connectors with an overconstrained member (e.g. terminal for terminal.theta)"
3167 input list<Variable> variables;
3168 output UnorderedMap<String, String> members = UnorderedMap.new<String>(stringHashDjb2, stringEq);
3169 output UnorderedSet<String> prefixes = UnorderedSet.new(stringHashDjb2, stringEq);
3170 protected
3171 ComponentRef c, conn;
3172 String key, name;
3173 Option<String> old;
3174 algorithm
3175 ✗ for var in variables loop
3176 ✗ c := var.name;
3177 ✗ while not ComponentRef.isEmpty(c) loop
3178 ✗ if InstNode.isComponent(ComponentRef.node(c)) and
3179 Class.isOverdetermined(InstNode.getClass(ComponentRef.node(c))) then
3180 ✗ conn := ComponentRef.rest(c);
3181 ✗ if not ComponentRef.isEmpty(conn) then
3182 ✗ key := ComponentRef.toString(ComponentRef.stripSubscriptsAll(conn));
3183 ✗ name := ComponentRef.firstName(c);
3184 ✗ old := UnorderedMap.get(key, members);
3185 ✗ if isSome(old) and Util.getOption(old) <> name then
3186 ✗ ocUnsupported("the connector " + key + " has several overconstrained members");
3187 end if;
3188 ✗ UnorderedMap.add(key, name, members);
3189 ✗ while not ComponentRef.isEmpty(conn) loop
3190 ✗ UnorderedSet.add(ComponentRef.toString(ComponentRef.stripSubscriptsAll(conn)), prefixes);
3191 ✗ conn := ComponentRef.rest(conn);
3192 end while;
3193 end if;
3194 break;
3195 end if;
3196 ✗ c := ComponentRef.rest(c);
3197 end while;
3198 end for;
3199 end ocMembers;
3200
3201 function connectorKey
3202 input Expression exp;
3203 output String key = ComponentRef.toString(ComponentRef.stripSubscriptsAll(Expression.toCref(exp)));
3204 end connectorKey;
3205
3206 function ocConnector
3207 "the connector of an overconstrained member, e.g. a[i].terminal for a[i].terminal.theta"
3208 input Expression arg;
3209 input OcGraph g;
3210 output Expression conn;
3211 protected
3212 ComponentRef cr = Expression.toCref(arg), rest;
3213 String key;
3214 algorithm
3215 ✗ rest := ComponentRef.rest(cr);
3216 ✗ key := if ComponentRef.isEmpty(rest) then "" else ComponentRef.toString(ComponentRef.stripSubscriptsAll(rest));
3217 ✗ if key == "" or not UnorderedMap.contains(key, g.members) or
3218 UnorderedMap.getOrFail(key, g.members) <> ComponentRef.firstName(cr) or
3219 not listEmpty(ComponentRef.getSubscripts(cr)) then
3220 ✗ ocUnsupported("the argument " + Expression.toString(arg) + " of a Connections operator");
3221 end if;
3222 ✗ conn := Expression.fromCref(rest);
3223 end ocConnector;
3224
3225 function sideElements
3226 "the vertex and the elements of a connector (array) in the domain of the
3227 surrounding loops, every slice an own dimension"
3228 input Expression conn;
3229 input list<String> iterNames;
3230 input Box dom;
3231 input DAE.ElementSource source;
3232 input Context ctx;
3233 output Integer v;
3234 output Box elements;
3235 protected
3236 list<SubEntry> entries;
3237 list<Integer> iters;
3238 list<Sym> offs;
3239 Box d, slice_ivs = {};
3240 list<Aff> facts = {};
3241 algorithm
3242 ✗ (v, entries) := connectSide(conn, iterNames, source, ctx);
3243 ✗ for e in entries loop
3244 ✗ if isSliced(e) then
3245 ✗ slice_ivs := IV(symInt(1), symAddInt(symSub(sliceHi(e), sliceLo(e), facts), 1, facts)) :: slice_ivs;
3246 end if;
3247 end for;
3248 ✗ d := listAppend(dom, listReverse(slice_ivs));
3249 ✗ (iters, offs) := sideDims(entries, listLength(dom), facts);
3250 ✗ elements := sideImage(SIDE(v, iters, offs), d, facts);
3251 end sideElements;
3252
3253 type OcOp = enumeration(BRANCH, ROOT, POTENTIAL_ROOT, IS_ROOT, OTHER, NOT_OPERATOR)
3254 "the Connections operators (OTHER: rooted, uniqueRoot, uniqueRootIndices)";
3255
3256 function connectionsOperator
3257 input Expression exp;
3258 output OcOp op;
3259 algorithm
3260 op := match exp
3261 local Function fn;
3262 case Expression.CALL(call = Call.TYPED_CALL(fn = fn))
3263 then match AbsynUtil.pathString(Function.name(fn))
3264 case "Connections.branch" then OcOp.BRANCH;
3265 case "Connections.root" then OcOp.ROOT;
3266 case "Connections.potentialRoot" then OcOp.POTENTIAL_ROOT;
3267 case "Connections.isRoot" then OcOp.IS_ROOT;
3268 case "Connections.rooted" then OcOp.OTHER;
3269 case "rooted" then OcOp.OTHER;
3270 case "Connections.uniqueRoot" then OcOp.OTHER;
3271 case "Connections.uniqueRootIndices" then OcOp.OTHER;
3272 else OcOp.NOT_OPERATOR;
3273 end match;
3274 else OcOp.NOT_OPERATOR;
3275 end match;
3276 end connectionsOperator;
3277
3278 function callArgs
3279 input Expression exp;
3280 output list<Expression> args;
3281 algorithm
3282 ✗ Expression.CALL(call = Call.TYPED_CALL(arguments = args)) := exp;
3283 end callArgs;
3284
3285 function collectOc
3286 "the connections of overconstrained connectors, the branches, roots and the
3287 elements of the isRoot arguments"
3288 input Equation eq;
3289 input list<String> iterNames;
3290 input Box dom;
3291 input Context ctx;
3292 input output OcGraph g;
3293 protected
3294 Sym lo, hi;
3295 Integer v;
3296 Box elements;
3297 list<Expression> args;
3298 Integer prio;
3299 algorithm
3300 () := match eq
3301 local
3302 Expression range, e;
3303 case Equation.CONNECT()
3304 algorithm
3305 ✗ if UnorderedMap.contains(connectorKey(eq.lhs), g.members) and UnorderedMap.contains(connectorKey(eq.rhs), g.members) then
3306 ✗ g.connections := makeEdge(eq.lhs, eq.rhs, iterNames, dom, eq.source, ctx) :: g.connections;
3307 elseif UnorderedSet.contains(connectorKey(eq.lhs), g.prefixes) or UnorderedSet.contains(connectorKey(eq.rhs), g.prefixes) then
3308 ✗ ocUnsupported("a connection of connectors that contain overconstrained connectors");
3309 end if;
3310 then ();
3311
3312 case Equation.FOR(range = SOME(range as Expression.RANGE()))
3313 algorithm
3314 ✗ checkStep(range.step, eq.source);
3315 ✗ lo := expToSym(range.start, ctx, eq.source);
3316 ✗ hi := expToSym(range.stop, ctx, eq.source);
3317 ✗ for b in eq.body loop
3318 ✗ g := collectOc(b, listAppend(iterNames, {InstNode.name(eq.iterator)}), listAppend(dom, {IV(lo, hi)}), ctx, g);
3319 end for;
3320 then ();
3321
3322 case Equation.NORETCALL(exp = e)
3323 algorithm
3324 () := match connectionsOperator(e)
3325 case OcOp.BRANCH
3326 algorithm
3327 ✗ args := callArgs(e);
3328 ✗ g.branches := makeEdge(ocConnector(listHead(args), g), ocConnector(listGet(args, 2), g),
3329 iterNames, dom, eq.source, ctx) :: g.branches;
3330 then ();
3331 case OcOp.ROOT
3332 algorithm
3333 ✗ (v, elements) := sideElements(ocConnector(listHead(callArgs(e)), g), iterNames, dom, eq.source, ctx);
3334 ✗ g.roots := OC_ROOT(v, elements, -1) :: g.roots;
3335 ✗ g.boxes := (v, elements) :: g.boxes;
3336 then ();
3337 case OcOp.POTENTIAL_ROOT
3338 algorithm
3339 ✗ args := callArgs(e);
3340 prio := match listGet(args, 2)
3341 local Integer pv;
3342 case Expression.INTEGER(value = pv) then pv;
3343 ✗ else algorithm ocUnsupported("a potential root priority that is no literal"); then fail();
3344 end match;
3345 ✗ (v, elements) := sideElements(ocConnector(listHead(args), g), iterNames, dom, eq.source, ctx);
3346 ✗ g.roots := OC_ROOT(v, elements, prio) :: g.roots;
3347 ✗ g.boxes := (v, elements) :: g.boxes;
3348 then ();
3349 case OcOp.NOT_OPERATOR
3350 algorithm
3351 ✗ g := collectIsRoot(eq, iterNames, dom, ctx, g);
3352 then ();
3353 ✗ else algorithm ocUnsupported(Expression.toString(e)); then fail();
3354 end match;
3355 then ();
3356
3357 else algorithm
3358 ✗ g := collectIsRoot(eq, iterNames, dom, ctx, g);
3359 then ();
3360 end match;
3361 end collectOc;
3362
3363 function collectIsRoot
3364 "the elements of the isRoot arguments of an equation"
3365 input Equation eq;
3366 input list<String> iterNames;
3367 input Box dom;
3368 input Context ctx;
3369 input output OcGraph g;
3370 protected
3371 Pointer<OcGraph> gp = Pointer.create(g);
3372 algorithm
3373 ✗ _ := Equation.mapExp(eq, function collectIsRootExp(iterNames = iterNames, dom = dom, ctx = ctx, gp = gp, source = Equation.source(eq)));
3374 ✗ g := Pointer.access(gp);
3375 end collectIsRoot;
3376
3377 function collectIsRootExp
3378 input output Expression exp;
3379 input list<String> iterNames;
3380 input Box dom;
3381 input Context ctx;
3382 input Pointer<OcGraph> gp;
3383 input DAE.ElementSource source;
3384 algorithm
3385 ✗ _ := Expression.map(exp, function collectIsRootCall(iterNames = iterNames, dom = dom, ctx = ctx, gp = gp, source = source));
3386 end collectIsRootExp;
3387
3388 function collectIsRootCall
3389 input output Expression exp;
3390 input list<String> iterNames;
3391 input Box dom;
3392 input Context ctx;
3393 input Pointer<OcGraph> gp;
3394 input DAE.ElementSource source;
3395 protected
3396 OcGraph g;
3397 Integer v;
3398 Box elements;
3399 algorithm
3400 () := match connectionsOperator(exp)
3401 case OcOp.NOT_OPERATOR then ();
3402 case OcOp.IS_ROOT
3403 algorithm
3404 ✗ g := Pointer.access(gp);
3405 ✗ (v, elements) := sideElements(ocConnector(listHead(callArgs(exp)), g), iterNames, dom, source, ctx);
3406 ✗ g.boxes := (v, elements) :: g.boxes;
3407 ✗ Pointer.update(gp, g);
3408 then ();
3409 ✗ else algorithm ocUnsupported(Expression.toString(exp)); then fail();
3410 end match;
3411 end collectIsRootCall;
3412
3413 function piecesOf
3414 "the pieces of the elements (that have to be whole pieces)"
3415 input Integer v;
3416 input Box elements;
3417 input Sets sets;
3418 input list<Aff> facts;
3419 output list<Integer> pieces = {};
3420 protected
3421 Box pbox;
3422 algorithm
3423 ✗ for p in sets.vertexPieces[v] loop
3424 ✗ pbox := Vector.get(sets.pieceBox, p);
3425 ✗ if boxEmpty(boxInter(elements, pbox, facts), facts) <> YES then
3426 ✗ if not boxContains(elements, pbox, facts) then
3427 ✗ ocUnsupported("elements that are no whole pieces");
3428 end if;
3429 pieces := p :: pieces;
3430 end if;
3431 end for;
3432 end piecesOf;
3433
3434 function rootsOfSet
3435 "the root pieces of a set: its definite root, else its potential root of the
3436 smallest priority, both unique per component of the set"
3437 input list<Integer> members;
3438 input UnorderedMap<Integer, Integer> priority "piece -> smallest priority, -1 for a definite root";
3439 input Sets sets;
3440 input Vector<Vertex> vertices;
3441 input list<Aff> facts;
3442 output list<Integer> roots = {};
3443 protected
3444 UnorderedMap<Integer, ClassNodes> cls;
3445 list<Integer> free, cands;
3446 Integer best = -2, pr;
3447 UnorderedSet<Integer> free_set;
3448 Poly count;
3449 Integer c;
3450 Sym off;
3451 algorithm
3452 ✗ for m in members loop
3453 ✗ if UnorderedMap.contains(m, priority) then
3454 ✗ pr := UnorderedMap.getOrFail(m, priority);
3455 ✗ if best == -2 or pr < best then
3456 best := pr;
3457 end if;
3458 end if;
3459 end for;
3460 ✗ if best == -2 then
3461 ✗ return;
3462 end if;
3463 ✗ cands := list(m for m guard UnorderedMap.contains(m, priority) and UnorderedMap.getOrFail(m, priority) == best in members);
3464 ✗ (cls, free) := setFreeClasses(members, sets, facts);
3465 ✗ if listEmpty(free) then
3466 // one component: exactly one root element
3467 ✗ count := polyConst(0);
3468 ✗ for m in cands loop
3469 ✗ count := polyAdd(count, boxCount(Vector.get(sets.pieceBox, m), facts));
3470 end for;
3471 ✗ if not polyEq(count, polyConst(1)) then
3472 ✗ ocUnsupported(polyString(count) + " roots in one component");
3473 end if;
3474 else
3475 // a component for every value of the free dimensions: one root piece,
3476 // all of its other dimensions single elements
3477 ✗ if listLength(cands) <> 1 then
3478 ✗ ocUnsupported("several roots in one component");
3479 end if;
3480 ✗ free_set := UnorderedSet.fromList(free, Util.id, intEq);
3481 ✗ for d in 1:listLength(Vector.get(sets.pieceBox, listHead(cands))) loop
3482 ✗ (c, off) := dimFind(sets, dimNode(sets, listHead(cands), d), facts);
3483 ✗ if not UnorderedSet.contains(c, free_set) and
3484 not symEq(ivLo(listGet(Vector.get(sets.pieceBox, listHead(cands)), d)), ivHi(listGet(Vector.get(sets.pieceBox, listHead(cands)), d))) then
3485 ✗ ocUnsupported("several roots in one component");
3486 end if;
3487 end for;
3488 end if;
3489 roots := cands;
3490 end rootsOfSet;
3491
3492 function evalIsRoot
3493 "isRoot of elements: true if all of them are roots, false if none"
3494 input Integer v;
3495 input Box elements;
3496 input Sets sets;
3497 input UnorderedSet<Integer> roots;
3498 input list<Aff> facts;
3499 output Boolean isRoot;
3500 protected
3501 list<Integer> pieces = piecesOf(v, elements, sets, facts);
3502 Boolean all_roots = true, any_root = false;
3503 algorithm
3504 ✗ for p in pieces loop
3505 ✗ if UnorderedSet.contains(p, roots) then
3506 any_root := true;
3507 else
3508 all_roots := false;
3509 end if;
3510 end for;
3511 ✗ if any_root and not all_roots then
3512 ✗ ocUnsupported("isRoot of elements of which only some are roots");
3513 end if;
3514 isRoot := any_root;
3515 end evalIsRoot;
3516
3517 function rewriteOc
3518 "the equations with isRoot evaluated and the Connections operators removed"
3519 input Equation eq;
3520 input list<String> iterNames;
3521 input Box dom;
3522 input Context ctx;
3523 input OcGraph g;
3524 input Sets sets;
3525 input UnorderedSet<Integer> roots;
3526 input list<Aff> facts;
3527 output Equation outEq;
3528 protected
3529 Sym lo, hi;
3530 algorithm
3531 outEq := match eq
3532 local
3533 Expression range;
3534 case Equation.FOR(range = SOME(range as Expression.RANGE()))
3535 algorithm
3536 ✗ lo := expToSym(range.start, ctx, eq.source);
3537 ✗ hi := expToSym(range.stop, ctx, eq.source);
3538 ✗ eq.body := list(rewriteOc(b, listAppend(iterNames, {InstNode.name(eq.iterator)}), listAppend(dom, {IV(lo, hi)}),
3539 ctx, g, sets, roots, facts) for b in eq.body);
3540 then eq;
3541 ✗ else Equation.mapExp(eq, function rewriteIsRootExp(iterNames = iterNames, dom = dom, ctx = ctx, g = g,
3542 sets = sets, roots = roots, facts = facts, source = Equation.source(eq)));
3543 end match;
3544 end rewriteOc;
3545
3546 function rewriteIsRootExp
3547 input output Expression exp;
3548 input list<String> iterNames;
3549 input Box dom;
3550 input Context ctx;
3551 input OcGraph g;
3552 input Sets sets;
3553 input UnorderedSet<Integer> roots;
3554 input list<Aff> facts;
3555 input DAE.ElementSource source;
3556 algorithm
3557 ✗ exp := Expression.map(exp, function rewriteIsRootCall(iterNames = iterNames, dom = dom, ctx = ctx, g = g,
3558 sets = sets, roots = roots, facts = facts, source = source));
3559 end rewriteIsRootExp;
3560
3561 function rewriteIsRootCall
3562 input output Expression exp;
3563 input list<String> iterNames;
3564 input Box dom;
3565 input Context ctx;
3566 input OcGraph g;
3567 input Sets sets;
3568 input UnorderedSet<Integer> roots;
3569 input list<Aff> facts;
3570 input DAE.ElementSource source;
3571 protected
3572 Integer v;
3573 Box elements;
3574 algorithm
3575 () := match connectionsOperator(exp)
3576 case OcOp.IS_ROOT
3577 algorithm
3578 ✗ (v, elements) := sideElements(ocConnector(listHead(callArgs(exp)), g), iterNames, dom, source, ctx);
3579 ✗ exp := Expression.BOOLEAN(evalIsRoot(v, elements, sets, roots, facts));
3580 then ();
3581 else ();
3582 end match;
3583 end rewriteIsRootCall;
3584
3585 function isConnectionsOperatorCall
3586 input Expression exp;
3587 output Boolean b = connectionsOperator(exp) <> OcOp.NOT_OPERATOR;
3588 end isConnectionsOperatorCall;
3589
3590 function hasConnectionsOperator
3591 input Expression exp;
3592 output Boolean b;
3593 algorithm
3594 ✗ b := Expression.contains(exp, isConnectionsOperatorCall);
3595 end hasConnectionsOperator;
3596
3597 function resolveOverconstrainedSymbolic
3598 input output FlatModel flatModel;
3599 protected
3600 Context ctx;
3601 OcGraph g;
3602 Sets g1, g2;
3603 list<Aff> facts1, facts2;
3604 Vector<Vertex> v1, v2;
3605 Poly c1, c2, b;
3606 UnorderedMap<Integer, Integer> priority;
3607 UnorderedSet<Integer> roots;
3608 Integer pr;
3609 Option<Expression> obind;
3610 UnorderedMap<String, String> members;
3611 UnorderedSet<String> prefixes;
3612 algorithm
3613 ✗ ctx := CONTEXT(Vector.new<Vertex>(), UnorderedMap.new<Integer>(stringHashDjb2, stringEq),
3614 UnorderedMap.new<Expression>(stringHashDjb2, stringEq), UnorderedMap.new<Variable>(stringHashDjb2, stringEq));
3615 ✗ for var in flatModel.variables loop
3616 ✗ UnorderedMap.add(ComponentRef.toString(var.name), var, ctx.variables);
3617 ✗ obind := Binding.getExpOpt(var.binding);
3618 ✗ if isSome(obind) and hasConnectionsOperator(Util.getOption(obind)) then
3619 ✗ ocUnsupported("a Connections operator in the binding of " + ComponentRef.toString(var.name));
3620 end if;
3621 end for;
3622 ✗ for eq in flatModel.initialEquations loop
3623 ✗ _ := Equation.mapExp(eq, failOnConnectionsOperator);
3624 end for;
3625
3626 ✗ (members, prefixes) := ocMembers(flatModel.variables);
3627 ✗ g := OC_GRAPH(members, prefixes, {}, {}, {}, {});
3628 ✗ for eq in flatModel.equations loop
3629 ✗ g := collectOc(eq, {}, {}, ctx, g);
3630 end for;
3631
3632 // G1 (connections) and G2 (connections and branches), on copies of the
3633 // vertices (the sets add virtual hubs to them)
3634 ✗ v1 := Vector.copy(ctx.vertices);
3635 ✗ v2 := Vector.copy(ctx.vertices);
3636 ✗ (g1, facts1) := connectionSets(v1, g.connections, g.boxes);
3637 ✗ (g2, facts2) := connectionSets(v2, listAppend(g.connections, g.branches), g.boxes);
3638 ✗ c1 := componentCount(g1, v1, facts1);
3639 ✗ c2 := componentCount(g2, v2, facts2);
3640 ✗ b := polyConst(0);
3641 ✗ for e in g.branches loop
3642 ✗ b := polyAdd(b, boxCount(e.dom, facts2));
3643 end for;
3644 ✗ if Flags.isSet(Flags.CGRAPH) then
3645 ✗ print("NFResizableConnections: components of the connections " + polyString(c1) + ", branches " + polyString(b) +
3646 ", components " + polyString(c2) + "\n");
3647 end if;
3648 ✗ if not polyEq(polySub(c1, b), c2) then
3649 ✗ ocUnsupported("the branches close loops (broken connections)");
3650 end if;
3651
3652 // the roots: the smallest priority of every piece, -1 for a definite root
3653 ✗ priority := UnorderedMap.new<Integer>(Util.id, intEq);
3654 ✗ for r in g.roots loop
3655 ✗ for p in piecesOf(r.vertex, r.elements, g2, facts2) loop
3656 ✗ UnorderedMap.addUpdate(p, function minPriority(pr = r.priority), priority);
3657 end for;
3658 end for;
3659 ✗ roots := UnorderedSet.new(Util.id, intEq);
3660 ✗ for set_members in setGroups(g2) loop
3661 ✗ for p in rootsOfSet(set_members, priority, g2, v2, facts2) loop
3662 ✗ UnorderedSet.add(p, roots);
3663 end for;
3664 end for;
3665
3666 ✗ flatModel.equations := removeGraphOperators(flatModel.equations);
3667 ✗ flatModel.equations := list(rewriteOc(eq, {}, {}, ctx, g, g2, roots, facts2) for eq in flatModel.equations);
3668 end resolveOverconstrainedSymbolic;
3669
3670 function removeGraphOperators
3671 "removes Connections.root, potentialRoot and branch, also inside for loops;
3672 a for loop with nothing else is removed"
3673 input list<Equation> equations;
3674 output list<Equation> outEquations = {};
3675 algorithm
3676 ✗ for eq in equations loop
3677 outEquations := match eq
3678 local
3679 Expression e;
3680 case Equation.NORETCALL(exp = e)
3681 guard connectionsOperator(e) == OcOp.ROOT or
3682 connectionsOperator(e) == OcOp.POTENTIAL_ROOT or
3683 connectionsOperator(e) == OcOp.BRANCH
3684 then outEquations;
3685 case Equation.FOR()
3686 algorithm
3687 ✗ eq.body := removeGraphOperators(eq.body);
3688 ✗ then if listEmpty(eq.body) then outEquations else eq :: outEquations;
3689 else eq :: outEquations;
3690 end match;
3691 end for;
3692 ✗ outEquations := listReverseInPlace(outEquations);
3693 end removeGraphOperators;
3694
3695 function minPriority
3696 input Option<Integer> old;
3697 input Integer pr;
3698 output Integer res = if isSome(old) then intMin(Util.getOption(old), pr) else pr;
3699 end minPriority;
3700
3701 function failOnConnectionsOperator
3702 input output Expression exp;
3703 algorithm
3704 ✗ if hasConnectionsOperator(exp) then
3705 ✗ ocUnsupported("a Connections operator in an initial equation");
3706 end if;
3707 end failOnConnectionsOperator;
3708
3709 // ---------------------------------------------------------------------------
3710 // entry point
3711 // ---------------------------------------------------------------------------
3712
3713 public
3714 function resolveOverconstrained
3715 "Connections.root, potentialRoot, branch and isRoot of overconstrained
3716 connectors with symbolic sizes (the graph does not depend on the sizes).
3717 Returns false without changing the model if this is not supported, the
3718 caller uses the classic graph on the unrolled connections then."
3719 input output FlatModel flatModel;
3720 output Boolean ok;
3721 algorithm
3722 ✗ ErrorExt.setCheckpoint(getInstanceName());
3723 try
3724 ✗ flatModel := resolveOverconstrainedSymbolic(flatModel);
3725 ok := true;
3726 ✗ ErrorExt.delCheckpoint(getInstanceName());
3727 else
3728 ok := false;
3729 ✗ ErrorExt.rollBack(getInstanceName());
3730 end try;
3731 end resolveOverconstrained;
3732
3733 function resolve
3734 "Generates the connection equations of the connect equations of the model
3735 with symbolic sizes and adds them to the equations."
3736 input output FlatModel flatModel;
3737 protected
3738 list<Equation> conns, eql;
3739 Context ctx;
3740 list<Edge> edges = {};
3741 Sets sets;
3742 list<Aff> facts;
3743 array<list<ConnVar>> potentials, flows;
3744 ComponentRef c;
3745 algorithm
3746 12 (conns, eql) := List.splitOnTrue(flatModel.equations, isConnection);
3747 12 ctx := CONTEXT(Vector.new<Vertex>(), UnorderedMap.new<Integer>(stringHashDjb2, stringEq),
3748 UnorderedMap.new<Expression>(stringHashDjb2, stringEq), UnorderedMap.new<Variable>(stringHashDjb2, stringEq));
3749
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 12 times.
123 for var in flatModel.variables loop
3750 111 UnorderedMap.add(ComponentRef.toString(var.name), var, ctx.variables);
3751 end for;
3752
3753 // every connector with flow variables is a vertex: unconnected flows are zero
3754
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 12 times.
123 for var in flatModel.variables loop
3755
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 79 times.
111 if Variable.isFlow(var) then
3756 32 c := ComponentRef.rest(var.name);
3757
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 if not ComponentRef.isEmpty(c) then
3758 32 vertexOf(c, ElementSource.createElementSource(var.info), ctx);
3759 end if;
3760 end if;
3761 end for;
3762
3763
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12 times.
32 for eq in conns loop
3764 20 edges := collectEdges(eq, {}, {}, ctx, edges);
3765 end for;
3766 12 edges := listReverseInPlace(edges);
3767
3768 12 (sets, facts) := connectionSets(ctx.vertices, edges);
3769
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if Flags.isSet(Flags.DUMP_SET_BASED_GRAPHS) then
3770 ✗ dumpSets(sets, ctx.vertices, facts);
3771 end if;
3772
3773 12 (potentials, flows) := connectorVariables(flatModel.variables, ctx.vertices, ctx.vertexIndex);
3774 12 flatModel.equations := listAppend(eql, generateEquations(sets, ctx.vertices, potentials, flows, ctx.params, facts));
3775 end resolve;
3776
3777 annotation(__OpenModelica_Interface="nf_frontend");
3778 end NFResizableConnections;
3779