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 |