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

Where to apply an Absolute/Relative distinction? #140

Closed
keilw opened this issue Nov 3, 2018 · 91 comments
Closed

Where to apply an Absolute/Relative distinction? #140

keilw opened this issue Nov 3, 2018 · 91 comments

Comments

@keilw
Copy link
Member

keilw commented Nov 3, 2018

Based on input from https://reference.wolfram.com/language/tutorial/TemperatureUnits.html
or https://www.quora.com/What-are-absolute-units (the latter contradicts itself quite a bit because after saying "Measurement units can be either absolute or relative… " it continues like "However, physical measurements are often relative. ") Let's figure out, if Unit or Quantity are the best place for such Absolute/Relative indicator?

It is drafted in Quantity (mainly to find out the payload it adds on top of other elements like the measurement level) but e.g. the Mathematica/Wolfram examples are pretty clear about using specialized Unit definitions like Kelvin vs. KelvinDifference for that.

Not entirely sure, which is best, something like an attribute on Unit which could allow Units.KELVIN vs. Units.KELVIN_DIFFERENCE (I recall @desruisseaux mentioned that option once in an earlier thread) or an attribute on Quantity?

Also how should it be defined either statically or dynamically and how does it affect unit and quantity operations?

Needs #95
Needs #131

@keilw
Copy link
Member Author

keilw commented Nov 8, 2018

Is it possible to answer this?
Especially those who already participated in other parts of this "epic" like @desruisseaux, @dautelle, @filipvanlaenen or @andi-huber could you tell your preference please?
I sense from some discussions in other threads, it may change e.g. based on a particular operation. If that's a requirement, I believe, Quantity is the safer option. If placed on a Unit it should be a constant value on that particular Unit instance, even if that would mean having something like CELSIUS and CELSIUS_INTERVAL.

@keilw
Copy link
Member Author

keilw commented Nov 8, 2018

A) Define the absolute/relative indicator in Quantity

@keilw
Copy link
Member Author

keilw commented Nov 8, 2018

B) Define it in Unit

@keilw
Copy link
Member Author

keilw commented Nov 8, 2018

C) Define it somewhere else, please comment where you prefer.

@andi-huber
Copy link
Member

As demonstrated with [1], the Mathematica way of handling this seems almost trivial to implement.
Note: That would be Option c), where the distinction (absolute vs relative) is made by using different units.

That's not my vote, because I'm not sure how well the other options could be implemented. But I have a feeling, that option c) could be the most simple one.

[1] unitsofmeasurement/indriya#164

@andi-huber
Copy link
Member

We also need to carefully consider impact on Unit formatting and parsing. E.g. currently I'm using this JAXB-Adapter to allow for Quantities to be marshaled/unmarshaled ...

public static final class QuantityAdapter extends XmlAdapter<String, Quantity<?>>{
	
	private final UnitFormat format = SimpleUnitFormat.getInstance();

	public Quantity<?> unmarshal(String v) throws Exception {
		final String unitLiteral = Strings.substringEmbeddedBy(v, "[", "]");
		final String valueLiteral = Strings.substringEmbeddedBy(v, null, "[");
		final double value = Double.parseDouble(valueLiteral);
		final Unit<?> unit = format.parse(unitLiteral);
		return Quantities.getQuantity(value, unit);
	}

	public String marshal(Quantity<?> v) throws Exception {
		
		String formattedUnit = format
				.format(v.getUnit(), new StringBuilder()).toString();
		
		return v.getValue().doubleValue() + "[" + formattedUnit + "]";
	}

}

Having different Units that yield the same format, would break this code. So even if we choose option c) an additional flag indicating 'absolute' vs 'relative' would be required somewhere.

@desruisseaux
Copy link
Contributor

Absolute/relative indicator in Quantity is fine. Note however that it has the following impact: Since Unit.getConverterTo(Unit) does not know if we want to convert absolute or relative quantities, we may need another mechanism to distinguish the two cases at UnitConverter level. One possible approach is to add a new method in UnitConverter interface, which would apply on relative values:

double deltaConvert(double);

This is the same approach than java.awt.geom.AffineTransform, which has a deltaTransform method (javadoc here).

@keilw
Copy link
Member Author

keilw commented Nov 11, 2018

@andi-huber Thanks a lot for demonstrating this.
I thought using different units would also rather be option B.
If we don't need a "flag" (as you demonstrated it should work) it would be using the Unit of a Quantity, not the Quantity itself.

@dautelle
Copy link

dautelle commented Nov 12, 2018

Thank you andi-huber, the Wolfram article made me change my mind. It is elegant and makes senses mathematically (indeed Wolfram is a reference for that matter).
For the user, it is quite simple, there can be a property in Unit (e.g. isDifference()) and a few additional units (e.g. CELSIUS_DIFF, FAHRENHEIT_DIFF). Of course, some quantity operations will not be allowed (e.g. adding quantity in CELSIUS and quantity in FAHRENHEIT not allowed, but adding quantity in CELSIUS_DIFF and quantity in FAHRENHEIT_DIFF allowed).

@keilw
Copy link
Member Author

keilw commented Nov 12, 2018

@andi-huber @dautelle Thanks for helping with the decision making and showing how the Wolfram calculations may best work here. A property (not really biassed towards isAbsolute(), isRelative() or isDifference()) on Unit instead of Quantity looks fine in this case. As the Units class also implements a SystemOfUnits, does it make sense to add those _DIFF or _DIFFERENCE units there? Keep in mind, SI in https://github.com/unitsofmeasurement/si-units extends this class. It is not written in stone, but adds reusability. Thus is it better to define those units in the Units class or add a new SystemOfUnits implementation like UnitsDifference?

@desruisseaux
Copy link
Contributor

desruisseaux commented Nov 12, 2018

I'm not convinced by the Wolfram examples. They are mathematically inconsistent (they said themselves that their operations are not commutative) and their approach would bring new problems in JSR-363 context, discussed below. The Wolfram example stating 3°F + 2°C as illegal but 3°F + 2°F = 5°F as legal is inconsistent: for 3°F + 2°F they implicitly handle at least one °F value as a temperature difference despite the two units declared as "DegreesFahrenheit" (and not "DegreesFahrenheitDifference"). In addition of bringing the mathematical problems described in #95, this is inconsistent with the distinction they make between "DegreesFahrenheit" and "DegreesFahrenheitDifference" in other examples, and is also inconsistent with the fact that they do not handle any value as a temperature difference in 3°F + 2°C.

The examples they provide can be practically the same with absolute/difference flag in Quantity instead of Unit. For example instead of (in Mathematica syntax):

Quantity[3., "DegreesCelciusDifference"]

We would have (in Java syntax)

Quantities.create(3., Units.CELSIUS, someFlagForDifference)

Having "DegreesCelciusDifference" and "DegreesCelcius" as two different Unit instances raises new problems in JSR 363 context. Do we create a new TemperatureDifference quantity in addition of Temperature?

  • If we do not define new Quantity subtypes, then the assumption that Unit.getConverterTo(Unit) do not fail if the parameterized Quantity is the same does not hold anymore, because "DegreesCelciusDifference" would have the same parameterized Quantity than "DegreesCelcius" and still be unconvertible. In other words, we would sacrifice the type safety provided by Quantity parameterized type.
  • If we define new Quantity subtype, then we have a problem with Quantity add and subtract methods, which can no longer be parameterized the way they are. For example the difference of two Temperature would be a TemperatureDifference, which is another type. Furthermore we would have to duplicate other Quantity subtypes, not just temperature. Example: density sigma-t, pH… And what would be our justification for duplicating only some quantities instead of all of them?

@keilw
Copy link
Member Author

keilw commented Nov 12, 2018

Based on @andi-huber's sample it would be Quantities.create(3., Units.CELSIUS_DIFFERENCE) or similar, The flag if any was needed (@dautelle hinted it might help) would be on the Unit.
Having separate Unit instances with different properties, underneath seems relatively uninvasive, but having to introduce Temperature vs. TemperatureDifference, that would totally blow up the API (even if it was only done for Temperature) and introduce all sorts of compatibility problems.

Btw, what about LevelOfMeasurement, is there any use for that at all?

@desruisseaux
Copy link
Contributor

That is my point: putting this flag at Unit level as implications on parameterization: either we add quantities like TemperatureDifference and we get problems with Quantity.add and Quantity.subtract methods (in addition of increasing the amount of quantities subclasses), or either we don't and we compromise the Unit.getConverterTo(Unit) type safety (i.e. compiling the code without warnings is no longer a guarantee that getConverterTo will not fail), unless we provide some arbitrary definition of what a conversion between "temperature" and "temperature difference" should be (Wolfram does not - they are supposed to not be the same thing).

By contrast, if we add this flag in Quantity instead, the only implication I can see at this time is to add a UnitConverter.deltaConvert(double) method.

About LevelOfMeasurement: we need this information at least implicitly. An explicit enum is not strictly needed because its value can be partially inferred from unit.getConverterTo(unit.getSystemUnit()).isLinear(), but explicit enum may help to make things clearer. That LevelOfMeasurement enum would be in Unit instead of Quantity. As explained before, I think its role is complementary with the absolute/difference flag rather than an overlap.

@keilw
Copy link
Member Author

keilw commented Nov 12, 2018

LevelOfMeasurement on Unit contradicts everything that was discussed and concluded earlier in #131

While it currently sits on Quantity and (if that needs to explicitly be set remains to be seen) can be set via calls to Quantities.create(3., Units.CELSIUS, INTERVAL) or similar.
Unless we end up with CELSIUS_INTERVAL after all (the on with INTERVAL set by default) where would it be set on a Unit? Claiming that Mathematica, a system that is considered the de facto standard for these kinds of mathematical calculations is wrong or insufficient seems a bit bold.
Sure, there are several approaches to many things especially in the JDK itself (Date/Time, how to deal with nullness, Jigsaw or the multi-release JARs) and you find wide-spread criticism of most in one form or another. As a temporary precondition, I gave an isAbsolute() flag to both Unit and Quantity. @andi-huber already demonstrated an approach that solves most of them.
We should find a way to get in7, in8 and in12 likely throw a MeasurementException (I would hesitate to create yet another sub-exception beyond what we already have) based on a combination of newly added properties or something else. To control multiply or divide we could use the LevelOfMeasurement INTERVAL to prevent those, based on https://cf.ppt-online.org/files/slide/b/Belm5PkGts9ORjA0N4Qzu6VJa2dFW8L7iETghU/slide-13.jpg (the Wikipedia comparison is a bit misleading but RATIO allows both addition and multiplication)

For add and subtract it is not so obvious. The Mathematica example i7 states:

A sum cannot generally be calculated for two different absolute temperatures.

With the Unit being different. However, should we really throw an exception every time an operation between two absolute values with a different unit is attempted?

Just one example 10m + 5ft should be possible. And unless we looked at the difference above sea level, there's no case where either of them are not absolute. So how to tell, simply by the Quantity? Which due to type erasure at runtime Java still isn't good at telling apart.

@dautelle
Copy link

What Wolfram shows is that to be mathematically consistent some operations may be forbidden.
This can be done at the Quantity level or at the Unit level.
The problem occurs with offset units (e.g. Temperature), no need to create a new Quantity class (e.g. TemperatureDifference). It can be resolved elegantly at the Unit level.
For exemple by adding the "add" and "subtract" operation to Unit returning themselves except for offset units such as CELSIUS for which 'add' raises an exception and 'subtract' returns CELSIUS_DIFFERENCE (note: KELVIN will inherit the default behaviour of returning itself for both operations since it is not an offset unit).

@andi-huber
Copy link
Member

I believe Martin aims for higher goals, but as a user of UOM I'm satisfied with what Mathematica has to offer. If certain operations are not allowed, at least if I understand why, than I'm fine with it. Also Wolfram's TemperatureUnits guide is short and easy to comprehend.

The discussion whether or not the math with Mathematica is 'consistent', in my opinion depends on how one defines 'consistent'. I do have a feeling, that avoiding surprises for the UOM user is more important.

So, regarding the big picture, it appears to me that we have 2 competing ideas:

  1. keep it simple, disallow ambiguous calculations, do it the Mathematica way
  2. implement Martin's requirement for 'physical consistency' of arithmetic, but also introduce/allow calculations that might surprise users

As a user, I have no strong preference for 1) or 2), all I'm saying is that for me 1) is sufficient.

... regarding exceptions: One way to deal with operations, that are not allowed, is to return a special class like NonEvaluatedQuantity, analog to Double.NaN when Number calculation yields 'not a number' result

@andi-huber
Copy link
Member

Also note, with Mathematica ...

  1. 10m + 5ft -> 37.8084 ft
  2. UnitConvert[Quantity[3., "KelvinsDifference"], "Kelvins"] -> 'KelvinsDifference and Kelvins are incompatible units'
  3. there is no "MetersDifference"

@desruisseaux
Copy link
Contributor

desruisseaux commented Nov 13, 2018

@keilw: LevelOfMeasurement in Unit contradicting #131 is the reason why I asked for not too early call for vote (before the issue is fully understood) and for a wiki page summarizing the big picture.

The Mathematica examples are inconsistent in the following ways:

  • In A + B where A and B use absolute units:
    • "DegreesFahrenheit" sometime implicitly interpreted as "DegreesFahrenheitDifference"
    • "DegreesFahrenheit" sometime not interpreted as "DegreesFahrenheitDifference" (note the contradiction with above point).
  • Operations on quantities do not obey arithmetic laws (not commutative). More on it below.

@dautelle: Mathematica is not showing that to be mathematically consistent some operations must be forbidden. We can be mathematically consistent by requiring all operations to be performed in system units - which is also consistent with thermodynamic laws. Furthermore Mathematica approach is not mathematically consistent - see above-cited contradictions, which I guess have historical roots.

@andi-huber: I fully agree about the goal to not be surprising to the user. This is precisely the problem I see with Mathematica approach. They have a whole section warnings the user that their operations are not commutative (A*B*C != C*B*A). If a user write (from thermodynamic equations):

    E1 = 1.5 * k * T1
    E2 = 1.5 * k * T2
    E = E1 + E2

If we can not rely on commutativity and associativity laws, rearranging those equations as E = 1.5 * k * (T1 + T2) can produce a different result. Isn't surprising? Furthermore how to tell now which equation is the correct one?

@andi-huber
Copy link
Member

andi-huber commented Nov 13, 2018

@desruisseaux, thanks for the examples. These could be key to understanding, what the discussion is all about.

I suggest we translate your examples into Mathematica syntax for having concrete cases we can focus the discussion. Otherwise I feel the subject is too abstract to comprehend. (At least for me.)

Best outcome would be, that we all come to an agreement, what we mean by 'consistent' or 'surprising' and/or identify cases, where we agree Mathematica does something 'wrong' and we can do better.

@desruisseaux
Copy link
Contributor

No objection for MeasurementKind on my side. Other proposals welcome too if other peoples have ideas.

@andi-huber
Copy link
Member

andi-huber commented Dec 1, 2018

Just by simple synonym search, shortest first ...

MeasurementForm (ABSOLUTE, INCREMENTAL)
MeasurementKind (ABSOLUTE, INCREMENTAL)
MeasurementSort (ABSOLUTE, INCREMENTAL)
MeasurementType (ABSOLUTE, INCREMENTAL)
MeasurementNature (ABSOLUTE, INCREMENTAL)
MeasurementCategory (ABSOLUTE, INCREMENTAL)
MeasurementCharacter (ABSOLUTE, INCREMENTAL)

I have no objection to Kind, its simple and short. The idea of having a nature of measurement somehow also appeals to me, but then more characters needed. Basically, I'm happy with most of the above.

@dautelle
Copy link

dautelle commented Dec 1, 2018

Sorry, but I hate all generic words which do not bring much informative value. In this particular case, I would go with MeasurementScale (e.g. Absolute Scale, Incremental Scale). Other words would apply to almost anything and applied to measurement don't say much.

@andi-huber
Copy link
Member

andi-huber commented Dec 1, 2018

As Martin managed to work out in great detail here Wiki Page, we do have 2 enums to distinguish and not to confuse with one another. One is a property associated with Unit, the other is a property associated with Quantity, both at first glance seem similar, but do serve a different purpose within the API.

  1. Property of Unit ... LevelOfMeasurement (NOMINAL, ORDINAL, INTERVAL, RATIO)
  2. Property of Quantity ... MeasurementType (ABSOLUTE, INCREMENTAL)

I guess the challenge now is to find a good terminology, that also accounts for the distinct purpose of the two enums above.

I have a feeling, that the term Scale might suggest association with LevelOfMeasurement, which we should(?) avoid. I don't know.

@dautelle
Copy link

dautelle commented Dec 1, 2018

Since an "incremental quantity" should be stated using an interval unit (LevelOfMeasurement). Do we need a property "MeasurementType" on quantity?

@desruisseaux
Copy link
Contributor

An incremental quantity can be stated with both INTERVAL and RATIO units. For example we can have an increment of 5°C or 5 K. As Andy said, those two enumerations are related but not redundant. LevelOfMeasurement defines the arithmetic operations than we can apply between quantities, approximately as shown in Wikipedia table (I said "approximately" because keeping + consistent with - is tricky, which has leaded us to propose a new Quantity.difference(Quantity) method). When computing 2×3°C, the enumerations are used as below:

  1. The INTERVAL level of measurement associated to the °C units tells us that we can not apply multiplications or divisions on quantities in that unit.
  2. We need to convert the quantities to a unit having RATIO level of measurement (namely Kelvin degrees) so we can apply the multiplication.
  3. Only after we have determined that such conversion is necessary - that not doing it would produce different numerical results - only then the "absolute" versus "incremental" characteristics tell us how to do the conversion from the INTERVAL to the RATIO unit.

We can convert an ABSOLUTE quantity from INTERVAL unit to RATIO unit (e.g. 3°C converted to 275.15 K), but we can also convert an INCREMENTAL quantity from INTERVAL unit to RATIO unit (e.g. 3°C converted to 3 K).

It may be possible to use the same LevelOfMeasurement enumeration for those two different purposes, but we would have to check if we can do that in a way to do not introduce confusion.

@desruisseaux
Copy link
Contributor

However, as said previously, a LevelOfMeasurement enumeration on Units is technically not strictly required. The only information which is mandatory (as far as I can see) is the "absolute" versus "incremental" aspect of Quantity. So whether keeping LevelOfMeasurement would bring more clarity or confusion is an open question.

@keilw
Copy link
Member Author

keilw commented Dec 3, 2018

If only the absolute/relative enum (or flag) is required, then I would remove LevelOfMeasurement for now. It would help data analysis, but unless we really have a use case e.g. with BigData, monitoring, etc. that would greatly benefit, we could still add it later with a MR or update release.

@keilw
Copy link
Member Author

keilw commented Dec 3, 2018

I hope, some of us are able to start implementing what's necessary also over Christmas? We should file a Public Review no later than first thing in January. @filipvanlaenen and I have a talk at OOP in Munich late January, so it would be great to have the Public Review ongoing by then. Given the SI redefinition milestone, PFD (practically final) and a Final Release should take place in the first half of Q2 at the latest. There are only 2 EC F2F meetings in 2019, one is scheduled on May 15 in Tokyo: https://jcp.org/en/whatsnew/calendar2019. Which would be a great opportunity to report to the EC after the JSR went final or immediately before (The SI does on May 20, 2019)

@keilw
Copy link
Member Author

keilw commented Dec 7, 2018

@andi-huber @dautelle I mentioned earlier, that MeasurementType can be too easily confused with the actual (generic) type of the Quantity, so I suggested to call it MeasurementKind which @desruisseaux said, he would also be fine with. LevelOfMeasurement is often called "Scale of Measure" or "Scale of Measurement", so MeasurementScale IMO is out of question. It would be totally confusing with what we currently called LevelOfMeasurement, even if we dropped one of them. If you use a translation like Leo for the German word "Art", then you get "Kind", "Sort" and "Type" as the first three options. "Form" only 11th. I would outrule "Sort" because it creates the impression of sorting, so "Kind" is the first choice. Feel free to try that with a French-English translation tool of your choice, I could not find any, but both @andi-huber and I are German native speakers, and the English translations should be clear to everyone.

@dautelle
Copy link

dautelle commented Dec 7, 2018

Found on the web: "The clock is an absolute measurement system, it will tell you a point in time. A stop watch is an incremental system, it will tell you how many seconds (increments) have gone by since the start of the measurement. " - What about MeasurementSystem ?

@keilw
Copy link
Member Author

keilw commented Dec 7, 2018

IMO it sounds to close to SystemOfUnits which some sources also call SystemOfMeasurement.

@keilw
Copy link
Member Author

keilw commented Dec 7, 2018

If this goes strictly into Quantity, why not define a public static enum there like Scale?
See https://en.wikipedia.org/wiki/Absolute_scale The term "scale" sounds reasonable, but I would rather not call it MeasurementScale, because it can be more easily confused with https://en.wikipedia.org/wiki/Level_of_measurement

@keilw keilw added in progress and removed ready labels Dec 11, 2018
@keilw
Copy link
Member Author

keilw commented Dec 11, 2018

I added a static enum Scale to Quantity. Please have a look. It's not applied yet, but the idea would be to replace the boolean flag with a getScale() method or similar. The names are based on the terms in Wikipedia, ABSOLUT and RELATIVE. There is no mention of aliases like "iterative", etc. No problem to mention any of these in JavaDoc, but I would stick to official terms here.

@keilw
Copy link
Member Author

keilw commented Dec 17, 2018

Hi, Hope somebody can do something with that enum over the holidays? Unless the LevelOfMeasurement was still of value for any operation, maybe we'll remove it for now until an actual need comes up?

@desruisseaux
Copy link
Contributor

Hello Werner. I'm in a very big rush at work those time, including weekends. When I will start having some weekends free, I will implement the new feature in Seshat. Sorry for the delay…

@keilw
Copy link
Member Author

keilw commented Dec 17, 2018

Hi Martin. No worries, it is similar here (not on weekends yet;-) If nobody needs LevelOfMeasurement I'd remove it (still stashed away in a branch) and then all implementations can use the remaining enum.

@keilw keilw pinned this issue Dec 17, 2018
keilw added a commit that referenced this issue Dec 17, 2018
@keilw
Copy link
Member Author

keilw commented Dec 18, 2018

Btw, LevelOfMeasurement was removed now. And all places that referred to a boolean isAbsolute() flag now use the Quantity.Scale.
I added a placeholder in the spec: https://docs.google.com/document/d/12KhosAFriGCczBs6gwtJJDfg_QlANT92_lhxUWO2gCY/edit#heading=h.o50xwl22u27k. Please feel free to fill that or the initial definition chapter on Quantity. It could be based on e.g. the Wiki page here, but not too lengthy. Of course if a concrete example for Temperature calculation could fit either under Use Cases or somewhere under Definitions, that sounds fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants