Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle units of literal constants by means of rules for the empty unit #3257

Open
wants to merge 26 commits into
base: MCP/0027
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7524b11
Initial commit of ReadMe.md for MCP-0027
henrikt-ma Oct 4, 2022
1309d10
Handle units of literal constants by means of rules for the empty unit
henrikt-ma Oct 4, 2022
2dfb4e8
Clarify that all operands of additive operators need to have the same…
henrikt-ma Oct 5, 2022
0c62f41
Clarify "component references outside functions"
henrikt-ma Oct 5, 2022
203c16e
Fix small mistake in example
henrikt-ma Oct 5, 2022
5d42065
Change "common sense" -> "(standard) dimensional analysis"
henrikt-ma Oct 5, 2022
fc4abde
Add section with examples of "standard dimensional analysis"
henrikt-ma Oct 5, 2022
b86567b
Fix minor typo
henrikt-ma Oct 5, 2022
1f5131c
Put quotes around 'standard dimensional analysis'
henrikt-ma Oct 5, 2022
3291a73
Say "multiplying the operands' units"
henrikt-ma Oct 5, 2022
f639afc
Make use of terminology 'dimensional homogenity'
henrikt-ma Oct 5, 2022
3a3b1f0
Don't present lists as "Examples of...", but add "other" item at the end
henrikt-ma Oct 26, 2022
e411347
Say "reject or approve"
henrikt-ma Oct 26, 2022
f3755a2
Add missing "on"
henrikt-ma Oct 26, 2022
fccda32
Reformulate "In some cases"
henrikt-ma Oct 26, 2022
89092ba
Change 'can be' -> 'is'
henrikt-ma Oct 26, 2022
82bb062
Fix typo in comment
henrikt-ma Oct 26, 2022
99ff3c9
Describe unit propagation and elaborate bottom-up nature of unit deri…
henrikt-ma Oct 26, 2022
13529f7
Also describe unit mappings of transcendental functions to match othe…
henrikt-ma Oct 31, 2022
847e645
Change 'are expected to' -> 'should'
henrikt-ma Nov 1, 2022
ebfc229
Add example illustrating bottom-up nature of unit inference
henrikt-ma Nov 1, 2022
232c4fa
Make stronger statement about when unit inference takes place
henrikt-ma Nov 1, 2022
9aeb15f
Change example on undefined unit to user-defined function instead of …
henrikt-ma Nov 2, 2022
a28b795
Simplify proposal by not omitting rules concerning non-empty units
henrikt-ma Nov 2, 2022
089f554
Move 'Otherwise...' into item list
henrikt-ma Nov 7, 2022
35e4f01
Rule out unit inference for components with empty unit-attribute
henrikt-ma Nov 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions RationaleMCP/0027/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Modelica Change Proposal MCP-0027<br/>Units of Literal Constants
Francesco Casella, Henrik Tidefelt

**(In Development)**

## Summary
The purpose of this MCP is to allow more unit errors to be detected by giving more expressions the unit `"1"` instead of having an undefined unit.
The problem with undefined unit is that it gets in the way of carrying out checking of units (which tools tend to deal with by not checking units at all where this happens).

## Revisions
| Date | Description |
| --- | --- |
| 2022-10-04 | Henrik Tidefelt. Filling this document with initial content. |

## Contributor License Agreement
All authors of this MCP or their organizations have signed the "Modelica Contributor License Agreement".

## Rationale
FIXME

## Backwards Compatibility
As current Modelica doesn't clearly reject some models with non-sensical combination of units, this MCP will break backwards compatibility by turning at least some of these invalid.

## Tool Implementation
None, so far.

### Experience with Prototype
N/A

## Required Patents
To the best of our knowledge, there are no patents that would conflict with the incorporation of this MCP.

## References
(None.)
168 changes: 168 additions & 0 deletions chapters/unitexpressions.tex
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,171 @@ \section{The Syntax of Unit Expressions}\label{the-syntax-of-unit-expressions}

The unit expression \lstinline!"T"! means tesla, but note that the letter \lstinline!T! is also the symbol for the prefix tera which has a multiplier value of 10\textsuperscript{12}.
\end{example}


\section{Unit Checking}\label{unit-checking}

How to verify that units are used in compatible ways is current not fully determined by the specification.
This section gives rules for certain situations, but in general tools are expected to reason according to standard dimensional analysis..


\subsection{Standard Dimensional Analysis}\label{standard-dimensional-analysis}

This section gives an incomplete characterization of \emph{standard dimensional analysis}, also referred to as just \firstuse{dimensional analysis}.
What is described below applies to unit checking in Modelica -- \emph{standard dimensional analysis} could have additional meanings in other contexts.
While Modelica has a concept of \willintroduce{empty units} (described in later sections), standard dimensional analysis only deals with non-empty units such as \lstinline!"m"!, \lstinline!"m/s"!, or \lstinline!"1"!.
It consists of two parts:
\begin{itemize}
\item
Unit compatibility requirements.
\item
Rules for deriving the unit of an expression.
\end{itemize}

Examples of unit compatibility requirements that must be met in Modelica:
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved
\begin{itemize}
\item
The expression of a declaration equation must have the same unit as the component to which it belongs.
\item
The two sides of an equation or assignment statement must have the same unit.
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved
\item
The two operands of the additive operators \lstinline!+! and \lstinline!-! must have the same unit.
\item
In a function call, the unit of an argument expression must match the unit (unless empty, see below) of the corresponding function input component.
\end{itemize}

% TODO: Replace these examples by giving unit semantics where operators and built-in functions are defined in the specification,
% and just include some likes to places where such semantics are given.
Examples of unit derivation in Modelica:
\begin{itemize}
\item
The result of additive operators has the same unit as the operands.
\item
The result of multiplication has a unit obtained by adding the exponents of the operands' units.
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved
\item
Built-in operators such as \lstinline!pre! and \lstinline!previous! preserve units, while \lstinline!der! changes the unit by dividing it by \lstinline!"s"!.
\end{itemize}


\subsection{Empty and Undefined Units}\label{empty-and-undefined-units}

In situations where the specification does not prescribe how to determine the unit of an expression, the unit of that expression is said to be \firstuse[undefined unit]{undefined}.
It is then not possible for a tool to reject the equation (or other construct) with support in the specification, and options for the tool include silently not performing unit checking, or applying checks based dimensional analysis.
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved

The unit of an expression can also be defined as being \firstuse[empty unit]{empty}, see below.
In certain places (see below), expressions with empty unit can be implicitly cast to suitable units.
Copy link

@bilderbuchi bilderbuchi Oct 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a friend of "soft" qualifiers like "certain", "some of", etc. in specification texts. I think it would be better to enumerate all the specific places/sections/cases you mean to include in this statement. Include a "fall-through" case handling the situation if no element of the enumeration fits, if appropriate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paragraph is part of the section introduction, so it isn't meant to convey the details. I agree that later, when the actual rule is given, one has to avoid formulations that risk causing unnecessary ambiguity.

Does it work better now, with the reformulated presentation of the corresponding list of cases? (I don't think this list needs a fall-through case, as it should always be possible to add more cases in a backwards compatible way.)

Copy link

@bilderbuchi bilderbuchi Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paragraph is part of the section introduction, so it isn't meant to convey the details.

Should it then be marked non-normative? Otherwise, it's normative, right? (I'm not too firm on the procedural details of this)

reformulated presentation of the corresponding list of cases?

I can't tell which list you mean here?

I don't think this list needs a fall-through case, as it should always be possible to add more cases in a backwards compatible way.)

This is not a contradiction! You can first specify (random example: operators covered by some procedure):

  • Addition: do A
  • Subtraction: do B
  • All others: undefined

and then later refine by adding more cases

  • Addition: do A
  • Subtraction: do B
  • Multiplication: do C
  • Division: do D
  • All others: undefined

The set of the fallthrough/uncovered cases shrinks by adding more cases, but it's always crystal clear which ones are "undefined" (first: everything except +,-, then everything except +,-,*,/)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paragraph is part of the section introduction, so it isn't meant to convey the details.

Should it then be marked non-normative? Otherwise, it's normative, right? (I'm not too firm on the procedural details of this)

I'm not sure. It's a matter of style where we have at least decided to not explicitly mark chapter introductions as non-normative even though their nature is non-normative. In the case at hand, I'd say that the (see below) should be sufficient to ensure the reader that there's a specific meaning of certain places to be found by looking at the sections below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reformulated presentation of the corresponding list of cases?

I can't tell which list you mean here?

I refer to formulations like this one:

When encountering the empty unit in the following situations, the inferred unit is (uniquely) determined by ensuring that unit compatibility requirements are fulfilled:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this list needs a fall-through case, as it should always be possible to add more cases in a backwards compatible way.)

This is not a contradiction! You can first specify (random example: operators covered by some procedure):

The nice thing about this list (When encountering the empty unit in…) is that it doesn't leave any situation undefined. In the other cases, the unit isn't determined by the context, but always becomes "1" as (now) described directly below the list.

When an expression with empty unit is implicitly cast to some unit, that unit is referred to as the \firstuse{inferred unit} of the expression.


\subsection{Unit Inference}\label{unit-inference}

Where the empty unit has no other defined meaning, it can be implicitly cast to unit \lstinline!"1"!.

In some cases the empty unit can be implicitly cast to an inferred unit:
\begin{itemize}
\item
In binding equations and modifications:
\begin{itemize}
\item The entire expression of the binding or modification.
\item When the entire expression is an array construction, array concatenation and array range, then apply rules recursively for the direct subexpressions.
\end{itemize}
\item
The entire argument expression in a function call.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing it to #3266 (comment) it seems that it has the same critical flaw.

In general, my issue with some proposals is that there are way too much text and theory, but too little actual practice.

I can see that the other proposal - changed to not apply to addition and subtraction - does detect actual modeling issues; but also that it finds a number of issues that aren't that important and will take time to correct. I don't know if people are actually willing to put in the effort to correct - and I don't know the corresponding result for this proposal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, my issue with some proposals is that there are way too much text and theory, but too little actual practice.

I think it needs to be seen in the light of me looking for a solid ground on which to implement unit checking in System Modeler, where the specification at least marks a starting point for something that can evolve into a set of rules that make unit consistency a model feature that is portable between tools.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, my issue with some proposals is that there are way too much text and theory, but too little actual practice.

Can you explain what you mean by that, because to me this proposal is very clear?

Copy link
Collaborator

@qlambert-pro qlambert-pro Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that the other proposal - changed to not apply to addition and subtraction - does detect actual modeling issues; but also that it finds a number of issues that aren't that important and will take time to correct. I don't know if people are actually willing to put in the effort to correct - and I don't know the corresponding result for this proposal.

You are just describing the properties of a sound logical system, as opposed to a complete one.
At the end of the day, if MA wants the models produced with modelica to be portable it will have to opine on where unit checking should fall on the sound to complete spectrum. And most people, expects a sounder system when we talk about type checking. This will obviously imply a lack of backward compatibility, but then again the spec does not currently support the rejection of models relying on invalid unit use anyway.

% To keep the design close to the bare minimum, this part is currently excluded:
%\item
% For a translation-time constant with value 0.0:
% \begin{itemize}
% \item Expressions constituting the entire side of an equality or relation.
% \item The right hand side of an assignment.
% \item The direct subexpressions of array construction, array concatenation, and array range.
% \end{itemize}
\end{itemize}


\subsection{Expressions with Empty Unit}\label{expressions-with-empty-unit}

This section describes conditions under which an expression has empty unit.
Conditions not given here must not be interpreted as definitely not implying empty unit; instead, the unit may be currently undefined for some expressions, allowing the unit to be properly defined in future versions of the specification.

Basic expressions defined to have empty unit:
\begin{itemize}
\item
\lstinline!Real! literals.
\item
\lstinline!Integer! expressions implicitly cast to \lstinline!Real!.
\end{itemize}

Expressions defined to \emph{not} propagate the empty unit, thereby forcing inference of unit \lstinline!"1"!:
\begin{itemize}
\item
Addition, subtraction, multiplication and division operators when either operand has non-empty unit.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in these cases you don't 'force inference of unit "1"', nor propagate the empty unit, but these expressions (i.e. the operators + operands) propagate the unit of the operand with the non-empty unit (addition, subtraction, multiplication, division when in numerator) or its reciprocal (division when in denominator).

Copy link

@bilderbuchi bilderbuchi Oct 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better?

"If an addition, subtraction, multiplication and division, operands that have empty unit get inferred unit "1" if the other operand has non-empty unit, and remain empty-unit otherwise"
..and deal elsewhere with the correct units for these operators' result when both operands have been assigned units (both empty, one empty, both non-empty cases)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way it is formulated comes from the need to leave gray areas open for future design, with the understanding that tools may take the easy way out and propagate empty units up the expression three whenever the specification doesn't define what to do, in order to not reject things without support in the specification. I am thus categorizing all expressions (where one or more subexpressions have empty unit) according to:

  • Expressions where the empty unit is propagated up the expression tree.
  • Expressions where the empty unit is not propagated up the expression tree.
  • Expressions not yet covered by the specification (meaning their unit is so far undefined).

Maybe there are alternative ways of achieving the same effect, but I'm not sure how to do it without ending up with a proposal that covers more than what the language group wants as a solution for MCP-0027. In the future, when all cases are covered by the specification, there should be an easier way to express the rule:

  • Describe situations where the empty unit gets propagated up the expression tree.
  • In all other situations, unit "1" is inferred.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, it's fine to have grey areas (if you mean areas that the spec does not cover), they just should be clearly delineated. IMO, having grey areas is not a contradiction to having a clearly defined process for the rest.

I think I understand what you mean better, but I'll have to think on this a bit, I'm a bit confused.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This that mean we are ready to close this conversation as resolved?

\item
Right operand of binary exponentiation.
Copy link

@bilderbuchi bilderbuchi Oct 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to specify a "bottom-up" approach as you have previously expressed, an expression cannot be required to know about its parents, only children.

I suggest reformulating the operator items throughout to be from the perspective of the operator expression, not the operand, and adjusting the resulting units of other expressions accordingly
E.g. here 'For binary exponentiation, if the right operand has empty unit, infer unit "1" for it'

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point that I've also considered myself. The bottom-up nature of the approach needs to be crystal clear, or else much of the point of this proposal is lost. I'll start with something that may be overly clear, and then one might be able to shorten the formulation once everybody is on board…

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better now? (It turned out that I also needed to say a few words on unit propagation in order to separate between the declared unit-attribute of a variable, and its unit after unit propagation; for most purposes, it is the unit after propagation that is of interest.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like what you added in the section bottom-up-unit-derivation (although the flow could be further simplified I think). Does this really change the fact that here an expression (the operand) needs to know its context (I'm the right operand of a binary exponentiation) to decide how to act, which violates the bottom-up principle?

Copy link
Collaborator Author

@henrikt-ma henrikt-ma Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be happy to hear your thoughts about how to further simplify the design or its presentation. (I've considered completely separating everything concerning the empty unit from the reasoning about non-empty units. Then I think one would end up with a fairly short list of rules for where empty units may appear in the expression tree, and how they can be propagated up the tree.)

Does this really change the fact that here an expression (the operand) needs to know its context (I'm the right operand of a binary exponentiation) to decide how to act, which violates the bottom-up principle?

Yes, this is the idea I'm trying to convey. Consider determining the unit of exponentiation here:

  Real x(unit = "m");
  Real y = x ^ (1 + 1)
  • One starts by determining the units of x and (1 + 1) by themselves. Here, x has unit "m", and (1 + 1) is an Integer expression implicitly cast to Real, so it has empty unit.
  • Now that the units of x and (1 + 1) are known, it is time to consider the context of being operands to exponentiation. Here, the particular rules for the operator at hand comes into the picture. In particular, one needs to see if there is a rule giving meaning to operands with empty units; when that's the case, no unit inference takes place. Otherwise, unit inference takes place, implicitly casting the empty unit to a non-empty unit.
  • At this stage, there's an intermediate form where implicit unit conversions may have been inserted. With some pseudo code, it might look like x ^ unit((1 + 1), "1"), where unit is the pseudo-code operator taking an operand with empty unit and producing the corresponding expression with some other unit attached.
  • The rest is "standard dimensional analysis" of the exponentiation operator (disregarding the fact that this happens to be a pretty messy operator in Modelica, as it doesn't distinguish between rational and irrational exponents).

\item
Component references outside of functions, where the component's declared \lstinline!unit!-attribute is \lstinline!""! (possibly by not being specified).
(Unit checking involving user-defined functions with empty unit on inputs and outputs is currently not defined.)
\end{itemize}

Built-in non-array operators, functions, and special expressions that propagate any unit (including empty):
\begin{itemize}
\item
When all operands have the same unit: negation, addition, and subtraction (scalar or element-wise).
\end{itemize}

Special situations in which the empty unit can be propagated up the expression tree:
henrikt-ma marked this conversation as resolved.
Show resolved Hide resolved
\begin{itemize}
\item
Multiplication and division when both operands have empty unit.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also addition/subtraction? Or all operators? I'm not sure...

Copy link
Collaborator Author

@henrikt-ma henrikt-ma Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is where I see a possibility to simplify the presentation by concentrating on just the propagations of the empty unit up the expression tree. Then we get this simple rule:

  • All binary numeric operators as well as unary negation result in the empty unit if and only if all operands have empty unit. If one of the operands has non-empty unit, all operands must have non-empty unit, meaning that unit inference gets applied to the operands with empty unit.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a try, and it seems to me that this led to considerable simplification of the proposal. Maybe we could consider this conversation resolved, and continue the discussion in conversations attached to the updated text?

(By the way, I'll be away until Monday.)

\end{itemize}

\begin{example}
Consider unit checking of the following declaration equation:
\begin{lstlisting}[language=modelica]
Real y(unit = "m") = 1 + 2.5 * 3;
\end{lstlisting}
The unit of the declaration equation right-hand side is determined as follows:
\begin{itemize}
\item The \lstinline!Real! literal \lstinline!2.5! has empty unit.
\item The \lstinline!Integer! literals \lstinline!1! and \lstinline!3! are implicitly cast to \lstinline!Real!, and therefore have empty unit.
\item The multiplication \lstinline!2.5 * 3! has empty unit, as multiplication can propagate the empty unit of both operands.
\item The addition \lstinline!1 + (2.5 * 3)! has empty unit, as addition can propagate any unit as long as both operands have the same unit.
\item The entire right-hand side expression gets inferred unit \lstinline!"m"! in order to be compatible with the component's declared \lstinline!unit!.
\end{itemize}
\end{example}

\begin{example}
Consider unit checking of the following erroneous declaration equation for \lstinline!y!:
\begin{lstlisting}[language=modelica]
Real x(unit = "m") = 1.0;
Real y(unit = "m") = x^2 / 2;
\end{lstlisting}
The unit of the declaration equation right-hand side is determined as follows:
\begin{itemize}
\item The unit of \lstinline!x! is \lstinline!"m"!, and dimensional analysis gives that \lstinline!x^2! has unit \lstinline!"m2"!
\item The \lstinline!Real! literal \lstinline!1.0! has empty unit, and gets inferred unit \lstinline!"m"!.
\item As the left operand of \lstinline!x^2 / 2! is non-empty, the right operand cannot be empty, and hence empty unit of \lstinline!2! is implicitly cast to \lstinline!"1"!.
\item Dimensional analysis then gives that the unit of \lstinline!x^2 / 2! is \lstinline!"m2"!, which is an error due to the required unit being \lstinline!"m"!.
\end{itemize}
\end{example}

\begin{example}
Consider the potential consequences of an undefined unit in the following declaration equation:
\begin{lstlisting}[language=modelica]
Real y(unit = "m") = sin(1.57);
\end{lstlisting}
To see that the unit of the declaration equation right-hand side is undefined, note that:
\begin{itemize}
\item The \lstinline!Real! literal \lstinline!1.57! has empty unit.
\item The expression \lstinline!sin(1.57)! is not covered by the specification, and hence has undefined unit.
\end{itemize}
If a tool wants to proceed according to ``standard dimensional analysis'', alternatives include:
\begin{itemize}
\item
Assume that \lstinline!sin! is a mapping from unit \lstinline!"1"! to unit \lstinline!"1"!.
The unit of \lstinline!1.57! then defaults to \lstinline!"1"! (alternatively, the same unit could be obtained by inference).
The unit of \lstinline!sin(1.57)! is then found to be \lstinline!"1"!, which is an error due to the required unit being \lstinline!"m"!.
\item
Assume that \lstinline!sin! preserves both the unit \lstinline!"1"! and the empty unit.
The empty unit of \lstinline!1.57! gets propagated to \lstinline!sin(1.57)!, which in turn gets inferred unit \lstinline!"m"!.
\end{itemize}
\end{example}