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 |