ModelicaSpecification icon indicating copy to clipboard operation
ModelicaSpecification copied to clipboard

Constant variability inside functions and input dimensions

Open qlambert-pro opened this issue 3 years ago • 12 comments

I am surprised that m is a constant here, since its value clearly depends on the size of the input which is not constant for functions.

function ToSpacePhasor "Conversion from multi phase input to space phasor and zero sequence component"
  import Modelica.Constants.pi;
  extends Modelica.Icons.Function;
  input Real x[:] "Multi phase (voltage or current) input";
  output Real y[2] "Space phasor";
  output Real y0 "Zero sequence component (of voltage or current)";
protected
  constant Integer m = size(x, 1) "Number of phases" annotation(Dialog(group = "Constants"));
  parameter Modelica.SIunits.Angle phi[m] = Modelica.Electrical.MultiPhase.Functions.symmetricOrientation(m);
  parameter Real TransformationMatrix[2, m] = 2 / m * {+cos(+phi), +sin(+phi)};
  parameter Real InverseTransformation[m, 2] = array({+cos(-phi[k]), -sin(-phi[k])} for k in 1:m);
algorithm
  y := TransformationMatrix * x;
  y0 := 1 / m * sum(x);
  annotation(Inline = true, Documentation(info = "<html>
Transformation of multi phase values (of voltages or currents) to space phasor and zero sequence value.
</html>"));
end ToSpacePhasor;

At the same time the specs don´t really cover this case, and I think it should. Section 3.8.2, talks about the variability of size and makes an exception for the case of functions. I think a non-normative text pointing out the error made in ToSpacePhasor could be useful.

qlambert-pro avatar Apr 14 '21 07:04 qlambert-pro

Unfortunately, @qlambert-pro has been exposed to a tool-specific tweak made to the 3.2.3 version of this function in order to ensure certain translation time simplifications based phase indices. It's probably best to close this as an invalid issue.

Related to this, I must also warn that Evaluate = true has been added inside a function by mistake in this commit: https://github.com/modelica/ModelicaStandardLibrary/commit/6bd7d95ad5aa598b4942d514847ec8a20d109308

The 4.0.0 declaration looks like this, and I'll open a PR for fixing this:

  parameter Integer m=size(x, 1) "Number of phases" annotation(Evaluate=true);

henrikt-ma avatar Apr 14 '21 17:04 henrikt-ma

Actually, before I jump to a PR on the MSL, could we take the opportunity to discuss the meaning of Evaluate inside an inlined function?

In the particular case at hand, I'd say that the Evaluate = true on m shouldn't be needed, but the reasons for this is a bit technical: After inlining ToSpacePhasor in a non-function context, the size(x, 1) will become a parameter expression (even though the effective variability is that of 1, that is, constant). Since the declaration equation for the inlined protected parameter m should not be exposed to overriding after translation, the declaration equation can effectively be seen as final. That is, the inlined m is a parameter with final binding to a constant parameter expression, showing that one doesn't need Evaluate = true as an excuse for evaluating it. It can be argued that adding Evaluate = true would express a clear desire that it should really be evaluated, but most tools will probably take the opportunity to do so anyway.

The question becomes more interesting when the binding isn't constant:

model InlineAndEvaluate
  function f
    input Real x;
    output Boolean y;
  protected
    parameter Real p = x annotation(Evaluate = true); /* Special variability rules inside functions allow this. */
  algorithm
    b := p > 0.5;
    annotation(Inline = true);
  end f;

  parameter Real p1 = true; /* Should this become evaluated? */
  Boolean y1 = f(p1);
  Boolean y2 = f(time); /* Is the Evaluate = true even legal in this case? */
end InlineAndEvaluate;

henrikt-ma avatar Apr 14 '21 18:04 henrikt-ma

Actually, before I jump to a PR on the MSL, could we take the opportunity to discuss the meaning of Evaluate inside an inlined function?

The proposed handling of variables inside functions in the PR for evaluable variables (or parameters) makes it redundant: https://github.com/modelica/ModelicaSpecification/pull/2754

HansOlsson avatar Apr 26 '21 19:04 HansOlsson

The proposed handling of variables inside functions in the PR for evaluable variables (or parameters) makes it redundant: #2754

Even though I've been very involved in #2754, I don't see that it answers any questions related to inlining, hence the question. If we need a reason to not answer the question, I suggest calling it esoteric rather than a duplicate of #2754.

henrikt-ma avatar Apr 26 '21 19:04 henrikt-ma

The proposed handling of variables inside functions in the PR for evaluable variables (or parameters) makes it redundant: #2754

Even though I've been very involved in #2754, I don't see that it answers any questions related to inlining, hence the question. If we need a reason to not answer the question, I suggest calling it esoteric rather than a duplicate of #2754.

How and when to implement inlining is an internal implementation detail in tools and shouldn't impact the semantics.

The idea with evaluable variables (or parameters) in #2754 is that a function behaves as if all inputs etc have actual values and then evaluate the body (etc); and without any variability requirements. (A function call preserves the variability of the arguments.)

HansOlsson avatar Apr 30 '21 11:04 HansOlsson

Sounds like you would be in favor of forbidding the use of Evaluate inside functions?

henrikt-ma avatar May 02 '21 20:05 henrikt-ma

Sounds like you would be in favor of forbidding the use of Evaluate inside functions?

That's one way of viewing it - or expressed as function calls are always fully evaluated when used; so internally in functions everything is evaluated.

If we want to have a function where parts are evaluated in advance the question is what benefit it brings. Evaluating a function call with a literal value before translation is obvious, and similarly only evaluating a parametric function call once. But trying to track dependency from one literal input is messier, especially as people expect an algorithm to be executed in order.

HansOlsson avatar May 10 '21 15:05 HansOlsson

Sounds like you would be in favor of forbidding the use of Evaluate inside functions?

That's one way of viewing it - or expressed as function calls are always fully evaluated when used; so internally in functions everything is evaluated.

If we want to have a function where parts are evaluated in advance the question is what benefit it brings. Evaluating a function call with a literal value before translation is obvious, and similarly only evaluating a parametric function call once. But trying to track dependency from one literal input is messier, especially as people expect an algorithm to be executed in order.

Note that the original question about Evaluate inside a function was formulated in the context of inlining, in which case it's a bit odd to consider the body as fully evaluated. The point of Evaluate = true would be to force evaluation of parameters passed to the function, just like one could do if the function was inlined manually. I must say myself that even though I can see this possibility, I don't see a need for it, and would much rather just forbid Evaluate inside functions. I believe forbidding it would make our functions be more like functions and less like macros, which would be a good thing for my conceptual understanding of Modelica.

henrikt-ma avatar May 10 '21 21:05 henrikt-ma

I believe forbidding it would make our functions be more like functions and less like macros, which would be a good thing for my conceptual understanding of Modelica.

Yes, forbidding Evaluate in functions would be one possibility.

HansOlsson avatar May 11 '21 07:05 HansOlsson

I think the issue of variability of size inside functions was not addressed in this issue so far. In 3.8.2 it only says:

• Some function calls are parameter expressions even if the arguments are not: ... – end in A[ end ] if A is variable declared in a non-function class. – size(A) (including size(A, j) where j is parameter expression) if A is variable declared in a non-function class. ...

I have not find an explanation of how size should be interpreted in functions. I only looked through every mention of "size" in the chapter on functions. It is possible it is mentioned somewhere else but then it should perhaps moved closer to 3.8.2.

In functions there can be arrays that change sizes during the function execution (12.4.2). But arrays of inputs would stay constant for the duration of the function execution.

eshmoylova avatar May 18 '21 14:05 eshmoylova

size(A) (including size(A, j) where j is parameter expression) if A is variable declared in a non-function class.

I think this says implicitly that within the context of a function, size is handled as a user defined function. That is, it is as variable as its inputs. Maybe some non-normative text would be helpful to highlight that aspect of the variability of size?

qlambert-pro avatar May 19 '21 07:05 qlambert-pro

size(A) (including size(A, j) where j is parameter expression) if A is variable declared in a non-function class.

I think this says implicitly that within the context of a function, size is handled as a user defined function. That is, it is as variable as its inputs.

Yes.

HansOlsson avatar May 19 '21 07:05 HansOlsson