ModelicaSpecification icon indicating copy to clipboard operation
ModelicaSpecification copied to clipboard

Subscripting of general expressions

Open sjoelund opened this issue 3 years ago • 3 comments

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).

sjoelund avatar Sep 10 '20 07:09 sjoelund

To me this looks like a good idea, but I'm a bit hesitant to add it to Modelica 3.5.

HansOlsson avatar Oct 26 '20 10:10 HansOlsson

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.

HansOlsson avatar Oct 30 '20 14:10 HansOlsson

This seems to be already supported by Dymola (even in Pedantic Mode). I had to "fix" a library that is already using it.

adrpo avatar Feb 03 '21 16:02 adrpo

Should we push it back once more?

HansOlsson avatar Nov 20 '22 19:11 HansOlsson

I see the following possibilities:

  1. Add now: include in Modelica 3.6
  2. Add directly after Modelica 3.6 is complete
  3. 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.

HansOlsson avatar Nov 22 '22 09:11 HansOlsson

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?

henrikt-ma avatar Dec 13 '22 21:12 henrikt-ma

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.

HansOlsson avatar Dec 14 '22 12:12 HansOlsson

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?

perost avatar Dec 14 '22 12:12 perost

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);

HansOlsson avatar Dec 14 '22 15:12 HansOlsson

Any progress on this front? We are now "fixing" another library to make it usable in OMC because of that (@AndreaBartolini).

casella avatar Jul 05 '23 10:07 casella

This is open since Sept. 2020 and should be solved asap.

olivleno avatar Jul 05 '23 13:07 olivleno

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.

HansOlsson avatar Jul 05 '23 13:07 HansOlsson

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}}).

HansOlsson avatar Jul 06 '23 06:07 HansOlsson