Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 74.3% 231 / 0 / 311
Functions: -% 0 / 1 / 1
Branches: 50.8% 129 / 0 / 254

OMCompiler/Compiler/NBackEnd/Util/NBResizable.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 NBResizable
37 " file: NBResizable.mo
38 package: NBResizable
39 description: This file contains util functions for resizable parameters.
40 "
41
42 protected
43 // frontend imports
44 import NFBackendExtension.{BackendInfo, VariableAttributes};
45 import Binding = NFBinding;
46 import Call = NFCall;
47 import ComponentRef = NFComponentRef;
48 import Dimension = NFDimension;
49 import Expression = NFExpression;
50 import SimplifyExp = NFSimplifyExp;
51 import Subscript = NFSubscript;
52 import Type = NFType;
53 import Operator = NFOperator;
54 import Variable = NFVariable;
55
56 // backend imports
57 import NFInstNode.InstNode;
58 import NBEquation.{Equation, Iterator, EquationPointers, EquationAttributes, EquationKind};
59 import NBVariable.{VarData, VariablePointers};
60 import BVariable = NBVariable;
61 import Differentiate = NBDifferentiate;
62 import NBDifferentiate.DifferentiationArguments;
63 import Replacements = NBReplacements;
64 import Solve = NBSolve;
65 public
66 constant Boolean debug = false; // SET TO TRUE FOR DEBUGGING
67 type EvalOrder = enumeration(INDEPENDENT, FORWARD, BACKWARD, FAILED);
68
69 function resize
70 "this resizes the resizable parameters to the optimal value to get the smallest possible system"
71 input output EquationPointers equations;
72 input output VarData varData;
73 protected
74 UnorderedSet<ComponentRef> parameters, min_parameters;
75 UnorderedMap<ComponentRef, Expression> optimal_values;
76 // c2p: constraint to parameters map
77 // p2c: parameter to constraints map
78 // i: inequality constraints
79 // e: equality constraints
80 UnorderedMap<Expression, ParameterList> c2pi, c2pe;
81 UnorderedMap<ComponentRef, ConstraintList> p2ci, p2ce;
82 partial function applyFunc
83 input output Type ty;
84 end applyFunc;
85 applyFunc func;
86 algorithm
87 varData := match varData
88 // only do something if there are resizable variables
89 case VarData.VAR_DATA_SIM() guard(VariablePointers.size(varData.resizables) > 0) algorithm
90 // prepare sets and maps
91 15 parameters := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
92 15 min_parameters := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
93 15 optimal_values := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
94 15 c2pi := UnorderedMap.new<ParameterList>(Expression.hash, Expression.isEqual);
95 15 c2pe := UnorderedMap.new<ParameterList>(Expression.hash, Expression.isEqual);
96
97 // find the optimal values for iterators
98 15 EquationPointers.map(equations, function findOptimalResizableValues(parameters = parameters, min_parameters = min_parameters, optimal_values = optimal_values, c2pi = c2pi, c2pe = c2pe));
99
100 // initialize the optimal values for parameters with their min or max attribute (or 0 if none available)
101 15 parameters := UnorderedSet.selfMap(parameters, function setInitialValues(min_parameters = min_parameters, optimal_values = optimal_values));
102
103 if debug then
104 print(optimalValuesToString(optimal_values, StringUtil.headline_2("[debug] Initial Resizable Parameter Values:") + "\n"));
105 print(StringUtil.headline_2("[debug] Final Inequality Constraints:"));
106 if UnorderedMap.isEmpty(c2pi) then
107 print(" <No Constraints>\n\n");
108 else
109 print(List.toStringCustom(UnorderedMap.keyList(c2pi), Expression.toString, "", " 0 >= ", "\n 0 >= ", "\n") + "\n");
110 end if;
111 print(StringUtil.headline_2("[debug] Final Equality Constraints:"));
112 if UnorderedMap.isEmpty(c2pe) then
113 print(" <No Constraints>\n\n");
114 else
115 print(List.toStringCustom(UnorderedMap.keyList(c2pe), Expression.toString, "", " 0 = ", "\n 0 = ", "\n") + "\n");
116 end if;
117 end if;
118
119 // compute the optimal values by checking constraints
120 15 p2ci := invertConstraintParameterMap(c2pi, parameters);
121 15 p2ce := invertConstraintParameterMap(c2pe, parameters);
122 15 computeOptimalValues(optimal_values, c2pi, p2ci, c2pe, p2ce);
123
124 // traverse the model and update all values depending on these resizable parameters
125 15 func := function Type.applyToDims(func = function updateDimension(optimal_values = optimal_values));
126 // update variables
127 30 varData.variables := VariablePointers.map(varData.variables, function Variable.applyToType(func = func));
128 30 varData.variables := VariablePointers.mapPtr(varData.variables, function BVariable.updateResizableParameter(optimal_values = optimal_values));
129 30 varData.variables := VariablePointers.mapPtr(varData.variables, function BVariable.mapExp(funcExp = function Expression.applyToType(func = func), mapFunc = Expression.map));
130 // update equations
131 15 EquationPointers.mapPtr(equations, function Equation.applyToType(func = func));
132 15 equations := EquationPointers.mapExp(equations, function Expression.applyToType(func = func));
133 15 EquationPointers.mapRes(equations, function BVariable.applyToType(func = func));
134
135
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if Flags.isSet(Flags.DUMP_RESIZABLE) or debug then
136 ✗ print(optimalValuesToString(optimal_values));
137 end if;
138 then varData;
139
140 else algorithm
141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 178 times.
178 if Flags.isSet(Flags.DUMP_RESIZABLE) or debug then
142 ✗ print(StringUtil.headline_2("[dumpResizable] No resizable parameters were detected in the model."));
143 end if;
144 then varData;
145 end match;
146 end resize;
147
148 function detect
149 "this function detects if an equation is resizable"
150 input Equation eqn;
151 input ComponentRef cref_to_solve;
152 output UnorderedMap<ComponentRef, EvalOrder> order = UnorderedMap.new<EvalOrder>(ComponentRef.hash, ComponentRef.isEqual);
153 protected
154 Pointer<Variable> var_ptr = BVariable.getVarPointer(cref_to_solve, sourceInfo());
155 UnorderedSet<ComponentRef> var_occurences = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
156 UnorderedSet<ComponentRef> ite_occurences;
157 list<ComponentRef> occ_lst, iterators;
158 list<list<Subscript>> subs;
159 list<Subscript> subs_to_solve, local_subs;
160 Subscript sub_to_solve;
161 ComponentRef iter;
162 DifferentiationArguments args;
163 Option<Integer> opt_factor;
164 Integer factor;
165 Integer shift_value, v2;
166 EvalOrder eval;
167 Boolean stop;
168 algorithm
169 order := match eqn
170 case Equation.FOR_EQUATION() algorithm
171
2/2
✓ Branch 0 taken 589 times.
✓ Branch 1 taken 589 times.
1178 for body in eqn.body loop
172 589 Equation.map(body, function collectVars(func = function BVariable.equalName(var_ptr2 = var_ptr), collector = var_occurences));
173 589 occ_lst := UnorderedSet.toList(var_occurences);
174 589 (iterators, _) := Iterator.getFrames(Equation.getForIterator(eqn));
175
2/2
✓ Branch 0 taken 657 times.
✓ Branch 1 taken 589 times.
1246 order := UnorderedMap.fromLists(iterators, list(EvalOrder.INDEPENDENT for i in iterators), ComponentRef.hash, ComponentRef.isEqual);
176
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 567 times.
589 if not List.hasOneElement(occ_lst) then
177
4/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 22 times.
66 subs := list(ComponentRef.subscriptsAllWithWholeFlat(cref) for cref in occ_lst);
178 22 subs := List.transposeList(subs);
179 22 subs_to_solve := ComponentRef.subscriptsAllWithWholeFlat(cref_to_solve);
180 22 stop := false;
181
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 22 times.
49 for dim in List.zip(subs, subs_to_solve) loop
182 27 (local_subs, sub_to_solve) := dim;
183 27 ite_occurences := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
184
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 27 times.
81 for sub in local_subs loop
185 54 Subscript.mapExp(sub, function collectVars(func = BVariable.isIterator, collector = ite_occurences));
186 end for;
187 27 iterators := UnorderedSet.toList(ite_occurences);
188 () := match iterators
189 // literal subscripts in this dimension do not restrict the order
190 case {} then ();
191
192 case {iter} algorithm
193 27 eval := UnorderedMap.getSafe(iter, order, sourceInfo());
194
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if eval < EvalOrder.FAILED then
195 27 args := DifferentiationArguments.simpleCref(iter);
196 opt_factor := NONE();
197
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 27 times.
81 for sub in local_subs loop
198 54 factor := getFactor(Subscript.toExp(sub), args, opt_factor);
199 opt_factor := SOME(factor);
200 end for;
201
202 () := match opt_factor
203 case SOME(factor) guard(factor <> 0) algorithm
204 try
205
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
27 Expression.INTEGER(shift_value) := getShift(Subscript.toExp(sub_to_solve), iter);
206
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 27 times.
81 for sub in local_subs loop
207
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 54 times.
54 Expression.INTEGER(v2) := getShift(Subscript.toExp(sub), iter);
208 eval := match eval
209 case EvalOrder.INDEPENDENT guard(shift_value == v2) then EvalOrder.INDEPENDENT;
210 case EvalOrder.INDEPENDENT guard(shift_value > v2) then EvalOrder.FORWARD;
211 case EvalOrder.INDEPENDENT guard(shift_value < v2) then EvalOrder.BACKWARD;
212 case EvalOrder.FORWARD guard(shift_value >= v2) then EvalOrder.FORWARD;
213 case EvalOrder.BACKWARD guard(shift_value <= v2) then EvalOrder.BACKWARD;
214 else EvalOrder.FAILED;
215 end match;
216 end for;
217
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if eval == EvalOrder.FAILED then break; end if;
218 else
219 eval := EvalOrder.FAILED;
220 end try;
221 27 UnorderedMap.add(iter, eval, order);
222 then ();
223 else ();
224 end match;
225 end if;
226 then ();
227
228 else algorithm
229 ✗ for it in iterators loop
230 ✗ UnorderedMap.add(it, EvalOrder.FAILED, order);
231 end for;
232 stop := true;
233 then ();
234 end match;
235
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if stop then break; end if;
236 end for;
237 end if;
238 end for;
239 589 then order;
240 else algorithm
241 540 order := UnorderedMap.fromLists({ComponentRef.EMPTY()}, {EvalOrder.FAILED}, ComponentRef.hash, ComponentRef.isEqual);
242 then order;
243 end match;
244 end detect;
245
246 function addDimensionParameters
247 "A dimension of a resizable array that is an expression of parameters (N-1,
248 nT+1) gets an Integer parameter $DIM_k with the expression as start value.
249 The init.xml refers to it by value reference like to a plain size parameter,
250 and the generated function updateStructuralParameters computes it from the
251 start values of the other parameters (possibly changed with -override) before
252 the runtime allocates the arrays."
253 input output VarData varData;
254 protected
255 list<Expression> dim_exps = {};
256 list<Pointer<Variable>> dim_params = {};
257 Pointer<Variable> var_ptr;
258 Variable var;
259 ComponentRef cref;
260 Integer idx = 1;
261 algorithm
262 varData := match varData
263 case VarData.VAR_DATA_SIM() algorithm
264 // all lists: e.g. the function alias variables of the initialization are not in variables
265
6/6
✓ Branch 7 taken 84 times.
✓ Branch 8 taken 12 times.
✓ Branch 9 taken 84 times.
✓ Branch 10 taken 12 times.
✓ Branch 13 taken 439 times.
✓ Branch 14 taken 12 times.
535 for v in List.flatten(list(VariablePointers.toList(l) for l in {varData.variables, varData.unknowns,
266 varData.knowns, varData.initials, varData.auxiliaries, varData.aliasVars, varData.nonTrivialAlias})) loop
267 // calculated size parameters (s.N = N) have no start value at runtime
268 439 Pointer.update(v, Variable.applyToType(Pointer.access(v), function Type.applyToDims(func = resolveSizeParameters)));
269
2/2
✓ Branch 3 taken 294 times.
✓ Branch 4 taken 439 times.
733 for dim in Type.arrayDims(Variable.typeOf(Pointer.access(v))) loop
270 dim_exps := match dim
271 case Dimension.RESIZABLE() guard isDimensionExpression(dim.exp) and not List.isMemberOnTrue(dim.exp, dim_exps, Expression.isEqual)
272 9 then dim.exp :: dim_exps;
273 case Dimension.EXP() guard isDimensionExpression(dim.exp) and not List.isMemberOnTrue(dim.exp, dim_exps, Expression.isEqual)
274 ✗ then dim.exp :: dim_exps;
275 else dim_exps;
276 end match;
277 end for;
278 end for;
279
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 12 times.
21 for e in listReverse(dim_exps) loop
280 9 (var_ptr, cref) := BVariable.makeAuxVar("$DIM", idx, Type.INTEGER(), true);
281 9 var := BVariable.setStartAttribute(Pointer.access(var_ptr), e, true);
282 9 Pointer.update(var_ptr, var);
283 dim_params := var_ptr :: dim_params;
284 9 idx := idx + 1;
285 end for;
286
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if not listEmpty(dim_params) then
287 6 dim_params := listReverse(dim_params);
288 6 varData.variables := VariablePointers.addList(dim_params, varData.variables);
289 6 varData.knowns := VariablePointers.addList(dim_params, varData.knowns);
290 6 varData.resizables := VariablePointers.addList(dim_params, varData.resizables);
291 end if;
292 then varData;
293 else varData;
294 end match;
295 end addDimensionParameters;
296
297 function resolveSizeParameters
298 "replaces Integer parameters with a non-literal binding in a resizable
299 dimension by their binding, e.g. s.N with binding N"
300 input output Dimension dim;
301 algorithm
302 dim := match dim
303 case Dimension.RESIZABLE() algorithm
304 500 dim.exp := Expression.map(dim.exp, resolveSizeParameter);
305 then dim;
306 else dim;
307 end match;
308 end resolveSizeParameters;
309
310 function resolveSizeParameter
311 input output Expression exp;
312 protected
313 Pointer<Variable> var_ptr;
314 Variable var;
315 algorithm
316 exp := match exp
317 case Expression.CREF(ty = Type.INTEGER()) guard InstNode.isVar(ComponentRef.node(exp.cref)) algorithm
318 506 var_ptr := BVariable.getVarPointer(exp.cref, sourceInfo());
319 506 var := Pointer.access(var_ptr);
320 then match Binding.getExpOpt(var.binding)
321 local
322 Expression b;
323 case SOME(b) guard BVariable.isParamOrConst(var_ptr) and not Expression.isLiteral(b)
324 6 then Expression.map(b, resolveSizeParameter);
325 else exp;
326 end match;
327 else exp;
328 end match;
329 end resolveSizeParameter;
330
331 function isDimensionExpression
332 "true for a dimension that is neither a literal nor a plain parameter"
333 input Expression exp;
334 output Boolean b;
335 algorithm
336 b := match exp
337 case Expression.INTEGER() then false;
338 case Expression.CREF() then false;
339 105 else not Expression.isLiteral(exp);
340 end match;
341 end isDimensionExpression;
342
343 function orderFailed
344 input EvalOrder eo;
345 output Boolean b = eo == EvalOrder.FAILED;
346 end orderFailed;
347
348 function orderString
349 input EvalOrder eo;
350 output String str;
351 algorithm
352 str := match eo
353 case EvalOrder.INDEPENDENT then "INDEPENDENT";
354 case EvalOrder.FORWARD then "FORWARD";
355 case EvalOrder.BACKWARD then "BACKWARD";
356 else "FAILED";
357 end match;
358 end orderString;
359
360 protected
361 type ParameterList = list<ComponentRef>;
362 type ConstraintList = list<Expression>;
363 type Occurences = UnorderedSet<Expression>;
364 constant tuple<ComponentRef, Expression> END_TPL = (ComponentRef.EMPTY(), Expression.END());
365
366 function findOptimalResizableValues
367 input output Equation eqn;
368 input UnorderedSet<ComponentRef> parameters;
369 input UnorderedSet<ComponentRef> min_parameters;
370 input UnorderedMap<ComponentRef, Expression> optimal_values;
371 input UnorderedMap<Expression, ParameterList> c2pi;
372 input UnorderedMap<Expression, ParameterList> c2pe;
373 protected
374 UnorderedMap<ComponentRef, Expression> resizables, replacements;
375 UnorderedMap<ComponentRef, Occurences> occs;
376 UnorderedSet<ComponentRef> constrained_vars = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
377 Dimension lhs_dim, rhs_dim;
378 Expression const;
379 algorithm
380 if debug then
381 print("[debug] checking equation:\n" + Equation.toString(eqn) + "\n");
382 end if;
383
384 () := match eqn
385 // Main Routine: collect iterators and resizable parameter constraints and target equations
386 case Equation.FOR_EQUATION() algorithm
387 55 resizables := getResizableIterators(eqn.iter);
388 55 replacements := getVarReplacements(eqn.iter);
389
390 // add all resizable iterators with empty occurence sets to intialize
391 55 occs := UnorderedMap.new<Occurences>(ComponentRef.hash, ComponentRef.isEqual);
392
2/2
✓ Branch 1 taken 59 times.
✓ Branch 2 taken 55 times.
169 for res in UnorderedMap.keyList(resizables) loop
393 59 UnorderedMap.add(res, UnorderedSet.new(Expression.hash, Expression.isEqual), occs);
394 end for;
395
396 // fill the occurence sets traversing the body of the equation
397
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 55 times.
110 for body in eqn.body loop
398 55 Equation.map(body, function collectOccurences(occs = occs));
399 55 Equation.map(body, function collectVars(func = BVariable.isArray, collector = constrained_vars));
400 end for;
401 55 findOptimalValue(eqn, occs, resizables, parameters, min_parameters, optimal_values, c2pi);
402 55 UnorderedSet.fold(constrained_vars, function addVariableConstraint(eqn = eqn, replacements = SOME(replacements)), c2pi);
403 then ();
404
405 case Equation.ARRAY_EQUATION() algorithm
406 10 Equation.map(eqn, function collectVars(func = BVariable.isResizable, collector = constrained_vars));
407 10 UnorderedSet.fold(constrained_vars, function addVariableConstraint(eqn = eqn, replacements = NONE()), c2pi);
408
2/2
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 10 times.
21 for tpl in List.zip(Type.arrayDims(Expression.typeOf(eqn.lhs)), Type.arrayDims(Expression.typeOf(eqn.rhs))) loop
409 11 (lhs_dim, rhs_dim) := tpl;
410
3/4
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
11 if Dimension.isResizable(lhs_dim) or Dimension.isResizable(rhs_dim) then
411 6 const := Expression.MULTARY({Dimension.sizeExp(lhs_dim)}, {Dimension.sizeExp(rhs_dim)}, Operator.makeAdd(Type.INTEGER()));
412 try
413 2 addConstraint(const, NONE(), c2pe, Expression.isZero, "array dimension", "=");
414 2 Expression.map(const, function collectResizables(collector = parameters));
415 else
416 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed.\nViolation of implicit constraint `" + Dimension.toString(lhs_dim) + " = " + Dimension.toString(rhs_dim)
417 + "` for LHS and RHS type dimensions in equation:\n" + Equation.toString(eqn)});
418 ✗ fail();
419 end try;
420 end if;
421 end for;
422 then ();
423
424 else algorithm
425 // create dummy replacements (no iterators to replace)
426 43 Equation.map(eqn, function collectVars(func = BVariable.isResizable, collector = constrained_vars));
427 // subs: [n] dim [m] --> m >= n --> implication on split?
428 // subs: [10] dim [m] --> m >= 10 also implication on split values
429 43 UnorderedSet.fold(constrained_vars, function addVariableConstraint(eqn = eqn, replacements = NONE()), c2pi);
430 then ();
431 end match;
432
433 if debug then
434 print("\n");
435 end if;
436 end findOptimalResizableValues;
437
438 function getResizableIterators
439 input Iterator iter;
440 output UnorderedMap<ComponentRef, Expression> resizables = UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
441 protected
442 list<ComponentRef> names;
443 list<Expression> ranges;
444 algorithm
445 55 (names, ranges) := Iterator.getFrames(iter);
446
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 55 times.
115 for tpl in List.zip(names, ranges) loop
447
2/2
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 1 time.
60 if iteratorIsResizable(Util.tuple22(tpl)) then
448 59 UnorderedMap.add(Util.tuple21(tpl), Util.tuple22(tpl), resizables);
449 end if;
450 end for;
451 end getResizableIterators;
452
453 function getVarReplacements
454 input Iterator iter;
455 output UnorderedMap<ComponentRef, Expression> replacements = UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
456 protected
457 list<ComponentRef> names;
458 list<Expression> ranges;
459 ComponentRef name;
460 Expression range, max_call;
461 algorithm
462 55 (names, ranges) := Iterator.getFrames(iter);
463
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 55 times.
115 for tpl in List.zip(names, ranges) loop
464 60 (name, range) := tpl;
465 () := match range
466 case Expression.RANGE() algorithm
467
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
60 if isSome(range.step) and Expression.isNegative(Util.getOption(range.step)) then
468 ✗ UnorderedMap.add(name, range.start, replacements);
469 elseif isNone(range.step) or Expression.isPositive(Util.getOption(range.step)) then
470 60 UnorderedMap.add(name, range.stop, replacements);
471 else
472 ✗ max_call := Expression.CALL(Call.makeTypedCall(
473 fn = NFBuiltinFuncs.MAX_INT,
474 args = {range.start, range.stop},
475 variability = Expression.variability(range.start),
476 purity = NFPrefixes.Purity.PURE
477 ));
478 ✗ UnorderedMap.add(name, max_call, replacements);
479 end if;
480 then ();
481 else ();
482 end match;
483 end for;
484 end getVarReplacements;
485
486 public
487 function restrictIterator
488 "The iterator restricted to the box of iterations given by flat slice indices
489 at the analysis sizes (zero based, last frame fastest). Positions near the
490 start of a range are counted from its start, positions near its end from
491 its stop, so the restriction holds for every value of the resizable
492 parameters. NONE() if the indices are no box or a bound is ambiguous."
493 input Iterator iter;
494 input list<Integer> indices;
495 output Option<Iterator> res = NONE();
496 protected
497 list<ComponentRef> names;
498 list<Expression> ranges, new_ranges = {};
499 list<Option<Iterator>> maps;
500 array<Integer> sizes, lo, hi;
501 list<Integer> uniq;
502 Integer n, tmp, p, count = 1;
503 Option<Expression> range;
504 algorithm
505 130 (names, ranges, maps) := Iterator.getFrames(iter);
506
2/4
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 130 times.
130 if listEmpty(indices) or List.any(maps, isSome) then
507 ✗ return;
508 end if;
509
4/4
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 171 times.
✓ Branch 3 taken 130 times.
301 sizes := listArray(list(Expression.rangeSize(r, true) for r in ranges));
510 n := arrayLength(sizes);
511 130 lo := arrayCopy(sizes);
512 130 hi := arrayCreate(n, -1);
513 130 uniq := UnorderedSet.toList(UnorderedSet.fromList(indices, Util.id, intEq));
514
2/2
✓ Branch 0 taken 460 times.
✓ Branch 1 taken 98 times.
558 for idx in uniq loop
515 460 tmp := idx;
516
1/2
✓ Branch 0 taken 460 times.
✗ Branch 1 not taken.
1205 for d in n:-1:1 loop
517
1/2
✓ Branch 1 taken 745 times.
✗ Branch 2 not taken.
745 p := mod(tmp, sizes[d]);
518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
745 tmp := intDiv(tmp, sizes[d]);
519 745 lo[d] := intMin(lo[d], p);
520 745 hi[d] := intMax(hi[d], p);
521 end for;
522
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 428 times.
460 if tmp <> 0 then
523 32 return;
524 end if;
525 end for;
526
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 for d in 1:n loop
527 139 count := count * (hi[d] - lo[d] + 1);
528 end for;
529
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
98 if count <> listLength(uniq) then
530 ✗ return;
531 end if;
532
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
237 for d in 1:n loop
533 139 range := restrictedRange(listGet(ranges, d), lo[d], hi[d], sizes[d]);
534
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 139 times.
139 if isNone(range) then
535 ✗ return;
536 end if;
537 139 new_ranges := Util.getOption(range) :: new_ranges;
538 end for;
539 98 res := SOME(Iterator.fromFrames(List.zip3(names, listReverse(new_ranges), maps)));
540 end restrictIterator;
541
542 function boxes
543 "splits flat indices (zero based, last dimension fastest) of an array with
544 the given sizes into boxes, ordered by their first index"
545 input list<Integer> indices;
546 input list<Integer> sizes;
547 output list<list<Integer>> parts = {};
548 protected
549 list<list<Integer>> locs;
550 algorithm
551 ✗ locs := list(indexLocation(i, sizes) for i in List.sort(UnorderedSet.toList(UnorderedSet.fromList(indices, Util.id, intEq)), intGt));
552 ✗ for spec in boxSpecs(locs) loop
553 ✗ parts := boxIndices(spec, sizes) :: parts;
554 end for;
555 ✗ parts := List.sort(parts, function boxGreater());
556 end boxes;
557
558 function boxGreater
559 input list<Integer> a;
560 input list<Integer> b;
561 output Boolean res = listHead(a) > listHead(b);
562 end boxGreater;
563
564 function indexLocation
565 "zero based location of a flat index, last dimension fastest"
566 input Integer index;
567 input list<Integer> sizes;
568 output list<Integer> loc = {};
569 protected
570 Integer tmp = index;
571 algorithm
572 ✗ for s in listReverse(sizes) loop
573 ✗ loc := mod(tmp, s) :: loc;
574 ✗ tmp := intDiv(tmp, s);
575 end for;
576 end indexLocation;
577
578 function boxSpecs
579 "the locations as boxes of (first, last) per dimension: groups of the first
580 coordinate with equal boxes of the others are merged"
581 input list<list<Integer>> locs;
582 output list<list<tuple<Integer, Integer>>> specs = {};
583 protected
584 type Locations = list<list<Integer>>;
585 UnorderedMap<Integer, Locations> groups = UnorderedMap.new<Locations>(Util.id, intEq);
586 list<Integer> values;
587 list<list<tuple<Integer, Integer>>> sub, prev_sub = {};
588 Integer lo = -1, hi = -1;
589 String key, prev_key = "#none";
590 algorithm
591 ✗ if listEmpty(locs) then
592 ✗ return;
593 elseif listEmpty(listHead(locs)) then
594 specs := {{}};
595 ✗ return;
596 end if;
597 ✗ for loc in locs loop
598 ✗ UnorderedMap.add(listHead(loc), listRest(loc) :: UnorderedMap.getOrDefault(listHead(loc), groups, {}), groups);
599 end for;
600 ✗ values := List.sort(UnorderedMap.keyList(groups), intGt);
601 ✗ for v in values loop
602 ✗ sub := boxSpecs(listReverse(UnorderedMap.getOrFail(v, groups)));
603 ✗ key := specString(sub);
604 ✗ if v == hi + 1 and key == prev_key then
605 hi := v;
606 else
607 ✗ specs := addSpecs(lo, hi, prev_sub, specs);
608 ✗ (lo, hi, prev_sub, prev_key) := (v, v, sub, key);
609 end if;
610 end for;
611 ✗ specs := listReverse(addSpecs(lo, hi, prev_sub, specs));
612 end boxSpecs;
613
614 function addSpecs
615 input Integer lo;
616 input Integer hi;
617 input list<list<tuple<Integer, Integer>>> sub;
618 input output list<list<tuple<Integer, Integer>>> specs;
619 algorithm
620 ✗ if lo >= 0 then
621 ✗ for s in sub loop
622 ✗ specs := ((lo, hi) :: s) :: specs;
623 end for;
624 end if;
625 end addSpecs;
626
627 function specString
628 input list<list<tuple<Integer, Integer>>> specs;
629 output String str = stringDelimitList(list(stringDelimitList(list(intString(Util.tuple21(t)) + ":" + intString(Util.tuple22(t)) for t in s), ",") for s in specs), ";");
630 end specString;
631
632 function boxIndices
633 "the flat indices of a box"
634 input list<tuple<Integer, Integer>> spec;
635 input list<Integer> sizes;
636 output list<Integer> indices = {0};
637 protected
638 Integer lo, hi;
639 algorithm
640 ✗ for tpl in List.zip(spec, sizes) loop
641 ✗ ((lo, hi), _) := tpl;
642 ✗ indices := List.flatten(list(list(i * Util.tuple22(tpl) + p for p in lo:hi) for i in indices));
643 end for;
644 end boxIndices;
645
646 function restrictedRange
647 "the positions lo..hi (zero based) of a range with n elements at the analysis sizes"
648 input Expression range;
649 input Integer lo;
650 input Integer hi;
651 input Integer n;
652 output Option<Expression> res = NONE();
653 protected
654 Option<Expression> start, stop;
655 Expression size_exp;
656 Dimension dim;
657 algorithm
658 () := match range
659 case Expression.RANGE() guard isNone(range.step) or Expression.isOne(Util.getOption(range.step)) algorithm
660
4/4
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 57 times.
✓ Branch 3 taken 46 times.
139 if lo == 0 and hi == n - 1 then
661 res := SOME(range);
662 57 return;
663 end if;
664 82 start := rangeBound(lo, n, range.start, range.stop);
665 82 stop := rangeBound(hi, n, range.start, range.stop);
666
4/12
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 82 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 82 times.
82 if isNone(start) or isNone(stop) then
667 ✗ return;
668 end if;
669 246 size_exp := SimplifyExp.simplify(Expression.MULTARY({Util.getOption(stop), Expression.INTEGER(1)}, {Util.getOption(start)}, Operator.makeAdd(Type.INTEGER())));
670 dim := match Type.nthDimension(range.ty, 1)
671 82 case dim as Dimension.RESIZABLE() then Dimension.RESIZABLE(dim.size - (n - (hi - lo + 1)), SOME(hi - lo + 1), size_exp, dim.var);
672 ✗ else Dimension.fromInteger(hi - lo + 1);
673 end match;
674 82 res := SOME(Expression.RANGE(Type.ARRAY(Type.INTEGER(), {dim}), Util.getOption(start), NONE(), Util.getOption(stop)));
675 then ();
676 else ();
677 end match;
678 end restrictedRange;
679
680 function rangeBound
681 "position p (zero based) of a range with n elements, counted from the nearer end"
682 input Integer p;
683 input Integer n;
684 input Expression start;
685 input Expression stop;
686 output Option<Expression> bound;
687 algorithm
688
2/4
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 164 times.
164 if Expression.isInteger(start) and Expression.isInteger(stop) then
689 ✗ bound := SOME(Expression.INTEGER(Expression.integerValue(start) + p));
690 elseif p < n - 1 - p then
691 164 bound := SOME(SimplifyExp.simplify(Expression.MULTARY({start, Expression.INTEGER(p)}, {}, Operator.makeAdd(Type.INTEGER()))));
692 elseif p > n - 1 - p then
693 164 bound := SOME(SimplifyExp.simplify(Expression.MULTARY({stop}, {Expression.INTEGER(n - 1 - p)}, Operator.makeAdd(Type.INTEGER()))));
694 else
695 bound := NONE();
696 end if;
697 end rangeBound;
698
699 protected
700
701 function iteratorIsResizable
702 input Expression range;
703 output Boolean b = Expression.fold(range, expContainsResizable, false);
704 end iteratorIsResizable;
705
706 function expContainsResizable
707 "needs to be mapped with Expression.fold()"
708 input Expression exp;
709 input output Boolean b;
710 algorithm
711
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 85 times.
232 if not b then
712 b := match exp
713 59 case Expression.CREF() then BVariable.checkCref(exp.cref, BVariable.isResizableParameter, sourceInfo());
714 else false;
715 end match;
716 end if;
717 end expContainsResizable;
718
719 function collectResizables
720 "needs to be mapped with Expression.map()"
721 input output Expression exp;
722 input UnorderedSet<ComponentRef> collector;
723 algorithm
724 () := match exp
725 case Expression.CREF() guard(BVariable.checkCref(exp.cref, BVariable.isResizableParameter, sourceInfo())) algorithm
726 694 UnorderedSet.add(exp.cref, collector);
727 then ();
728 else ();
729 end match;
730 end collectResizables;
731
732 function collectOccurences
733 input output Expression exp;
734 input UnorderedMap<ComponentRef, Occurences> occs;
735 algorithm
736 () := match exp
737 case Expression.CREF() algorithm
738 282 ComponentRef.mapSubscripts(exp.cref, function collectOccurencesSubscript(occs = occs));
739 then ();
740 else ();
741 end match;
742 end collectOccurences;
743
744 function collectOccurencesSubscript
745 input output Subscript sub;
746 input UnorderedMap<ComponentRef, Occurences> occs;
747 protected
748 UnorderedSet<ComponentRef> acc = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
749 Expression subExp;
750 algorithm
751 72 Subscript.mapExp(sub, function collectOccurencesSubscriptExp(occs = occs, acc = acc));
752
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 65 times.
72 if not UnorderedSet.isEmpty(acc) then
753 65 subExp := Subscript.toExp(sub);
754 65 acc := UnorderedSet.selfMap(acc, function addOccurence(subExp = subExp, occs = occs));
755 end if;
756 end collectOccurencesSubscript;
757
758 function collectOccurencesSubscriptExp
759 input output Expression exp;
760 input UnorderedMap<ComponentRef, Occurences> occs;
761 input UnorderedSet<ComponentRef> acc;
762 algorithm
763 () := match exp
764 case Expression.CREF() guard(UnorderedMap.contains(exp.cref, occs)) algorithm
765 65 UnorderedSet.add(exp.cref, acc);
766 then ();
767 else ();
768 end match;
769 end collectOccurencesSubscriptExp;
770
771 function addOccurence
772 input output ComponentRef iter;
773 input Expression subExp;
774 input UnorderedMap<ComponentRef, Occurences> occs;
775 protected
776 UnorderedSet<Expression> local_occ = UnorderedMap.getSafe(iter, occs, sourceInfo());
777 algorithm
778 65 UnorderedSet.add(subExp, local_occ);
779 end addOccurence;
780
781 function collectVars
782 input output Expression exp;
783 input BVariable.checkVar func;
784 input UnorderedSet<ComponentRef> collector;
785 algorithm
786 () := match exp
787 case Expression.CREF() guard(func(BVariable.getVarPointer(exp.cref, sourceInfo()))) algorithm
788 853 UnorderedSet.add(exp.cref, collector);
789 then ();
790 else();
791 end match;
792 end collectVars;
793
794 function findOptimalValue
795 input Equation eqn;
796 input UnorderedMap<ComponentRef, Occurences> occs;
797 input UnorderedMap<ComponentRef, Expression> resizables;
798 input UnorderedSet<ComponentRef> parameters;
799 input UnorderedSet<ComponentRef> min_parameters;
800 input UnorderedMap<ComponentRef, Expression> optimal_values;
801 input UnorderedMap<Expression, ParameterList> c2pi;
802 protected
803 Expression range, target;
804 UnorderedSet<ComponentRef> local_parameters;
805 DifferentiationArguments args;
806 algorithm
807
2/2
✓ Branch 1 taken 59 times.
✓ Branch 2 taken 55 times.
114 for cref in UnorderedMap.keyList(occs) loop
808 59 range := UnorderedMap.getSafe(cref, resizables, sourceInfo());
809 59 Expression.map(range, function collectResizables(collector = parameters));
810 () := match range
811 case Expression.RANGE() algorithm
812 // optimization target function (minimum)
813 // min! f(x) = stop - start
814 59 target := Expression.MULTARY({range.stop}, {range.start}, Operator.makeAdd(Type.INTEGER()));
815 59 target := SimplifyExp.simplify(target);
816
817 // collect local parameters and merge with global parameters
818 59 local_parameters := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
819 59 Expression.map(range, function collectResizables(collector = local_parameters));
820 59 UnorderedSet.merge(parameters, local_parameters);
821
822 // differentiate the target by all contained parameters to determine initial values
823 59 args := DifferentiationArguments.simpleCref(cref);
824 59 local_parameters := UnorderedSet.selfMap(local_parameters, function getInitialValues(target = target, args = args, min_parameters = min_parameters, optimal_values = optimal_values));
825
826 59 getRangeConstraint(range.start, range.step, range.stop, local_parameters, c2pi, "equation");
827 then ();
828 else ();
829 end match;
830 end for;
831 end findOptimalValue;
832
833 function getRangeConstraint
834 input Expression start;
835 input Option<Expression> step_opt;
836 input Expression stop;
837 input UnorderedSet<ComponentRef> parameters;
838 input UnorderedMap<Expression, ParameterList> c2pi;
839 input String const_kind;
840 protected
841 Expression step, target, distance_const;
842 algorithm
843 82 step := Util.getOptionOrDefault(step_opt, Expression.INTEGER(1));
844 82 target := Expression.MULTARY({stop}, {start}, Operator.makeAdd(Type.INTEGER()));
845
846 // the optimal distance constraint
847 // 2 - (stop - start)/start <= 0
848 // ToDo: reduce the constant to 1. current structures do not support size 1 for loops well
849 164 distance_const := Expression.MULTARY({Expression.INTEGER(2)}, {Expression.MULTARY({target}, {step}, Operator.makeMul(Type.INTEGER()))}, Operator.makeAdd(Type.INTEGER()));
850 82 distance_const := SimplifyExp.simplify(distance_const);
851 82 distance_const := SimplifyExp.combineBinaries(distance_const);
852 82 distance_const := SimplifyExp.simplify(distance_const);
853 82 UnorderedMap.add(distance_const, UnorderedSet.toList(parameters), c2pi);
854 if debug then
855 print("[debug] adding " + const_kind + " constraint: 0 >= " + Expression.toString(distance_const) + "\n");
856 end if;
857 end getRangeConstraint;
858
859 function getFactor
860 input Expression exp;
861 input DifferentiationArguments args;
862 input Option<Integer> opt_factor;
863 output Integer factor;
864 protected
865 Expression diff;
866 algorithm
867 54 diff := Differentiate.differentiateExpression(exp, args);
868 54 diff := SimplifyExp.simplify(diff);
869 54 factor := Expression.integerValueOrDefault(diff, 0);
870
871
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 27 times.
✓ Branch 5 taken 27 times.
✗ Branch 6 not taken.
54 if isSome(opt_factor) and factor <> Util.getOption(opt_factor) then
872 factor := 0;
873 end if;
874 end getFactor;
875
876 function getShift
877 input Expression exp;
878 input ComponentRef cref;
879 output Expression shift;
880 algorithm
881 81 shift := Replacements.single(exp, Expression.CREF(Type.INTEGER(), cref), Expression.INTEGER(0));
882 81 shift := SimplifyExp.simplify(shift);
883 end getShift;
884
885 function getDistance
886 input ComponentRef cref;
887 input Expression exp;
888 input DifferentiationArguments args;
889 input output Option<Integer> opt_factor;
890 input output Integer min_distance;
891 input output Integer max_distance;
892 protected
893 Expression shift;
894 Integer factor, distance;
895 algorithm
896 ✗ if isNone(opt_factor) or Util.getOption(opt_factor) <> 0 then
897 ✗ factor := getFactor(exp, args, opt_factor);
898
899 ✗ if factor <> 0 then
900 // if the factor checks out, get shift and update min and max
901 ✗ shift := getShift(exp, cref);
902 try
903 // the shift has to be a single integer value
904 ✗ Expression.INTEGER(distance) := shift;
905 ✗ if isNone(opt_factor) then
906 ✗ min_distance := distance;
907 ✗ max_distance := distance;
908 opt_factor := SOME(factor);
909 else
910 ✗ min_distance := intMin(distance, min_distance);
911 max_distance := intMax(distance, max_distance);
912 end if;
913 else
914 // failed
915 min_distance := 0;
916 max_distance := 0;
917 opt_factor := SOME(0);
918 end try;
919 else
920 // failed
921 min_distance := 0;
922 max_distance := 0;
923 opt_factor := SOME(0);
924 end if;
925 end if;
926 end getDistance;
927
928 function addVariableConstraint
929 input ComponentRef cref;
930 input Equation eqn "for debugging only";
931 input Option<UnorderedMap<ComponentRef, Expression>> replacements;
932 input output UnorderedMap<Expression, ParameterList> c2pi;
933 protected
934 Variable var = Pointer.access(BVariable.getVarPointer(cref, sourceInfo()));
935 list<Dimension> dims = Type.arrayDims(var.ty);
936 list<Subscript> subs = ComponentRef.subscriptsAllWithWholeFlat(cref); // needs list reverse?
937 Dimension dim;
938 Subscript sub;
939 Expression sub_exp, const;
940 Operator op = Operator.makeAdd(Type.INTEGER());
941 algorithm
942
2/2
✓ Branch 1 taken 214 times.
✓ Branch 2 taken 178 times.
392 for tpl in List.zip(dims, subs) loop
943 214 (dim, sub) := tpl;
944 214 sub_exp := Subscript.toExp(sub);
945 428 const := Expression.MULTARY({sub_exp}, {Dimension.sizeExp(dim)}, op);
946 try
947 214 addConstraint(const, replacements, c2pi, Expression.isNonPositive, ComponentRef.toString(cref) + " (variable)", ">=");
948 else
949 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed.\nViolation of implicit constraint `" + Dimension.toString(dim) + " >= " + Subscript.toString(sub)
950 + "` for component reference `" + ComponentRef.toString(cref) + "` of variable `" + Variable.toString(Pointer.access(BVariable.getVarPointer(cref, sourceInfo()))) + "`\nin equation:\n" + Equation.toString(eqn)});
951 ✗ fail();
952 end try;
953 428 const := Expression.MULTARY({Expression.INTEGER(1)}, {Dimension.sizeExp(dim)}, op);
954 try
955 214 addConstraint(const, replacements, c2pi, Expression.isNonPositive, ComponentRef.toString(cref) + " (variable)", ">=");
956 else
957 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed.\nViolation of implicit constraint `" + Dimension.toString(dim) + " >= 1"
958 + "` for component reference `" + ComponentRef.toString(cref) + "` of variable `" + Variable.toString(Pointer.access(BVariable.getVarPointer(cref, sourceInfo()))) + "`\nin equation:\n" + Equation.toString(eqn)});
959 ✗ fail();
960 end try;
961 end for;
962 end addVariableConstraint;
963
964 function addConstraint
965 input Expression old_const;
966 input Option<UnorderedMap<ComponentRef, Expression>> replacements;
967 input UnorderedMap<Expression, ParameterList> c2p;
968 input checkFunc func "checks validity of redundant constraints";
969 input String const_kind "only for debugging: words to describe the kind of constraint";
970 input String eq_kind "only for debugging: in/equality symbols (=, >=, <=, >, <)";
971 partial function checkFunc
972 input Expression exp;
973 output Boolean b;
974 end checkFunc;
975 protected
976 Expression const, diff;
977 UnorderedSet<ComponentRef> parameters;
978 list<ComponentRef> params;
979 Boolean redundant;
980 DifferentiationArguments args;
981 UnorderedMap<ComponentRef, Expression> zero_replacements;
982 algorithm
983
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✓ Branch 2 taken 314 times.
✓ Branch 3 taken 116 times.
430 if isSome(replacements) then
984 314 const := Expression.map(old_const, function Replacements.applySimpleExp(replacements = Util.getOption(replacements)));
985 else
986 const := old_const;
987 end if;
988
989 430 const := Expression.map(const, function addRangeConstraints(replacements = replacements, c2p = c2p, func = func, const_kind = const_kind, eq_kind = eq_kind));
990
991 430 parameters := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
992 430 Expression.map(const, function collectResizables(collector = parameters));
993 430 params := UnorderedSet.toList(parameters);
994
995 redundant := true;
996
2/2
✓ Branch 0 taken 388 times.
✓ Branch 1 taken 219 times.
607 for p in params loop
997 388 args := DifferentiationArguments.simpleCref(p);
998 388 diff := Differentiate.differentiateExpression(const, args);
999 388 diff := SimplifyExp.simplify(diff);
1000
2/2
✓ Branch 1 taken 177 times.
✓ Branch 2 taken 211 times.
388 if not Expression.isZero(diff) then
1001 redundant := false; break;
1002 end if;
1003 end for;
1004
1005
2/2
✓ Branch 0 taken 211 times.
✓ Branch 1 taken 219 times.
430 if not redundant then
1006 211 UnorderedMap.add(const, params, c2p);
1007 else
1008 219 zero_replacements := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
1009
2/2
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 219 times.
396 for p in params loop
1010 177 UnorderedMap.add(p, Expression.INTEGER(0), zero_replacements);
1011 end for;
1012 219 const := Expression.map(const, function Replacements.applySimpleExp(replacements = zero_replacements));
1013 219 const := SimplifyExp.simplify(const);
1014 // Only fail for literals: symbolic CREFs remaining after replacement are inner iterators
1015 // (e.g. sum/product) whose bounds are guaranteed by their own range expression.
1016
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 219 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
219 if not func(const) and Expression.isLiteral(const) then fail(); end if;
1017 end if;
1018
1019 if debug then
1020 if redundant then
1021 print("[debug] not adding redundant " + const_kind + " constraint: 0 " + eq_kind + " " + Expression.toString(old_const) + " simplified to: 0 " + eq_kind + " " + Expression.toString(const) + "\n");
1022 else
1023 print("[debug] adding " + const_kind + " constraint: 0 " + eq_kind + " " + Expression.toString(old_const) + " simplified to: 0 " + eq_kind + " " + Expression.toString(const) + "\n");
1024 end if;
1025 end if;
1026 end addConstraint;
1027
1028 function addRangeConstraints
1029 input output Expression exp;
1030 input Option<UnorderedMap<ComponentRef, Expression>> replacements;
1031 input UnorderedMap<Expression, ParameterList> c2p;
1032 input checkFunc func "checks validity of redundant constraints";
1033 input String const_kind "only for debugging: words to describe the kind of constraint";
1034 input String eq_kind "only for debugging: in/equality symbols (=, >=, <=, >, <)";
1035 partial function checkFunc
1036 input Expression exp;
1037 output Boolean b;
1038 end checkFunc;
1039 protected
1040 UnorderedSet<ComponentRef> parameters;
1041 algorithm
1042 exp := match exp
1043 case Expression.RANGE() algorithm
1044 // collect local parameters
1045 23 parameters := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1046 23 Expression.map(exp, function collectResizables(collector = parameters));
1047 23 getRangeConstraint(exp.start, exp.step, exp.stop, parameters, c2p, "variable");
1048 23 then Expression.rangeSizeExp(exp);
1049 else exp;
1050 end match;
1051 end addRangeConstraints;
1052
1053 function getInitialValues
1054 input output ComponentRef cref;
1055 input Expression target;
1056 input DifferentiationArguments args;
1057 input UnorderedSet<ComponentRef> min_parameters;
1058 input UnorderedMap<ComponentRef, Expression> optimal_values;
1059 protected
1060 Expression diff, binding;
1061 Variable var;
1062 algorithm
1063 59 args.diffCref := cref;
1064 59 diff := Differentiate.differentiateExpression(target, args);
1065 59 diff := SimplifyExp.simplify(diff);
1066
1067
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 if Expression.isPositive(diff) then
1068 // use min value as initial guess
1069 59 UnorderedSet.add(cref, min_parameters);
1070 elseif Expression.isNegative(diff) then
1071 // use max value as initial guess
1072 else
1073 // cannot be determined -> use actual binding value
1074 ✗ var := Pointer.access(BVariable.getVarPointer(cref, sourceInfo()));
1075 ✗ binding := Binding.getExp(var.binding);
1076 ✗ UnorderedMap.add(cref, binding, optimal_values);
1077 end if;
1078 end getInitialValues;
1079
1080 function setInitialValues
1081 input output ComponentRef cref;
1082 input UnorderedSet<ComponentRef> min_parameters;
1083 input UnorderedMap<ComponentRef, Expression> optimal_values;
1084 protected
1085 Variable var;
1086 VariableAttributes attributes;
1087 Expression value;
1088 algorithm
1089
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if not UnorderedMap.contains(cref, optimal_values) then
1090 14 var := Pointer.access(BVariable.getVarPointer(cref, sourceInfo()));
1091 value := match var
1092 case Variable.VARIABLE(backendinfo = BackendInfo.BACKEND_INFO(attributes = attributes as VariableAttributes.VAR_ATTR_INT())) algorithm
1093
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
14 if UnorderedSet.contains(cref, min_parameters) then
1094
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if isSome(attributes.min) then
1095 ✗ value := Binding.getTypedExp(Util.getOption(attributes.min));
1096 else
1097 value := Expression.INTEGER(0);
1098 end if;
1099 elseif isSome(attributes.max) then
1100 ✗ value := Binding.getTypedExp(Util.getOption(attributes.max));
1101 else
1102 value := Expression.INTEGER(0);
1103 end if;
1104 then value;
1105 else Expression.INTEGER(0);
1106 end match;
1107 14 UnorderedMap.add(cref, value, optimal_values);
1108 end if;
1109 end setInitialValues;
1110
1111 function invertConstraintParameterMap
1112 input UnorderedMap<Expression, ParameterList> c2p;
1113 input UnorderedSet<ComponentRef> parameters;
1114 output UnorderedMap<ComponentRef, ConstraintList> p2c = UnorderedMap.new<ConstraintList>(ComponentRef.hash, ComponentRef.isEqual);
1115 protected
1116 Expression const;
1117 list<ComponentRef> params;
1118 algorithm
1119 // initializing map
1120
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 30 times.
58 for param in UnorderedSet.toList(parameters) loop
1121 28 UnorderedMap.add(param, {}, p2c);
1122 end for;
1123
1124 // inverting map
1125
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 30 times.
65 for tpl in UnorderedMap.toList(c2p) loop
1126 35 (const, params) := tpl;
1127
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 35 times.
69 for param in params loop
1128 // constraints of single elements (e.g. from expanded connections) may contain parameters that were not collected
1129 68 UnorderedMap.add(param, const :: UnorderedMap.getOrDefault(param, p2c, {}), p2c);
1130 end for;
1131 end for;
1132 end invertConstraintParameterMap;
1133
1134 function computeOptimalValues
1135 input UnorderedMap<ComponentRef, Expression> optimal_values;
1136 input UnorderedMap<Expression, ParameterList> c2pi;
1137 input UnorderedMap<ComponentRef, ConstraintList> p2ci;
1138 input UnorderedMap<Expression, ParameterList> c2pe;
1139 input UnorderedMap<ComponentRef, ConstraintList> p2ce;
1140 protected
1141 UnorderedSet<ComponentRef> failed_parameters = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1142 algorithm
1143 if debug then
1144 print("FIXING CONSTRAINTS\n\n");
1145 end if;
1146 15 fixConstraints(optimal_values, c2pi, p2ci, failed_parameters, function intLe(i2 = 0));
1147 15 fixConstraints(optimal_values, c2pe, p2ce, failed_parameters, function intEq(i2 = 0));
1148
1149 if debug then
1150 print("\n");
1151 end if;
1152 end computeOptimalValues;
1153
1154 function fixConstraints
1155 input UnorderedMap<ComponentRef, Expression> optimal_values;
1156 input UnorderedMap<Expression, ParameterList> c2p;
1157 input UnorderedMap<ComponentRef, ConstraintList> p2c;
1158 input UnorderedSet<ComponentRef> failed_parameters;
1159 input checkVal func "function to check if constraint is complied with";
1160 partial function checkVal
1161 input Integer i;
1162 output Boolean b;
1163 end checkVal;
1164 protected
1165 UnorderedSet<Expression> parsed_constraints = UnorderedSet.new(Expression.hash, Expression.isEqual);
1166 Expression constraint, old_optimal_value;
1167 list<ComponentRef> crefs;
1168 Equation eqn, solved_eqn;
1169 Solve.Status status;
1170 Integer value;
1171 Boolean failed;
1172 algorithm
1173 // traversing constraints
1174
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 30 times.
65 for tpl in UnorderedMap.toList(c2p) loop
1175 35 (constraint, crefs) := tpl;
1176 failed := false;
1177 () := match checkConstraint(constraint, optimal_values)
1178 // this constraint is not violated
1179 case SOME(value) guard(func(value)) algorithm
1180 if debug then
1181 print(Expression.toString(constraint) + " || is not violated " + intString(value) + "\n");
1182 end if;
1183 then ();
1184
1185 // this constraint is violated
1186 case SOME(value) algorithm
1187 if debug then
1188 print(Expression.toString(constraint) + " || is violated by " + intString(value) + "\n");
1189 end if;
1190 // create artificial equation
1191 20 eqn := Equation.makeAssignmentEqn(constraint, Expression.INTEGER(0), Iterator.EMPTY(), EquationAttributes.default(EquationKind.DISCRETE, false));
1192
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 for cref in crefs loop
1193 failed := false;
1194 // solve the artificial equation for cref
1195 20 (solved_eqn, status, _) := Solve.solveBody(eqn, cref);
1196
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if status == NBSolve.Status.EXPLICIT then
1197 // try to used solved value as new parameter value to fulfill constraint
1198 () := match checkConstraint(Util.getOption(Equation.getRHS(solved_eqn)), optimal_values)
1199 case SOME(value) algorithm
1200 // saving previous optimal value and checking new one
1201 20 old_optimal_value := UnorderedMap.getSafe(cref, optimal_values, sourceInfo());
1202 20 UnorderedMap.add(cref, Expression.INTEGER(value), optimal_values);
1203
1204 // check all constraints of this parameter
1205
2/2
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 20 times.
72 for cons in UnorderedMap.getSafe(cref, p2c, sourceInfo()) loop
1206
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
52 if UnorderedSet.contains(constraint, parsed_constraints) and not func(Util.getOptionOrDefault(checkConstraint(cons, optimal_values), 1)) then
1207 failed := true;
1208 break;
1209 end if;
1210 end for;
1211
1212 // if this parameter would violate any old constraint, use old value and check next parameter
1213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if failed then
1214 ✗ UnorderedMap.add(cref, old_optimal_value, optimal_values);
1215 end if;
1216 then ();
1217 else ();
1218 end match;
1219 else
1220 // if the equation could not be solved, fail
1221 failed := true;
1222 end if;
1223
1224 // this constraint has been resolved if one parameter could be adjusted to fulfill it
1225 // without breaking other constraints
1226
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if not failed then
1227 20 UnorderedSet.add(constraint, parsed_constraints); break;
1228 end if;
1229 end for;
1230 then ();
1231
1232 // this constraint cannot be determined
1233 else algorithm
1234 ✗ for cref in crefs loop
1235 ✗ UnorderedSet.add(cref, failed_parameters);
1236 end for;
1237 then ();
1238 end match;
1239
1240 // this constraint cannot be fixed
1241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if failed then
1242 ✗ for cref in crefs loop
1243 ✗ UnorderedSet.add(cref, failed_parameters);
1244 end for;
1245 end if;
1246 end for;
1247 end fixConstraints;
1248
1249 function checkConstraint
1250 input Expression constraint;
1251 input UnorderedMap<ComponentRef, Expression> optimal_values;
1252 output Option<Integer> value;
1253 protected
1254 Expression replaced;
1255 algorithm
1256 632 replaced := Expression.map(constraint, function Replacements.applySimpleExp(replacements = optimal_values));
1257 632 replaced := SimplifyExp.simplify(replaced);
1258 value := match replaced
1259 632 case Expression.INTEGER() then SOME(replaced.value);
1260 else NONE();
1261 end match;
1262 end checkConstraint;
1263
1264 function updateDimension
1265 input output Dimension dim;
1266 input UnorderedMap<ComponentRef, Expression> optimal_values;
1267 algorithm
1268 dim := match dim
1269 case Dimension.RESIZABLE() algorithm
1270 577 dim.opt_size := checkConstraint(dim.exp, optimal_values);
1271 then dim;
1272
1273 else dim;
1274 end match;
1275 end updateDimension;
1276
1277 // *****************************
1278 // DEBUGGING TOSTRING FUNCTIONS
1279 // *****************************
1280
1281 function optimalValuesToString
1282 "called using -d=dumpResizable"
1283 input UnorderedMap<ComponentRef, Expression> optimal_values;
1284 input output String str = StringUtil.headline_2("[dumpResizable] Evaluated Optimal Resizable Parameter Values:") + "\n";
1285 protected
1286 ComponentRef param;
1287 Expression value;
1288 Variable var;
1289 list<String> names = {}, old_vals = {}, new_vals = {};
1290 String name, old, new;
1291 Integer names_len;
1292 algorithm
1293 ✗ for tpl in UnorderedMap.toList(optimal_values) loop
1294 ✗ (param, value) := tpl;
1295 ✗ var := Pointer.access(BVariable.getVarPointer(param, sourceInfo()));
1296 ✗ names := ComponentRef.toString(param) :: names;
1297 ✗ new_vals := Expression.toString(value) :: new_vals;
1298 ✗ old_vals := Binding.toString(var.binding) :: old_vals;
1299 end for;
1300
1301 ✗ names_len := max(stringLength(n) for n in names);
1302
1303 ✗ while not listEmpty(names) loop
1304 ✗ name :: names := names;
1305 ✗ new :: new_vals := new_vals;
1306 ✗ old :: old_vals := old_vals;
1307 ✗ str := str + " " + name + " " + StringUtil.repeat(".", names_len + 5 - stringLength(name)) + " OPTIMAL: " + new + " (ORIGINAL: " + old + ")\n";
1308 end while;
1309 ✗ str := str + "\n";
1310 end optimalValuesToString;
1311
1312 function occurencesToString
1313 "helper function for debugging"
1314 input UnorderedMap<ComponentRef, Occurences> occs;
1315 output String str = "";
1316 algorithm
1317 ✗ for tpl in UnorderedMap.toList(occs) loop
1318 ✗ str := ComponentRef.toString(Util.tuple21(tpl)) + ": {" + UnorderedSet.toString(Util.tuple22(tpl), Expression.toString, ", ") + "}\n";
1319 end for;
1320 end occurencesToString;
1321
1322 function distancesToString
1323 "helper function for debugging"
1324 input tuple<ComponentRef, Integer> tpl;
1325 output String str = ComponentRef.toString(Util.tuple21(tpl)) + ":" + intString(Util.tuple22(tpl));
1326 end distancesToString;
1327
1328 function parametersToString
1329 "helper function for debugging"
1330 input list<ComponentRef> parameters;
1331 output String str = List.toString(parameters, ComponentRef.toString);
1332 end parametersToString;
1333
1334 function constraintsToString
1335 "helper function for debugging"
1336 input list<Expression> constraints;
1337 output String str = List.toString(constraints, Expression.toString);
1338 end constraintsToString;
1339
1340 annotation(__OpenModelica_Interface="nbackend");
1341 end NBResizable;
1342