Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 62.3% 362 / 0 / 581
Functions: -% 0 / 1 / 1
Branches: 53.9% 251 / 0 / 466

OMCompiler/Compiler/NBackEnd/Modules/3_Post/NBSolve.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NBSolve
37 " file: NBSolve.mo
38 package: NBSolve
39 description: This file contains all functions for the solving process.
40 "
41
42 import Module = NBModule;
43
44 public
45 // OF imports
46 import Absyn.Path;
47
48 // NF imports
49 import Algorithm = NFAlgorithm;
50 import Call = NFCall;
51 import ComponentRef = NFComponentRef;
52 import Dimension = NFDimension;
53 import Expression = NFExpression;
54 import NFFunction.Function;
55 import Operator = NFOperator;
56 import SimplifyExp = NFSimplifyExp;
57 import ExpandExp = NFExpandExp;
58 import Subscript = NFSubscript;
59 import Type = NFType;
60 import Variable = NFVariable;
61
62 // backend imports
63 import BackendDAE = NBackendDAE;
64 import BackendUtil = NBBackendUtil;
65 import Causalize = NBCausalize;
66 import Differentiate = NBDifferentiate;
67 import NBEquation.{Equation, EquationPointer, EquationPointers, EqData, EquationAttributes, Iterator, IfEquationBody, WhenEquationBody, WhenStatement, SlicingStatus};
68 import NBVariable.{VariablePointer, VariablePointers, VarData};
69 import BVariable = NBVariable;
70 import Inline = NBInline;
71 import Replacements = NBReplacements;
72 import Slice = NBSlice;
73 import StrongComponent = NBStrongComponent;
74 import BPartition = NBPartition;
75 import NBPartition.Partition;
76 import Tearing = NBTearing;
77
78 type Status = enumeration(UNPROCESSED, EXPLICIT, IMPLICIT, UNSOLVABLE);
79 // TRUE -> relation must be inverted, FALSE -> relation must not be inverted, UNKNOWN -> TODO: make relation depend on derivative of the expr
80 type RelationInversion = enumeration(TRUE, FALSE, UNKNOWN);
81
82 function statusString
83 input Status status;
84 output String str;
85 algorithm
86 str := match status
87 case Status.UNPROCESSED then "Solve.UNPROCESSED";
88 case Status.EXPLICIT then "Solve.EXPLICIT";
89 case Status.IMPLICIT then "Solve.IMPLICIT";
90 case Status.UNSOLVABLE then "Solve.UNSOLVABLE";
91 else "Solve.FAILED";
92 end match;
93 end statusString;
94
95 function main
96 "solves each strong component and creates ALIAS strong components for each one already solved the exact same way."
97 extends Module.wrapper;
98 protected
99 Pointer<Integer> implicit_index_ptr = Pointer.create(1);
100 type StrongComponentLst = list<StrongComponent>;
101 UnorderedMap<StrongComponent, list<StrongComponent>> duplicate_map = UnorderedMap.new<StrongComponentLst>(StrongComponent.hash, StrongComponent.isEqual);
102 algorithm
103 bdae := match bdae
104 case BackendDAE.MAIN() algorithm
105 // The order here is important. Whatever comes first is declared the "original", same components afterwards will be alias
106 // Has to be the same order as in SimCode!
107
5/6
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 174 times.
✓ Branch 3 taken 188 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 188 times.
550 bdae.init := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.init);
108
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 182 times.
188 if isSome(bdae.init_0) then
109
4/4
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
18 bdae.init_0 := SOME(list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in Util.getOption(bdae.init_0)));
110 end if;
111
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 187 times.
188 if isSome(bdae.dae) then
112
4/4
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
3 bdae.dae := SOME(list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in Util.getOption(bdae.dae)));
113 end if;
114
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 188 times.
442 bdae.ode := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.ode);
115
4/4
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 168 times.
✓ Branch 3 taken 188 times.
356 bdae.algebraic := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.algebraic);
116
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 188 times.
205 bdae.ode_event := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.ode_event);
117
4/4
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 188 times.
246 bdae.alg_event := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.alg_event);
118
4/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 188 times.
394 bdae.clocked := list(solvePartition(par, bdae.funcMap, implicit_index_ptr, duplicate_map, bdae.varData, bdae.eqData) for par in bdae.clocked);
119 then bdae;
120
121 else algorithm
122 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
123 ✗ then fail();
124 end match;
125 end main;
126
127 function solvePartition
128 input output Partition partition;
129 input UnorderedMap<Path, Function> funcMap;
130 input Pointer<Integer> implicit_index_ptr;
131 input UnorderedMap<StrongComponent, list<StrongComponent>> duplicate_map;
132 input VarData varData;
133 input EqData eqData;
134 protected
135 BPartition.Kind kind = Partition.getKind(partition);
136 type EquationPointerList = list<Pointer<Equation>>;
137 UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map = UnorderedMap.new<EquationPointerList>(ComponentRef.hash, ComponentRef.isEqual);
138 list<StrongComponent> solved_comps = {};
139 Integer implicit_index = Pointer.access(implicit_index_ptr);
140 Pointer<Integer> sliced_idx, comp_idx = Pointer.create(1);
141 ComponentRef name;
142 list<Pointer<Equation>> sliced_eqns;
143 algorithm
144
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 508 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 508 times.
508 if isSome(partition.strongComponents) then
145
2/2
✓ Branch 2 taken 6015 times.
✓ Branch 3 taken 508 times.
7031 for comp in Util.getOption(partition.strongComponents) loop
146 solved_comps := match UnorderedMap.get(comp, duplicate_map)
147 local list<StrongComponent> alias_comps;
148 2690 case SOME(alias_comps) then listAppend(alias_comps, solved_comps); // strong component already solved -> get alias comps
149 else algorithm
150 // solve strong component -> create alias comps
151 3325 (alias_comps, implicit_index) := solveStrongComponent(comp, funcMap, kind, implicit_index, slicing_map, varData, eqData);
152
4/4
✓ Branch 0 taken 3325 times.
✓ Branch 1 taken 3325 times.
✓ Branch 2 taken 3325 times.
✓ Branch 3 taken 3325 times.
6650 UnorderedMap.add(comp, list(StrongComponent.createAlias(kind, partition.index, comp_idx, c) for c in alias_comps), duplicate_map);
153 3325 then listAppend(alias_comps, solved_comps);
154 end match;
155 end for;
156 508 partition.strongComponents := SOME(listArray(listReverse(solved_comps)));
157 // update sliced eqn names
158
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 508 times.
508 for tpl in UnorderedMap.toList(slicing_map) loop
159 ✗ (name, sliced_eqns) := tpl;
160 ✗ if not listEmpty(sliced_eqns) then
161 ✗ sliced_idx := Pointer.create(1);
162 ✗ for eqn_ptr in sliced_eqns loop
163 ✗ Equation.subIdxName(eqn_ptr, sliced_idx);
164 end for;
165 end if;
166 end for;
167 508 Pointer.update(implicit_index_ptr, implicit_index);
168 else
169 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " cannot solve partition without strong components: " + Partition.toString(partition) + "\n\n"});
170 ✗ fail();
171 end if;
172 end solvePartition;
173
174 function solveStrongComponent
175 input StrongComponent comp;
176 output list<StrongComponent> solved_comps = {};
177 input UnorderedMap<Path, Function> funcMap;
178 input BPartition.Kind kind;
179 input output Integer implicit_index;
180 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
181 input VarData varData;
182 input EqData eqData;
183 protected
184 Status solve_status;
185 StrongComponent implicit_comp;
186 algorithm
187 try
188 (solved_comps, solve_status) := match comp
189 local
190 Pointer<Equation> eqn_ptr;
191 Equation eqn;
192 Slice<VariablePointer> var_slice;
193 Slice<EquationPointer> eqn_slice;
194 ComponentRef var_cref;
195 StrongComponent generic_comp, solved_comp;
196 list<StrongComponent> entwined_slices = {};
197 Tearing strict;
198 list<StrongComponent> tmp, inner_comps = {}, failed_inner = {};
199 String err_str;
200
201 Algorithm alg;
202 list<ComponentRef> solved_crefs, inputs, outputs;
203 UnorderedSet<ComponentRef> output_crefs, input_crefs, solved_inputs;
204 list<tuple<ComponentRef, ComponentRef>> tmp_crefs;
205 list<Pointer<Variable>> tmp_vars;
206 Boolean is_mixed;
207 list<Pointer<Equation>> tmp_eqns;
208 Pointer<Integer> idx;
209 UnorderedMap<ComponentRef, ComponentRef> cref_repl;
210 UnorderedMap<ComponentRef, Expression> exp_repl;
211
212 case StrongComponent.SINGLE_COMPONENT() algorithm
213 1867 (eqn, solve_status, implicit_index) := solveSingleStrongComponent(Pointer.access(comp.eqn), Pointer.access(comp.var), funcMap, kind, implicit_index, slicing_map, varData, eqData);
214 3734 then ({StrongComponent.SINGLE_COMPONENT(comp.var, Pointer.create(eqn), solve_status)}, solve_status);
215
216 // solve component that was simplified
217 case StrongComponent.MULTI_COMPONENT(vars = {var_slice}) guard(not Equation.isCompound(Slice.getT(comp.eqn))) algorithm
218 // var_slice can be a genuine partial slice; resolve the cref that actually
219 // occurs in the equation with the matching size instead of the bare declared
220 // name (var_cref is documented to carry subscripts, see the comment below).
221 ✗ var_cref := if Slice.isFull(var_slice) then BVariable.getVarName(Slice.getT(var_slice))
222 else Slice.resolveSlicedCref(BVariable.getVarName(Slice.getT(var_slice)), Pointer.access(Slice.getT(comp.eqn)), Slice.size(var_slice, function BVariable.size(resize = false)));
223 ✗ (solved_comps, implicit_index) := solveStrongComponent(StrongComponent.createSliceOrSingle(var_cref, var_slice, comp.eqn), funcMap, kind, implicit_index, slicing_map, varData, eqData);
224 ✗ then (solved_comps, Status.UNPROCESSED); // status is unknown, but does not matter because errors were handled in the recursive call.
225
226 case StrongComponent.MULTI_COMPONENT() algorithm
227 143 eqn_ptr := Slice.getT(comp.eqn);
228 143 eqn := Pointer.access(eqn_ptr);
229 (solved_comp, solve_status) := match eqn
230 case Equation.ALGORITHM(alg = alg) algorithm
231 // X = set of solved variables
232
4/4
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 40 times.
128 solved_crefs := list(BVariable.getVarName(Slice.getT(var)) for var in comp.vars);
233
234 // Y = set of inputs
235
4/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 40 times.
95 inputs := List.flatten(list(BVariable.getRecordChildrenCrefOrSelf(i) for i in alg.inputs));
236 40 input_crefs := UnorderedSet.fromList(inputs, ComponentRef.hash, ComponentRef.isEqual);
237 // Y^ <- Y n X set of inputs that are solved (iteration vars, no need to create temporary variables)
238 40 solved_inputs := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
239
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 40 times.
128 for solved_cref in solved_crefs loop
240
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
88 if UnorderedSet.contains(solved_cref, input_crefs) then
241 ✗ UnorderedSet.add(solved_cref, solved_inputs);
242 end if;
243 end for;
244
245 // Z = set of outputs
246
4/4
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 40 times.
128 outputs := List.flatten(list(BVariable.getRecordChildrenCrefOrSelf(o) for o in alg.outputs));
247 40 output_crefs := UnorderedSet.fromList(outputs, ComponentRef.hash, ComponentRef.isEqual);
248 // Z^ <- Z \ X set of outputs that are not solved
249
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 40 times.
128 for solved_cref in solved_crefs loop
250 88 UnorderedSet.remove(solved_cref, output_crefs);
251 end for;
252
253
1/2
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
40 if UnorderedSet.isEmpty(output_crefs) then
254 // EXPLICIT: no unsolved outputs
255 40 (eqn_slice, solve_status, implicit_index) := solveMultiStrongComponent(comp.eqn, comp.vars, funcMap, kind, implicit_index, slicing_map, Iterator.EMPTY(), varData, eqData);
256 40 solved_comp := StrongComponent.MULTI_COMPONENT(comp.vars, eqn_slice, solve_status);
257 else
258 // IMPLICIT: some outputs are not solved in the body
259 ✗ solve_status := Status.IMPLICIT;
260 ✗ idx := Pointer.create(0);
261 // create temporary variables for all outputs that are not solved for
262 ✗ tmp_crefs := list((cref, BVariable.makeTmpVar(cref)) for cref in UnorderedSet.toList(output_crefs));
263 ✗ tmp_vars := list(BVariable.getVarPointer(Util.tuple22(tpl), sourceInfo()) for tpl in tmp_crefs);
264
265 // create equations equalizing the temporary variables and the real ones
266 // res = z - z^ for z^ in Z^
267 ✗ tmp_eqns := list(Equation.makeAssignment(
268 lhs = Expression.fromCref(Util.tuple21(tpl)),
269 rhs = Expression.fromCref(Util.tuple22(tpl)),
270 idx = idx,
271 str = "TMP",
272 iter = Iterator.EMPTY(),
273 attr = EquationAttributes.default(NBEquation.EquationKind.CONTINUOUS, false)) for tpl in tmp_crefs);
274 // make residual equations
275 ✗ tmp_eqns := list(Equation.createResidual(e) for e in tmp_eqns);
276 // create z -> z^ replacements for the algorithm body and for the algorithm outputs
277 ✗ cref_repl := UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual);
278 ✗ exp_repl := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
279 ✗ for tpl in tmp_crefs loop
280 ✗ UnorderedMap.add(Util.tuple21(tpl), Util.tuple22(tpl), cref_repl);
281 ✗ UnorderedMap.add(Util.tuple21(tpl), Expression.fromCref(Util.tuple22(tpl)), exp_repl);
282 end for;
283 // replace z -> z^ in the algorithm outputs and set the solved variables to those outputs
284 ✗ alg.outputs := list(UnorderedMap.getOrDefault(c, cref_repl, c) for c in eqn.alg.outputs);
285 ✗ comp.vars := list(Slice.SLICE(BVariable.getVarPointer(c, sourceInfo()), {}) for c in alg.outputs);
286 ✗ comp.status := Status.EXPLICIT;
287 ✗ eqn.alg := alg;
288 // replace z -> z^ in the algorithm body
289 ✗ Pointer.update(eqn_ptr, Equation.map(eqn, function Replacements.applySimpleExp(replacements = exp_repl)));
290 // create the algebraic loop, already torn
291 ✗ strict := Tearing.TEARING_SET(list(Slice.SLICE(BVariable.getVarPointer(c, sourceInfo()), {}) for c in UnorderedSet.toList(solved_inputs)), list(Slice.SLICE(e, {}) for e in tmp_eqns), listArray({comp}), NONE());
292 // a loop over discrete variables is mixed and must not get a Jacobian
293 ✗ is_mixed := List.any(list(Slice.getT(v) for v in strict.iteration_vars), function BVariable.isDiscontinuous(staticAsContinuous = Partition.kindIsInitial(kind)));
294 // ToDo: set the other booleans correctly
295 ✗ solved_comp := StrongComponent.ALGEBRAIC_LOOP(implicit_index, strict, NONE(), false, is_mixed, false, solve_status, true);
296
297 // add new equations and new variables
298 ✗ EqData.addTypedList(eqData, tmp_eqns, NBEquation.EqData.EqType.CONTINUOUS);
299 ✗ VarData.addTypedList(varData, tmp_vars, NBVariable.VarData.VarType.ALGEBRAIC);
300 // ToDo: create sub routine for this (?)
301 end if;
302 40 then (solved_comp, solve_status);
303
304 else algorithm
305 103 (eqn_slice, solve_status, implicit_index) := solveMultiStrongComponent(comp.eqn, comp.vars, funcMap, kind, implicit_index, slicing_map, Iterator.EMPTY(), varData, eqData);
306 103 then (StrongComponent.MULTI_COMPONENT(comp.vars, eqn_slice, solve_status), solve_status);
307 end match;
308 143 then ({solved_comp}, solve_status);
309
310 case StrongComponent.ALGEBRAIC_LOOP(strict = strict) algorithm
311
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 8 times.
553 for index in arrayLength(strict.innerEquations):-1:1 loop
312 403 (tmp, implicit_index) := solveStrongComponent(strict.innerEquations[index], funcMap, kind, implicit_index, slicing_map, varData, eqData);
313 403 inner_comps := listAppend(tmp, inner_comps);
314
2/2
✓ Branch 0 taken 403 times.
✓ Branch 1 taken 403 times.
806 for elem in tmp loop
315
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 403 times.
403 if StrongComponent.getSolveStatus(elem) <> NBSolve.Status.EXPLICIT then
316 failed_inner := elem :: failed_inner;
317 end if;
318 end for;
319 end for;
320 // throw an error if there are non explicit inner equations
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 if not listEmpty(failed_inner) then
322 ✗ if Flags.isSet(Flags.TEARING_DUMP) then
323 ✗ err_str := " Following inner equations could not be solved explicitely:\n"
324 + List.toString(failed_inner, function StrongComponent.toString(index = -1), List.Style.NEWLINE);
325 else
326 err_str := " Use -d=tearingdump for more information.";
327 end if;
328 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. " + err_str});
329 ✗ fail();
330 end if;
331
332 75 strict.innerEquations := listArray(inner_comps);
333 75 comp.strict := strict;
334 75 comp.status := Status.IMPLICIT;
335 75 then ({comp}, Status.IMPLICIT);
336
337 // a whole dimension in the cref to solve (e.g. x[i, :] for x[i, j] inside a reduction over j)
338 // does not determine the solved elements, solve each matched element separately
339 case StrongComponent.SLICED_COMPONENT() guard(StrongComponent.solvesInsideReduction(Slice.getT(comp.eqn), comp.var_cref)) algorithm
340 ✗ (tmp, solve_status) := solveSliceElementwise(comp.var, comp.eqn, funcMap);
341 then (tmp, solve_status);
342
343 case StrongComponent.SLICED_COMPONENT() guard(Equation.isForEquation(Slice.getT(comp.eqn))) algorithm
344 79 (generic_comp, solve_status, implicit_index) := solveGenericEquation(comp, funcMap, kind, implicit_index, slicing_map, varData, eqData);
345 79 then ({generic_comp}, solve_status);
346
347 case StrongComponent.SLICED_COMPONENT() guard(Equation.isArrayEquation(Slice.getT(comp.eqn))) algorithm
348 // array equation solved for the a sliced variable.
349 // get all slices of the variable occurring in the equation and select the slice that fits the indices
350 226 (eqn_slice, implicit_index, solve_status) := solveForVarSlice(comp.eqn, comp.var, comp.var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
351 226 comp.eqn := eqn_slice;
352 226 comp.status := solve_status;
353 226 then ({comp}, solve_status);
354
355 case StrongComponent.SLICED_COMPONENT() algorithm
356 // just a regular equation solved for a sliced variable
357 // use cref instead of var because it has subscripts!
358 1045 (eqn, solve_status, implicit_index) := solveSingleStrongComponent(Pointer.access(Slice.getT(comp.eqn)), Variable.fromCref(comp.var_cref), funcMap, kind, implicit_index, slicing_map, varData, eqData);
359
2/2
✓ Branch 0 taken 1030 times.
✓ Branch 1 taken 15 times.
1045 if solve_status == Status.EXPLICIT then
360 // successfully solved explicitly; use result directly
361 2060 comp.eqn := Slice.SLICE(Pointer.create(eqn), {});
362 // the matched cref has resizable parameters in its subscripts replaced by
363 // their values (x[N+1] -> x[11]), the solved equation keeps them symbolic
364 1030 comp.var_cref := solvedCref(eqn, comp.var_cref);
365 else
366 // IMPLICIT (cref hidden inside array expression) or UNSOLVABLE:
367 // try expanding array sums to find an explicit solution for the slice
368 15 (eqn_slice, implicit_index, solve_status) := solveForVarSlice(comp.eqn, comp.var, comp.var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
369
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 comp.eqn := eqn_slice;
370
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if solve_status == Status.EXPLICIT then
371 15 comp.var_cref := solvedCref(Pointer.access(Slice.getT(eqn_slice)), comp.var_cref);
372 end if;
373 end if;
374 1045 comp.status := solve_status;
375 1045 then ({comp}, solve_status);
376
377 case StrongComponent.RESIZABLE_COMPONENT() algorithm
378 // a resizable component with trivial solution
379 // ToDo 1: resolve the eval order
380 // ToDo 2: resolve potential equation slicing
381 297 (eqn, solve_status, implicit_index, _) := solveEquation(Pointer.access(Slice.getT(comp.eqn)), comp.var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
382 297 eqn := Equation.applyForOrder(eqn, comp.order);
383 594 comp.eqn := Slice.SLICE(Pointer.create(eqn), comp.eqn.indices);
384 297 comp.status := solve_status;
385 297 then ({comp}, solve_status);
386
387 /* for now handle all entwined equations generically and don't try to solve */
388 case StrongComponent.ENTWINED_COMPONENT() algorithm
389
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for slice in comp.entwined_slices loop
390 6 (generic_comp, solve_status, implicit_index) := solveGenericEquation(slice, funcMap, kind, implicit_index, slicing_map, varData, eqData);
391 // make loop on any solve_status != explicit
392 entwined_slices := generic_comp :: entwined_slices;
393 end for;
394 2 comp.entwined_slices := listReverse(entwined_slices);
395 2 then ({comp}, NBSolve.Status.EXPLICIT);
396
397 ✗ else ({comp}, Status.UNSOLVABLE);
398 end match;
399 else
400 // this fails in the next case because of the unsolvable status
401 ✗ (solved_comps, solve_status) := ({comp}, Status.UNSOLVABLE);
402 end try;
403
404 // solve implicit equation (algebraic loop is always implicit)
405
3/4
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 3649 times.
✓ Branch 3 taken 85 times.
✗ Branch 4 not taken.
3734 if solve_status == Status.IMPLICIT and List.hasOneElement(solved_comps) then
406 85 (implicit_comp, implicit_index) := Tearing.implicit(
407 comp = listHead(solved_comps),
408 funcMap = funcMap,
409 index = implicit_index,
410 kind = kind
411 );
412 solved_comps := {implicit_comp};
413 elseif solve_status > Status.EXPLICIT then
414 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed with status = " + statusString(solve_status)
415 + " while trying to solve following strong component:\n" + StrongComponent.toString(comp) + "\n"});
416 ✗ fail();
417 end if;
418 end solveStrongComponent;
419
420 function solveGenericEquation
421 input output StrongComponent comp;
422 input UnorderedMap<Path, Function> funcMap;
423 input BPartition.Kind kind;
424 output Status solve_status;
425 input output Integer implicit_index;
426 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
427 input VarData varData;
428 input EqData eqData;
429 algorithm
430 (comp, solve_status) := match comp
431 local
432 Slice<VariablePointer> var_slice;
433 Slice<EquationPointer> eqn_slice;
434 Equation eqn;
435
436 case StrongComponent.SLICED_COMPONENT(var = var_slice, eqn = eqn_slice) guard(Equation.isForEquation(Slice.getT(eqn_slice))) algorithm
437 79 (comp, solve_status, implicit_index) := solveGenericEquationSlice(var_slice, eqn_slice, comp.var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
438 79 then (comp, solve_status);
439
440 // ToDo: make these actually resizable inside entwined equations (?)
441 case StrongComponent.RESIZABLE_COMPONENT(var = var_slice, eqn = eqn_slice) guard(Equation.isForEquation(Slice.getT(eqn_slice))) algorithm
442 6 eqn_slice := Slice.apply(eqn_slice, function Pointer.apply(func = function Equation.applyForOrder(order = comp.order)));
443 6 (comp, solve_status, implicit_index) := solveGenericEquationSlice(var_slice, eqn_slice, comp.var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
444 6 then (comp, solve_status);
445
446 // Scalar component inside an entwined block (no subscripts, full slice)
447 case StrongComponent.SINGLE_COMPONENT() algorithm
448 ✗ (eqn, solve_status, implicit_index) := solveSingleStrongComponent(Pointer.access(comp.eqn), Pointer.access(comp.var), funcMap, kind, implicit_index, slicing_map, varData, eqData);
449 ✗ comp := StrongComponent.SINGLE_COMPONENT(comp.var, Pointer.create(eqn), solve_status);
450 ✗ then (comp, solve_status);
451
452 // Scalar component with subscripted variable (e.g. x[1] = ...) inside an entwined block
453 case StrongComponent.SLICED_COMPONENT() algorithm
454 ✗ (eqn, solve_status, implicit_index) := solveSingleStrongComponent(Pointer.access(Slice.getT(comp.eqn)), Variable.fromCref(comp.var_cref), funcMap, kind, implicit_index, slicing_map, varData, eqData);
455 ✗ if solve_status < Status.UNSOLVABLE then
456 ✗ comp.eqn := Slice.SLICE(Pointer.create(eqn), comp.eqn.indices);
457 end if;
458 ✗ comp.status := solve_status;
459 then (comp, solve_status);
460
461 // Compound equation (when/algorithm/if) inside an entwined block
462 case StrongComponent.MULTI_COMPONENT() algorithm
463 ✗ (eqn_slice, solve_status, implicit_index) := solveMultiStrongComponent(comp.eqn, comp.vars, funcMap, kind, implicit_index, slicing_map, Iterator.EMPTY(), varData, eqData);
464 ✗ comp := StrongComponent.MULTI_COMPONENT(comp.vars, eqn_slice, solve_status);
465 ✗ then (comp, solve_status);
466
467 else algorithm
468 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for:\n" + StrongComponent.toString(comp) + "\n"});
469 ✗ then fail();
470 end match;
471 end solveGenericEquation;
472
473 function solveGenericEquationSlice
474 input Slice<VariablePointer> var_slice;
475 input Slice<EquationPointer> eqn_slice;
476 input ComponentRef cref;
477 output StrongComponent comp;
478 output Status solve_status;
479 input UnorderedMap<Path, Function> functions;
480 input BPartition.Kind kind;
481 input output Integer implicit_index;
482 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
483 input VarData varData;
484 input EqData eqData;
485 protected
486 Pointer<Equation> eqn_ptr = Slice.getT(eqn_slice);
487 Equation eqn;
488 Slice<EquationPointer> solved_slice;
489 UnorderedMap<ComponentRef, Expression> replacements;
490 Option<ComponentRef> iter_cref;
491 ComponentRef solve_cref = cref;
492 algorithm
493 // single iterations of resizable loops stay generic, their index is only known at the analysis sizes
494
4/4
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 51 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 32 times.
85 if List.hasOneElement(eqn_slice.indices) and isResizableForSlice(eqn_slice) then
495 2 iter_cref := iteratorCref(eqn_ptr, listHead(eqn_slice.indices), cref);
496 else
497 iter_cref := NONE();
498 end if;
499
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 83 times.
✓ Branch 3 taken 2 times.
85 if isSome(iter_cref) then
500 2 solve_cref := Util.getOption(iter_cref);
501 2 (eqn, solve_status, implicit_index, _) := solveEquation(Pointer.access(eqn_ptr), solve_cref, functions, kind, implicit_index, slicing_map, varData, eqData);
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if solve_status >= Status.UNSOLVABLE then
503 // solve the single iteration instead
504 solve_cref := cref;
505 ✗ (eqn, solve_status) := Equation.singleSlice(eqn_ptr, listHead(eqn_slice.indices), Equation.sizes(eqn_ptr, true), cref,
506 UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual), functions);
507 end if;
508 elseif List.hasOneElement(eqn_slice.indices) then
509 32 replacements := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
510 // the index refers to the analysis sizes of resizable arrays
511 32 (eqn, solve_status) := Equation.singleSlice(eqn_ptr, listHead(eqn_slice.indices), Equation.sizes(eqn_ptr, Flags.getConfigBool(Flags.RESIZABLE_ARRAYS)), cref, replacements, functions);
512 else
513 51 (eqn, solve_status, implicit_index, _) := solveEquation(Pointer.access(eqn_ptr), cref, functions, kind, implicit_index, slicing_map, varData, eqData);
514 end if;
515
516 // ToDo: if solve_status not explicit -> algebraic loop with residual and Status.IMPLICIT
517
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if solve_status < Status.UNSOLVABLE then
518 85 solved_slice := Slice.SLICE(Pointer.create(eqn), eqn_slice.indices);
519 else
520 ✗ (solved_slice, implicit_index, solve_status) := solveForVarSlice(eqn_slice, var_slice, cref, functions, kind, implicit_index, slicing_map, varData, eqData);
521 end if;
522
523 // create a generic component if its a for-equation, otherwise create a sliced component
524
2/2
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 32 times.
85 if Equation.isForEquation(Slice.getT(solved_slice)) then
525 53 comp := StrongComponent.GENERIC_COMPONENT(solve_cref, var_slice, solved_slice);
526 else
527 32 comp := StrongComponent.SLICED_COMPONENT(cref, var_slice, solved_slice, solve_status);
528 end if;
529 end solveGenericEquationSlice;
530
531 function isResizableForSlice
532 "a slice of a for-equation with a scalar body over a resizable range"
533 input Slice<EquationPointer> eqn_slice;
534 output Boolean b = false;
535 protected
536 Pointer<Equation> eqn_ptr = Slice.getT(eqn_slice);
537 Iterator iter;
538 algorithm
539
3/4
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
34 if Flags.getConfigBool(Flags.RESIZABLE_ARRAYS) and Equation.isForEquation(eqn_ptr) then
540 2 iter := Equation.getForIterator(Pointer.access(eqn_ptr));
541
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 b := Iterator.isResizable(iter) and Equation.size(eqn_ptr, true) == Iterator.size(iter, true);
542 end if;
543 end isResizableForSlice;
544
545 function iteratorCref
546 "the occurrence of the variable of cref in the body of a for-equation that is
547 cref in the iteration of the scalar index"
548 input Pointer<Equation> eqn_ptr;
549 input Integer index;
550 input ComponentRef cref;
551 output Option<ComponentRef> res = NONE();
552 protected
553 UnorderedMap<ComponentRef, Expression> replacements = UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
554 ComponentRef stripped = ComponentRef.stripSubscriptsAll(cref), target = ComponentRef.simplifySubscripts(cref);
555 Expression e;
556 algorithm
557 2 Iterator.createLocationReplacements(Equation.getForIterator(Pointer.access(eqn_ptr)), listArray(Slice.indexToLocation(index, Equation.sizes(eqn_ptr, true))), replacements);
558
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 for c in Equation.collectCrefs(Pointer.access(eqn_ptr), function sameVariable(stripped = stripped)) loop
559 2 e := SimplifyExp.simplify(Expression.map(Expression.fromCref(c), function Replacements.applySimpleExp(replacements = replacements)));
560
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
2 if Expression.isCref(e) and ComponentRef.isEqual(ComponentRef.simplifySubscripts(Expression.toCref(e)), target) then
561 res := SOME(c);
562 2 return;
563 end if;
564 end for;
565 end iteratorCref;
566
567 function sameVariable extends Slice.filterCref;
568 input ComponentRef stripped;
569 algorithm
570
2/2
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 2 times.
26 if ComponentRef.isEqual(ComponentRef.stripSubscriptsAll(cref), stripped) then
571 2 UnorderedSet.add(cref, acc);
572 end if;
573 end sameVariable;
574
575 function solveSliceElementwise
576 "Solves each matched element of a sliced for-equation separately for its variable element.
577 Used if the variable occurs inside a reduction over one of its dimensions, e.g. solving
578 y[i] = sum(x[i, j] for j in 1:n) for x[i, 1]: the reductions are expanded for each element."
579 input Slice<VariablePointer> var_slice;
580 input Slice<EquationPointer> eqn_slice;
581 input UnorderedMap<Path, Function> funcMap;
582 output list<StrongComponent> comps = {};
583 output Status solve_status = Status.EXPLICIT;
584 protected
585 Pointer<Equation> eqn_ptr = Slice.getT(eqn_slice);
586 Pointer<Variable> var_ptr = Slice.getT(var_slice);
587 list<Integer> eqn_sizes = Equation.sizes(eqn_ptr);
588 list<Dimension> dims = Type.arrayDims(Variable.typeOf(Pointer.access(var_ptr)));
589 list<Integer> var_sizes = list(Dimension.size(dim) for dim in dims);
590 list<Integer> vals;
591 list<Subscript> subs;
592 ComponentRef cref;
593 Equation eqn;
594 Status status;
595 algorithm
596 // equation and variable indices are aligned by the matching
597 ✗ for tpl in List.zip(eqn_slice.indices, var_slice.indices) loop
598 // the scalar element of the variable
599 ✗ vals := listReverse(Slice.indexToLocation(Util.tuple22(tpl), var_sizes));
600 ✗ subs := list(Subscript.nth(dim, val + 1) threaded for dim in dims, val in vals);
601 ✗ cref := ComponentRef.mergeSubscripts(subs, BVariable.getVarName(var_ptr), true, true);
602 // the scalar equation with expanded reductions
603 ✗ (eqn, _) := Equation.singleSlice(eqn_ptr, Util.tuple21(tpl), eqn_sizes, ComponentRef.EMPTY(),
604 UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual), funcMap);
605 ✗ eqn := Equation.map(eqn, expandReduction);
606 ✗ (eqn, status, _) := solveBody(eqn, cref, funcMap);
607 ✗ solve_status := if status > solve_status then status else solve_status;
608 ✗ comps := StrongComponent.SLICED_COMPONENT(cref, Slice.SLICE(var_ptr, {Util.tuple22(tpl)}),
609 Slice.SLICE(Pointer.create(eqn), {}), status) :: comps;
610 end for;
611 ✗ comps := listReverse(comps);
612 end solveSliceElementwise;
613
614 function expandReduction
615 "expands sum and product reductions over constant ranges, e.g.
616 sum(x[1, j] for j in 1:3) -> x[1, 1] + x[1, 2] + x[1, 3]"
617 input output Expression exp;
618 protected
619 Call call;
620 String name;
621 Type ty;
622 Operator op;
623 Expression default_exp, range;
624 Boolean expanded;
625 list<tuple<NFInstNode.InstNode, Expression>> iters = {};
626 algorithm
627 exp := match exp
628 case Expression.CALL(call = call as Call.TYPED_REDUCTION()) algorithm
629 ✗ name := AbsynUtil.pathString(Function.name(call.fn));
630 ✗ ty := Expression.typeOf(call.exp);
631 ✗ if not (name == "sum" or name == "product") or Type.isRecord(Type.arrayElementType(ty)) then
632 ✗ return;
633 end if;
634 ✗ for iter in call.iters loop
635 ✗ (range, expanded) := ExpandExp.expand(Util.tuple22(iter));
636 ✗ if not expanded then
637 ✗ return;
638 end if;
639 ✗ iters := (Util.tuple21(iter), range) :: iters;
640 end for;
641 ✗ (default_exp, op) := if name == "sum" then (Expression.makeZero(ty), Operator.makeAdd(ty))
642 else (Expression.makeOne(ty), Operator.makeMul(ty));
643 ✗ then SimplifyExp.simplify(Expression.foldReduction(call.exp, listReverse(iters), default_exp,
644 function SimplifyExp.simplify(includeScope = false), function makeBinary(op = op)));
645 else exp;
646 end match;
647 end expandReduction;
648
649 function makeBinary
650 input Expression exp1;
651 input Expression exp2;
652 input Operator op;
653 output Expression exp = Expression.BINARY(exp1, op, exp2);
654 end makeBinary;
655
656 function solvedCref
657 "The cref an explicitly solved equation assigns, if it is the variable of
658 var_cref (same name, possibly other but equal subscripts), else var_cref.
659 Only without scalarization: scalarized, the evaluated element is the variable."
660 input Equation eqn;
661 input output ComponentRef var_cref;
662 algorithm
663
2/2
✓ Branch 1 taken 887 times.
✓ Branch 2 taken 158 times.
1045 if Flags.getConfigBool(Flags.SIM_CODE_SCALARIZE) then
664 887 return;
665 end if;
666 var_cref := match Equation.getLHS(eqn)
667 local
668 ComponentRef lhs;
669 case SOME(Expression.CREF(cref = lhs))
670 guard ComponentRef.isEqual(ComponentRef.stripSubscriptsAll(lhs), ComponentRef.stripSubscriptsAll(var_cref))
671 then lhs;
672 else var_cref;
673 end match;
674 end solvedCref;
675
676 function solveSingleStrongComponent
677 input output Equation eqn;
678 input Variable var;
679 input UnorderedMap<Path, Function> funcMap;
680 input BPartition.Kind kind;
681 output Status status;
682 input output Integer implicit_index;
683 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
684 input VarData varData;
685 input EqData eqData;
686 protected
687 ComponentRef var_cref;
688 algorithm
689
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2912 times.
2912 if ComponentRef.isEmpty(var.name) then
690 // empty variable name implies equation without return value
691 ✗ (eqn, status) := (eqn, Status.EXPLICIT);
692 else
693 2912 (var_cref, status) := getVarSlice(var.name, SOME(var.name), eqn);
694
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2897 times.
2912 var_cref := if status < Status.UNSOLVABLE then var_cref else var.name;
695 2912 (eqn, status, implicit_index, _) := solveEquation(eqn, var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
696 end if;
697 end solveSingleStrongComponent;
698
699 function solveMultiStrongComponent
700 input output Slice<EquationPointer> eqn_slice;
701 input list<Slice<VariablePointer>> var_slices;
702 input UnorderedMap<Path, Function> funcMap;
703 input BPartition.Kind kind;
704 output Status status;
705 input output Integer implicit_index;
706 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
707 input Iterator iter;
708 input VarData varData;
709 input EqData eqData;
710 protected
711 Equation eqn = Pointer.access(Slice.getT(eqn_slice));
712 algorithm
713 (eqn_slice, status) := match eqn
714 local
715 Equation solved_eqn;
716 IfEquationBody if_body;
717
718 case Equation.IF_EQUATION() algorithm
719
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
6 (if_body, status, implicit_index) := solveIfBody(eqn.body, VariablePointers.fromList(list(Slice.getT(v) for v in var_slices)), funcMap, kind, implicit_index, slicing_map, iter, varData, eqData);
720 // keep the original equation if a branch is implicit, it is used as the residual
721
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if status == Status.EXPLICIT then
722 3 eqn.body := if_body;
723 3 eqn_slice := Slice.SLICE(Pointer.create(eqn), eqn_slice.indices);
724 end if;
725 3 then (eqn_slice, status);
726
727 // ToDo: inverse algorithms
728 case Equation.ALGORITHM()
729 40 then (Slice.SLICE(Pointer.clone(Slice.getT(eqn_slice)), eqn_slice.indices), Status.EXPLICIT);
730
731 // for now assume they are solved
732 case Equation.WHEN_EQUATION()
733 51 then (Slice.SLICE(Pointer.clone(Slice.getT(eqn_slice)), eqn_slice.indices), Status.EXPLICIT);
734
735 // solve record and tuple equations
736 case Equation.RECORD_EQUATION() algorithm
737 51 (solved_eqn, status) := solveMultiRecordStrongComponent(eqn, var_slices, funcMap);
738 51 then (Slice.SLICE(Pointer.create(solved_eqn), eqn_slice.indices), status);
739
740 // solve arrays of record and tuple equations
741 case Equation.ARRAY_EQUATION() algorithm
742 ✗ (solved_eqn, status) := solveMultiRecordStrongComponent(eqn, var_slices, funcMap);
743 ✗ then (Slice.SLICE(Pointer.create(solved_eqn), eqn_slice.indices), status);
744
745 // for-equation of a tuple, e.g. (a[i], b[i]) = f(x[i]), solved for all elements of the tuple
746 case Equation.FOR_EQUATION(body = {solved_eqn as Equation.RECORD_EQUATION()}) algorithm
747 1 (solved_eqn, status) := solveMultiRecordStrongComponent(solved_eqn, var_slices, funcMap, true);
748 1 eqn.body := {solved_eqn};
749 1 then (Slice.SLICE(Pointer.create(eqn), eqn_slice.indices), status);
750
751 // dummy equation implies removed equation (occurs only in simulation systems)
752 case Equation.DUMMY_EQUATION() then (eqn_slice, Status.EXPLICIT);
753
754 else algorithm
755 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for equation:\n" + Slice.toString(eqn_slice, function Equation.pointerToString(str = ""))});
756 ✗ then fail();
757 end match;
758 end solveMultiStrongComponent;
759
760 function solveMultiRecordStrongComponent
761 input Equation eqn "has to be RECORD_EQUATION or ARRAY_EQUATION";
762 input list<Slice<Pointer<Variable>>> var_slices;
763 output Equation solved_eqn = eqn;
764 input UnorderedMap<Path, Function> funcMap;
765 output Status status = Status.UNPROCESSED;
766 input Boolean inFor = false "true if the equation is the body of a for-equation, its tuple elements are iterated slices of the vars";
767 protected
768 list<Pointer<Variable>> vars = list(Slice.getT(v) for v in var_slices);
769 Expression lhs = Util.getOption(Equation.getLHS(eqn));
770 Expression rhs = Util.getOption(Equation.getRHS(eqn));
771 UnorderedSet<ComponentRef> record_crefs;
772 ComponentRef var_cref;
773 algorithm
774 (solved_eqn, status) := match (lhs, rhs)
775 local
776 Expression exp;
777
778 // handle tuples
779 case (exp as Expression.TUPLE(), _) guard(tupleSolvable(exp.elements, vars, inFor)) then (solved_eqn, Status.EXPLICIT);
780 case (_, exp as Expression.TUPLE()) guard(tupleSolvable(exp.elements, vars, inFor)) algorithm
781 ✗ solved_eqn := Equation.setRHS(solved_eqn, lhs);
782 ✗ solved_eqn := Equation.setLHS(solved_eqn, rhs);
783 then (solved_eqn, Status.EXPLICIT);
784
785 // handle records
786 else algorithm
787 // check if all belong to the same record
788 51 record_crefs := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
789
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 51 times.
171 for var_slice in var_slices loop
790 120 var_cref := BVariable.getVarName(Slice.getT(var_slice));
791 120 (var_cref, status) := getVarSlice(var_cref, SOME(var_cref), eqn);
792 120 UnorderedSet.add(var_cref, record_crefs);
793
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if status == Status.UNSOLVABLE then break; end if;
794 end for;
795
796 solved_eqn := match (UnorderedSet.toList(record_crefs), status)
797 case ({var_cref}, Status.UNPROCESSED) algorithm
798 51 (solved_eqn, status, _) := solveBody(eqn, var_cref, funcMap);
799 then solved_eqn;
800 else eqn;
801 end match;
802
803 51 then (solved_eqn, status);
804 end match;
805 end solveMultiRecordStrongComponent;
806
807 function solveEquation
808 input output Equation eqn;
809 input ComponentRef cref;
810 input UnorderedMap<Path, Function> funcMap;
811 input BPartition.Kind kind;
812 output Status status;
813 input output Integer implicit_index;
814 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
815 output RelationInversion invertRelation "If the equation represents a relation, this tells if the sign should be inverted";
816 input VarData varData;
817 input EqData eqData;
818 algorithm
819 (eqn, status, invertRelation) := match eqn
820 local
821 Equation body;
822 Slice<EquationPointer> body_slice;
823 Pointer<Variable> indexed_var;
824 Iterator dummy;
825
826 // For equations are expected to only have one body equation at this point
827 case Equation.FOR_EQUATION(body = {body as Equation.IF_EQUATION()}) algorithm
828 // create indexed variable to trick matching algorithm to solve for it
829 3 indexed_var := BVariable.makeVarPtr(BVariable.getVar(cref, sourceInfo()), cref);
830 3 dummy := Iterator.dummy(eqn.iter);
831 6 (body_slice, status, implicit_index) := solveMultiStrongComponent(Slice.SLICE(Pointer.create(body), {}), {Slice.SLICE(indexed_var, {})}, funcMap, kind, implicit_index, slicing_map, dummy, varData, eqData);
832 6 eqn.body := {Pointer.access(Slice.getT(body_slice))};
833 3 then (eqn, status, RelationInversion.FALSE);
834
835 case Equation.FOR_EQUATION(body = {body}) algorithm
836 347 (body, status, invertRelation) := solveBody(body, cref, funcMap);
837 347 eqn.body := {body};
838 347 then (eqn, status, invertRelation);
839
840 case Equation.FOR_EQUATION() algorithm
841 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
842 + " failed to solve a for-equation with multiple body eqns for a single cref. Please iterate over body elements individually.\n"
843 + "cref: " + ComponentRef.toString(cref) + " in equation:\n" + Equation.toString(eqn)});
844 ✗ then fail();
845
846 // dummy equation implies removed equation (occurs only in simulation systems)
847 ✗ case Equation.DUMMY_EQUATION() then (eqn, Status.EXPLICIT, RelationInversion.FALSE);
848
849 3153 else solveBody(eqn, cref, funcMap);
850 end match;
851 end solveEquation;
852
853 function singleElement
854 "expands an array expression of size one and returns its only scalar element"
855 input Expression exp;
856 output Option<Expression> element = NONE();
857 protected
858 Expression expanded;
859 Boolean success;
860 algorithm
861
2/2
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
20 if not Type.isArray(Expression.typeOf(exp)) then
862 element := SOME(exp);
863 10 return;
864 end if;
865
866 10 (expanded, success) := ExpandExp.expand(exp, true);
867
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if success then
868 element := match expanded
869 10 case Expression.ARRAY() guard(arrayLength(expanded.elements) == 1) then singleElement(expanded.elements[1]);
870 else NONE();
871 end match;
872 end if;
873 end singleElement;
874
875 function scalarElementEquation
876 "the scalar equation of the only element of an array equation that contains the cref, unchanged if there is none"
877 input Equation eqn;
878 input ComponentRef cref;
879 output Equation result = eqn;
880 protected
881 Expression lhs, rhs;
882 Boolean success_lhs, success_rhs;
883 Integer idx = 0;
884 algorithm
885 () := match eqn
886 case Equation.ARRAY_EQUATION() algorithm
887 24 (lhs, success_lhs) := ExpandExp.expand(eqn.lhs, true);
888 24 (rhs, success_rhs) := ExpandExp.expand(eqn.rhs, true);
889
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 if success_lhs and success_rhs then
890 () := match (lhs, rhs)
891 case (Expression.ARRAY(), Expression.ARRAY()) guard(arrayLength(lhs.elements) == arrayLength(rhs.elements)) algorithm
892
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
96 for i in 1:arrayLength(lhs.elements) loop
893
3/4
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 48 times.
72 if Expression.containsCref(lhs.elements[i], cref) or Expression.containsCref(rhs.elements[i], cref) then
894 // the cref has to be in exactly one element
895
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 idx := if idx == 0 then i else -1;
896 end if;
897 end for;
898
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if idx > 0 then
899 24 result := Equation.SCALAR_EQUATION(Type.arrayElementType(eqn.ty), lhs.elements[idx], rhs.elements[idx], eqn.source, eqn.attr);
900 end if;
901 then ();
902 else ();
903 end match;
904 end if;
905 then ();
906 else ();
907 end match;
908 end scalarElementEquation;
909
910 function arrayElementEquation
911 "the element equation x[k] = e[k] of an array equation x = e"
912 input Equation eqn;
913 input ComponentRef cref;
914 output Equation result = eqn;
915 protected
916 ComponentRef name = ComponentRef.stripSubscriptsAll(cref);
917 list<Subscript> subs = ComponentRef.subscriptsAllWithWholeFlat(cref);
918 algorithm
919 result := match eqn
920 local
921 ComponentRef side;
922 case Equation.ARRAY_EQUATION(lhs = Expression.CREF(cref = side)) guard(ComponentRef.isEqual(side, name))
923 ✗ then Equation.SCALAR_EQUATION(Type.arrayElementType(eqn.ty), Expression.fromCref(cref), Expression.applySubscripts(subs, eqn.rhs, true), eqn.source, eqn.attr);
924 case Equation.ARRAY_EQUATION(rhs = Expression.CREF(cref = side)) guard(ComponentRef.isEqual(side, name))
925 6 then Equation.SCALAR_EQUATION(Type.arrayElementType(eqn.ty), Expression.applySubscripts(subs, eqn.lhs, true), Expression.fromCref(cref), eqn.source, eqn.attr);
926 // expanding the equation for every element is quadratic, only do it for small arrays
927 24 case Equation.ARRAY_EQUATION() guard(Type.sizeOf(eqn.ty) <= 64) then scalarElementEquation(eqn, cref);
928 else eqn;
929 end match;
930 end arrayElementEquation;
931
932 function solveIfBranches
933 "solves every branch of an if-equation with a single equation per branch for the cref"
934 input output Equation eqn;
935 input ComponentRef cref;
936 input UnorderedMap<Path, Function> funcMap;
937 output Status status;
938 protected
939 IfEquationBody body;
940 algorithm
941 (eqn, status) := match eqn
942 case Equation.IF_EQUATION() algorithm
943 ✗ (body, status) := solveIfBranchesBody(eqn.body, cref, funcMap);
944 ✗ eqn.body := body;
945 ✗ then (eqn, status);
946 else (eqn, Status.UNSOLVABLE);
947 end match;
948 end solveIfBranches;
949
950 function solveIfBranchesBody
951 input output IfEquationBody body;
952 input ComponentRef cref;
953 input UnorderedMap<Path, Function> funcMap;
954 output Status status;
955 protected
956 Equation branch_eqn;
957 IfEquationBody else_if;
958 list<ComponentRef> crefs;
959 algorithm
960 ✗ if not List.hasOneElement(body.then_eqns) then
961 ✗ status := Status.UNSOLVABLE;
962 ✗ return;
963 end if;
964 ✗ branch_eqn := Pointer.access(listHead(body.then_eqns));
965 // solve for the element that occurs in the branch, e.g. p[2] for p
966 ✗ crefs := UnorderedSet.unique_list(Equation.collectCrefs(branch_eqn, function Slice.getSliceCandidates(name = ComponentRef.stripSubscriptsAll(cref))), ComponentRef.hash, ComponentRef.isEqual);
967 ✗ if not List.hasOneElement(crefs) then
968 ✗ status := Status.UNSOLVABLE;
969 ✗ return;
970 end if;
971 ✗ (branch_eqn, status, _) := solveBody(branch_eqn, listHead(crefs), funcMap);
972 ✗ if status <> Status.EXPLICIT then
973 ✗ return;
974 end if;
975 ✗ body.then_eqns := {Pointer.create(branch_eqn)};
976 ✗ if isSome(body.else_if) then
977 ✗ (else_if, status) := solveIfBranchesBody(Util.getOption(body.else_if), cref, funcMap);
978 ✗ body.else_if := SOME(else_if);
979 end if;
980 end solveIfBranchesBody;
981
982 function solveBody
983 input output Equation eqn;
984 input ComponentRef cref;
985 input UnorderedMap<Path, Function> funcMap = UnorderedMap.new<Function>(AbsynUtil.pathHash, AbsynUtil.pathEqual);
986 output Status status;
987 output RelationInversion invertRelation "If the equation represents a relation, this tells if the sign should be inverted";
988 protected
989 Type ty;
990 ComponentRef fixed_cref;
991 Expression residual, derivative;
992 Differentiate.DifferentiationArguments diffArgs;
993 algorithm
994 // a row of a for-equation with an if-equation body, solve every branch
995
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7405 times.
7405 if Equation.isIfEquation(Pointer.create(eqn)) then
996 ✗ (eqn, status) := solveIfBranches(eqn, cref, funcMap);
997 ✗ invertRelation := RelationInversion.FALSE;
998 ✗ return;
999 end if;
1000
1001 // fix crefs where the array is of size one
1002 7405 fixed_cref := ComponentRef.stripSubscriptsAll(cref);
1003 7405 ty := ComponentRef.getSubscriptedType(fixed_cref, true);
1004
4/4
✓ Branch 1 taken 2425 times.
✓ Branch 2 taken 4980 times.
✓ Branch 4 taken 253 times.
✓ Branch 5 taken 2172 times.
7405 if Type.isArray(ty) and Type.sizeOf(ty) == 1 then
1005 253 fixed_cref := getVarSlice(fixed_cref, SOME(cref), eqn);
1006 else
1007 7152 fixed_cref := cref;
1008 // a scalar solved from a single element array equation, e.g. mXi = m * Xi for Real[1] mXi, Xi:
1009 // use the scalar equation, otherwise the solution is array valued
1010 eqn := match eqn
1011 local
1012 Option<Expression> lhs, rhs;
1013 case Equation.ARRAY_EQUATION(recordSize = NONE()) guard(not Type.isArray(ty) and Type.sizeOf(eqn.ty) == 1) algorithm
1014 5 lhs := singleElement(eqn.lhs);
1015 5 rhs := singleElement(eqn.rhs);
1016
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 then if isSome(lhs) and isSome(rhs) then Equation.SCALAR_EQUATION(Type.arrayElementType(eqn.ty), Util.getOption(lhs), Util.getOption(rhs), eqn.source, eqn.attr) else eqn;
1017 // a scalar solved from a bigger array equation, e.g. {v, i} = if c then {a, b} else {d, e}
1018 case Equation.ARRAY_EQUATION(recordSize = NONE()) guard(not Type.isArray(ty) and Type.sizeOf(eqn.ty) > 1)
1019 ✗ then scalarElementEquation(eqn, cref);
1020 // an array element solved from an equation for the whole array, e.g. S[2] from S = v .* i
1021 case Equation.ARRAY_EQUATION(recordSize = NONE()) guard(not Type.isArray(ComponentRef.getSubscriptedType(cref, true)))
1022 30 then arrayElementEquation(eqn, cref);
1023 else eqn;
1024 end match;
1025 end if;
1026
1027
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 7402 times.
7405 if Flags.isSet(Flags.DUMP_SOLVE) then
1028 3 solvePrintInput(eqn, fixed_cref);
1029 end if;
1030
1031 7405 (eqn, status, invertRelation) := solveSimple(eqn, fixed_cref);
1032 // if the equation does not have a simple structure try to solve with other strategies
1033
2/2
✓ Branch 0 taken 984 times.
✓ Branch 1 taken 6421 times.
7405 if status == Status.UNPROCESSED then
1034 984 residual := Equation.getResidualExp(eqn);
1035 // call general solving routine, can solve an equation, if a cref is contained once in the equation
1036 984 (eqn, status) := solveUnique(eqn, residual, fixed_cref);
1037
2/2
✓ Branch 0 taken 947 times.
✓ Branch 1 taken 37 times.
984 if status == Status.EXPLICIT then
1038 947 invertRelation := RelationInversion.UNKNOWN; // TODO: make me depend on the derivative
1039 else
1040 37 diffArgs := Differentiate.DifferentiationArguments.simpleCref(fixed_cref, funcMap);
1041 try
1042 37 (derivative, diffArgs) := Differentiate.differentiateExpressionDump(residual, diffArgs, getInstanceName());
1043 36 derivative := SimplifyExp.simplifyDump(derivative, true, getInstanceName());
1044 else
1045 // not everything can be differentiated, e.g. functions with function inputs
1046 1 derivative := Expression.fromCref(fixed_cref);
1047 end try;
1048
1049
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 22 times.
37 if Expression.isZero(derivative) then
1050 15 invertRelation := RelationInversion.FALSE;
1051 // an array that only occurs element wise, e.g. f({x[1], x[2]}), has to be solved implicitly
1052
1/6
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
15 status := if Type.isArray(ComponentRef.getSubscriptedType(fixed_cref, true)) and not Expression.containsCref(residual, fixed_cref)
1053 and not listEmpty(Equation.collectCrefs(eqn, function Slice.getSliceCandidates(name = ComponentRef.stripSubscriptsAll(fixed_cref))))
1054 then Status.IMPLICIT else Status.UNSOLVABLE;
1055 elseif not Expression.containsCref(derivative, fixed_cref) then
1056 // If eqn is linear in cref:
1057 12 eqn := solveLinear(eqn, residual, derivative, diffArgs, fixed_cref);
1058 // If the derivative is negative, invert possible inequality sign
1059
3/4
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 7 times.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
12 invertRelation := if Expression.isPositive(derivative) then RelationInversion.FALSE else if Expression.isNegative(derivative) then RelationInversion.TRUE else RelationInversion.UNKNOWN;
1060 12 status := Status.EXPLICIT;
1061 else
1062 10 invertRelation := RelationInversion.FALSE;
1063
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
10 if Flags.isSet(Flags.FAILTRACE) and status <> Status.EXPLICIT then
1064 ✗ Error.addCompilerWarning(getInstanceName() + " cref: " + ComponentRef.toString(fixed_cref)
1065 + " has to be solved implicitely in equation:\n" + Equation.toString(eqn));
1066 end if;
1067 end if;
1068 end if;
1069 end if;
1070 7405 eqn := Equation.simplify(eqn, getInstanceName());
1071
2/2
✓ Branch 1 taken 7402 times.
✓ Branch 2 taken 3 times.
7405 if Flags.isSet(Flags.DUMP_SOLVE) then
1072 3 solvePrintOutput(eqn, status);
1073 end if;
1074 end solveBody;
1075
1076 function solveIfBody
1077 input output IfEquationBody body;
1078 input VariablePointers vars;
1079 input UnorderedMap<Path, Function> funcMap;
1080 output Status status;
1081 input BPartition.Kind kind;
1082 input output Integer implicit_index;
1083 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
1084 input Iterator iter;
1085 input VarData varData;
1086 input EqData eqData;
1087 protected
1088 IfEquationBody else_if;
1089 list<StrongComponent> comps, solved_comps;
1090 list<Pointer<Equation>> new_then_eqns = {};
1091 Boolean explicit = true;
1092 algorithm
1093 // causalize this branch equations for the unknowns
1094 6 (_, comps) := Causalize.simple(vars, EquationPointers.fromList(body.then_eqns), kind, iter = iter);
1095 // solve each strong component explicitly and save equations to branch
1096
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for comp in comps loop
1097 6 (solved_comps, implicit_index) := solveStrongComponent(comp, funcMap, kind, implicit_index, slicing_map, varData, eqData);
1098
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for solved_comp in solved_comps loop
1099
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if StrongComponent.getSolveStatus(solved_comp) == Status.EXPLICIT then
1100 6 new_then_eqns := StrongComponent.toSolvedEquation(solved_comp) :: new_then_eqns;
1101 else
1102 explicit := false;
1103 end if;
1104 end for;
1105 end for;
1106
1107 // a branch that can only be solved implicitly makes the whole if equation implicit
1108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if not explicit then
1109 ✗ status := Status.IMPLICIT;
1110 ✗ return;
1111 end if;
1112
1113 6 body.then_eqns := listReverse(new_then_eqns);
1114 // if there is an else branch -> go deeper
1115
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
6 if isSome(body.else_if) then
1116 3 (else_if, status, implicit_index) := solveIfBody(Util.getOption(body.else_if), vars, funcMap, kind, implicit_index, slicing_map, iter, varData, eqData);
1117 3 body.else_if := SOME(else_if);
1118 else
1119 // StrongComponent.toSolvedEquation fails for everything that is not explicitly solvable so at this point one can assume it is
1120 3 status := Status.EXPLICIT;
1121 end if;
1122 end solveIfBody;
1123
1124 function solveSimple
1125 input output Equation eqn;
1126 input ComponentRef cref;
1127 output Status status;
1128 output RelationInversion invertRelation;
1129 algorithm
1130 (eqn, status, invertRelation) := match eqn
1131 local
1132 Equation body;
1133 IfEquationBody if_body;
1134
1135 // check lhs and rhs for simple structure
1136 6519 case Equation.SCALAR_EQUATION() then solveSimpleLhsRhs(eqn.lhs, eqn.rhs, cref, eqn);
1137 742 case Equation.ARRAY_EQUATION() then solveSimpleLhsRhs(eqn.lhs, eqn.rhs, cref, eqn);
1138 142 case Equation.RECORD_EQUATION() then solveSimpleLhsRhs(eqn.lhs, eqn.rhs, cref, eqn);
1139
1140 // ToDo: need to check if implicit
1141 8 case Equation.WHEN_EQUATION() then solveSimpleWhen(eqn.body, cref, eqn);
1142
1143 case Equation.FOR_EQUATION(body = {body}) algorithm
1144 ✗ (body, status, invertRelation) := solveSimple(body, cref);
1145 ✗ if status == Status.EXPLICIT then
1146 ✗ eqn.body := {body};
1147 else
1148 ✗ status := Status.UNPROCESSED;
1149 end if;
1150 ✗ then (eqn, status, invertRelation);
1151
1152 case Equation.IF_EQUATION() algorithm
1153 ✗ (if_body, status, invertRelation) := solveSimpleIf(eqn.body, cref);
1154 ✗ if status == Status.EXPLICIT then
1155 ✗ eqn.body := if_body;
1156 else
1157 ✗ status := Status.UNPROCESSED;
1158 end if;
1159 ✗ then (eqn, status, invertRelation);
1160
1161 // ToDo: more cases
1162 // ToDo: tuples, record elements, array constructors
1163
1164 6 else (eqn, Status.UNPROCESSED, RelationInversion.FALSE);
1165 end match;
1166 end solveSimple;
1167
1168 protected
1169 function solveSimpleLhsRhs
1170 input Expression lhs;
1171 input Expression rhs;
1172 input ComponentRef cref;
1173 input output Equation eqn;
1174 output Status status;
1175 output RelationInversion invertRelation;
1176 algorithm
1177 (eqn, status, invertRelation) := match (lhs, rhs)
1178 local
1179 ComponentRef checkCref;
1180 Expression exp;
1181 list<Expression> elements;
1182
1183 // always checks if exp is independent of cref!
1184
1185 // 1. already solved
1186 // cref = exp
1187 case (Expression.CREF(cref = checkCref), exp)
1188 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1189 then (eqn, Status.EXPLICIT, RelationInversion.FALSE);
1190
1191 // 2. only swap lsh and rhs
1192 // exp = cref
1193 case (exp, Expression.CREF(cref = checkCref))
1194 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1195 2043 then (Equation.swapLHSandRHS(eqn), Status.EXPLICIT, RelationInversion.TRUE);
1196
1197 // 3.1 negate (MINUS) lhs and rhs
1198 // -cref = exp
1199 case (Expression.UNARY(exp = Expression.CREF(cref = checkCref)), exp)
1200 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1201 17 then (Equation.updateLHSandRHS(eqn, Expression.negate(lhs), Expression.negate(rhs)), Status.EXPLICIT, RelationInversion.TRUE);
1202
1203 // 3.2 negate (NOT) lhs and rhs
1204 // not cref = exp
1205 case (Expression.LUNARY(exp = Expression.CREF(cref = checkCref)), exp)
1206 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1207 1 then (Equation.updateLHSandRHS(eqn, Expression.logicNegate(lhs), Expression.logicNegate(rhs)), Status.EXPLICIT, RelationInversion.FALSE);
1208
1209 // 4.1 negate (MINUS) and swap lhs and rhs
1210 // exp = -cref
1211 case (exp, Expression.UNARY(exp = Expression.CREF(cref = checkCref)))
1212 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1213 232 then (Equation.updateLHSandRHS(eqn, Expression.negate(rhs), Expression.negate(lhs)), Status.EXPLICIT, RelationInversion.FALSE);
1214
1215 // 4.2 negate (NOT) and swap lhs and rhs
1216 // exp = not cref
1217 case (exp, Expression.LUNARY(exp = Expression.CREF(cref = checkCref)))
1218 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(exp, cref))
1219 20 then (Equation.updateLHSandRHS(eqn, Expression.logicNegate(rhs), Expression.logicNegate(lhs)), Status.EXPLICIT, RelationInversion.FALSE);
1220
1221 // simple solve tuples
1222 case (exp as Expression.TUPLE(), _) guard(tupleSolvable(exp.elements, {BVariable.getVarPointer(cref, sourceInfo())})) then (eqn, Status.EXPLICIT, RelationInversion.FALSE);
1223 ✗ case (_, exp as Expression.TUPLE()) guard(tupleSolvable(exp.elements, {BVariable.getVarPointer(cref, sourceInfo())})) then (Equation.swapLHSandRHS(eqn), Status.EXPLICIT, RelationInversion.FALSE);
1224
1225 else (eqn, Status.UNPROCESSED, RelationInversion.FALSE);
1226 end match;
1227 end solveSimpleLhsRhs;
1228
1229 function solveSimpleWhen
1230 input WhenEquationBody body;
1231 input ComponentRef cref;
1232 input Equation eqn;
1233 output Equation eqnOut = eqn "don't change the equation";
1234 output Status status = Status.UNSOLVABLE;
1235 output RelationInversion invertRelation = RelationInversion.FALSE;
1236 algorithm
1237
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 for stmt in body.when_stmts loop
1238 status := match stmt
1239 local
1240 ComponentRef checkCref;
1241 case WhenStatement.ASSIGN(lhs = Expression.CREF(cref = checkCref))
1242 guard(ComponentRef.isEqual(cref, checkCref) and not Expression.containsCref(stmt.rhs, cref))
1243 then Status.EXPLICIT;
1244 else Status.UNSOLVABLE;
1245 end match;
1246
1247 if status == Status.EXPLICIT then
1248 break;
1249 end if;
1250 end for;
1251 end solveSimpleWhen;
1252
1253 function solveSimpleIf
1254 input output IfEquationBody body;
1255 input ComponentRef cref;
1256 output Status status = Status.EXPLICIT;
1257 output RelationInversion invertRelation = RelationInversion.FALSE;
1258 protected
1259 IfEquationBody else_if;
1260 Equation eqn;
1261 algorithm
1262 ✗ if isSome(body.else_if) then
1263 ✗ (else_if, status, _) := solveSimpleIf(Util.getOption(body.else_if), cref);
1264 ✗ if status == Status.EXPLICIT then
1265 ✗ body.else_if := SOME(else_if);
1266 end if;
1267 end if;
1268
1269 ✗ if status == Status.EXPLICIT and List.hasOneElement(body.then_eqns) then
1270 ✗ eqn := Pointer.access(listHead(body.then_eqns));
1271 ✗ (eqn, status, _) := solveSimple(eqn, cref);
1272 ✗ if status == Status.EXPLICIT then
1273 ✗ Pointer.update(listHead(body.then_eqns), eqn);
1274 end if;
1275 else
1276 ✗ status := Status.UNPROCESSED;
1277 end if;
1278 end solveSimpleIf;
1279
1280 function solveLinear
1281 "author: kabdelhak, phannebohm
1282 solves a linear equation with one newton step
1283 0 = f(x) ---> x = -f(0)/f`(0)"
1284 input output Equation eqn;
1285 input Expression residual;
1286 input Expression derivative;
1287 input Differentiate.DifferentiationArguments diffArgs;
1288 input ComponentRef cref;
1289 protected
1290 Expression crefExp, numerator;
1291 Operator mulOp, uminOp;
1292 Type ty;
1293 algorithm
1294 12 crefExp := Expression.fromCref(cref);
1295 12 ty := ComponentRef.getSubscriptedType(cref, true);
1296 12 numerator := Replacements.single(residual, crefExp, Expression.makeZero(ty));
1297 12 mulOp := Operator.OPERATOR(ty, NFOperator.Op.MUL);
1298 12 uminOp := Operator.OPERATOR(ty, NFOperator.Op.UMINUS);
1299 // Set eqn: cref = - f/f'
1300 12 eqn := Equation.setLHS(eqn, crefExp);
1301 12 eqn := Equation.setRHS(eqn, Expression.UNARY(uminOp, Expression.MULTARY({numerator},{derivative}, mulOp)));
1302 end solveLinear;
1303
1304 function solveUnique
1305 "author: linuslangenkamp
1306 solves a generic equation in terms of a cref that is contained only once
1307 returns Status.IMPLICIT if the equation can't be solved or the cref is contained multiple times"
1308 input output Equation eqn;
1309 input Expression residual;
1310 input ComponentRef cref;
1311 output Status status;
1312 protected
1313 Expression crefExp = Expression.fromCref(cref), solvedRHS;
1314 Boolean crefFound;
1315 list<Expression> inverseInstructions = {};
1316 Type ty = ComponentRef.getSubscriptedType(cref, true);
1317 algorithm
1318 // find a list of inverse operations or detect that a cref is contained more than once
1319 984 (crefFound, inverseInstructions, status) := solveUniqueFindInstructions(residual, cref, false, inverseInstructions);
1320
1321
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 982 times.
984 if Flags.isSet(Flags.DUMP_SOLVE) then
1322 2 solveUniquePrintInstructions(inverseInstructions, status);
1323 end if;
1324
1325 // apply the inverse operations or return if implicit
1326 eqn := match status
1327 case Status.IMPLICIT
1328 then eqn;
1329 else algorithm
1330
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 947 times.
962 if not crefFound then
1331 // cref was not found in the residual expression; cannot solve explicitly
1332 15 status := Status.IMPLICIT;
1333 else
1334 947 status := Status.EXPLICIT;
1335 947 solvedRHS := Expression.makeZero(ty);
1336
2/2
✓ Branch 0 taken 1779 times.
✓ Branch 1 taken 947 times.
2726 for instruction in inverseInstructions loop
1337 1779 solvedRHS := applyInstruction(solvedRHS, instruction);
1338 end for;
1339 947 eqn := Equation.setLHS(eqn, crefExp);
1340 947 eqn := Equation.setRHS(eqn, solvedRHS);
1341 end if;
1342 then eqn;
1343 end match;
1344 end solveUnique;
1345
1346 function solveUniqueFindInstructions
1347 "performs the recursion steps"
1348 input Expression exp;
1349 input ComponentRef cref;
1350 input output Boolean crefFound;
1351 input output list<Expression> inverseInstructions;
1352 output Status status = Status.EXPLICIT; // just set this per default, since the algorithm detects implicit equations
1353 protected
1354 Expression substExp = NBVariable.toExpression(Pointer.create(NBVariable.SUBST_VARIABLE));
1355 Type ty = ComponentRef.getSubscriptedType(cref, true);
1356 Call call;
1357 algorithm
1358 // TODO: update crefFounds, hard to read!
1359 // TODO: potential types
1360 // TODO: add missing cases
1361
2/2
✓ Branch 0 taken 242 times.
✓ Branch 1 taken 4140 times.
4382 if crefFound then
1362 // cref was already found elsewhere
1363
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 237 times.
242 if Expression.containsCref(exp, cref) then
1364 // cref appears more than once, abort
1365 5 status := Status.IMPLICIT;
1366 else
1367 // set crefFound = false, since the cref is not found in this branch
1368 crefFound := false;
1369 end if;
1370 242 return;
1371 end if;
1372
1373 () := match exp
1374 case Expression.REAL() then ();
1375 case Expression.INTEGER() then ();
1376 case Expression.CREF() algorithm
1377
2/2
✓ Branch 1 taken 956 times.
✓ Branch 2 taken 875 times.
1831 if ComponentRef.isEqual(cref, exp.cref) then
1378 crefFound := true;
1379 end if;
1380 then ();
1381 case Expression.CAST() algorithm
1382 20 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsCast(substExp, exp, cref, crefFound, inverseInstructions);
1383 then ();
1384 case Expression.MULTARY() algorithm
1385 1524 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsMultary(substExp, exp, cref, crefFound, inverseInstructions);
1386 then ();
1387 case Expression.BINARY() algorithm
1388 () := match exp.operator
1389 case Operator.OPERATOR(op = NFOperator.Op.POW) algorithm
1390 6 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsBinaryPow(ty, substExp, exp, cref, crefFound, inverseInstructions);
1391 then ();
1392 case Operator.OPERATOR(op = NFOperator.Op.ADD) algorithm
1393 18 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsBinaryComOp(substExp, exp, cref, crefFound, inverseInstructions);
1394 then ();
1395 case Operator.OPERATOR(op = NFOperator.Op.MUL) algorithm
1396 6 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsBinaryComOp(substExp, exp, cref, crefFound, inverseInstructions);
1397 then ();
1398 else algorithm // fallback -> set implicit
1399
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if Flags.isSet(Flags.DUMP_SOLVE) then
1400 ✗ solveUniquePrintImplicitFallback(exp);
1401 end if;
1402 7 status := Status.IMPLICIT;
1403 then ();
1404 end match;
1405 then ();
1406 case Expression.UNARY() algorithm
1407 () := match exp.operator
1408 case Operator.OPERATOR(op = NFOperator.Op.UMINUS) algorithm
1409 415 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsUnaryUminus(ty, substExp, exp, cref, crefFound, inverseInstructions);
1410 then ();
1411 else algorithm // fallback -> set implicit
1412 ✗ if Flags.isSet(Flags.DUMP_SOLVE) then
1413 ✗ solveUniquePrintImplicitFallback(exp);
1414 end if;
1415 ✗ status := Status.IMPLICIT;
1416 then ();
1417 end match;
1418 then ();
1419
1420 // cases where the cref does not appear
1421 case Expression.CALL(call = call as Call.TYPED_CALL()) guard(List.none(Call.arguments(exp.call), function solveUniqueExpressionNoCref(cref = cref))) then ();
1422 case Expression.CALL(call = call as Call.TYPED_ARRAY_CONSTRUCTOR()) guard(List.none(Call.arguments(exp.call), function solveUniqueExpressionNoCref(cref = cref))) then ();
1423 case Expression.CALL(call = call as Call.TYPED_REDUCTION()) guard(List.none(Call.arguments(exp.call), function solveUniqueExpressionNoCref(cref = cref))) then ();
1424
1425 // check if invertable if occurs
1426 case Expression.CALL(call = call as Call.TYPED_CALL()) guard(List.hasOneElement(Call.arguments(exp.call))) algorithm
1427 5 (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsCallOneArg(ty, substExp, exp, cref, crefFound, inverseInstructions);
1428 then ();
1429 case Expression.CALL(call = call as Call.TYPED_CALL()) guard(listLength(Call.arguments(exp.call)) == 2) algorithm
1430 ✗ (crefFound, inverseInstructions, status) := solveUniqueFindInstructionsCallTwoArgs(ty, substExp, exp, cref, crefFound, inverseInstructions);
1431 then ();
1432
1433 // fallback -> set implicit
1434 else algorithm
1435
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if Flags.isSet(Flags.DUMP_SOLVE) then
1436 ✗ solveUniquePrintImplicitFallback(exp);
1437 end if;
1438 6 status := Status.IMPLICIT;
1439 then ();
1440 end match;
1441 end solveUniqueFindInstructions;
1442
1443 function solveUniqueFindInstructionsMultary
1444 "find instructions for a multary"
1445 input Expression substExp;
1446 input Expression exp;
1447 input ComponentRef cref;
1448 input output Boolean crefFound;
1449 input output list<Expression> inverseInstructions;
1450 output Status status = Status.EXPLICIT; // just set this per default, since the algorithm detects implicit equations
1451 protected
1452 list<Expression> argList = {}, invargList = {};
1453 Boolean crefFoundInRecursion;
1454 algorithm
1455 () := match exp
1456 case Expression.MULTARY() algorithm
1457
2/2
✓ Branch 0 taken 2644 times.
✓ Branch 1 taken 1528 times.
4172 for arg in exp.arguments loop
1458 2644 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(arg, cref, crefFound, inverseInstructions);
1459
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 2624 times.
2644 if status == Status.IMPLICIT then
1460 20 return;
1461 end if;
1462
2/2
✓ Branch 0 taken 1471 times.
✓ Branch 1 taken 1153 times.
2624 if not crefFoundInRecursion then
1463 argList := arg :: argList;
1464 else
1465 crefFound := true;
1466 end if;
1467 end for;
1468
2/2
✓ Branch 0 taken 1148 times.
✓ Branch 1 taken 380 times.
1528 if crefFound then
1469
2/2
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1147 times.
1148 if List.any(exp.inv_arguments, function Expression.containsCref(cref=cref)) then
1470 1 status := Status.IMPLICIT;
1471 1 return;
1472 else
1473 // inverse multary for cref in args
1474 1147 inverseInstructions := Expression.MULTARY(substExp :: exp.inv_arguments, argList, exp.operator) :: inverseInstructions;
1475 end if;
1476 else
1477
2/2
✓ Branch 0 taken 307 times.
✓ Branch 1 taken 371 times.
678 for invarg in exp.inv_arguments loop
1478 307 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(invarg, cref, crefFound, inverseInstructions);
1479
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 298 times.
307 if status == Status.IMPLICIT then
1480 9 return;
1481 end if;
1482
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 218 times.
298 if not crefFoundInRecursion then
1483 invargList := invarg :: invargList;
1484 else
1485 crefFound := true;
1486 end if;
1487 end for;
1488
2/2
✓ Branch 0 taken 218 times.
✓ Branch 1 taken 153 times.
371 if crefFound then
1489 // inverse multary for cref in invargs
1490 218 inverseInstructions := Expression.MULTARY(argList, substExp :: invargList, exp.operator) :: inverseInstructions;
1491 end if;
1492 end if;
1493 then ();
1494 else algorithm
1495 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.MULTARY."});
1496 ✗ then fail();
1497 end match;
1498 end solveUniqueFindInstructionsMultary;
1499
1500 function solveUniqueFindInstructionsBinaryPow
1501 "find instructions for a binary with power operator"
1502 input Type ty;
1503 input Expression substExp;
1504 input Expression exp;
1505 input ComponentRef cref;
1506 input output Boolean crefFound;
1507 input output list<Expression> inverseInstructions;
1508 output Status status;
1509 protected
1510 Boolean crefFoundInRecursion;
1511 Expression local_exp1, local_exp2;
1512 algorithm
1513 () := match exp
1514 case Expression.BINARY() algorithm
1515 6 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(exp.exp1, cref, crefFound, inverseInstructions);
1516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if status == Status.IMPLICIT then
1517 ✗ return;
1518 end if;
1519
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
6 if crefFoundInRecursion then
1520 // case for f(cref) ^ exp2 -> $SUBST_CREF^(1 / exp2)
1521 crefFound := true;
1522
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if Expression.containsCref(exp.exp2, cref) then
1523 ✗ status := Status.IMPLICIT;
1524 else
1525 5 inverseInstructions := Expression.BINARY(substExp, exp.operator, Expression.MULTARY({}, {exp.exp2}, Operator.OPERATOR(ty, NFOperator.Op.MUL))) :: inverseInstructions;
1526 end if;
1527 else
1528 1 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(exp.exp2, cref, crefFound, inverseInstructions);
1529
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if status == Status.IMPLICIT then
1530 ✗ return;
1531 end if;
1532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if crefFoundInRecursion then
1533 // case for exp1 ^ f(cref) -> log($SUBST_CREF)/log(exp1)
1534 crefFound := true;
1535 ✗ local_exp1 := Expression.CALL(Call.makeTypedCall(
1536 fn = NFBuiltinFuncs.LOG_REAL,
1537 args = {substExp},
1538 variability = Expression.variability(substExp),
1539 purity = NFPrefixes.Purity.PURE
1540 ));
1541 ✗ local_exp2 := Expression.CALL(Call.makeTypedCall(
1542 fn = NFBuiltinFuncs.LOG_REAL,
1543 args = {exp.exp1},
1544 variability = Expression.variability(exp.exp1),
1545 purity = NFPrefixes.Purity.PURE
1546 ));
1547 // split the instructions, s.t. only top level search for substExp/dummyCref has to be performed in applyInstruction
1548 ✗ inverseInstructions := local_exp1 :: Expression.MULTARY({substExp}, {local_exp2}, Operator.OPERATOR(ty, NFOperator.Op.MUL)) :: inverseInstructions;
1549 end if;
1550 end if;
1551 then ();
1552 else algorithm
1553 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.BINARY with operator POW."});
1554 ✗ then fail();
1555 end match;
1556 end solveUniqueFindInstructionsBinaryPow;
1557
1558 function solveUniqueFindInstructionsBinaryComOp
1559 "find instructions for a binary with commutative operator
1560 simply calls multary with the corresponding op"
1561 input Expression substExp;
1562 input Expression exp;
1563 input ComponentRef cref;
1564 input output Boolean crefFound;
1565 input output list<Expression> inverseInstructions;
1566 output Status status;
1567 protected
1568 Boolean crefFoundInRecursion;
1569 algorithm
1570 () := match exp
1571 case Expression.BINARY() algorithm
1572 24 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructionsMultary(substExp,
1573 Expression.MULTARY({exp.exp1, exp.exp2}, {}, exp.operator), cref, crefFound, inverseInstructions);
1574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if status == Status.IMPLICIT then
1575 ✗ return;
1576 end if;
1577
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if crefFoundInRecursion then
1578 crefFound := true;
1579 end if;
1580 then ();
1581 else algorithm
1582 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.BINARY with commutative operator."});
1583 ✗ then fail();
1584 end match;
1585 end solveUniqueFindInstructionsBinaryComOp;
1586
1587 function solveUniqueFindInstructionsCast
1588 "find instructions for a cast"
1589 input Expression substExp;
1590 input Expression exp;
1591 input ComponentRef cref;
1592 input output Boolean crefFound;
1593 input output list<Expression> inverseInstructions;
1594 output Status status;
1595 protected
1596 Boolean crefFoundInRecursion;
1597 algorithm
1598 () := match exp
1599 case Expression.CAST() algorithm
1600 20 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(exp.exp, cref, crefFound, inverseInstructions);
1601
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if status == Status.IMPLICIT then
1602 ✗ return;
1603 end if;
1604
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 12 times.
20 if crefFoundInRecursion then
1605 crefFound := true;
1606 end if;
1607 then ();
1608 else algorithm
1609 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.CAST."});
1610 ✗ then fail();
1611 end match;
1612 end solveUniqueFindInstructionsCast;
1613
1614 function solveUniqueFindInstructionsUnaryUminus
1615 "find instructions for an unary with uminus operator"
1616 input Type ty;
1617 input Expression substExp;
1618 input Expression exp;
1619 input ComponentRef cref;
1620 input output Boolean crefFound;
1621 input output list<Expression> inverseInstructions;
1622 output Status status;
1623 protected
1624 Boolean crefFoundInRecursion;
1625 algorithm
1626 () := match exp
1627 case Expression.UNARY() algorithm
1628 415 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(exp.exp, cref, crefFound, inverseInstructions);
1629
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 415 times.
415 if status == Status.IMPLICIT then
1630 ✗ return;
1631 end if;
1632
2/2
✓ Branch 0 taken 412 times.
✓ Branch 1 taken 3 times.
415 if crefFoundInRecursion then
1633 // case for -(f(cref)) -> -($SUBST_CREF)
1634 crefFound := true;
1635 412 inverseInstructions := Expression.UNARY(Operator.OPERATOR(ty, NFOperator.Op.UMINUS), substExp) :: inverseInstructions;
1636 end if;
1637 then ();
1638 else algorithm
1639 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.BINARY with commutative operator."});
1640 ✗ then fail();
1641 end match;
1642 end solveUniqueFindInstructionsUnaryUminus;
1643
1644 function solveUniqueFindInstructionsCallOneArg
1645 "find instructions for a call with one argument"
1646 input Type ty;
1647 input Expression substExp;
1648 input Expression exp;
1649 input ComponentRef cref;
1650 input output Boolean crefFound;
1651 input output list<Expression> inverseInstructions;
1652 output Status status;
1653 protected
1654 Boolean crefFoundInRecursion;
1655 Expression argExp;
1656 Call call;
1657 String name;
1658 algorithm
1659 () := match exp
1660 case Expression.CALL(call = call as Call.TYPED_CALL()) guard List.hasOneElement(Call.arguments(exp.call)) algorithm
1661 5 name := AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn));
1662 5 argExp := listHead(Call.arguments(call));
1663 5 (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(argExp, cref, crefFound, inverseInstructions);
1664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if status == Status.IMPLICIT then
1665 ✗ return;
1666 end if;
1667
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if crefFoundInRecursion then
1668 // case for call(f(cref)) -> call^{-1}($SUBST_CREF)
1669 crefFound := true;
1670 inverseInstructions := match name
1671 ✗ case "sqrt" then // sqrt(f(cref)) -> $SUBST_CREF^2
1672 Expression.BINARY(substExp, Operator.OPERATOR(ty, NFOperator.Op.POW), Expression.REAL(2)) :: inverseInstructions;
1673 ✗ case "cos" then // cos(f(cref)) -> acos($SUBST_CREF)
1674 solveUniqueCreateSubstCall(NFBuiltinFuncs.ACOS_REAL, substExp, inverseInstructions);
1675 1 case "sin" then // sin(f(cref)) -> asin($SUBST_CREF)
1676 solveUniqueCreateSubstCall(NFBuiltinFuncs.ASIN_REAL, substExp, inverseInstructions);
1677 ✗ case "tan" then // tan(f(cref)) -> atan($SUBST_CREF)
1678 solveUniqueCreateSubstCall(NFBuiltinFuncs.ATAN_REAL, substExp, inverseInstructions);
1679 ✗ case "acos" then // acos(f(cref)) -> cos($SUBST_CREF)
1680 solveUniqueCreateSubstCall(NFBuiltinFuncs.COS_REAL, substExp, inverseInstructions);
1681 ✗ case "asin" then // asin(f(cref)) -> sin($SUBST_CREF)
1682 solveUniqueCreateSubstCall(NFBuiltinFuncs.SIN_REAL, substExp, inverseInstructions);
1683 ✗ case "atan" then // atan(f(cref)) -> tan($SUBST_CREF)
1684 solveUniqueCreateSubstCall(NFBuiltinFuncs.TAN_REAL, substExp, inverseInstructions);
1685 ✗ case "cosh" then // cosh(f(cref)) -> acosh($SUBST_CREF)
1686 solveUniqueCreateSubstCall(NFBuiltinFuncs.ACOSH_REAL, substExp, inverseInstructions);
1687 ✗ case "sinh" then // sinh(f(cref)) -> asinh($SUBST_CREF)
1688 solveUniqueCreateSubstCall(NFBuiltinFuncs.ASINH_REAL, substExp, inverseInstructions);
1689 1 case "tanh" then // tanh(f(cref)) -> atanh($SUBST_CREF)
1690 solveUniqueCreateSubstCall(NFBuiltinFuncs.ATANH_REAL, substExp, inverseInstructions);
1691 ✗ case "acosh" then // acosh(f(cref)) -> cosh($SUBST_CREF)
1692 solveUniqueCreateSubstCall(NFBuiltinFuncs.COSH_REAL, substExp, inverseInstructions);
1693 ✗ case "asinh" then // asinh(f(cref)) -> sinh($SUBST_CREF)
1694 solveUniqueCreateSubstCall(NFBuiltinFuncs.SINH_REAL, substExp, inverseInstructions);
1695 ✗ case "atanh" then // atanh(f(cref)) -> tanh($SUBST_CREF)
1696 solveUniqueCreateSubstCall(NFBuiltinFuncs.TANH_REAL, substExp, inverseInstructions);
1697 ✗ case "exp" then // exp(f(cref)) -> log($SUBST_CREF)
1698 solveUniqueCreateSubstCall(NFBuiltinFuncs.LOG_REAL, substExp, inverseInstructions);
1699 ✗ case "log" then // log(f(cref)) -> exp($SUBST_CREF)
1700 solveUniqueCreateSubstCall(NFBuiltinFuncs.EXP_REAL, substExp, inverseInstructions);
1701 ✗ case "log10" then // log_10(f(cref)) -> 10^($SUBST_CREF)
1702 Expression.BINARY(Expression.REAL(10), Operator.OPERATOR(ty, NFOperator.Op.POW), substExp) :: inverseInstructions;
1703 else algorithm // fallback -> set implicit
1704
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.DUMP_SOLVE) then
1705 ✗ solveUniquePrintImplicitFallback(exp);
1706 end if;
1707 3 status := Status.IMPLICIT;
1708 3 then inverseInstructions;
1709 end match;
1710 end if;
1711 then ();
1712 else algorithm
1713 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.CALL with one argument."});
1714 ✗ then fail();
1715 end match;
1716 end solveUniqueFindInstructionsCallOneArg;
1717
1718 function solveUniqueFindInstructionsCallTwoArgs
1719 "find instructions for a call with two arguments"
1720 input Type ty;
1721 input Expression substExp;
1722 input Expression exp;
1723 input ComponentRef cref;
1724 input output Boolean crefFound;
1725 input output list<Expression> inverseInstructions;
1726 output Status status;
1727 protected
1728 Boolean crefFoundInRecursion;
1729 Expression argExp1, argExp2;
1730 Call call;
1731 String name;
1732 algorithm
1733 () := match exp
1734 case Expression.CALL(call = call as Call.TYPED_CALL()) guard(listLength(Call.arguments(exp.call)) == 2) algorithm
1735 ✗ name := AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn));
1736 ✗ {argExp1, argExp2} := Call.arguments(call);
1737 ✗ (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(argExp1, cref, crefFound, inverseInstructions);
1738 ✗ if status == Status.IMPLICIT then
1739 ✗ return;
1740 end if;
1741 ✗ if crefFoundInRecursion then
1742 crefFound := true;
1743 ✗ if Expression.containsCref(argExp2, cref) then
1744 ✗ status := Status.IMPLICIT;
1745 else
1746 // calc inverse w.r.t first argument and append to instructions
1747 inverseInstructions := match name
1748 case "atan2" algorithm // atan2(x,y) -> x=y*tan($SUBST_CREF)
1749 ✗ inverseInstructions := Expression.MULTARY({substExp, argExp2}, {}, Operator.OPERATOR(ty, NFOperator.Op.MUL)) :: inverseInstructions;
1750 ✗ then solveUniqueCreateSubstCall(NFBuiltinFuncs.TAN_REAL, substExp, inverseInstructions);
1751 else algorithm // fallback -> set implicit
1752 ✗ if Flags.isSet(Flags.DUMP_SOLVE) then
1753 ✗ solveUniquePrintImplicitFallback(exp);
1754 end if;
1755 ✗ status := Status.IMPLICIT;
1756 ✗ then inverseInstructions;
1757 end match;
1758 end if;
1759 else
1760 ✗ (crefFoundInRecursion, inverseInstructions, status) := solveUniqueFindInstructions(argExp2, cref, crefFound, inverseInstructions);
1761 ✗ if status == Status.IMPLICIT then
1762 ✗ return;
1763 end if;
1764 ✗ if crefFoundInRecursion then
1765 crefFound := true;
1766 // calc inverse w.r.t second argument and append to instructions
1767 inverseInstructions := match name
1768 case "atan2" algorithm // atan2(x,y) -> y=x/tan($SUBST_CREF)
1769 ✗ inverseInstructions := Expression.MULTARY({argExp1}, {substExp}, Operator.OPERATOR(ty, NFOperator.Op.MUL)) :: inverseInstructions;
1770 ✗ then solveUniqueCreateSubstCall(NFBuiltinFuncs.TAN_REAL, substExp, inverseInstructions);
1771 else algorithm // fallback -> set implicit
1772 ✗ if Flags.isSet(Flags.DUMP_SOLVE) then
1773 ✗ solveUniquePrintImplicitFallback(exp);
1774 end if;
1775 ✗ status := Status.IMPLICIT;
1776 ✗ then inverseInstructions;
1777 end match;
1778 end if;
1779 end if;
1780 then ();
1781 else algorithm
1782 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only be called for Expression.CALL with two arguments."});
1783 ✗ then fail();
1784 end match;
1785 end solveUniqueFindInstructionsCallTwoArgs;
1786
1787 function solveUniqueCreateSubstCall
1788 "helper to create call with substitute cref and to the instructions"
1789 input Function fn;
1790 input Expression exp;
1791 input output list<Expression> inverseInstructions;
1792 algorithm
1793 2 inverseInstructions := Expression.CALL(Call.makeTypedCall(
1794 fn = fn,
1795 args = {exp},
1796 variability = Expression.variability(exp),
1797 purity = NFPrefixes.Purity.PURE
1798 )) :: inverseInstructions;
1799 end solveUniqueCreateSubstCall;
1800
1801 function solveUniqueExpressionNoCref
1802 "checks if the expression does not contain the cref"
1803 input Expression exp;
1804 input ComponentRef cref;
1805 output Boolean b;
1806 protected
1807 Pointer<Boolean> res = Pointer.create(false);
1808 function solveUniqueExpressionNoCrefTraverse
1809 "traversal helper"
1810 input output Expression exp;
1811 input ComponentRef cref;
1812 input Pointer<Boolean> res;
1813 algorithm
1814 // check if cref was already found for early abort
1815
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 if not Pointer.access(res) then
1816 exp := match exp
1817 case Expression.CREF() algorithm
1818
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 6 times.
26 Pointer.update(res, ComponentRef.isEqual(exp.cref, cref));
1819 then exp;
1820 23 else Expression.mapShallow(exp, function solveUniqueExpressionNoCrefTraverse(cref = cref, res = res));
1821 end match;
1822 end if;
1823 end solveUniqueExpressionNoCrefTraverse;
1824 algorithm
1825 29 Expression.fakeMap(exp, function solveUniqueExpressionNoCrefTraverse(cref = cref, res = res));
1826 29 b := Pointer.access(res);
1827 end solveUniqueExpressionNoCref;
1828
1829 function solvePrintInput
1830 input Equation eqn;
1831 input ComponentRef crefExp;
1832 algorithm
1833 3 print("\n##########################################\nSTART - Solve\n\n");
1834 3 print("Solve Input:\n");
1835 3 print("### Variable:\n\t" + ComponentRef.toString(crefExp) + "\n### Equation:\n\t" + Equation.toString(eqn) + "\n\n");
1836 end solvePrintInput;
1837
1838 function solvePrintOutput
1839 input Equation eqn;
1840 input Status status;
1841 algorithm
1842 3 print("Solve Output:\n");
1843 3 print("### Status:\n\t" + statusString(status) + "\n");
1844 3 print("### Equation:\n\t" + Equation.toString(eqn) + "\n");
1845 3 print("\nEND - Solve\n##########################################\n\n");
1846 end solvePrintOutput;
1847
1848 function solveUniquePrintInstructions
1849 input list<Expression> inverseInstructions;
1850 input Status status;
1851 algorithm
1852 2 print("SolveUnique Instructions (substitute from top to bottom):\n");
1853 2 print("\t0 (is initial)\n");
1854
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 for instruction in inverseInstructions loop
1855 7 print("\t" + Expression.toString(instruction) + "\n");
1856 end for;
1857 2 print("### Status:\n\t" + statusString(status) + "\n");
1858 2 print("\n");
1859 end solveUniquePrintInstructions;
1860
1861 function solveUniquePrintImplicitFallback
1862 input Expression exp;
1863 algorithm
1864 ✗ print("Setting Status.Implicit (fallback) due to:\n");
1865 ✗ print("### Expression:\n\t" + Expression.toString(exp) + "\n");
1866 ✗ print("\n");
1867 end solveUniquePrintImplicitFallback;
1868
1869 function applyInstruction
1870 "substitute insertExp for $SUBST_CREF in instruction"
1871 input output Expression insertExp;
1872 input Expression instruction;
1873 algorithm
1874 insertExp := match instruction
1875 local
1876 list<Expression> argList = {}, invargList = {};
1877 Expression exp;
1878 case Expression.MULTARY() algorithm
1879
2/2
✓ Branch 0 taken 1616 times.
✓ Branch 1 taken 1363 times.
2979 for arg in instruction.arguments loop
1880
2/2
✓ Branch 1 taken 471 times.
✓ Branch 2 taken 1145 times.
1616 if not Expression.isSubstitute(arg) then
1881 argList := arg :: argList;
1882 else
1883 argList := insertExp :: argList;
1884 end if;
1885 end for;
1886
2/2
✓ Branch 0 taken 1209 times.
✓ Branch 1 taken 1363 times.
2572 for invarg in instruction.inv_arguments loop
1887
2/2
✓ Branch 1 taken 991 times.
✓ Branch 2 taken 218 times.
1209 if not Expression.isSubstitute(invarg) then
1888 invargList := invarg :: invargList;
1889 else
1890 invargList := insertExp :: invargList;
1891 end if;
1892 end for;
1893 1363 then Expression.MULTARY(argList, invargList, instruction.operator);
1894 case Expression.BINARY() algorithm
1895
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if Expression.isSubstitute(instruction.exp1) then
1896 2 instruction.exp1 := insertExp;
1897 end if;
1898
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Expression.isSubstitute(instruction.exp2) then
1899 ✗ instruction.exp2 := insertExp;
1900 end if;
1901 then instruction;
1902 case Expression.UNARY() algorithm
1903
1/2
✓ Branch 1 taken 412 times.
✗ Branch 2 not taken.
412 if Expression.isSubstitute(instruction.exp) then
1904 412 instruction.exp := insertExp;
1905 end if;
1906 then instruction;
1907 case exp as Expression.CALL() algorithm
1908 () := match instruction.call
1909 local Call local_call;
1910 case local_call as Call.TYPED_CALL() algorithm
1911
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for arg in local_call.arguments loop
1912
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if not Expression.isSubstitute(arg) then
1913 argList := arg :: argList;
1914 else
1915 argList := insertExp :: argList;
1916 end if;
1917 end for;
1918 2 local_call.arguments := listReverse(argList);
1919 2 exp.call := local_call;
1920 then ();
1921 else algorithm
1922 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " can only handle TYPED_CALL."});
1923 ✗ then fail();
1924 end match;
1925 then exp;
1926 else algorithm
1927 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed for instruction: " + Expression.toString(instruction)});
1928 ✗ then fail();
1929 end match;
1930 end applyInstruction;
1931
1932 function tupleSolvable
1933 "checks if the tuple expression exactly represents the variables we need to solve for"
1934 input list<Expression> tuple_exps;
1935 input list<Pointer<Variable>> vars;
1936 input Boolean inFor = false "elements are one iteration of the (sliced) variables";
1937 output Boolean b = false;
1938 protected
1939 list<Expression> filtered_exps = list(e for e guard(not Expression.isWildCref(e)) in tuple_exps);
1940 UnorderedMap<ComponentRef, Boolean> map;
1941 UnorderedMap<ComponentRef, Integer> sizes;
1942 ComponentRef stripped;
1943 algorithm
1944
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if List.compareLength(filtered_exps, vars) == 0 then
1945 1 map := UnorderedMap.new<Boolean>(ComponentRef.hash, ComponentRef.isEqual);
1946 1 sizes := UnorderedMap.new<Integer>(ComponentRef.hash, ComponentRef.isEqual);
1947 // add all variables to solve for
1948
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for var in vars loop
1949 2 UnorderedMap.add(BVariable.getVarName(var), false, map);
1950 2 UnorderedMap.add(BVariable.getVarName(var), BVariable.size(var), sizes);
1951 end for;
1952 // set the map entry for all variables that occur to true
1953
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for exp in filtered_exps loop
1954 () := match exp
1955 case Expression.CREF() guard(UnorderedMap.contains(exp.cref, map)) algorithm
1956 ✗ UnorderedMap.add(exp.cref, true, map);
1957 then ();
1958
1959 // a subscripted element that covers the whole variable, e.g. x[1] for Real[1] x
1960 case Expression.CREF() algorithm
1961 2 stripped := ComponentRef.stripSubscriptsAll(exp.cref);
1962
2/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 if UnorderedMap.contains(stripped, map) and (inFor or UnorderedMap.getSafe(stripped, sizes, sourceInfo()) == Type.sizeOf(Expression.typeOf(exp))) then
1963 2 UnorderedMap.add(stripped, true, map);
1964 else
1965 ✗ return;
1966 end if;
1967 then ();
1968
1969 ✗ else algorithm return; then ();
1970 end match;
1971 end for;
1972 // check if all variables occured
1973 1 b := List.all(UnorderedMap.valueList(map), Util.id);
1974 end if;
1975 end tupleSolvable;
1976
1977 function expandArraySumExp
1978 "Replaces sum(arrayCref) with arrayCref[1] + ... + arrayCref[n] for 1D arrays.
1979 sum(A) without explicit iterator is represented as TYPED_CALL, not TYPED_REDUCTION."
1980 input output Expression exp;
1981 input ComponentRef arrayCref;
1982 protected
1983 Call call;
1984 Type arrTy, elemTy;
1985 list<Dimension> dims;
1986 list<Integer> sizes;
1987 list<Expression> elements;
1988
1989 ComponentRef arg_cref, elemCref;
1990 Expression new_exp;
1991 algorithm
1992 exp := match exp
1993 case Expression.CALL(call = call as Call.TYPED_CALL(arguments = {Expression.CREF(cref = arg_cref)}))
1994 guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn)) == "sum" and ComponentRef.isEqual(arg_cref, arrayCref)) algorithm
1995 2 arrTy := ComponentRef.getSubscriptedType(arrayCref, true);
1996 2 elemTy := Type.arrayElementType(arrTy);
1997 2 dims := Type.arrayDims(arrTy);
1998
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
4 sizes := list(Dimension.size(dim) for dim in dims);
1999 2 elements := expandArraySumExpDim(sizes, arrayCref, elemTy);
2000 2 new_exp := Expression.MULTARY(listReverse(elements), {}, Operator.makeAdd(elemTy));
2001 then new_exp;
2002 else exp;
2003 end match;
2004 end expandArraySumExp;
2005
2006 function expandArraySumExpDim
2007 input list<Integer> sizes;
2008 input ComponentRef arrayCref;
2009 input Type elemTy;
2010 input list<Subscript> subs = {};
2011 input output list<Expression> elements = {};
2012 algorithm
2013 elements := match sizes
2014 local
2015 Integer n;
2016 list<Integer> rest;
2017 ComponentRef elemCref;
2018
2019 // create all combinations of the current subscript
2020 case n :: rest algorithm
2021
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
6 for i in 1:n loop
2022 8 elements := expandArraySumExpDim(rest, arrayCref, elemTy, Subscript.INDEX(Expression.INTEGER(i)) :: subs, elements);
2023 end for;
2024 then elements;
2025
2026 // no further subscripts, add the element
2027 else algorithm
2028 4 elemCref := ComponentRef.mergeSubscripts(listReverse(subs), arrayCref);
2029 4 then Expression.CREF(elemTy, elemCref) :: elements;
2030 end match;
2031 end expandArraySumExpDim;
2032
2033 function getVarSlice
2034 input output ComponentRef var_cref;
2035 input Option<ComponentRef> reference;
2036 input Equation eqn;
2037 output Status solve_status;
2038 protected
2039 Pointer<Variable> var_ptr = BVariable.getVarPointer(var_cref, sourceInfo());
2040 list<ComponentRef> slices_lst, filtered;
2041 Option<Pointer<Variable>> record_parent;
2042 function checkReference
2043 input ComponentRef var_cref;
2044 input Option<ComponentRef> reference_opt;
2045 output Boolean fit;
2046 algorithm
2047 fit := match reference_opt
2048 local
2049 ComponentRef reference;
2050 3387 case SOME(reference) then Type.sizeOf(ComponentRef.getSubscriptedType(var_cref), true) == Type.sizeOf(ComponentRef.getSubscriptedType(reference), true);
2051 else true;
2052 end match;
2053 end checkReference;
2054 algorithm
2055 3650 slices_lst := Equation.collectCrefs(eqn, function Slice.getSliceCandidates(name = var_cref));
2056
2057
2/2
✓ Branch 1 taken 3511 times.
✓ Branch 2 taken 139 times.
3650 if List.hasOneElement(slices_lst) then
2058 3511 var_cref := listHead(slices_lst);
2059 // only accept the cref if it has the same size as the one we are trying to solve for
2060
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 3479 times.
3511 if checkReference(var_cref, reference) then
2061 solve_status := Status.UNPROCESSED;
2062 else
2063 solve_status := Status.IMPLICIT;
2064 end if;
2065 else
2066 // check if the record parents occur (todo: vice versa?)
2067 139 record_parent := BVariable.getParent(BVariable.getVarPointer(var_cref, sourceInfo()));
2068
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 124 times.
139 if isSome(record_parent) then
2069 // do not pass reference for record elements
2070 124 (var_cref, solve_status) := getVarSlice(BVariable.getVarName(Util.getOption(record_parent)), NONE(), eqn);
2071 elseif listEmpty(slices_lst) then
2072 solve_status := Status.UNSOLVABLE;
2073 else
2074 // more than one candidate slice occurs (e.g. the whole variable on one side and
2075 // a partial slice on the other) -- narrow down with the same size check used for
2076 // the single-candidate case above.
2077 ✗ filtered := list(c for c guard(checkReference(c, reference)) in slices_lst);
2078 ✗ if List.hasOneElement(filtered) then
2079 ✗ var_cref := listHead(filtered);
2080 solve_status := Status.UNPROCESSED;
2081 else
2082 solve_status := Status.IMPLICIT;
2083 end if;
2084 end if;
2085 end if;
2086 end getVarSlice;
2087
2088 function solveForVarSlice
2089 input output Slice<EquationPointer> eqn_slice;
2090 input Slice<VariablePointer> var_slice;
2091 input ComponentRef cref;
2092 input UnorderedMap<Path, Function> funcMap;
2093 input BPartition.Kind kind;
2094 input output Integer implicit_index;
2095 input UnorderedMap<ComponentRef, list<Pointer<Equation>>> slicing_map;
2096 input VarData varData;
2097 input EqData eqData;
2098 output Status solve_status;
2099 protected
2100 Equation eqn;
2101 ComponentRef var_cref;
2102 algorithm
2103 241 eqn := Pointer.access(Slice.getT(eqn_slice));
2104 241 (var_cref, solve_status) := getVarSlice(BVariable.getVarName(Slice.getT(var_slice)), SOME(cref), eqn);
2105
2106
2/2
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 32 times.
241 if solve_status < Status.IMPLICIT then
2107 209 (eqn, solve_status, implicit_index, _) := solveEquation(eqn, var_cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
2108 209 eqn_slice := Slice.SLICE(Pointer.create(eqn), {});
2109 elseif solve_status == Status.IMPLICIT then
2110 // var_cref is a parent array containing cref as a slice; expand array sums to enable explicit solving
2111 32 eqn := Equation.map(eqn, function expandArraySumExp(arrayCref = var_cref));
2112 32 (eqn, solve_status, implicit_index, _) := solveEquation(eqn, cref, funcMap, kind, implicit_index, slicing_map, varData, eqData);
2113
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 if solve_status < Status.UNSOLVABLE then
2114 32 eqn_slice := Slice.SLICE(Pointer.create(eqn), {});
2115 else
2116 // all expansion tactics failed; allow implicit solution as last resort
2117 ✗ solve_status := Status.IMPLICIT;
2118 end if;
2119 end if;
2120 end solveForVarSlice;
2121
2122 annotation(__OpenModelica_Interface="nbackend");
2123 end NBSolve;
2124