ModelicaSpecification
ModelicaSpecification copied to clipboard
Subscripting of general expressions
In Flat Modelica it is possible to have a subscript on any (parenthesized) expression. The reason for this generalization is that some manipulations, in particular inlining of function calls, can lead to such expressions and without the slight generalization we could not generate flat Modelica for them. It does not add any real complication to the translator.
The reason it is restricted to parenthesized expressions is that a.x[1]
(according to normal Modelica semantics) and (a.x)[1]
will often work differently.
Consider
record R
Real x[2];
end R;
R a[3];
Here a.x[1]
is a slice operation in Modelica generating the array {a[1].x[1],a[2].x[1],a[3].x[1]}
, whereas (a.x)[1]
is a subscripted slice operation generating the array {a[1].x[1],a[1].x[2]}
(assuming trailing subscripts can be skipped, otherwise it is illegal).
It would be possible to extend subscripting to {a,b}[1]
, [a,b][1,1]
,
and foo()[1]
without causing any similar ambiguity - but it was not deemed necessary at the moment.
This text was taken from #2540 (MCP 0031). I would propose that we stage something like this for either the next Modelica specification or the one after that. The change to the grammar is quite small and the semantics are quite easy (treat it as the parenthesis returning a temporary variable and the subscripting as if working on a variable of that type).
To me this looks like a good idea, but I'm a bit hesitant to add it to Modelica 3.5.
Seems like a good idea, postpone? Poll: 3.5: Martin, Stephan Later: Axel, Gerd, Quentin Abstain: Christoff, Filippo, Leo, Henrik, Markus, Hans
No strong majority for 3.5; so postpone.
This seems to be already supported by Dymola (even in Pedantic Mode). I had to "fix" a library that is already using it.
Should we push it back once more?
I see the following possibilities:
- Add now: include in Modelica 3.6
- Add directly after Modelica 3.6 is complete
- Don't make a decision now
Note that tools may already support this. Basically, manipulations may sometimes replace variables with their values, and that naturally leads to subscripting of general expressions. The restriction to any (parenthesized) expression avoids some corner cases - and it would be possible to extend it more later.
When reviewing #3306 I realized that there's a detail we need to consider here. Take this code as a starting point, using only the classic subscripting of component references:
type E = enumeration(one, two);
Real[E] arr;
Real dummy1 = arr[E.one]; // OK
Real dummy2 = arr[2]; // Error: Expected subscript of type E.
I'd assume that this generalizes to a function call returning an array, as the array type is clearly declared:
function f
output Real[E] y = {1, 2};
end f;
Real dummy3 = (f())[E.one]; // OK
Real dummy4 = (f())[2]; // Error: Expected subscript of type E.
What I think needs to be clarified in https://specification.modelica.org/master/arrays.html#array-constructor-with-iterators is what type of subscript that can be applied to the resulting array. I suppose only one of these should be considered correct:
Real dummy5 = ({ arr[i] for i in E })[E.one]; // OK?
Real dummy6 = ({ arr[i] for i in E })[1]; // OK?
When reviewing #3306 I realized that there's a detail we need to consider here. Take this code as a starting point, using only the classic subscripting of component references: ... What I think needs to be clarified in https://specification.modelica.org/master/arrays.html#array-constructor-with-iterators is what type of subscript that can be applied to the resulting array. I suppose only one of these should be considered correct:
Real dummy5 = ({ arr[i] for i in E })[E.one]; // OK? Real dummy6 = ({ arr[i] for i in E })[1]; // OK?
Given that the array is constructed using the enumeration type E
one could argue that it makes sense to also use it when indexing.
However, we then also have:
Real dummy7 = ({ arr[i] for i in E.one:E.two })[E.one]; // OK?
Real dummy8 = ({ arr[i] for i in E.one:E.two })[1]; // OK?
For these specific values for the range it is similar to using E, but for other values it is not, and to me only dummy8 makes sense in general.
Thus, to avoid having different semantics for two variants that look as though they are the same, I believe it is simplest if all resulting array expressions are indexed with 1..n
.
I guess the question is, what is the type of e.g. {arr[i] for i in E}
? I don't think the specification actually says what the dimension of such an expression is. It's not entirely clear what the dimension of E
or E.one:E.two
is either actually.
So I guess there are two separate issues: what is the type of e.g. {arr[i] for i in E}
, and how should an expression of a given type be subscripted?
Phone meeting: Defer until after 3.6 (due to enumeration-issue).
Work-around
function subscript
input Real A[:];
input Integer i;
output Real y;
algorithm
y:=A[i];
end subscript;
subscript({Integer(i) for i in E}, p);
Any progress on this front? We are now "fixing" another library to make it usable in OMC because of that (@AndreaBartolini).
This is open since Sept. 2020 and should be solved asap.
This is open since Sept. 2020 and should be solved asap.
It would be good, but there's a problem that too few persons actually work on proposals - and any edge-case blocks progress as in this case.
Thinking more about it I concluded that the blocking edge-case is in practice meaningless. So, the simplest solution is that any for-expression will generate an array indexed by integers.
That means that you cannot write
type E=enumeration(....);
E j;
E arr[2];
Real x=({foo(e) for e in E})[j];
Real x[:]=({foo(e) for e in E})[arr];
But if really desiring to use subscripting you have to write:
type E=enumeration(....);
E j;
E arr[2];
Real x=({foo(e) for e in E})[Integer(j)];
Real x[:]=({foo(e) for e in E})[Integer(arr)];
But a more convenient way is to avoid the subscripting as follows:
type E=enumeration(....);
E j;
E arr[2];
Real x=foo(j);
Real x[:]={foo(e) for e in arr};
Note that scalar({foo(e) for e in {j}})
added: as well as ({foo(e) for e in {j}})[1]
are alternatives to foo(j)
.
For this specific case it just seems needlessly verbose, but if both foo
and j
were a complicated expression it would make sense, e.g. scalar({if e==E.a then 1 elseif e==E.b then -1 else 3} for e in {if time<1 then e1 elseif time<2 then e2 else e3}})
.