Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 49.1% 286 / 0 / 583
Functions: -% 0 / 1 / 1
Branches: 38.0% 212 / 0 / 558

OMCompiler/Compiler/NBackEnd/Util/NBSlice.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 uniontype NBSlice<T>
37 " file: NBSlice.mo
38 package: NBSlice
39 description: This file contains util functions for slicing operations.
40 "
41
42 protected
43 import Slice = NBSlice;
44
45 // NF imports
46 import Call = NFCall;
47 import ComplexType = NFComplexType;
48 import ComponentRef = NFComponentRef;
49 import Dimension = NFDimension;
50 import Expression = NFExpression;
51 import Operator = NFOperator;
52 import SimplifyExp = NFSimplifyExp;
53 import Subscript = NFSubscript;
54 import Type = NFType;
55 import NFPrefixes.Variability;
56 import Variable = NFVariable;
57
58 // NB imports
59 import NBAdjacency.{IntMatrix, Mapping, Mode, ModeTable, Modes, Dependency};
60 import BackendUtil = NBBackendUtil;
61 import NBEquation.{Equation, Iterator, Frame, FrameLocation, RecollectStatus, FrameOrderingStatus};
62 import Replacements = NBReplacements;
63 import BVariable = NBVariable;
64 import NBVariable.VariablePointers;
65
66 // Util imports
67 import List;
68 import UnorderedMap;
69
70 public
71 type IntLst = list<Integer>;
72
73 record SLICE
74 T t;
75 IntLst indices;
76 end SLICE;
77
78 // ############################################################
79 // Member Functions
80 // ############################################################
81
82 function getT
83 input Slice<T> slice;
84 output T t = slice.t;
85 end getT;
86
87 function hash
88 input Slice<T> slice;
89 input hashT func;
90 output Integer h = func(slice.t);
91 algorithm
92 // first index should be unique enough
93
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 6457 times.
6535 for i in List.firstOrEmpty(slice.indices) loop
94 78 h := stringHashDjb2Continue(intString(i), h);
95 end for;
96 end hash;
97
98 function isEqual
99 input Slice<T> slice1;
100 input Slice<T> slice2;
101 input isEqualT func;
102 output Boolean b = func(slice1.t, slice2.t) and List.isEqualOnTrue(slice1.indices, slice2.indices, intEq);
103 end isEqual;
104
105 function toString
106 input Slice<T> slice;
107 input toStringT func;
108 input Integer maxLength = 10;
109 output String str;
110 algorithm
111
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 27 times.
48 str := func(slice.t);
112
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 if maxLength > 0 and not listEmpty(slice.indices) then
113 ✗ str := str + "\n\tslice: " + List.toStringCustom(inList = slice.indices, inPrintFunc = intString, maxLength = maxLength);
114 end if;
115 end toString;
116
117 function lstToString
118 input list<Slice<T>> lst;
119 input toStringT_ func;
120 input String indent = "";
121 input Integer maxLength = 10;
122 partial function toStringT_ = toStringT "ugly hack to make type T known to subfunction";
123 output String str = List.toStringCustom(lst, function toString(func = func, maxLength = maxLength), "", indent, "\n" + indent, "", false);
124 end lstToString;
125
126 function isFull
127 input Slice<T> slice;
128 output Boolean b = listEmpty(slice.indices);
129 end isFull;
130
131 function size
132 input Slice<T> slice;
133 input sizeT func;
134 output Integer s;
135 algorithm
136
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 302 times.
422 if listEmpty(slice.indices) then
137
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 s := func(slice.t);
138 else
139 302 s := listLength(slice.indices);
140 end if;
141 end size;
142
143 function simplify
144 "only to be used for unordered purposes!
145 lists of all indices are meaningful if they are not in the natural ascending order
146 and can indicate range reversal in for loops."
147 input output Slice<T> slice;
148 input sizeT func;
149 algorithm
150
3/4
✓ Branch 1 taken 8234 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8160 times.
✓ Branch 6 taken 74 times.
8234 if listLength(slice.indices) == func(slice.t) then
151 8160 slice.indices := {};
152 else
153 74 slice.indices := List.sort(slice.indices, intGt);
154 end if;
155 end simplify;
156
157 function addToSliceMap
158 input T t;
159 input Integer i;
160 input UnorderedMap<T, IntLst> map;
161 algorithm
162 37576 UnorderedMap.add(t, i :: UnorderedMap.getOrDefault(t, map, {}), map);
163 end addToSliceMap;
164
165 function fromTpl
166 input tuple<T, IntLst> tpl;
167 output Slice<T> slice;
168 protected
169 T t;
170 IntLst lst;
171 algorithm
172 8234 (t, lst) := tpl;
173 8234 slice := SLICE(t, lst);
174 end fromTpl;
175
176 function fromMap
177 input UnorderedMap<T, IntLst> map;
178 output list<Slice<T>> slices = list(fromTpl(tpl) for tpl in UnorderedMap.toList(map));
179 end fromMap;
180
181 function apply
182 input output Slice<T> slice;
183 input applyT func;
184 algorithm
185
1/2
✓ Branch 0 taken 1849 times.
✗ Branch 1 not taken.
1849 slice.t := func(slice.t);
186 end apply;
187
188 function applyMutable
189 input Slice<T> slice;
190 input applyMutableT func;
191 algorithm
192
1/2
✓ Branch 0 taken 276 times.
✗ Branch 1 not taken.
276 func(slice.t);
193 end applyMutable;
194
195 function check<T2>
196 input Slice<T> slice;
197 input checkT func;
198 output T2 t2 = func(slice.t);
199 end check;
200
201 // ############################################################
202 // Partial Functions
203 // ############################################################
204
205 partial function toStringT
206 input T t;
207 output String str;
208 end toStringT;
209
210 partial function sizeT
211 input T t;
212 output Integer s;
213 end sizeT;
214
215 partial function hashT
216 input T t;
217 output Integer i;
218 end hashT;
219
220 partial function isEqualT
221 input T t1;
222 input T t2;
223 output Boolean b;
224 end isEqualT;
225
226 partial function applyT
227 input output T t;
228 end applyT;
229
230 partial function applyMutableT
231 input T t;
232 end applyMutableT;
233
234 partial function checkT<T2>
235 input T t;
236 output T2 t2;
237 end checkT;
238
239 partial function filterCref
240 "partial function that needs to be provided.
241 decides if the the cref is added to the list pointer."
242 input output ComponentRef cref;
243 input UnorderedSet<ComponentRef> acc;
244 end filterCref;
245
246 partial function getDependentCrefIndices
247 input list<ComponentRef> dependencies "dependent var crefs";
248 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
249 input Mapping mapping "array <-> scalar index mapping";
250 input Integer eqn_arr_idx;
251 output array<list<Integer>> indices;
252 output array<array<Integer>> mode_to_var;
253 end getDependentCrefIndices;
254
255 // ############################################################
256 // cref accumulation Functions
257 // use with:
258 // Equation.collectCrefs()
259 // filterExp()
260 // ############################################################
261
262 function filterExp
263 "wrapper function that applies filter cref to
264 a cref expression."
265 input output Expression exp;
266 input filterCref filter;
267 input UnorderedSet<ComponentRef> acc;
268 algorithm
269 () := match exp
270 local
271 Expression call_exp;
272 Call call;
273
274
2/2
✓ Branch 0 taken 26939 times.
✓ Branch 1 taken 507 times.
27446 case Expression.CREF() algorithm filter(exp.cref, acc); then ();
275 case Expression.CALL(call = call as Call.TYPED_REDUCTION(exp = call_exp)) algorithm
276
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 for iter in call.iters loop
277 9 call_exp := Expression.replaceIterator(call_exp, Util.tuple21(iter), Util.tuple22(iter));
278 end for;
279 9 Expression.mapShallow(call_exp, function filterExp(filter = filter, acc = acc));
280 then ();
281
282 else algorithm
283 31958 Expression.mapShallow(exp, function filterExp(filter = filter, acc = acc));
284 then ();
285 end match;
286 end filterExp;
287
288 function getContinuous
289 extends filterCref;
290 input Boolean init;
291 algorithm
292
3/4
✓ Branch 0 taken 189 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 183 times.
378 if BVariable.checkCref(cref, function BVariable.isContinuous(staticAsContinuous = init), sourceInfo()) then
293 183 UnorderedSet.add(cref, acc);
294 end if;
295 end getContinuous;
296
297 function getSliceCandidates
298 "Used to collect all slices of a certain variable name.
299 Note: the name has to be stripped of all subscripts for this to work."
300 extends filterCref;
301 input ComponentRef name "the name of the variable";
302 protected
303 ComponentRef checkCref = ComponentRef.stripSubscriptsAll(cref);
304 algorithm
305 // check if the cref, the stripped version or the record parent are equal
306
5/6
✓ Branch 1 taken 23017 times.
✓ Branch 2 taken 2748 times.
✓ Branch 4 taken 21903 times.
✓ Branch 5 taken 1114 times.
✓ Branch 7 taken 21903 times.
✗ Branch 8 not taken.
25765 if ComponentRef.isEqual(name, checkCref) or ComponentRef.isEqual(name, cref) or ComponentRef.isEqualRecordChild(name, checkCref) then
307 3862 UnorderedSet.add(cref, acc);
308 end if;
309 end getSliceCandidates;
310
311 function resolveSlicedCref
312 "finds the cref referencing base_cref's variable inside eqn whose subscripted size
313 matches target_size, e.g. picking i_s[{1, 2}] (size 2) out from a co-occurring whole
314 reference i_s (size 3) in the same equation. Falls back to base_cref if no unique
315 match is found."
316 input ComponentRef base_cref "variable name, stripped of subscripts";
317 input Equation eqn;
318 input Integer target_size;
319 output ComponentRef cref = base_cref;
320 protected
321 list<ComponentRef> candidates, filtered;
322 algorithm
323 ✗ if target_size > 0 then
324 ✗ candidates := Equation.collectCrefs(eqn, function getSliceCandidates(name = base_cref));
325 ✗ filtered := list(c for c guard(Type.sizeOf(ComponentRef.getSubscriptedType(c), true) == target_size) in candidates);
326 ✗ if List.hasOneElement(filtered) then
327 ✗ cref := listHead(filtered);
328 end if;
329 end if;
330 end resolveSlicedCref;
331
332 function getDependentCref
333 "checks if crefs are relevant in the given context and collects them."
334 extends filterCref;
335 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
336 input Boolean pseudo;
337 protected
338 ComponentRef checkCref, childCref;
339 list<Pointer<Variable>> record_children;
340 algorithm
341 // if causalized in pseudo array mode, the variables will only have subscript-free variables
342
1/2
✓ Branch 0 taken 1781 times.
✗ Branch 1 not taken.
1781 checkCref := if pseudo then ComponentRef.stripSubscriptsAll(cref) else cref;
343 1781 record_children := BVariable.getRecordChildren(BVariable.getVarPointer(checkCref, sourceInfo()));
344
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1781 times.
1781 if listEmpty(record_children) then
345 // not a record
346
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1781 times.
1781 if UnorderedMap.contains(checkCref, map) then
347 ✗ UnorderedSet.add(cref, acc);
348 end if;
349 else
350 // its a record, instead parse all the children
351 ✗ for child in record_children loop
352 ✗ childCref := BVariable.getVarName(child);
353 ✗ if UnorderedMap.contains(childCref, map) then
354 ✗ UnorderedSet.add(childCref, acc);
355 end if;
356 end for;
357 end if;
358 end getDependentCref;
359
360 function getDependentCrefCausalized
361 "checks if crefs are relevant in the given context and collects them.
362 previously found crefs are replaced by their dependencies! only works on causalized systems."
363 extends filterCref;
364 input UnorderedSet<ComponentRef> set "unordered set to check for array crefs for relevance";
365 protected
366 ComponentRef checkCref, childCref;
367 list<Pointer<Variable>> record_children;
368 algorithm
369 // always remove subscripts here, this analysis is for sparsity pattern -> currently always scalarized!
370 ✗ checkCref := ComponentRef.stripSubscriptsAll(cref);
371 ✗ record_children := BVariable.getRecordChildren(BVariable.getVarPointer(checkCref, sourceInfo()));
372 ✗ if listEmpty(record_children) then
373 // not a record
374 ✗ if UnorderedSet.contains(checkCref, set) then
375 ✗ UnorderedSet.add(cref, acc);
376 end if;
377 else
378 // its a record, instead parse all the children
379 ✗ for child in record_children loop
380 ✗ childCref := BVariable.getVarName(child);
381 ✗ if UnorderedSet.contains(childCref, set) then
382 ✗ UnorderedSet.add(childCref, acc);
383 end if;
384 end for;
385 end if;
386 end getDependentCrefCausalized;
387
388 function getUnsolvableExpCrefs
389 "finds all unsolvable crefs in an expression."
390 input output Expression exp "the exp to check for unsolvable crefs";
391 input UnorderedSet<ComponentRef> acc "accumulator for relevant crefs";
392 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
393 input Boolean pseudo;
394 algorithm
395 // put all unsolvable logic here!
396 exp := match exp
397 ✗ case Expression.RANGE() then Expression.mapShallow(exp, function filterExp(filter = function getDependentCref(map = map, pseudo = pseudo), acc = acc));
398 ✗ case Expression.LBINARY() then Expression.mapShallow(exp, function filterExp(filter = function getDependentCref(map = map, pseudo = pseudo), acc = acc));
399 ✗ case Expression.RELATION() then Expression.mapShallow(exp, function filterExp(filter = function getDependentCref(map = map, pseudo = pseudo), acc = acc));
400 else exp;
401 end match;
402 end getUnsolvableExpCrefs;
403
404 function getDependentCrefIndicesPseudoScalar
405 "Scalar equations.
406 Turns cref dependencies into index lists, used for adjacency."
407 input list<ComponentRef> dependencies "dependent var crefs";
408 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
409 input Mapping mapping "array <-> scalar index mapping";
410 output list<Integer> indices = {};
411 protected
412 list<ComponentRef> scalarized_dependencies = List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
413 ComponentRef stripped;
414 Integer var_arr_idx, var_start, var_scal_idx;
415 list<Integer> sizes, int_subs;
416 algorithm
417 ✗ for cref in scalarized_dependencies loop
418 ✗ stripped := ComponentRef.stripSubscriptsAll(cref);
419 ✗ var_arr_idx := UnorderedMap.getSafe(stripped, map, sourceInfo());
420 ✗ (var_start, _) := mapping.var_AtS[var_arr_idx];
421 // the sizes of the scalarization (resized, like the mapping)
422 ✗ sizes := ComponentRef.sizes(stripped, false, true);
423 ✗ int_subs := ComponentRef.subscriptsToInteger(cref);
424 ✗ var_scal_idx := locationToIndex(sizes, int_subs, var_start);
425 indices := var_scal_idx :: indices;
426 end for;
427 // remove duplicates and sort
428 ✗ if not listEmpty(indices) then
429 ✗ indices := List.sort(List.uniqueIntN(indices, max(i for i in indices)), intLt);
430 end if;
431 end getDependentCrefIndicesPseudoScalar;
432
433 function getDependentCrefIndicesPseudoFull
434 "equations that will get full dependency.
435 Turns cref dependencies into index lists, used for adjacency."
436 extends getDependentCrefIndices;
437 protected
438 list<ComponentRef> scalarized_dependencies = List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
439 ComponentRef stripped;
440 Integer eqn_start, eqn_size, var_arr_idx, var_scal_idx, mode = 1;
441 list<Integer> scal_lst;
442 Integer idx;
443 array<Integer> mode_to_var_row;
444 list<Subscript> subs;
445 list<Dimension> dims;
446 Type ty;
447 algorithm
448 ✗ (eqn_start, eqn_size) := mapping.eqn_AtS[eqn_arr_idx];
449 ✗ indices := arrayCreate(eqn_size, {});
450 ✗ mode_to_var := arrayCreate(eqn_size, arrayCreate(0,0));
451 // create unique array for each equation
452 ✗ for i in 1:eqn_size loop
453 ✗ mode_to_var[i] := arrayCreate(listLength(scalarized_dependencies),-1);
454 end for;
455 ✗ for cref in scalarized_dependencies loop
456 ✗ stripped := ComponentRef.stripSubscriptsAll(cref);
457 ✗ var_arr_idx := UnorderedMap.getSafe(stripped, map, sourceInfo());
458
459 // build range in reverse, it will be flipped anyway
460 ✗ subs := ComponentRef.subscriptsAllWithWholeFlat(cref);
461 ✗ ty := ComponentRef.getSubscriptedType(stripped, true);
462 ✗ dims := Type.arrayDims(ty);
463 ✗ scal_lst := Mapping.getVarScalIndices(var_arr_idx, mapping, subs, dims, true);
464
465 ✗ if intMod(eqn_size, listLength(scal_lst)) <> 0 then
466 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
467 + " failed because flattened indices " + intString(listLength(scal_lst))
468 + " could not be repeated to fit equation size " + intString(eqn_size) + ". lst: " + List.toString(scal_lst, intString)});
469 ✗ fail();
470 else
471 // fill the equation with repeated scalar lists
472 ✗ scal_lst := List.repeat(scal_lst, intDiv(eqn_size, listLength(scal_lst)));
473 end if;
474
475 idx := 1;
476 ✗ for var_scal_idx in listReverse(scal_lst) loop
477 ✗ mode_to_var_row := mode_to_var[idx];
478 ✗ arrayUpdate(mode_to_var_row, mode, var_scal_idx);
479 ✗ arrayUpdate(mode_to_var, idx, mode_to_var_row);
480 ✗ indices[idx] := var_scal_idx :: indices[idx];
481 ✗ idx := idx + 1;
482 end for;
483 ✗ mode := mode + 1;
484 end for;
485
486 // sort
487 ✗ for i in 1:arrayLength(indices) loop
488 ✗ indices[i] := List.sort(UnorderedSet.unique_list(indices[i], Util.id, intEq), intLt);
489 end for;
490 end getDependentCrefIndicesPseudoFull;
491
492 function getDependentCrefIndicesPseudoFor
493 "For-Loop equations.
494 Turns cref dependencies into index lists, used for adjacency."
495 extends getDependentCrefIndices;
496 input Iterator iter "iterator frames";
497 protected
498 list<ComponentRef> names;
499 list<Expression> ranges;
500 list<Option<Iterator>> maps;
501 list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
502 Integer eqn_size, iter_size, body_size, mode = 1;
503 updateDependencies func;
504 algorithm
505 // get iterator size and frames
506 ✗ iter_size := Iterator.size(iter);
507 ✗ (names, ranges, maps) := Iterator.getFrames(iter);
508 ✗ frames := List.zip3(names, ranges, maps);
509
510 // get eqn size and create the adjacency matrix and causalization mode arrays
511 ✗ (_, eqn_size) := mapping.eqn_AtS[eqn_arr_idx];
512 ✗ indices := arrayCreate(eqn_size, {});
513 ✗ mode_to_var := arrayCreate(eqn_size, arrayCreate(0,0));
514
515 // sanity check for eqn size and get size of body equation
516 ✗ if mod(eqn_size, iter_size) == 0 then
517 ✗ body_size := intDiv(eqn_size, iter_size);
518 else
519 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
520 + " failed because the equation size " + intString(eqn_size)
521 + " could not be divided by the iterator size " + intString(iter_size) + " without rest."});
522 ✗ fail();
523 end if;
524
525 // create unique array for each equation
526 ✗ for i in 1:eqn_size loop
527 ✗ mode_to_var[i] := arrayCreate(listLength(dependencies),-1);
528 end for;
529
530 // create rows
531 ✗ for dep in dependencies loop
532 ✗ func := function updateDependenciesInteger(mode = mode, mode_to_var = mode_to_var, indices = indices);
533 // var_arr_idx not needed for this
534 ✗ fillDependencyArray(dep, body_size, frames, mapping, map, func, 0, true);
535 // increase mode index
536 ✗ mode := mode + 1;
537 end for;
538
539 // sort (kabdelhak: is this needed? try to FixMe)
540 ✗ for i in 1:arrayLength(indices) loop
541 ✗ indices[i] := List.sort(UnorderedSet.unique_list(indices[i], Util.id, intEq), intLt);
542 end for;
543 end getDependentCrefIndicesPseudoFor;
544
545 function getDependentCrefsPseudoForCausalized
546 "(Jacobian) For-Loop equations.
547 Turns cref dependencies into index lists, used for adjacency."
548 input ComponentRef row_cref "cref representing the current row";
549 input list<ComponentRef> dependencies "dependent var crefs";
550 input VariablePointers var_rep "scalarized variable representatives";
551 input VariablePointers eqn_rep "scalarized equation representatives";
552 input Mapping var_rep_mapping "index mapping for variable representatives";
553 input Mapping eqn_rep_mapping "index mapping for equation representatives";
554 input Iterator iter "iterator frames";
555 input Integer eqn_size "full equation size (not considering the slice)";
556 input list<Integer> slice = {} "optional slice, empty list implies full slice";
557 input Boolean implicit = false "do not compute row cref indices if implicit";
558 output list<tuple<ComponentRef, list<ComponentRef>>> tpl_lst "cref -> dependencies for each scalar cref";
559 protected
560 list<ComponentRef> names;
561 list<Expression> ranges;
562 list<Option<Iterator>> maps;
563 list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
564
565 Integer iter_size, body_size, var_arr_idx;
566 list<ComponentRef> row_crefs;
567 list<Integer> row_scal_lst;
568 list<list<Integer>> accum_row_lst = {};
569 array<list<ComponentRef>> accum_dep_arr;
570 list<list<ComponentRef>> accum_dep_lst;
571 updateDependencies func_var, func_eqn;
572 ComponentRef final_dep;
573 algorithm
574 // create the array of maximum equation size and slice afterwards
575 ✗ accum_dep_arr := arrayCreate(eqn_size, {});
576
577 // get iterator size and frames
578 ✗ iter_size := Iterator.size(iter);
579 ✗ (names, ranges, maps) := Iterator.getFrames(iter);
580 ✗ frames := List.zip3(names, ranges, maps);
581
582 // sanity check for eqn size and get size of body equation
583 ✗ if mod(eqn_size, iter_size) == 0 then
584 ✗ body_size := intDiv(eqn_size, iter_size);
585 else
586 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
587 + " failed because the equation size " + intString(eqn_size)
588 + " could not be divided by the iterator size " + intString(iter_size) + " without rest."});
589 ✗ fail();
590 end if;
591
592 // get row cref lst
593 ✗ if implicit then
594 ✗ row_crefs := ComponentRef.scalarizeSlice(row_cref, slice, false);
595 else
596 ✗ for cref in ComponentRef.scalarizeAll(row_cref, false) loop
597 ✗ row_scal_lst := getCrefInFrameIndices(cref, frames, eqn_rep_mapping, eqn_rep.map, false);
598 accum_row_lst := row_scal_lst :: accum_row_lst;
599 end for;
600 ✗ row_scal_lst := List.flatten(accum_row_lst);
601 ✗ row_scal_lst := if listEmpty(slice) or listLength(slice) > listLength(row_scal_lst) then row_scal_lst else List.getAtIndexLst(row_scal_lst, slice, true);
602 ✗ row_crefs := list(VariablePointers.varSlice(eqn_rep, i, eqn_rep_mapping.var_StA[i], eqn_rep_mapping, false) for i in row_scal_lst);
603 end if;
604
605 // prepare the functions to update dependencies
606 ✗ func_var := function updateDependenciesCref(accum_dep_arr = accum_dep_arr, vars = var_rep, mapping = var_rep_mapping, resize = false);
607 ✗ func_eqn := function updateDependenciesCref(accum_dep_arr = accum_dep_arr, vars = eqn_rep, mapping = eqn_rep_mapping, resize = false);
608
609 ✗ for dep in dependencies loop
610 ✗ if UnorderedMap.contains(dep, var_rep.map) then
611 ✗ (final_dep, var_arr_idx) := getVarArrIdx(dep, var_rep_mapping, var_rep.map);
612 ✗ fillDependencyArray(final_dep, body_size, frames, var_rep_mapping, var_rep.map, func_var, var_arr_idx, false);
613 elseif UnorderedMap.contains(dep, eqn_rep.map) then
614 ✗ (final_dep, var_arr_idx) := getVarArrIdx(dep, eqn_rep_mapping, eqn_rep.map);
615 ✗ fillDependencyArray(final_dep, body_size, frames, eqn_rep_mapping, eqn_rep.map, func_eqn, var_arr_idx, false);
616 end if;
617 end for;
618
619 ✗ accum_dep_lst := listReverse(arrayList(accum_dep_arr));
620 ✗ accum_dep_lst := if listEmpty(slice) or listLength(slice) > listLength(accum_dep_lst) then accum_dep_lst else List.getAtIndexLst(accum_dep_lst, slice, true);
621
622 ✗ tpl_lst := List.zip(row_crefs, accum_dep_lst);
623 end getDependentCrefsPseudoForCausalized;
624
625 function fillDependencyArray
626 "body function of getDependentCrefsPseudoFor and getDependentCrefsPseudoForCausalized
627 this generates all entries to jacobian or adjacency matrices for a specific dependency.
628 This dependency might be an array cref, part of a reduction or contain slices."
629 input ComponentRef dep;
630 input Integer body_size;
631 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
632 input Mapping mapping;
633 input UnorderedMap<ComponentRef, Integer> map;
634 input updateDependencies func;
635 input Integer var_arr_idx;
636 input Boolean resize;
637 protected
638 ComponentRef scal_cref;
639 Integer scal_length, body_repeat, element_repeat, eqn_idx;
640 list<Integer> scal_lst;
641 list<tuple<ComponentRef, list<Integer>>> scal_tpl_lst = {};
642 algorithm
643 // get all dependencies for each scalarized cref
644 // Note: scalarization does not remove the iterators, therefore it can still yield
645 // multiple scalar indices when evaluated along the iterator frames
646 ✗ for scal_cref in ComponentRef.scalarizeAll(dep, true) loop
647 ✗ scal_lst := getCrefInFrameIndices(scal_cref, frames, mapping, map, resize);
648 ✗ scal_tpl_lst := (scal_cref, scal_lst) :: scal_tpl_lst;
649 end for;
650
651 // check wether or not the element has to be repeated to fit the body
652 ✗ scal_length := listLength(scal_tpl_lst);
653 ✗ if mod(scal_length, body_size) == 0 then
654 // body has to be repeated
655 ✗ body_repeat := intDiv(scal_length, body_size);
656 element_repeat := 1;
657 elseif mod(body_size, scal_length) == 0 then
658 // element has to be repeated
659 body_repeat := 1;
660 ✗ element_repeat := intDiv(body_size, scal_length);
661 else
662 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
663 + " failed because number of flattened indices " + intString(scal_length)
664 + " for dependency " + ComponentRef.toString(dep)
665 + " could not be divided by or repeated to fit the body size " + intString(body_size) + " without rest."});
666 ✗ fail();
667 end if;
668
669 eqn_idx := 1;
670 // repeat the elements to fit the equation
671 ✗ for i in 1:element_repeat loop
672 ✗ for tpl in listReverse(scal_tpl_lst) loop
673 ✗ (scal_cref, scal_lst) := tpl;
674
675 // reverse the scalar index list to traverse it in the correct order
676 ✗ scal_lst := listReverse(scal_lst);
677
678 // check if body_repeat > 1 to set the causalization mode to -1 for unsolvable
679 ✗ if body_repeat > 1 then
680 // reset the counter to 1 if the body is supposed to be repeated
681 // ToDo: reductions are only tested for body equations of size 1!
682 eqn_idx := 1;
683 end if;
684
685 ✗ for var_idx in scal_lst loop
686 // we now know that there is a dependency of equation (eqn_idx) to variable (var_idx)
687 // call the function that adds this specific variable to the correct structure
688 ✗ if var_idx > 0 then
689 ✗ eqn_idx := func(eqn_idx, var_idx, var_arr_idx);
690 end if;
691 end for;
692 end for;
693 end for;
694 end fillDependencyArray;
695
696 partial function updateDependencies
697 input output Integer eqn_idx;
698 input Integer var_idx;
699 input Integer var_arr_idx;
700 end updateDependencies;
701
702 function updateDependenciesCref
703 "(jacobian) adds the variable of (var_idx) as a dependency to (eqn_idx)
704 jacobian depencies are stored as component references"
705 extends updateDependencies;
706 input array<list<ComponentRef>> accum_dep_arr;
707 input VariablePointers vars;
708 input Mapping mapping;
709 input Boolean resize;
710 algorithm
711 ✗ arrayUpdate(accum_dep_arr, eqn_idx, VariablePointers.varSlice(vars, var_idx, var_arr_idx, mapping, resize) :: accum_dep_arr[eqn_idx]);
712 ✗ eqn_idx := eqn_idx + 1;
713 end updateDependenciesCref;
714
715 function updateDependenciesInteger
716 "(adjacency) adds the variable of (var_idx) as a dependency to (eqn_idx)
717 adjacency dependencies are stored as integers
718 also updates the causalization modes"
719 extends updateDependencies;
720 input Integer mode;
721 input array<array<Integer>> mode_to_var; //mutable
722 input array<list<Integer>> indices; //mutable
723 protected
724 array<Integer> mode_to_var_row;
725 algorithm
726 // get the clean pointer to the scalar row to avoid double indexing (meta modelica jank)
727 ✗ mode_to_var_row := mode_to_var[eqn_idx];
728 // set the dependency mode for this scalar equation to the scalar variable
729 ✗ arrayUpdate(mode_to_var_row, mode, var_idx);
730 // this is the adjacency matrix row. each dependency cref
731 // will add exactly one integer to each row belonging to this for-equation
732 ✗ arrayUpdate(indices, eqn_idx, var_idx :: indices[eqn_idx]);
733 ✗ eqn_idx := eqn_idx + 1;
734 end updateDependenciesInteger;
735
736 function getDependentCrefsPseudoArrayCausalized
737 "Array equations.
738 Turns cref dependencies into index lists, used for adjacency."
739 input ComponentRef row_cref "cref representing the current row";
740 input list<ComponentRef> dependencies "dependent var crefs";
741 input list<Integer> slice = {} "optional slice, empty list means all";
742 output list<tuple<ComponentRef, list<ComponentRef>>> tpl_lst "cref -> dependencies for each scalar cref";
743 protected
744 list<ComponentRef> row_cref_scal, dependencies_resizable;
745 Integer row_size;
746 list<list<ComponentRef>> dependencies_scal;
747 Pointer<list<ComponentRef>> full_deps = Pointer.create({});
748 function fixSingleDep
749 "helper function to properly add a single dependency"
750 input Integer row_size;
751 input output list<ComponentRef> single_dep;
752 input Pointer<list<ComponentRef>> full_deps;
753 protected
754 Integer dep_size = listLength(single_dep);
755 algorithm
756 ✗ if row_size > dep_size then
757 // repeat the element until it fits
758 ✗ if intMod(row_size, dep_size) == 0 then
759 ✗ single_dep := List.repeat(single_dep, intDiv(row_size, dep_size));
760 else
761 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because dependencies of size " + intString(dep_size)
762 + " could not be repeated to fit row size " + intString(row_size) + "."});
763 ✗ fail();
764 end if;
765 elseif row_size < dep_size then
766 // assume full dependency (not entirely correct but practical)
767 ✗ Pointer.update(full_deps, listAppend(single_dep, Pointer.access(full_deps)));
768 single_dep := {};
769 end if;
770 end fixSingleDep;
771 algorithm
772 ✗ row_cref_scal := ComponentRef.scalarizeSlice(row_cref, slice, false);
773 ✗ row_size := listLength(row_cref_scal);
774
775 ✗ dependencies_resizable := list(ComponentRef.simplifySubscripts(ComponentRef.mapExp(dep, Expression.replaceResizableParameterWithOriginal)) for dep in dependencies);
776 ✗ dependencies_scal := list(ComponentRef.scalarizeSlice(dep, slice, false) for dep in dependencies_resizable);
777
778 ✗ if not listEmpty(dependencies_scal) then
779 // repeat lists that are too short to fit the equation size and collect full dependencies
780 ✗ dependencies_scal := list(fixSingleDep(row_size, d, full_deps) for d in dependencies_scal);
781 ✗ dependencies_scal := list(d for d guard(not listEmpty(d)) in dependencies_scal);
782 // transpose it such that each list now represents one row
783 ✗ if listEmpty(dependencies_scal) then
784 ✗ dependencies_scal := List.fill(Pointer.access(full_deps), row_size);
785 else
786 ✗ dependencies_scal := list(listAppend(Pointer.access(full_deps), d) for d in List.transposeList(dependencies_scal));
787 end if;
788 ✗ tpl_lst := List.zip(row_cref_scal, dependencies_scal);
789 else
790 ✗ tpl_lst := list((cref, {}) for cref in row_cref_scal);
791 end if;
792 end getDependentCrefsPseudoArrayCausalized;
793
794 function locationToIndex
795 "reverse function to indexToLocation()
796 maps a frame location to a scalar index starting from first index (one based!)"
797 input list<Integer> sizes;
798 input list<Integer> values;
799 input output Integer index;
800 protected
801 Integer factor = 1, val, siz;
802 list<Integer> val_trav = values, siz_trav = sizes;
803 algorithm
804
3/4
✓ Branch 0 taken 10419 times.
✓ Branch 1 taken 4893 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10419 times.
15312 while not (listEmpty(val_trav) or listEmpty(siz_trav)) loop
805 10419 val :: val_trav := val_trav;
806 10419 siz :: siz_trav := siz_trav;
807
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10419 times.
10419 if val > siz then
808 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because value of " + intString(val)
809 + " is too large for size " + intString(siz) + "."});
810 ✗ fail();
811 end if;
812 10419 index := index + (val - 1) * factor;
813 10419 factor := factor * siz;
814 end while;
815 end locationToIndex;
816
817 function indexToLocation
818 "reverse function to locationToIndex()
819 maps a scalar index to its frame location (zero based!)"
820 input Integer index;
821 input list<Integer> sizes;
822 output list<Integer> vals = {};
823 protected
824 Integer iterator = index;
825 Integer divisor = product(s for s in sizes);
826 algorithm
827
2/2
✓ Branch 0 taken 2975 times.
✓ Branch 1 taken 7095 times.
10070 for size in sizes loop
828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2975 times.
2975 divisor := intDiv(divisor, size);
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2975 times.
2975 vals := intDiv(iterator, divisor) :: vals;
830 iterator := mod(iterator, divisor);
831 end for;
832 end indexToLocation;
833
834 function transposeLocations
835 "transpose the location indices.
836 Before: Each inner list of indices represents a scalar equations
837 location inside all of the dimensions
838 After: Each inner array of indices represents the location of all
839 scalar equations for just one of the dimensions.
840 (still in order from Sorting)"
841 input list<list<Integer>> locations;
842 input Integer out_size;
843 output list<array<Integer>> locations_transposed;
844 protected
845 array<list<Integer>> lT_tmp = arrayCreate(out_size, {});
846 array<array<Integer>> lT_tmp2 = arrayCreate(out_size, arrayCreate(0,0));
847 Integer idx;
848 algorithm
849 ✗ for location in locations loop
850 idx := 1;
851 ✗ for i in location loop
852 ✗ lT_tmp[idx] := i :: lT_tmp[idx];
853 ✗ idx := idx + 1;
854 end for;
855 end for;
856 ✗ for j in 1:arrayLength(lT_tmp) loop
857 ✗ lT_tmp2[j] := listArray(listReverse(lT_tmp[j]));
858 end for;
859 ✗ locations_transposed := listReverse(arrayList(lT_tmp2));
860 end transposeLocations;
861
862 function orderTransposedFrameLocations
863 "order the frame locations by ascending inertia.
864 (the longer the chain of equal values at the start, the higher the inertia)
865 This is done to perform necessary reordering of nested for-loops"
866 input output list<FrameLocation> frame_locations_transposed;
867 output UnorderedMap<ComponentRef, Expression> replacements = UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
868 output FrameOrderingStatus status;
869 protected
870 list<tuple<Integer, FrameLocation>> frame_inertia_lst;
871 algorithm
872 // get inertia for each frame
873 ✗ frame_inertia_lst := list((frameLocationInertia(frame), frame) for frame in frame_locations_transposed);
874 // sort by inertia (ascending)
875 ✗ frame_inertia_lst := List.sort(frame_inertia_lst, Util.compareTupleIntGt);
876 // resolve equal inertia (diagonal slices)
877 ✗ (frame_inertia_lst, status) := resolveEqualInertia(frame_inertia_lst, replacements);
878 ✗ frame_locations_transposed := list(Util.tuple22(frame_inertia) for frame_inertia in frame_inertia_lst);
879 end orderTransposedFrameLocations;
880
881 protected function frameLocationInertia
882 "the longer the chain of equal values at the start, the higher the inertia"
883 input FrameLocation frameLocation;
884 output Integer inertia = 1;
885 protected
886 array<Integer> dim;
887 algorithm
888 ✗ dim := Util.tuple21(frameLocation);
889 ✗ while inertia < arrayLength(dim) and dim[inertia] == dim[inertia+1] loop
890 inertia := inertia + 1;
891 end while;
892 end frameLocationInertia;
893
894 protected function resolveEqualInertia
895 "Squashing all equal inertia frames (nested loops) into one.
896 Equal inertia for frames shows that they 'fire' at the same time.
897 These frames have to change in one step, therefore they should be merged to
898 a single one."
899 input list<tuple<Integer, FrameLocation>> frame_inertia_lst;
900 input UnorderedMap<ComponentRef, Expression> replacements;
901 output list<tuple<Integer, FrameLocation>> resolved = {};
902 output FrameOrderingStatus status = NBEquation.FrameOrderingStatus.UNCHANGED;
903 protected
904 tuple<Integer, FrameLocation> tpl1, tpl2;
905 list<tuple<Integer, FrameLocation>> rest;
906 algorithm
907 ✗ tpl1 :: rest := frame_inertia_lst;
908 ✗ while not listEmpty(rest) loop
909 ✗ tpl2 :: rest := rest;
910 tpl1 := match (tpl1, tpl2)
911 local
912 Integer inertia1, inertia2, m, b;
913 array<Integer> loc1, loc2;
914 ComponentRef name1, name2;
915 Operator addOp, mulOp;
916 Expression linMap;
917
918 // equal inertia, combine the frames
919 case ((inertia1, (loc1, (name1, _, _))), (inertia2, (loc2, (name2, _, _)))) guard(inertia1 == inertia2) algorithm
920 ✗ addOp := Operator.fromClassification((NFOperator.MathClassification.ADDITION, NFOperator.SizeClassification.SCALAR), Type.INTEGER());
921 ✗ mulOp := Operator.fromClassification((NFOperator.MathClassification.MULTIPLICATION, NFOperator.SizeClassification.SCALAR), Type.INTEGER());
922 ✗ if arrayLength(loc1) <> arrayLength(loc2) then
923 status := NBEquation.FrameOrderingStatus.FAILURE;
924 ✗ return;
925 elseif arrayLength(loc1) == 1 then
926 ✗ b := loc2[1] - loc1[1];
927 ✗ linMap := Expression.fromCref(name1);
928 ✗ if b <> 0 then
929 ✗ linMap := Expression.MULTARY({Expression.INTEGER(b), linMap}, {}, addOp);
930 end if;
931 ✗ UnorderedMap.add(name2, linMap, replacements);
932 status := NBEquation.FrameOrderingStatus.CHANGED;
933 else
934 // compute linear map from frame1 to frame2 (y = m*x + b)
935 // ToDo: integer to real conversion might be wrong?
936 ✗ m := realInt((loc2[1]-loc2[1+inertia2])/(loc1[1]-loc1[1+inertia1]));
937 ✗ b := loc2[1]-m*loc1[1];
938 // check if linear map holds
939 ✗ for i in 2:arrayLength(loc1) loop
940 ✗ if loc2[i] <> m*loc1[i] + b then
941 status := NBEquation.FrameOrderingStatus.FAILURE;
942 ✗ return;
943 end if;
944 end for;
945 ✗ linMap := Expression.fromCref(name1);
946 ✗ if m <> 1 then
947 ✗ linMap := Expression.MULTARY({Expression.INTEGER(m), linMap}, {}, mulOp);
948 end if;
949 ✗ if b <> 0 then
950 ✗ linMap := Expression.MULTARY({Expression.INTEGER(b), linMap}, {}, addOp);
951 end if;
952 ✗ UnorderedMap.add(name2, linMap, replacements);
953 status := NBEquation.FrameOrderingStatus.CHANGED;
954 end if;
955 then tpl1;
956
957 // different inertia
958 else algorithm
959 resolved := tpl1 :: resolved;
960 then tpl2;
961 end match;
962 end while;
963 ✗ resolved := listReverse(tpl1 :: resolved);
964 end resolveEqualInertia;
965
966 public function recollectRangesHeuristic
967 "consecutively builds up the new frames from frame locations.
968 Assumes that slicing along the dimensions is possible.
969 Basic Idea:
970 1. iterate over each frame location
971 2. take first (start) and second (stop) element of frame dim to start the search for a pattern (step = stop - start)
972 3. shift the stop location further until the step changes and safe the start-step-stop pattern
973 3.1 iterate over the rest of the dim and check if the pattern holds for all of it
974 3.2 if it not holds search a missing diagonal for this dimension (reconstruct diagonal)
975 4. increase the shift for the length of the previous pattern and go to next frame location (shifting happens inherently in step 3)"
976 input list<FrameLocation> frame_locations_transposed;
977 output list<Frame> frames = {};
978 output Option<UnorderedMap<ComponentRef, Expression>> removed_diagonal = NONE();
979 output RecollectStatus status;
980 protected
981 array<Integer> dim;
982 Frame frame;
983 Integer check_shift, pre_shift, shift = 1;
984 Integer start, step, stop, max_size, new_step, new_stop, check_stop;
985 Boolean fail_;
986 list<Integer> starts = {}, stops = {}, steps = {}, shifts = {};
987 list<Boolean> failed = {};
988 Integer min_dim, max_dim;
989 list<FrameLocation> diagonal;
990 UnorderedMap<ComponentRef, Expression> replacements;
991 FrameOrderingStatus fos;
992 algorithm
993 ✗ for tpl in frame_locations_transposed loop
994 // 1. iterate over each frame location
995 fail_ := false;
996 ✗ (dim, frame) := tpl;
997 pre_shift := shift;
998 max_size := arrayLength(dim);
999 ✗ if max_size == 1 then
1000 // if there is only one frame, it is a single equation at that exact point
1001 ✗ frames := applyNewFrameRange(frame, (dim[1], 1, dim[1])) :: frames;
1002 starts := dim[1] :: starts;
1003 steps := 0 :: steps;
1004 stops := dim[1] :: stops;
1005 shifts := shift :: shifts;
1006 else
1007 // 2. take first (start) and second (stop) element of frame dim to start the search for a pattern (step = stop - start)
1008 ✗ start := dim[1];
1009 ✗ stop := dim[1 + shift];
1010 ✗ step := stop - start;
1011 ✗ if step == 0 then
1012 // if the step size is zero, this range only has a single entry
1013 // this should not happen?
1014 ✗ frames := applyNewFrameRange(frame, (start, 1, stop)) :: frames;
1015 starts := start :: starts;
1016 steps := step :: steps;
1017 stops := stop :: stops;
1018 shifts := shift :: shifts;
1019 else
1020 // 3. shift the stop location further until the step changes and safe the start-step-stop pattern
1021 new_step := step;
1022 new_stop := stop;
1023 ✗ while (new_step == step) and (shift + pre_shift < max_size) loop
1024 stop := new_stop;
1025 shift := shift + pre_shift;
1026 ✗ new_stop := dim[1 + shift];
1027 ✗ new_step := new_stop - stop;
1028 end while;
1029 ✗ if new_step == step then
1030 // if new_step and step are still equal we hit the end (max_size)
1031 stop := new_stop;
1032 ✗ shift := shift + pre_shift; //not necessary but more correct
1033 else
1034 // 3.1 iterate over the rest of the dim and check if the pattern holds for all of it
1035 check_shift := shift;
1036 ✗ while (check_shift + pre_shift < max_size) loop
1037 new_step := step;
1038 ✗ while (new_step == step) and (check_shift + pre_shift < max_size) loop
1039 check_stop := new_stop;
1040 check_shift := check_shift + pre_shift;
1041 ✗ new_stop := dim[1 + check_shift];
1042 ✗ new_step := new_stop - check_stop;
1043 end while;
1044 // has to be the same amount of steps after the step size changes
1045 ✗ if (check_shift + pre_shift == max_size) then
1046 check_shift := check_shift + pre_shift;
1047 end if;
1048 ✗ if not intMod(check_shift, shift) == 0 then
1049 fail_ := true;
1050 break;
1051 end if;
1052 end while;
1053 end if;
1054 // use max/min dim instead of start and stop because the start or end
1055 // could be missing (missing diagonals)
1056 ✗ min_dim := min(d for d in dim);
1057 ✗ max_dim := max(d for d in dim);
1058 ✗ if fail_ then
1059 ✗ if step > 0 then
1060 ✗ frames := applyNewFrameRange(frame, (min_dim, step, max_dim)) :: frames;
1061 else
1062 ✗ frames := applyNewFrameRange(frame, (max_dim, step, min_dim)) :: frames;
1063 end if;
1064 else
1065 ✗ frames := applyNewFrameRange(frame, (start, step, stop)) :: frames;
1066 end if;
1067 steps := step :: steps;
1068 ✗ starts := if step > 0 then min_dim :: starts else max_dim :: starts;
1069 ✗ stops := if step > 0 then max_dim :: stops else min_dim :: stops;
1070 shifts := shift :: shifts;
1071 ✗ failed := fail_ :: failed;
1072 end if;
1073 end if;
1074 end for;
1075
1076 // 3.2 if it not holds search a missing diagonal for this dimension (reconstruct diagonal)
1077 // if any dimension was not consistent, try to find a missing diagonal
1078 // it is stored in an unordered map as linear map for the indices
1079 ✗ if List.fold(failed, boolOr, false) then
1080 ✗ diagonal := reconstructDiagonal(frame_locations_transposed, listReverse(starts), listReverse(steps), listReverse(stops), listReverse(shifts), listReverse(failed));
1081 ✗ (diagonal, replacements, fos) := orderTransposedFrameLocations(diagonal);
1082 ✗ if fos == NBEquation.FrameOrderingStatus.CHANGED then
1083 ✗ removed_diagonal := SOME(replacements);
1084 status := NBEquation.RecollectStatus.SUCCESS;
1085 else
1086 // no equal inertia to resolve or unable to resolve
1087 status := NBEquation.RecollectStatus.FAILURE;
1088 end if;
1089 else
1090 status := NBEquation.RecollectStatus.SUCCESS;
1091 end if;
1092 end recollectRangesHeuristic;
1093
1094 function reconstructDiagonal
1095 "reconstructs a supposed missing diagonal if it exists.
1096 ToDo1: create multiple diagonals if missing indices are found in one go without reset"
1097 input list<FrameLocation> frame_locations_transposed;
1098 input list<Integer> starts;
1099 input list<Integer> steps;
1100 input list<Integer> stops;
1101 input list<Integer> shifts;
1102 input list<Boolean> failed;
1103 output list<FrameLocation> diagonal = {};
1104 protected
1105 Integer start, step, stop, pos, shift = 1;
1106 Boolean fail_;
1107 list<Integer> start_rest = starts, step_rest = steps, stop_rest = stops, shift_rest = shifts;
1108 list<Boolean> fail_rest = failed;
1109 array<Integer> dim;
1110 list<Integer> missing_dims;
1111 Frame frame;
1112 algorithm
1113 // ToDo: all lists have to be of equal length!
1114 // default first shift to 1
1115 ✗ for tpl in frame_locations_transposed loop
1116 // get dims and frame from tpl
1117 ✗ (dim, frame) := tpl;
1118 // take out start, step, stop, fail
1119 ✗ start :: start_rest := start_rest;
1120 ✗ step :: step_rest := step_rest;
1121 ✗ stop :: stop_rest := stop_rest;
1122 ✗ fail_ :: fail_rest := fail_rest;
1123 // initialize missing dims and pos
1124 missing_dims := {};
1125 pos := start;
1126 ✗ if fail_ then
1127 ✗ for i in 1:shift:arrayLength(dim) loop
1128 ✗ while dim[i] <> pos loop
1129 // ToDo1
1130 missing_dims := pos :: missing_dims;
1131 ✗ pos := pos + step;
1132 ✗ if (sign(step)*pos > sign(step)*stop) then
1133 break;
1134 end if;
1135 end while;
1136 ✗ if (sign(step)*(pos+step) > sign(step)*stop) then
1137 pos := start;
1138 else
1139 pos := pos + step;
1140 end if;
1141 end for;
1142 ✗ while sign(step)*pos <= sign(step)*stop loop
1143 missing_dims := pos :: missing_dims;
1144 ✗ pos := pos + step;
1145 end while;
1146 else
1147 ✗ for i in 1:shift:arrayLength(dim) loop
1148 ✗ missing_dims := dim[i] :: missing_dims;
1149 end for;
1150 end if;
1151 ✗ diagonal := (listArray(listReverse(missing_dims)), frame) :: diagonal;
1152 // take out shift from shifts
1153 ✗ shift :: shift_rest := shift_rest;
1154 end for;
1155 ✗ diagonal := listReverse(diagonal);
1156 end reconstructDiagonal;
1157
1158 function naiveSeparation
1159 input list<Integer> indices "assumed to be sorted";
1160 output list<list<Integer>> index_clusters = {};
1161 protected
1162 Integer i;
1163 list<Integer> rest, current = {};
1164 algorithm
1165 ✗ if not listEmpty(indices) then
1166 ✗ i :: rest := listReverse(indices);
1167 current := {i};
1168 ✗ for i2 in rest loop
1169 ✗ if i-i2 == 1 then
1170 current := i2 :: current;
1171 else
1172 index_clusters := current :: index_clusters;
1173 current := {i2};
1174 end if;
1175 i := i2;
1176 end for;
1177 index_clusters := current :: index_clusters;
1178 end if;
1179 end naiveSeparation;
1180
1181 // #### KAB ### new adjacency util
1182 function upgradeRowFull
1183 "Scalar equations.
1184 Turns cref dependencies into index lists, used for adjacency."
1185 input list<ComponentRef> dependencies "dependent var crefs";
1186 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1187 input Mapping mapping "array <-> scalar index mapping";
1188 output list<Integer> indices = {};
1189 protected
1190 list<ComponentRef> scalarized_dependencies = List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
1191 ComponentRef replaced, stripped;
1192 Integer var_arr_idx, var_start, var_scal_idx;
1193 algorithm
1194
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 747 times.
980 for cref in scalarized_dependencies loop
1195 233 (replaced, stripped) := getReplacedAndStripped(cref);
1196 233 var_arr_idx := UnorderedMap.getSafe(stripped, map, sourceInfo());
1197 233 (var_start, _) := mapping.var_AtS[var_arr_idx];
1198 233 var_scal_idx := indexFromReplacedStripped(replaced, stripped, var_start, resize = true);
1199 indices := var_scal_idx :: indices;
1200 end for;
1201 end upgradeRowFull;
1202
1203 function upgradeRow
1204 "For-Loop equations.
1205 Turns cref dependencies into index lists, used for adjacency."
1206 input ComponentRef eqn_name;
1207 input Integer eqn_arr_idx;
1208 input Iterator iter "iterator frames";
1209 input Type ty;
1210 input list<ComponentRef> dependencies "dependent var crefs";
1211 input UnorderedMap<ComponentRef, Dependency> dep "dependency map";
1212 input UnorderedSet<ComponentRef> rep "repetition set";
1213 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1214 input UnorderedMap<ComponentRef, Integer> fullmap "unordered map to check for general relevance";
1215 input IntMatrix.Builder m;
1216 input Mapping mapping "array <-> scalar index mapping";
1217 input ModeTable modes;
1218 algorithm
1219
2/2
✓ Branch 0 taken 24044 times.
✓ Branch 1 taken 23441 times.
47485 for cref in dependencies loop
1220 24044 resolveDependency(cref, eqn_name, eqn_arr_idx, iter, ty, dep, rep, map, fullmap, m, mapping, modes);
1221 end for;
1222 end upgradeRow;
1223
1224 function getSingleIndex
1225 input ComponentRef cref;
1226 output Integer index;
1227 protected
1228 ComponentRef replaced, stripped;
1229 algorithm
1230 ✗ (replaced, stripped) := getReplacedAndStripped(cref);
1231 ✗ index := indexFromReplacedStripped(replaced, stripped, 0);
1232 end getSingleIndex;
1233
1234 // ############################################################
1235 // Protected Functions
1236 // ############################################################
1237
1238 protected
1239 function getReplacedAndStripped
1240 input ComponentRef cref;
1241 output ComponentRef replaced;
1242 output ComponentRef stripped;
1243 algorithm
1244 // remove all resizable parameters from cref
1245 233 replaced := ComponentRef.mapExp(cref, Expression.replaceResizableParameter);
1246 233 replaced := ComponentRef.simplifySubscripts(replaced);
1247
1248 // remove all subscripts from cref
1249 233 stripped := ComponentRef.stripSubscriptsAll(replaced);
1250 end getReplacedAndStripped;
1251
1252 function indexFromReplacedStripped
1253 input ComponentRef replaced;
1254 input ComponentRef stripped;
1255 input Integer var_start;
1256 input Boolean resize = false "the sizes of resizable dimensions as resized, like scalarizeAll(cref, true)";
1257 output Integer index;
1258 protected
1259 list<Integer> sizes, int_subs;
1260 algorithm
1261 // get the sizes and subscripts as integers and compute the final scalar index
1262 233 sizes := ComponentRef.sizes(stripped, false, resize);
1263 233 int_subs := ComponentRef.subscriptsToInteger(replaced);
1264 233 index := locationToIndex(sizes, int_subs, var_start);
1265 end indexFromReplacedStripped;
1266
1267 function resolveSkipsLst
1268 input Integer index;
1269 input Type ty;
1270 input list<list<Integer>> skips;
1271 input ComponentRef cref;
1272 input UnorderedMap<ComponentRef, Integer> fullmap "unordered map to check for general relevance";
1273 output list<tuple<Integer, Type>> skip_lst = {};
1274 protected
1275 list<list<Integer>> combinations = List.combination(skips);
1276 Integer sub_idx;
1277 Type sub_ty;
1278 algorithm
1279
2/2
✓ Branch 0 taken 611 times.
✓ Branch 1 taken 21965 times.
22576 if listEmpty(combinations) then
1280 21965 skip_lst := {(index, ty)};
1281 else
1282
2/2
✓ Branch 0 taken 926 times.
✓ Branch 1 taken 611 times.
1537 for com in combinations loop
1283 926 (sub_idx, sub_ty) := resolveSkips(index, ty, com, cref, fullmap);
1284 926 skip_lst := (sub_idx, sub_ty) :: skip_lst;
1285 end for;
1286 end if;
1287 end resolveSkipsLst;
1288
1289 function resolveSkips
1290 input output Integer index;
1291 input output Type ty;
1292 input list<Integer> skips;
1293 input ComponentRef cref;
1294 input UnorderedMap<ComponentRef, Integer> fullmap "unordered map to check for general relevance";
1295 algorithm
1296 (index, ty) := match (ty, skips)
1297 local
1298 Integer skip;
1299 list<Integer> rest, tail;
1300 list<Dimension> rest_dim, tail_dim;
1301 Type sub_ty;
1302 list<Type> rest_ty;
1303 Pointer<Variable> parent;
1304 list<ComponentRef> crefs;
1305 ComponentRef field;
1306 list<list<Subscript>> subs;
1307
1308 // 0 skips are full dependencies
1309 case (Type.TUPLE(), 0::_) then (index, ty);
1310
1311 // skip to a tuple element
1312 case (Type.TUPLE(types = rest_ty), skip::rest) guard(skip <= listLength(rest_ty)) algorithm
1313 // skip to the desired sub type and shift the starting index accordingly
1314
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for i in 1:skip-1 loop
1315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 sub_ty :: rest_ty := rest_ty;
1316 2 index := index + Type.sizeOf(sub_ty);
1317 end for;
1318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 sub_ty :: rest_ty := rest_ty;
1319 // see if there is nested skips
1320 927 then resolveSkips(index, sub_ty, rest, cref, fullmap);
1321
1322 // skip to a record element
1323 case (Type.COMPLEX(complexTy = ComplexType.RECORD()), skip::rest) algorithm
1324 // get the children and skip to correct one
1325 field := match BVariable.getParent(BVariable.getVarPointer(cref, sourceInfo()))
1326 case SOME(parent) algorithm
1327 314 subs := ComponentRef.subscriptsAll(cref);
1328 // the skip counts all fields, but only the relevant ones (e.g. not removed aliases) have an index
1329
4/4
✓ Branch 1 taken 850 times.
✓ Branch 2 taken 314 times.
✓ Branch 3 taken 850 times.
✓ Branch 4 taken 314 times.
1164 crefs := list(BVariable.getVarName(child) for child in BVariable.getRecordChildren(parent));
1330
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 if skip <= listLength(crefs) then
1331
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 134 times.
576 for i in 1:skip-1 loop
1332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 262 times.
262 field :: crefs := crefs;
1333
2/2
✓ Branch 1 taken 250 times.
✓ Branch 2 taken 12 times.
262 if UnorderedMap.contains(field, fullmap) then
1334 250 field := ComponentRef.setSubscriptsList(subs, field);
1335 250 index := index + Type.sizeOf(ComponentRef.getSubscriptedType(field));
1336 end if;
1337 end for;
1338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 314 times.
314 field :: crefs := crefs;
1339 314 field := ComponentRef.setSubscriptsList(subs, field);
1340 else
1341 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because skip of " + intString(skip)
1342 + " is too large for record elements " + List.toString(crefs, ComponentRef.toString) + "."});
1343 ✗ fail();
1344 end if;
1345 then field;
1346 else algorithm
1347 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because skip of " + intString(skip)
1348 + " for type " + Type.toString(ty) + " is requested, but the cref is not part of a record: " + ComponentRef.toString(cref) + "."});
1349 ✗ then fail();
1350 end match;
1351 // see if there is nested skips
1352 314 then resolveSkips(index, ComponentRef.getSubscriptedType(field), rest, cref, fullmap);
1353
1354 // size one arrays do not have a skip associated
1355 case (Type.ARRAY(), rest) guard(Dimension.sizesProduct(ty.dimensions, true) == 1) algorithm
1356 1 then resolveSkips(index, ty.elementType, rest, cref, fullmap);
1357
1358 // skip to an array element with more or equal skips to dimensions
1359 case (Type.ARRAY(), rest) guard List.compareLength(rest, ty.dimensions) >= 0 algorithm
1360 608 (rest, tail) := List.split(rest, listLength(ty.dimensions));
1361 // locationToIndex expects the innermost dimension first
1362
4/4
✓ Branch 0 taken 608 times.
✓ Branch 1 taken 608 times.
✓ Branch 2 taken 608 times.
✓ Branch 3 taken 608 times.
1216 index := locationToIndex(listReverse(list(Dimension.size(dim, true) for dim in ty.dimensions)), listReverse(rest), index);
1363 608 then resolveSkips(index, ty.elementType, tail, cref, fullmap);
1364
1365 // skip to an array with less skips then dimensions
1366 case (Type.ARRAY(), rest) algorithm
1367 83 (rest_dim, tail_dim) := List.split(ty.dimensions, listLength(rest));
1368 // offset by the skipped position times the size of the remaining dimensions (may be zero)
1369
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 83 times.
83 index := index + (locationToIndex(listReverse(list(Dimension.size(dim, true) for dim in rest_dim)), listReverse(rest), 1) - 1)
1370 * Dimension.sizesProduct(tail_dim, true);
1371 83 ty.dimensions := tail_dim;
1372 then (index, ty);
1373
1374 // skip for tuple or array, but the skip is too large
1375 case (_, skip::_) guard(Type.isTuple(ty) or Type.isArray(ty)) algorithm
1376 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because skip of " + intString(skip)
1377 + " for type " + Type.toString(ty) + " is too large."});
1378 ✗ then fail();
1379
1380 // there is no skip but there is a tuple (no-skip array is fine)
1381 case (Type.TUPLE(), {}) algorithm
1382 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because there is no skip for type "
1383 + Type.toString(ty)});
1384 ✗ then fail();
1385
1386 // invalid skip
1387 // skipping to the first element of any type that is not array, tuple or complex is technically allowed but does not do anything
1388 case (_, skip::_) guard(skip <> 1) algorithm
1389 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because skip of " + intString(skip)
1390 + " for type " + Type.toString(ty) + " is invalid."});
1391 ✗ then fail();
1392
1393 else (index, ty);
1394 end match;
1395 end resolveSkips;
1396
1397 // intermediate types and functions for resolveDependency()
1398 type Key = list<Integer>;
1399 type Val1 = list<ComponentRef>;
1400 type Val2 = list<Integer>;
1401
1402 function keyString
1403 input Key key;
1404 output String str = List.toString(key, intString);
1405 end keyString;
1406
1407 function keyHash
1408 input Key key;
1409 output Integer hash = Util.HASH_SEED;
1410 algorithm
1411
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 136 times.
408 for k in key loop
1412 272 hash := stringHashDjb2Continue(intString(k), hash);
1413 end for;
1414 end keyHash;
1415
1416 function keyEqual
1417 input Key key1;
1418 input Key key2;
1419 output Boolean b = List.isEqualOnTrue(key1, key2, intEq);
1420 end keyEqual;
1421
1422 function val1String
1423 input list<ComponentRef> val;
1424 output String str = List.toString(val, ComponentRef.toString);
1425 end val1String;
1426
1427 function resolveDependency
1428 "resolves the dependency of a component reference in an equation.
1429 I. Resolve skip dimensions (Tuples, Records, Array Constructors)
1430 II. Resolve regular vs. reduced dimensions"
1431 input ComponentRef original_cref;
1432 input ComponentRef eqn_name;
1433 input Integer eqn_arr_idx;
1434 input Iterator iter;
1435 input Type ty;
1436 input UnorderedMap<ComponentRef, Dependency> dep "dependency map";
1437 input UnorderedSet<ComponentRef> rep "repetition set";
1438 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1439 input UnorderedMap<ComponentRef, Integer> fullmap "unordered map to check for general relevance";
1440 input IntMatrix.Builder m;
1441 input Mapping mapping "array <-> scalar index mapping";
1442 input ModeTable modes;
1443 protected
1444 Dependency d;
1445 list<tuple<Integer, Type>> skip_lst;
1446 Type skip_ty;
1447 Integer skip_idx, start, size, body_size, iter_size;
1448 list<ComponentRef> names;
1449 list<Expression> ranges;
1450 list<Option<Iterator>> maps;
1451 list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1452 list<Boolean> regulars;
1453 ComponentRef cref;
1454 algorithm
1455 try
1456 // remove resizable parameters for index lookup
1457 24044 cref := ComponentRef.mapExp(original_cref, Expression.replaceResizableParameter);
1458 24044 cref := ComponentRef.simplifySubscripts(cref);
1459
1460 // I. resolve the skips
1461 24044 d := UnorderedMap.getSafe(original_cref, dep, sourceInfo());
1462 24044 (start, _) := mapping.eqn_AtS[eqn_arr_idx];
1463
2/2
✓ Branch 1 taken 22576 times.
✓ Branch 2 taken 1468 times.
24044 if not UnorderedSet.contains(cref, rep) then
1464 22576 skip_lst := resolveSkipsLst(start, ty, arrayList(d.skips), cref, fullmap);
1465 else
1466 1468 skip_lst := {(start, ty)};
1467 end if;
1468
1469
2/2
✓ Branch 0 taken 24359 times.
✓ Branch 1 taken 24044 times.
48403 for tpl in skip_lst loop
1470 24359 (skip_idx, skip_ty) := tpl;
1471 // get equation and iterator sizes and frames
1472 24359 body_size := Type.sizeOf(skip_ty, true);
1473 24359 iter_size := Iterator.size(iter, true);
1474 24359 size := body_size * iter_size;
1475 24359 (names, ranges, maps) := Iterator.getFrames(iter);
1476 24359 frames := List.zip3(names, ranges, maps);
1477
1478 // II. check for regular vs. reduced dimensions
1479 24359 regulars := Dependency.toBoolean(d);
1480
2/2
✓ Branch 1 taken 24038 times.
✓ Branch 2 taken 321 times.
24359 if List.all(regulars, Util.id) then
1481 // II.1 all regular - single dependency per row.
1482 // The rows of a for-equation are ordered by iteration, then by the elements of its body. If
1483 // the dependency is only an element of the body (e.g. one output of a tuple) its rows are not
1484 // contiguous but a whole body apart.
1485 24038 resolveAllRegular(cref, original_cref, eqn_name, skip_idx, size, iter_size, frames, rep, map, m, mapping, modes, Type.sizeOf(ty, true));
1486 elseif List.any(regulars, Util.id) then
1487 // II.2 mixed regularity - find all necessary configurations and add them to a map with a proper key
1488 12 resolveMixed(cref, original_cref, eqn_name, skip_idx, ty, frames, iter_size, regulars, map, m, mapping, modes);
1489 else
1490 // II.3 all reduced - full dependency per row. scalarize and add to all rows of the equation
1491 309 resolveAllReduced(cref, original_cref, eqn_name, skip_idx, size, iter_size, frames, rep, map, m, mapping, modes);
1492 end if;
1493
1494 end for;
1495 else
1496 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + ComponentRef.toString(original_cref) + "."});
1497 ✗ fail();
1498 end try;
1499 end resolveDependency;
1500
1501 function resolveAllRegular
1502 "II.1 all regular - single dependency per row."
1503 input ComponentRef cref;
1504 input ComponentRef original_cref;
1505 input ComponentRef eqn_name;
1506 input Integer skip_idx, size, iter_size;
1507 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1508 input UnorderedSet<ComponentRef> rep "repetition set";
1509 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1510 input IntMatrix.Builder m;
1511 input Mapping mapping "array <-> scalar index mapping";
1512 input ModeTable modes;
1513 input Integer stride = 0 "rows of a whole equation body (0: same as the dependency)";
1514 protected
1515 Integer mode;
1516 list<ComponentRef> scalarized;
1517 list<Val2> scal_indices = {};
1518 Val2 idx_lst;
1519 Integer scal_size = 0, shift, body_size = if iter_size > 0 then intDiv(size, iter_size) else size;
1520 algorithm
1521 24038 mode := Modes.add(modes, Mode.create(eqn_name, {original_cref}, false));
1522 24038 scalarized := listReverse(ComponentRef.scalarizeAll(cref, true));
1523
2/2
✓ Branch 0 taken 28653 times.
✓ Branch 1 taken 24038 times.
52691 for scal in scalarized loop
1524 28653 idx_lst := getCrefInFrameIndices(scal, frames, mapping, map, true);
1525 scal_indices := idx_lst :: scal_indices;
1526 28653 scal_size := scal_size + listLength(idx_lst);
1527 end for;
1528 24038 scal_indices := listReverse(scal_indices);
1529 // either the scalarized list has to be equal in length to the equation or it can be repeated enough times to fit
1530
7/8
✓ Branch 0 taken 24037 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 24011 times.
✓ Branch 5 taken 19 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 19 times.
✗ Branch 8 not taken.
24057 if scal_size > 0 and (size == scal_size or (UnorderedSet.contains(cref, rep) and intMod(size, scal_size) == 0)) then
1531 shift := 0;
1532
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 24030 times.
✓ Branch 3 taken 24030 times.
✗ Branch 4 not taken.
48118 for i in 1:size/scal_size loop
1533
2/2
✓ Branch 0 taken 28704 times.
✓ Branch 1 taken 24088 times.
52792 for indices in scal_indices loop
1534
2/2
✓ Branch 0 taken 45958 times.
✓ Branch 1 taken 28704 times.
74662 for scal_idx in indices loop
1535
2/2
✓ Branch 0 taken 1338 times.
✓ Branch 1 taken 44620 times.
45958 if stride > body_size and body_size > 0 then
1536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1338 times.
1338 addMatrixEntry(m, skip_idx + intDiv(shift, body_size) * stride + intMod(shift, body_size), scal_idx, mode);
1537 else
1538 44620 addMatrixEntry(m, skip_idx + shift, scal_idx, mode);
1539 end if;
1540 45958 shift := shift + 1;
1541 end for;
1542 end for;
1543 end for;
1544 elseif scal_size > 0 and scal_size < size then
1545 // partial out-of-bounds: some frame values produce invalid subscripts (e.g. $i1-1 at $i1=1).
1546 // evaluate each frame combination individually and assign deps to the correct equation row only.
1547 3 resolveAllRegularPartial(cref, original_cref, eqn_name, skip_idx, size, iter_size, frames, map, m, mapping, modes);
1548 elseif scal_size > size then
1549 // undetected full dependency caused by non-evaluable subscripts, this can happen by inlining functions
1550 // II.3 all reduced - full dependency per row. scalarize and add to all rows of the equation
1551 4 resolveAllReduced(cref, original_cref, eqn_name, skip_idx, size, iter_size, frames, rep, map, m, mapping, modes);
1552 end if;
1553 // scal_size == 0: all frame values are out-of-bounds, no real dependency — do nothing
1554 end resolveAllRegular;
1555
1556 function resolveMixed
1557 "II.2 mixed regularity - find all necessary configurations and add them to a map with a proper key"
1558 input ComponentRef cref;
1559 input ComponentRef original_cref;
1560 input ComponentRef eqn_name;
1561 input Integer skip_idx;
1562 input Type ty;
1563 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1564 input Integer iter_size;
1565 input list<Boolean> regulars;
1566 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1567 input IntMatrix.Builder m;
1568 input Mapping mapping "array <-> scalar index mapping";
1569 input ModeTable modes;
1570 protected
1571 ComponentRef stripped;
1572 list<Subscript> subs;
1573 list<Dimension> dims, eq_dims;
1574 array<Integer> key;
1575 UnorderedMap<Key, Val1> map1;
1576 array<UnorderedMap<Key, Val2>> frame_maps;
1577 list<array<Integer>> frame_indices;
1578 list<Integer> all_indices;
1579 Integer size_comp, frame_count = max(iter_size, 1), mode;
1580 Boolean per_frame;
1581 Pointer<Integer> eqn_idx_ptr;
1582 list<Boolean> eq_reg, scalars;
1583 list<tuple<Dimension, Boolean>> lst;
1584 list<tuple<Subscript, Dimension, Boolean>> var_lst;
1585 Boolean aligned;
1586 algorithm
1587 // 1. get the cref subscripts and dimensions as well as the equation dimensions (they have to match in length)
1588 12 subs := ComponentRef.subscriptsAllWithWholeFlat(cref);
1589 12 dims := Type.arrayDims(ComponentRef.getSubscriptedType(cref));
1590 12 eq_dims := Type.arrayDims(ty);
1591 12 (var_lst, scalars, aligned) := zipSubscripts(subs, dims, regulars);
1592
1593
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if aligned then
1594 // 2. create a map that maps a configuration key to the corresponding scalar crefs
1595 12 stripped := ComponentRef.stripSubscriptsAll(cref);
1596 12 key := arrayCreate(listLength(subs), 0);
1597 12 map1 := UnorderedMap.new<Val1>(keyHash, keyEqual);
1598 12 resolveReductions(var_lst, map1, key, stripped);
1599
1600 // 3. create maps that map a configuration key to the final variable indices, one map per
1601 // frame since each frame (iteration of a for-equation) has its own rows
1602
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 frame_maps := listArray(list(UnorderedMap.new<Val2>(keyHash, keyEqual) for i in 1:frame_count));
1603
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 12 times.
46 for k in UnorderedMap.keyList(map1) loop
1604
4/4
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 98 times.
✓ Branch 4 taken 34 times.
132 frame_indices := list(listArray(getCrefInFrameIndices(scal, frames, mapping, map, true))
1605 for scal in UnorderedMap.getSafe(k, map1, sourceInfo()));
1606
5/6
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 34 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 98 times.
230 per_frame := List.all(list(arrayLength(a) == frame_count for a in frame_indices), Util.id);
1607
1608
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if per_frame then
1609 34 for i in 1:frame_count loop
1610
4/4
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 34 times.
132 UnorderedMap.add(k, list(a[i] for a in frame_indices), frame_maps[i]);
1611 end for;
1612 else
1613 // indices that cannot be attributed to a frame (e.g. some frames are out of bounds) belong to all of them
1614 ✗ all_indices := List.flatten(list(arrayList(a) for a in frame_indices));
1615 ✗ for i in 1:frame_count loop
1616 ✗ UnorderedMap.add(k, all_indices, frame_maps[i]);
1617 end for;
1618 end if;
1619 end for;
1620
1621 // 4. check if equation and variable are of same length, if not: fixup the lists to be of equal length
1622 12 size_comp := List.compareLength(eq_dims, regulars);
1623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if size_comp > 0 then
1624 // bigger equation than variable
1625 ✗ eq_reg := listAppend(regulars, List.fill(false, listLength(eq_dims) - listLength(regulars)));
1626 ✗ lst := List.zip(eq_dims, eq_reg);
1627 elseif size_comp < 0 then
1628 // bigger variable than equation
1629 12 lst := resolveMixedDimensions(eq_dims, regulars);
1630 else
1631 ✗ lst := List.zip(eq_dims, regulars);
1632 end if;
1633 12 lst := insertScalarDimensions(lst, scalars);
1634
1635 // 5. iterate over all equation dimensions and use the map to get the correct dependencies,
1636 // the rows of the frames follow each other
1637 12 mode := Modes.add(modes, Mode.create(eqn_name, {original_cref}, false));
1638 12 eqn_idx_ptr := Pointer.create(skip_idx);
1639 24 for i in 1:frame_count loop
1640 12 key := arrayCreate(listLength(subs), 0);
1641 12 resolveEquationDimensions(lst, frame_maps[i], key, m, mode, eqn_idx_ptr);
1642 end for;
1643 else
1644 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because subscripts, dimensions and dependencies were not of equal length.\n"
1645 + "variable subscripts(" + intString(listLength(subs)) + "): " + List.toString(subs, Subscript.toString) + "\n"
1646 + "variable dimensions(" + intString(listLength(dims)) + "): " + List.toString(dims, Dimension.toString) + "\n"
1647 + "equation dimensions(" + intString(listLength(eq_dims)) + "): " + List.toString(eq_dims, Dimension.toString) + "\n"
1648 + "variable dependencies(" + intString(listLength(regulars)) + "): " + List.toString(regulars, boolString) + "\n"});
1649 ✗ fail();
1650 end if;
1651 end resolveMixed;
1652
1653 function zipSubscripts
1654 "Zips the subscripts of a cref with the dimensions and dependencies of its
1655 subscripted type. A scalar subscript (e.g. a[1].b[:]) has neither and gets
1656 a reduced dimension of size one, so that the key positions of the variable
1657 and the equation stay aligned (see insertScalarDimensions)."
1658 input list<Subscript> subs;
1659 input list<Dimension> dims;
1660 input list<Boolean> regulars;
1661 output list<tuple<Subscript, Dimension, Boolean>> lst = {};
1662 output list<Boolean> scalars = {};
1663 output Boolean aligned = true;
1664 protected
1665 list<Dimension> rest_dims = dims;
1666 list<Boolean> rest_regs = regulars;
1667 Dimension dim;
1668 Boolean reg;
1669 algorithm
1670
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for sub in subs loop
1671
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if Subscript.isScalar(sub) then
1672 ✗ lst := (sub, Dimension.fromInteger(1), false) :: lst;
1673 scalars := true :: scalars;
1674 elseif listEmpty(rest_dims) or listEmpty(rest_regs) then
1675 aligned := false;
1676 ✗ return;
1677 else
1678 24 dim :: rest_dims := rest_dims;
1679 24 reg :: rest_regs := rest_regs;
1680
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
36 lst := (sub, dim, reg) :: lst;
1681 scalars := false :: scalars;
1682 end if;
1683 end for;
1684
1685
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 aligned := listEmpty(rest_dims) and listEmpty(rest_regs);
1686 12 lst := listReverse(lst);
1687 12 scalars := listReverse(scalars);
1688 end zipSubscripts;
1689
1690 function insertScalarDimensions
1691 "Inserts a reduced dimension of size one into the equation dimensions for
1692 each scalar subscript of the variable."
1693 input list<tuple<Dimension, Boolean>> lst;
1694 input list<Boolean> scalars;
1695 output list<tuple<Dimension, Boolean>> outLst = {};
1696 protected
1697 list<tuple<Dimension, Boolean>> rest = lst;
1698 tuple<Dimension, Boolean> t;
1699 algorithm
1700
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for scalar in scalars loop
1701
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if scalar then
1702 ✗ outLst := (Dimension.fromInteger(1), false) :: outLst;
1703 elseif not listEmpty(rest) then
1704 24 t :: rest := rest;
1705 outLst := t :: outLst;
1706 end if;
1707 end for;
1708
1709 12 outLst := List.append_reverse(outLst, rest);
1710 end insertScalarDimensions;
1711
1712 function resolveMixedDimensions
1713 "fills the list with dimensions of size one at the proper place even if they are skipped.
1714 allows a function that resolves all configurations of variable and equation sizes.
1715 prepares for resolveEquationDimensions() if the variable is bigger than the equation"
1716 input list<Dimension> eq_dims;
1717 input list<Boolean> regulars;
1718 input output list<tuple<Dimension, Boolean>> lst = {};
1719 algorithm
1720 lst := match (eq_dims, regulars)
1721 local
1722 Dimension dim;
1723 list<Dimension> rest_dim;
1724 list<Boolean> rest_reg;
1725 // dummy dimension that will be skipped
1726 12 case (_, false :: rest_reg) then (Dimension.fromExp(Expression.INTEGER(1), Variability.CONSTANT), false) :: resolveMixedDimensions(eq_dims, rest_reg);
1727 // correct dimension that will be kept
1728 12 case (dim :: rest_dim, true :: rest_reg) then (dim, true) :: resolveMixedDimensions(rest_dim, rest_reg);
1729 // excess unfitting is ignored
1730 else {};
1731 end match;
1732 end resolveMixedDimensions;
1733
1734 function resolveAllReduced
1735 "II.3 all reduced - full dependency per row. scalarize and add to all rows of the equation"
1736 input ComponentRef cref;
1737 input ComponentRef original_cref;
1738 input ComponentRef eqn_name;
1739 input Integer skip_idx, size, iter_size;
1740 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1741 input UnorderedSet<ComponentRef> rep "repetition set";
1742 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
1743 input IntMatrix.Builder m;
1744 input Mapping mapping "array <-> scalar index mapping";
1745 input ModeTable modes;
1746 protected
1747 Boolean repeated;
1748 list<ComponentRef> scalarized;
1749 list<Val2> scal_indices = {};
1750 Integer shift;
1751 Integer mode;
1752 algorithm
1753 313 repeated := UnorderedSet.contains(cref, rep);
1754 313 scalarized := listReverse(ComponentRef.scalarizeAll(cref, true));
1755
2/2
✓ Branch 0 taken 3571 times.
✓ Branch 1 taken 313 times.
3884 for scal in scalarized loop
1756 3571 scal_indices := getCrefInFrameIndices(scal, frames, mapping, map, true) :: scal_indices;
1757 end for;
1758 313 scal_indices := listReverse(scal_indices);
1759
1760 // if its repeated, use the same cref always; otherwise use local cref
1761 313 mode := Modes.add(modes, Mode.create(eqn_name, {original_cref}, not repeated));
1762
1763
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 313 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 313 times.
980 for i in skip_idx:iter_size:skip_idx+size-iter_size loop
1764 shift := 0;
1765
2/2
✓ Branch 0 taken 5397 times.
✓ Branch 1 taken 667 times.
6064 for indices in scal_indices loop
1766
3/4
✓ Branch 0 taken 5841 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5841 times.
✓ Branch 3 taken 5397 times.
11238 for scal_idx in indices loop
1767
2/2
✓ Branch 0 taken 5401 times.
✓ Branch 1 taken 440 times.
5841 if intMod(shift, iter_size) == 0 then shift := 0; end if;
1768 5841 addMatrixEntry(m, i + shift, scal_idx, mode);
1769 5841 shift := shift + 1;
1770 end for;
1771 end for;
1772 end for;
1773 end resolveAllReduced;
1774
1775 function resolveAllRegularPartial
1776 "II.1 partial out-of-bounds variant: some frame values produce invalid (out-of-bounds) subscripts.
1777 Evaluates each frame combination individually and assigns each valid scalar index to the
1778 equation row that corresponds to that frame value only. Rows whose frame value maps to an
1779 out-of-bounds subscript receive no dependency entry."
1780 input ComponentRef cref;
1781 input ComponentRef original_cref;
1782 input ComponentRef eqn_name;
1783 input Integer skip_idx, size, iter_size;
1784 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1785 input UnorderedMap<ComponentRef, Integer> map;
1786 input IntMatrix.Builder m;
1787 input Mapping mapping;
1788 input ModeTable modes;
1789 protected
1790 Integer mode;
1791 ComponentRef final_cref;
1792 Integer var_arr_idx, var_start;
1793 list<Integer> sizes;
1794 list<Expression> subs;
1795 Integer body_size;
1796 Pointer<Integer> row;
1797 UnorderedMap<ComponentRef, Expression> replacements;
1798 algorithm
1799 3 mode := Modes.add(modes, Mode.create(eqn_name, {original_cref}, false));
1800 3 (final_cref, var_arr_idx) := getVarArrIdx(cref, mapping, map);
1801 3 (var_start, _) := mapping.var_AtS[var_arr_idx];
1802 3 sizes := ComponentRef.sizes(final_cref, false, true);
1803 3 subs := ComponentRef.subscriptsToExpression(cref, true);
1804
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 body_size := intDiv(size, iter_size);
1805 3 row := Pointer.create(skip_idx);
1806 3 replacements := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
1807 3 resolveFrames(frames, sizes, subs, var_start, replacements, true, m, mode, body_size, row);
1808 end resolveAllRegularPartial;
1809
1810 function resolveFrames
1811 "Recursively iterates over all frame combinations (one per equation row) and assigns
1812 each valid scalar index to the corresponding equation row. Advances the row counter
1813 for every frame combination regardless of whether the subscript is valid."
1814 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1815 input list<Integer> sizes;
1816 input list<Expression> subs;
1817 input Integer var_start;
1818 input UnorderedMap<ComponentRef, Expression> replacements;
1819 input Boolean resize;
1820 input IntMatrix.Builder m;
1821 input Integer mode "index into the mode table";
1822 input Integer body_size;
1823 input Pointer<Integer> row;
1824 algorithm
1825 () := match frames
1826 local
1827 ComponentRef iterator;
1828 Expression range;
1829 Option<Iterator> fmap;
1830 list<tuple<ComponentRef, Expression, Option<Iterator>>> rest;
1831 Integer start, step, stop, sub_idx, r;
1832 list<list<Integer>> values;
1833 list<Expression> iterator_exps;
1834 list<Integer> iterator_lst;
1835
1836 // leaf: all frame iterators bound — evaluate subscript and assign to current row
1837 case {} algorithm
1838 23 r := Pointer.access(row);
1839 23 values := resolveDimensionsSubscripts(sizes, subs, replacements, resize);
1840
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 23 times.
43 for v in listReverse(values) loop
1841 20 addMatrixEntry(m, r, locationToIndex(sizes, v, var_start), mode);
1842 end for;
1843 23 Pointer.update(row, r + body_size);
1844 then ();
1845
1846 // recurse over iterator range
1847 case (iterator, range, fmap) :: rest algorithm
1848 iterator_lst := match range
1849 case Expression.RANGE() algorithm
1850 3 (start, step, stop) := Expression.getIntegerRange(range, resize);
1851 3 then List.intRange3(start, step, stop);
1852 case Expression.ARRAY() algorithm
1853 ✗ iterator_exps := list(Expression.map(e, function Replacements.applySimpleExp(replacements = replacements)) for e in range.elements);
1854 ✗ iterator_lst := list(Expression.integerValue(SimplifyExp.simplifyDump(e, true, getInstanceName())) for e in iterator_exps);
1855 then iterator_lst;
1856 else algorithm
1857 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed to parse iterator range: "
1858 + ComponentRef.toString(iterator) + " in " + Expression.toString(range)});
1859 ✗ then fail();
1860 end match;
1861
1862 sub_idx := 1;
1863
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3 times.
26 for index in iterator_lst loop
1864 23 UnorderedMap.add(iterator, Expression.INTEGER(index), replacements);
1865 23 Iterator.createMappedLocationReplacement(fmap, sub_idx, replacements);
1866 23 resolveFrames(rest, sizes, subs, var_start, replacements, resize, m, mode, body_size, row);
1867 23 sub_idx := sub_idx + 1;
1868 end for;
1869 then ();
1870
1871 else algorithm
1872 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for an unknown reason."});
1873 ✗ then fail();
1874 end match;
1875 end resolveFrames;
1876
1877 function resolveEquationDimensions
1878 "a component reference in a list of equation dimensions. The second argument to the tuple
1879 is TRUE if its a regular occurence of the cref and FALSE if its a reduced occurence.
1880 The key is created from the dimensions and the additional boolean to look up the cref occurence in the map."
1881 input list<tuple<Dimension, Boolean>> lst "equation dimension and cref regularity tuple list";
1882 input UnorderedMap<Key, Val2> map "map to look up occurence";
1883 input Array<Integer> key "mutable key";
1884 input IntMatrix.Builder m "adjacency matrix builder";
1885 input Integer mode "index into the mode table";
1886 input Pointer<Integer> eqn_idx_ptr "mutable equation index";
1887 input Integer index = 1 "dimension index for the key";
1888 algorithm
1889 () := match lst
1890 local
1891 Dimension dim;
1892 list<tuple<Dimension, Boolean>> rest;
1893 Integer eqn_idx;
1894 list<Integer> scal_lst;
1895
1896 case {} algorithm
1897 // no further dimensions. resolve with current key config and bump equation index
1898 34 eqn_idx := Pointer.access(eqn_idx_ptr);
1899 34 scal_lst := UnorderedMap.getSafe(arrayList(key), map, sourceInfo());
1900
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 34 times.
132 for scal_idx in scal_lst loop
1901 98 addMatrixEntry(m, eqn_idx, scal_idx, mode);
1902 end for;
1903 34 Pointer.update(eqn_idx_ptr, eqn_idx + 1);
1904 then ();
1905
1906 case (dim, false)::rest algorithm
1907 // reduced dimension, keep key index at 0 and go deeper with next dimension
1908
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
68 for i in 1:Dimension.size(dim, true) loop
1909 34 resolveEquationDimensions(rest, map, key, m, mode, eqn_idx_ptr, index+1);
1910 end for;
1911 then ();
1912
1913 case (dim, true)::rest algorithm
1914 // regular dimension, update key index to corresponding dimension index
1915 // and go deeper with next dimension
1916
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
46 for i in 1:Dimension.size(dim, true) loop
1917 34 arrayUpdate(key, index, i);
1918 34 resolveEquationDimensions(rest, map, key, m, mode, eqn_idx_ptr, index+1);
1919 end for;
1920 then ();
1921 end match;
1922 end resolveEquationDimensions;
1923
1924 function addMatrixEntry
1925 input IntMatrix.Builder m "adjacency matrix builder";
1926 input Integer eqn_idx;
1927 input Integer var_idx;
1928 input Integer mode "index into the mode table";
1929 algorithm
1930 // only add the variable if its a viable index. due to unresolved if-expressions in for-loops some branches can access variables
1931 // that seem out of scope but are in fact valid because the if-condition ensures it.
1932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51917 times.
51917 if var_idx > 0 then
1933 51917 IntMatrix.builderAddAux(m, eqn_idx, var_idx, mode);
1934 end if;
1935 end addMatrixEntry;
1936
1937 function resolveReductions
1938 input list<tuple<Subscript, Dimension, Boolean>> lst;
1939 input UnorderedMap<Key, Val1> map;
1940 input Array<Integer> key;
1941 input ComponentRef stripped;
1942 input list<Subscript> acc = {};
1943 input Integer index = 1;
1944 algorithm
1945 () := match lst
1946 local
1947 list<tuple<Subscript, Dimension, Boolean>> rest;
1948 Subscript sub;
1949 Dimension dim;
1950 ComponentRef cref;
1951 Val1 val;
1952 Integer sub_idx;
1953
1954 case {} algorithm
1955 34 cref := ComponentRef.mergeSubscripts(listReverse(acc), stripped, true);
1956 34 val := ComponentRef.scalarizeAll(cref, true);
1957 34 UnorderedMap.add(arrayList(key), val, map);
1958 then ();
1959
1960 case (sub, _, false)::rest algorithm
1961 34 resolveReductions(rest, map, key, stripped, sub::acc, index+1);
1962 then ();
1963
1964 case (sub, dim, true)::rest algorithm
1965 sub_idx := 1;
1966
2/2
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 12 times.
46 for s in Subscript.scalarize(sub, dim, true) loop
1967 34 arrayUpdate(key, index, sub_idx);
1968 34 resolveReductions(rest, map, key, stripped, s::acc, index+1);
1969 34 sub_idx := sub_idx + 1;
1970 end for;
1971 then ();
1972
1973 end match;
1974 end resolveReductions;
1975
1976 function combineFrames2Indices
1977 "Iterates over all elements in nested iterators represented by frames.
1978 Converts each of the now integer subscript lists (in combination with
1979 subscript sizes) to a single scalar index of the subscripted cref."
1980 input Integer first "index of first variable. start counting from here";
1981 input list<Integer> sizes "list of variables sizes";
1982 input list<Expression> subs "list of cref subscripts";
1983 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames "list of frame tuples containing iterator name and range";
1984 input UnorderedMap<ComponentRef, Expression> replacements "replacement rules iterator cref -> integer (may have to be simplified)";
1985 input Boolean resize;
1986 input output list<Integer> indices = {} "list of scalarized indices";
1987 algorithm
1988 indices := match frames
1989 local
1990 list<tuple<ComponentRef, Expression, Option<Iterator>>> rest;
1991 ComponentRef iterator;
1992 Expression range;
1993 Option<Iterator> map;
1994 Integer start, step, stop;
1995 list<list<Integer>> values;
1996 list<Expression> iterator_exps;
1997 list<Integer> iterator_lst;
1998 Integer sub_idx;
1999
2000 // only occurs for non-for-loop equations (no frames to replace)
2001 case {} algorithm
2002 8 values := resolveDimensionsSubscripts(sizes, subs, replacements, resize);
2003
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 8 times.
24 then list(locationToIndex(sizes, v, first) for v in values);
2004
2005 // extract numeric information about the range
2006 case (iterator, range, map) :: rest algorithm
2007 iterator_lst := match range
2008 case Expression.RANGE() algorithm
2009 810 (start, step, stop) := Expression.getIntegerRange(range, resize);
2010 810 then List.intRange3(start,step, stop);
2011 case Expression.ARRAY() algorithm
2012
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 12 times.
96 iterator_exps := list(Expression.map(e, function Replacements.applySimpleExp(replacements = replacements)) for e in range.elements);
2013
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 12 times.
48 iterator_lst := list(Expression.integerValue(SimplifyExp.simplifyDump(e, true, getInstanceName())) for e in iterator_exps);
2014 then iterator_lst;
2015 else algorithm
2016 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because iterator binding could not be parsed: "
2017 + ComponentRef.toString(iterator) + " in " + Expression.toString(range)});
2018 ✗ then fail();
2019 end match;
2020
2021 // traverse every index in the range (and possible sub iterators)
2022 sub_idx := 1;
2023
2/2
✓ Branch 0 taken 4122 times.
✓ Branch 1 taken 822 times.
4944 for index in iterator_lst loop
2024 // create main iterator replacement
2025 4122 UnorderedMap.add(iterator, Expression.INTEGER(index), replacements);
2026 // create local sub replacements if existent
2027 4122 Iterator.createMappedLocationReplacement(map, sub_idx, replacements);
2028
2029
2/2
✓ Branch 0 taken 3936 times.
✓ Branch 1 taken 186 times.
4122 if listEmpty(rest) then
2030 // bottom line, resolve current configuration and create index for it
2031 3936 values := resolveDimensionsSubscripts(sizes, subs, replacements, resize);
2032
2/2
✓ Branch 1 taken 3933 times.
✓ Branch 2 taken 3936 times.
7869 for v in listReverse(values) loop
2033 3933 indices := locationToIndex(sizes, v, first) :: indices;
2034 end for;
2035 else
2036 // not last frame, go deeper
2037 186 indices := combineFrames2Indices(first, sizes, subs, rest, replacements, resize, indices);
2038 end if;
2039 4122 sub_idx := sub_idx + 1;
2040 end for;
2041 then indices;
2042
2043 case (iterator, range, _) :: _ algorithm
2044 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because uniontype records are wrong: "
2045 + ComponentRef.toString(iterator) + " in " + Expression.toString(range)});
2046 ✗ then fail();
2047
2048 else algorithm
2049 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for an unknown reason."});
2050 ✗ then fail();
2051
2052 end match;
2053 end combineFrames2Indices;
2054
2055 function combineFramesIndices
2056 "Turns the subscripts of a cref into scalar indices over all frame values.
2057 Tries the arithmetic evaluation first and falls back to the general
2058 expression machinery."
2059 input Integer first;
2060 input list<Integer> sizes;
2061 input list<Expression> subs;
2062 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
2063 input Boolean resize;
2064 output list<Integer> indices;
2065 protected
2066 Boolean ok;
2067 algorithm
2068 32583 (ok, indices) := combineFramesArithmetic(first, sizes, subs, frames);
2069
2/2
✓ Branch 0 taken 31939 times.
✓ Branch 1 taken 644 times.
32583 if not ok then
2070 644 indices := listReverse(combineFrames2Indices(first, sizes, subs, frames,
2071 UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual), resize));
2072 end if;
2073 end combineFramesIndices;
2074
2075 function combineFramesArithmetic
2076 "Fast path for combineFrames2Indices: every frame has to be a literal
2077 integer range without a mapped location and every subscript has to be
2078 integer arithmetic over the frame iterators. ok is false if it is not,
2079 the general path has to be used then."
2080 input Integer first;
2081 input list<Integer> sizes;
2082 input list<Expression> subs;
2083 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
2084 output Boolean ok = true;
2085 output list<Integer> indices = {};
2086 protected
2087 Integer n = listLength(frames);
2088 Integer sz = if n > 0 then n else 1;
2089 array<ComponentRef> names = arrayCreate(sz, ComponentRef.EMPTY());
2090 array<Integer> values = arrayCreate(sz, 0);
2091 array<Integer> starts = arrayCreate(sz, 0);
2092 array<Integer> steps = arrayCreate(sz, 0);
2093 array<Integer> stops = arrayCreate(sz, 0);
2094 Integer i = 1;
2095 ComponentRef name;
2096 Expression range;
2097 Option<Iterator> map;
2098 algorithm
2099 // an unsubscripted cref yields no combination at all, same as List.combination({})
2100
1/2
✓ Branch 0 taken 32583 times.
✗ Branch 1 not taken.
32583 if listEmpty(subs) then
2101 ✗ return;
2102 end if;
2103
2104
2/2
✓ Branch 0 taken 3082 times.
✓ Branch 1 taken 32081 times.
35163 for frame in frames loop
2105 3082 (name, range, map) := frame;
2106
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3082 times.
✓ Branch 2 taken 3082 times.
✗ Branch 3 not taken.
3082 if isSome(map) then
2107 ok := false;
2108 ✗ return;
2109 end if;
2110 3082 arrayUpdate(names, i, name);
2111 () := match range
2112 local
2113 Integer rstart, rstep, rstop;
2114 case Expression.RANGE(start = Expression.INTEGER(value = rstart), stop = Expression.INTEGER(value = rstop))
2115 algorithm
2116 rstep := match range.step
2117
1/2
✓ Branch 0 taken 2572 times.
✗ Branch 1 not taken.
2572 case NONE() then if rstart > rstop then -1 else 1;
2118 case SOME(Expression.INTEGER(value = rstep)) then rstep;
2119 else 0;
2120 end match;
2121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if rstep == 0 then
2122 ok := false;
2123 else
2124 2580 arrayUpdate(starts, i, rstart);
2125 2580 arrayUpdate(steps, i, rstep);
2126 2580 arrayUpdate(stops, i, rstop);
2127 end if;
2128 then ();
2129 else algorithm
2130 ok := false;
2131 then ();
2132 end match;
2133 if not ok then
2134 502 return;
2135 end if;
2136 2580 i := i + 1;
2137 end for;
2138
2139 32081 (ok, indices) := combineFramesArithmeticWork(first, sizes, subs, names, values, starts, steps, stops, 1, n, ok, indices);
2140
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 31939 times.
32081 if ok then
2141 31939 indices := listReverse(indices);
2142 end if;
2143 end combineFramesArithmetic;
2144
2145 function combineFramesArithmeticWork
2146 input Integer first;
2147 input list<Integer> sizes;
2148 input list<Expression> subs;
2149 input array<ComponentRef> names;
2150 input array<Integer> values;
2151 input array<Integer> starts;
2152 input array<Integer> steps;
2153 input array<Integer> stops;
2154 input Integer level;
2155 input Integer nframes;
2156 input output Boolean ok;
2157 input output list<Integer> indices;
2158 protected
2159 Integer v, step, stop, index;
2160 Boolean inBounds;
2161 algorithm
2162
2/2
✓ Branch 0 taken 46372 times.
✓ Branch 1 taken 3367 times.
49739 if level > nframes then
2163 46372 (ok, inBounds, index) := evalFrameIndex(subs, sizes, names, values, nframes, first);
2164
3/4
✓ Branch 0 taken 46230 times.
✓ Branch 1 taken 142 times.
✓ Branch 2 taken 46230 times.
✗ Branch 3 not taken.
46372 if ok and inBounds then
2165 46230 indices := index :: indices;
2166 end if;
2167 else
2168 3367 step := steps[level];
2169 3367 stop := stops[level];
2170 3367 v := starts[level];
2171
4/4
✓ Branch 0 taken 3240 times.
✓ Branch 1 taken 17646 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 3228 times.
20886 while (step > 0 and v <= stop) or (step < 0 and v >= stop) loop
2172 17658 arrayUpdate(values, level, v);
2173 17658 (ok, indices) := combineFramesArithmeticWork(first, sizes, subs, names, values, starts, steps, stops, level + 1, nframes, ok, indices);
2174
2/2
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 17519 times.
17658 if not ok then
2175 139 return;
2176 end if;
2177 17519 v := v + step;
2178 end while;
2179 end if;
2180 end combineFramesArithmeticWork;
2181
2182 function evalFrameIndex
2183 "Evaluates all subscripts at the current frame values and folds them into the
2184 scalar index, like locationToIndex() does for the general path. inBounds is
2185 false if one of them is outside its dimension, which is no dependency at all."
2186 input list<Expression> subs;
2187 input list<Integer> sizes;
2188 input array<ComponentRef> names;
2189 input array<Integer> values;
2190 input Integer nframes;
2191 input Integer first;
2192 output Boolean ok = true;
2193 output Boolean inBounds = true;
2194 output Integer index = first;
2195 protected
2196 list<Integer> rest_sizes = sizes;
2197 Integer size, value, factor = 1;
2198 algorithm
2199
2/2
✓ Branch 0 taken 130896 times.
✓ Branch 1 taken 46230 times.
177126 for sub in subs loop
2200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130896 times.
130896 if listEmpty(rest_sizes) then
2201 ok := false;
2202 ✗ return;
2203 end if;
2204 130896 size :: rest_sizes := rest_sizes;
2205 130896 (ok, value) := evalFrameExp(sub, names, values, nframes);
2206
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 130754 times.
130896 if not ok then
2207 142 return;
2208 end if;
2209
2/4
✓ Branch 0 taken 130754 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 130754 times.
130754 if value < 1 or value > size then
2210 inBounds := false;
2211 ✗ return;
2212 end if;
2213 130754 index := index + (value - 1) * factor;
2214 130754 factor := factor * size;
2215 end for;
2216 end evalFrameIndex;
2217
2218 function evalFrameExp
2219 "Evaluates an integer expression at the current frame values. ok is false
2220 for anything but integer literals, frame iterators and integer +, - and *."
2221 input Expression exp;
2222 input array<ComponentRef> names;
2223 input array<Integer> values;
2224 input Integer nframes;
2225 output Boolean ok;
2226 output Integer value;
2227 algorithm
2228 (ok, value) := match exp
2229 local
2230 Integer v1, v2;
2231 Boolean ok1, ok2;
2232
2233 111663 case Expression.INTEGER() then (true, exp.value);
2234
2235 case Expression.CREF() algorithm
2236 ok := false;
2237 19099 value := 0;
2238
2/2
✓ Branch 0 taken 19091 times.
✓ Branch 1 taken 8 times.
26355 for i in 1:nframes loop
2239
2/2
✓ Branch 2 taken 19091 times.
✓ Branch 3 taken 7256 times.
26347 if ComponentRef.isEqual(exp.cref, names[i]) then
2240 19091 value := values[i];
2241 ok := true;
2242 19091 break;
2243 end if;
2244 end for;
2245 19099 then (ok, value);
2246
2247 case Expression.BINARY() guard(Type.isInteger(Operator.typeOf(exp.operator))) algorithm
2248 ✗ (ok1, v1) := evalFrameExp(exp.exp1, names, values, nframes);
2249 ✗ (ok2, v2) := evalFrameExp(exp.exp2, names, values, nframes);
2250 ✗ value := 0;
2251 ✗ if ok1 and ok2 then
2252 (ok, value) := match exp.operator.op
2253 ✗ case NFOperator.Op.ADD then (true, v1 + v2);
2254 ✗ case NFOperator.Op.SUB then (true, v1 - v2);
2255 ✗ case NFOperator.Op.MUL then (true, v1 * v2);
2256 else (false, 0);
2257 end match;
2258 else
2259 ok := false;
2260 end if;
2261 ✗ then (ok, value);
2262
2263 case Expression.UNARY() guard(Type.isInteger(Operator.typeOf(exp.operator))
2264 and exp.operator.op == NFOperator.Op.UMINUS) algorithm
2265 ✗ (ok, value) := evalFrameExp(exp.exp, names, values, nframes);
2266 ✗ value := -value;
2267 then (ok, value);
2268
2269 else (false, 0);
2270 end match;
2271 end evalFrameExp;
2272
2273 function getCrefInFrameIndices
2274 input ComponentRef cref "cref to get indices from";
2275 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames "iterator frames at which to evaluate cref";
2276 input Mapping mapping "index mapping (only variable mapping needed)";
2277 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
2278 input Boolean resize;
2279 output list<Integer> scal_lst "scalar indices of cref";
2280 protected
2281 ComponentRef final_cref;
2282 Integer var_arr_idx, var_start;
2283 algorithm
2284 32322 (final_cref, var_arr_idx) := getVarArrIdx(cref, mapping, map);
2285 32322 (var_start, _) := mapping.var_AtS[var_arr_idx];
2286
2287 // add local indices to start index
2288 32322 scal_lst := getCrefInFrameIndicesLocal(cref, final_cref, frames, var_start, resize);
2289 end getCrefInFrameIndices;
2290
2291 function getVarArrIdx
2292 input output ComponentRef cref "cref to get indices from";
2293 input Mapping mapping "index mapping (only variable mapping needed)";
2294 input UnorderedMap<ComponentRef, Integer> map "unordered map to check for relevance";
2295 output Integer var_arr_idx;
2296 algorithm
2297 // try to get array index, if it fails, strip the subscripts
2298 (var_arr_idx, cref) := match UnorderedMap.get(cref, map)
2299 case SOME(var_arr_idx) then (var_arr_idx, cref);
2300 else algorithm
2301 155 cref := ComponentRef.stripSubscriptsAll(cref);
2302 155 then (UnorderedMap.getSafe(cref, map, sourceInfo()), cref);
2303 end match;
2304 end getVarArrIdx;
2305
2306 public
2307 function getCrefInFrameIndicesLocal
2308 input ComponentRef subscripted_cref;
2309 input ComponentRef stripped_cref;
2310 input list<tuple<ComponentRef, Expression, Option<Iterator>>> frames "iterator frames at which to evaluate cref";
2311 input Integer var_start;
2312 input Boolean resize;
2313 output list<Integer> scal_lst;
2314 protected
2315 list<Integer> sizes;
2316 list<Expression> subs;
2317 Type ty;
2318 Integer complex_size;
2319 algorithm
2320 // prepare the sizes of the full cref, the subscripts and the type to check if its complex
2321 32354 sizes := ComponentRef.sizes(stripped_cref, false, resize);
2322 32354 subs := ComponentRef.subscriptsToExpression(subscripted_cref, true);
2323 32354 ty := Type.arrayElementType(ComponentRef.getComponentType(subscripted_cref));
2324
2325 // check if it needs special record handling
2326 scal_lst := match Type.complexSize(ty)
2327 case SOME(complex_size) algorithm
2328 scal_lst := {};
2329
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
329 for i in complex_size:-1:1 loop
2330 558 scal_lst := listAppend(combineFramesIndices(var_start, complex_size :: sizes, Expression.INTEGER(i) :: subs, frames, resize), scal_lst);
2331 end for;
2332 then scal_lst;
2333 32304 else combineFramesIndices(var_start, sizes, subs, frames, resize);
2334 end match;
2335 end getCrefInFrameIndicesLocal;
2336
2337 protected
2338 function resolveDimensionsSubscripts
2339 "uses the replacement module to replace all iterator crefs in the subscript with the current position.
2340 Returns the current positions for each subscript."
2341 input list<Integer> sizes "dimension sizes";
2342 input list<Expression> subs "subscript expressions";
2343 input UnorderedMap<ComponentRef, Expression> replacements "replacement map for iterator crefs";
2344 input Boolean resize;
2345 output list<list<Integer>> values;
2346 protected
2347 list<Expression> replaced;
2348 algorithm
2349 // get all possible subscript combinations
2350
4/4
✓ Branch 0 taken 9357 times.
✓ Branch 1 taken 3967 times.
✓ Branch 2 taken 9357 times.
✓ Branch 3 taken 3967 times.
13324 replaced := list(Expression.map(sub, function Replacements.applySimpleExp(replacements = replacements)) for sub in subs);
2351
7/8
✓ Branch 0 taken 9357 times.
✓ Branch 1 taken 3967 times.
✓ Branch 2 taken 9357 times.
✓ Branch 3 taken 3967 times.
✓ Branch 4 taken 9357 times.
✓ Branch 5 taken 3967 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3967 times.
13324 values := list(resolveDimensionsSubscript(exp, size, resize) threaded for exp in replaced, size in sizes);
2352 3967 values := List.combination(values);
2353 end resolveDimensionsSubscripts;
2354
2355 function resolveDimensionsSubscript
2356 input Expression replaced;
2357 input Integer size;
2358 input Boolean resize;
2359 output list<Integer> res;
2360 protected
2361 Expression rep;
2362 algorithm
2363 9357 rep := SimplifyExp.simplifyDump(replaced, true, getInstanceName());
2364 res := match rep
2365 local
2366 Integer start, step, stop;
2367
2368 // just a single element; filter out-of-bounds values (Modelica arrays are 1-indexed)
2369
3/4
✓ Branch 0 taken 9343 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 9343 times.
✗ Branch 3 not taken.
9349 case Expression.INTEGER() then if rep.value >= 1 and rep.value <= size then {rep.value} else {};
2370 ✗ case Expression.ENUM_LITERAL() then {rep.index};
2371
2372 // build list from range
2373 case Expression.RANGE() algorithm
2374 ✗ (start, step, stop) := Expression.getIntegerRange(rep, resize);
2375 ✗ then List.intRange3(start,step, stop);
2376
2377 // resolve individual array elements
2378 case Expression.ARRAY()
2379 ✗ then List.flatten(list(resolveDimensionsSubscript(e, size, resize) for e in rep.elements));
2380
2381 // assume full dependency if it cannot be evaluated
2382 8 else List.intRange(size);
2383 end match;
2384 end resolveDimensionsSubscript;
2385
2386 function applyNewFrameRange
2387 "applies new start, step and stop to a frame"
2388 input output Frame frame;
2389 input tuple<Integer, Integer, Integer> range;
2390 algorithm
2391 frame := match frame
2392 local
2393 ComponentRef name;
2394 Expression exp;
2395 Option<Iterator> map;
2396
2397 ✗ case (name, exp as Expression.RANGE(), map) then (name, Expression.sliceRange(exp, range), map);
2398
2399 case (_, exp, _) algorithm
2400 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
2401 + " failed because frame expression was not Expression.RANGE(): " + Expression.toString(exp)});
2402 ✗ then fail();
2403 end match;
2404 end applyNewFrameRange;
2405
2406 annotation(__OpenModelica_Interface="nbackend");
2407 end NBSlice;
2408