modelica / ModelicaSpecification

Specification of the Modelica Language
https://specification.modelica.org
Creative Commons Attribution Share Alike 4.0 International
98 stars 41 forks source link

What to do with parameter start-values not available at beginning of initial algorithm? #2668

Open henrikt-ma opened 4 years ago

henrikt-ma commented 4 years ago

In #2602, the following was added (merged to master) without prior discussion in the language group, with added emphasis on the controversial part:

A parameter assigned in an initial algorithm, \cref{initialization-initial-equation-and-initial-algorithm}, is initialized with its start-value (i.e.\ the value of the \lstinline!start! attribute) provided the start-value can be evaluated before evaluating the initial algorithm (a literal value can always be evaluated); otherwise a default start-value is used (together with appropriate diagnostics) as if the \lstinline!start! attribute was not set.

Tentatively removing this part is the topic of #2666, and this issue is about putting this, or something else back.

henrikt-ma commented 4 years ago

In my opinion, a better alternative to overriding the start attribute with the default start-value is to define this as an error. A less pedantic tool could still choose to go against the specification and do something else together with a warning, but this doesn't mean it should be standardized.

When this error occurs, I see two possible resolutions:

henrikt-ma commented 4 years ago

Another reason to not fall back to default start values along with a warning is that this seems comparatively complicated in terms of the symbolic processing. One would first need to form the strong components of the initialization problem with the goal of respecting all start attributes, and when this fails, look for those that can be ignored to see if this can break the strong component apart — one start at a time to not disregard more of them than necessary (perhaps there are two candidates, where disregarding either one would be sufficient…) — and if that actually resolves the entangling, produce the warnings and proceed.

I'm not saying this because I am afraid of carrying out symbolic processing — in the short term, this is how we make our living after all. I'm saying it because the more complicated processing like this we add to a tool's responsibilities, the harder it becomes for the end user to understand what is going on. Hence, in the long term, it can serve all of us bad.

henrikt-ma commented 4 years ago

Let's add some example for illustration.

First one where a strange result would be obtained if q.start is ignored:

model StartMatters
  parameter Boolean b = false;
  parameter Boolean p(start = true, fixed = false); /* Should become equal to b, which is false. */
  parameter Integer q(start = if p then 1 else 2, fixed = false); /* Should become 2, if anything. */
initial algorithm
  if b then
    q := 3;
  else
    p := false;
  end if;
end StartMatters;

As explained by the comments in the model, the expected value of q after initialization, if anything, would be 2.

If q.start is ignored, one will obtain q = 0.

If q.start is evaluated before having determined p, some sort of uninitialize read will determine the initialization of q:

Then one example where q.start can be disregarded without affecting the initialization:

model UnusedStart
  parameter Boolean b = false;
  parameter Boolean p(start = true, fixed = false); /* Should become equal to b, which is false. */
  parameter Integer q(start = if p then 1 else 2, fixed = false); /* The 'start' has no effect.  Should become 4, if anything. */
initial algorithm
  q := 4; /* Assigning to q before first use in algorithm, no need to initialize to q.start. */
  if b then
    q := 3;
  else
    p := false;
  end if;
end UnusedStart;

Note that it only requires a simple local analysis to figure out that the algorithm in UnusedStart doesn't depend on q.start for initialization. This can be done before constructing the incidence graph of the initialization problem, so there is no need to manipulate the initial incidence graph to find a way out.

In case the examples above are confusingly minimalistic in that they are entangled even though they consist of just a single algorithm, one can extend them to also include an equation:

model StartMattersExtended
  parameter Boolean b = false;
  parameter Boolean p(start = true, fixed = false); /* Should become equal to b, which is false. */
  parameter Boolean p2(fixed = false); /* Should become equal to p. */
  parameter Integer q(start = if p2 then 1 else 2, fixed = false); /* Should become 2, if anything. */
initial algorithm
  if b then
    q := 3;
  else
    p := false;
  end if;
initial equation
  p2 = p;
end StartMattersExtended;
model UnusedStartExtended
  parameter Boolean b = false;
  parameter Boolean p(start = true, fixed = false); /* Should become equal to b, which is false. */
  parameter Boolean p2(fixed = false); /* Should become equal to p. */
  parameter Integer q(start = if p2 then 1 else 2, fixed = false); /* Should become 4, if anything. */
initial algorithm
  q := 4; /* Assigning to q before first use in algorithm. */
  if b then
    q := 3;
  else
    p := false;
  end if;
initial equation
  p2 = p;
end UnusedStartExtended;
HansOlsson commented 4 years ago

In my opinion, a better alternative to overriding the start attribute with the default start-value is to define this as an error. A less pedantic tool could still choose to go against the specification and do something else together with a warning, but this doesn't mean it should be standardized.

That is also a possibility. The problem is that we then need to define it in more detail.

However, to me these models seem broken in some way. I know that technically an (initial) algorithm start with p:=pre(p); and thus they are well-defined and for a normal algorithm it makes sense that the assignment to p is conditional.

However, I cannot see that having the only assignment to a variable conditional in an initial algorithm is a good idea, and I couldn't find any such examples in MSL.

henrikt-ma commented 4 years ago

In my opinion, a better alternative to overriding the start attribute with the default start-value is to define this as an error. A less pedantic tool could still choose to go against the specification and do something else together with a warning, but this doesn't mean it should be standardized.

That is also a possibility. The problem is that we then need to define it in more detail.

I don't think we have to. We always depend on tools to perform symbolic analysis with error detection and reasonable user feedback in case of errors. Compared to other requirements on doing sane symbolic processing, the processing needed to work out the examples above is really simple. Still, it's a quality of implementation to even do this — a less ambitious tool could ignore the possibility to figure out which non-fixed parameter start values an initial algorithm needs for initialization, treat all as needed, and thereby fail to translate some models that other tools will be able to prove valid.

I would expect that if a model relying on this kind of analysis was added to MSL, and some tools wouldn't be able to see that a start-value is unused, then it would be the library author's (not some tool's) responsibility to explain why the start ends up unused, so that this reasoning can be implemented in tools. However, an easier way out for the library author is probably going to be to switch to a start that is trivial to evaluate before the start of the algorithm.

However, to me these models seem broken in some way.

Yes, and I'm just looking for a clean way of rejecting the worst ones while at the same time introducing what I believe is a more useful way to think of initial algorithm initialization (only relevant for initial algorithms; normal algorithms don't assign to non-fixed parameters, and the pre-values of discrete-time variables are always available).

I know that technically an (initial) algorithm start with p:=pre(p); and thus they are well-defined and for a normal algorithm it makes sense that the assignment to p is conditional.

Note that these examples only use parameters, which is why we need to avoid reference to pre(p) for initialization. (However, te comment is still relevant since it would — in my opinion — make sense to also ease up the requirement to initialize discrete-time variables to pre(x) when it can be proven unused.)

However, I cannot see that having the only assignment to a variable conditional in an initial algorithm is a good idea, and I couldn't find any such examples in MSL.

Yes, it's good if the MSL isn't relying on the use of unavailable start-values.

HansOlsson commented 3 years ago

To me this focus on "initial algorithm" and pre-values for discrete-valued variables misses an important aspect of the overall problem. The "initial algorithm" above together with the guess-value (=start) form a system of equations.

But we don't need an initial algorithm to have guess-values that are implicitly defined as part of the system of equations.

Consider the following problem:

model StartMatters
  parameter Real p2 = -2;
  parameter Real p(start = r.x, fixed = false);
  record R
    Real x,y;
  end R;
  parameter R r=R(p2,p);
initial equation 
  p^4+p^2=3;
end StartMatters;

Depending on whether r=R(p2,p) is split into two equations or treated as one "atomic operation" there is either a known guess-value for the parameter p or not. (Using start=-2 would avoid that ambiguity.)

henrikt-ma commented 3 years ago

To me this focus on "initial algorithm" and pre-values for discrete-valued variables misses an important aspect of the overall problem. The "initial algorithm" above together with the guess-value (=start) form a system of equations.

I agree that the particular case of initial algorithm and discrete-valued variables is a special case of the general problem of not having start-values ready when needed. Indeed, saying something about the general problem would be a highly desirable outcome in the end, but particular cases such as the initial algorithms or your record binding equation below will both be necessary as input to the general discussion, could also require us to make up rules for special cases if we don't find the general formulations we come up with precise enough.

But we don't need an initial algorithm to have guess-values that are implicitly defined as part of the system of equations. … Depending on whether r=R(p2,p) is split into two equations or treated as one "atomic operation" there is either a known guess-value for the parameter p or not. (Using start=-2 would avoid that ambiguity.)

This is a good example to work on, and here is how I would like to handle it.

HansOlsson commented 3 years ago

Filippo, Gerd, and Martin S. will volunteer to read and hopefully give feedback.

filippomariadonida commented 3 years ago

Assuming that there's a clear distinction between the declaration and the initialization sections, one could think at the first being the pre-processing to the second one. I try to add some details in the following, please comment/add details in case you think I'm missing something.

The declaration section contains parameters declarations that are instantiated before the initialization process occurs. Here the start values (both fixed and/or not) and the binding expressions are present and are used to create the input to the initialization phase.

During the instantiation phase, a list of symbols, where each symbol can have a binding expression is formed. Here the binding equations/statements can be derived from the start values or from an assignment (binding expressions in case of a parameter). If both are present the latter is used.

Whenever a parameter is missing such an equation, a warning should be given, something like: "Warning parameter "a" value is unknown at declaration time. Please add a start value / binding expression."? This might already be offered by some Modelica environments.

Alongside and aligned with the previous, whenever a parameter value cannot be computed using the previous system a warning should be thrown, such as: "Warning parameter "a" value cannot be evaluated at declaration time."

For the sake of simplicity and clarity, I think that these initial values should then be available for being used inside the "initial algorithm" section.

Summarizing, given the freedom of the Modelica language in describing things, which results in a high level of complexity that has to be handled by the compiler, I propose to define something as simple and linear as possible.