Git Product home page Git Product logo

Comments (21)

mpusz avatar mpusz commented on June 11, 2024 3

The more I think about it the more I think we need affine space support and I like the way how @jansende did it in benri. However, we will not have any UDLs or other shortcats to define those (they should be explicit and verbose or otherwise people will make a lot of mistakes).

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024 3

@kiwikus: Note that I'm neither a committee member, nor affiliated in any way. Strict affine quantities made you think about what you are doing. You may struggle with getting the code to compile, but eventually, you came up with a perfectly valid (and correct!) solution. I absolutely agree that error messages should be as clear as possible, to guide inexperienced programmers in the right direction. However, if you are working with affine quantities with mixed origins, there is no way around being explicit in the point of reference.
So what would you propose, how should less strict temperature quantities behave? You will find that whichever behaviour you choose, there will be cases in which they behave surprisingly (i.e. silently producing wrong results). I have modified our example to show two ways you could implement multiplication rules for affine quantities: https://godbolt.org/z/34p5gH
However, both of those rules give wrong results for some cases. I believe even the "average programmer" is better served by more strict rules than relaxed rules that silently risk wrong results. These errors are hard to spot even in regression testing, but can be fatal (such as in the destruction of Mars Climate Orbiter). This is precisely the reason we want strict checking by the compiler.

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024 2

Unfortunately, I'm not familiar enough with the code-base (I'm not even a user), and more importantly, the design details of this library to implement something useful right now. So far I'm just following it's development because I like the philosophy set out by @mpusz, and try to add in my five cents on design choices that are go/no-go for me, such as the way affine quantities are handled.

Regarding your latest example: It's an ideal demonstrator for affine quantities: initial_temperature an-td predicted_temperature would be of type quantity_point<some_temperature_scale> (i.e. affine or absolute quantity), while dK would be of type quantity<same_or_other_temperature_scale> (i.e. "normal" or relative quantity). Then, your code would compile and you get all the benefits from a unit package, even with strict affine units - which is the point we're still discussing here, right?

The alternative approach would be to completely ignore affine quantities, use just relative quantities everywhere, and you arrive at the example that you just presented. This is what we have now in this library, and you already showed that it compiles. However, in this case, the library doesn't help you with conversion between different scales of temperature, and it's obvious why: Imagine, you'd like to express either dK or initial_temperature in Fahrenheit. In the case of dK the library would only have to scale the values with 5/9. In the case of initial_temperature, it would first have to scale it with 5/9 and add 255.372. But because there is just one type in the library, it couldn't know which is requested. The only sensible choice is interpreting it as relative quantity, not performing any affine shifting. Not all libraries chose the sensible choice however; e.g. https://github.com/nholthaus/units gives you affine quantities in some circumstances, but not in other - it is essentially broken, and a reason why I'm looking to this library.

So I hope that this convinces you that either there is no affine quantities at all (i.e. no conversion of temperature points between Fahrenheit and Celsius), or there are, and the only sensible choice is for them to be strict - because everything else is plain wrong. But apart from the implementation effort, having strict affine quantities doesn't hurt your desire either: No-one prevents you from just ignoring those and doing all calculations in purely relative quantities.

The only need for a discussion I see is with the literal suffixes: How do you disambiguate in a literal value if it should be considered an absolute/affine temperature, or if it shall imply a relative temperature. The library benri chose to use the suffix _degree_fahrenheit to denote absolute literals, while just _fahrenheit to denote relative literals. I haven't seen this convention before, but there is some system to it in that precisely those temperatures that need differentiation are written with a "°". Alternative literal suffixes could be _abs_F and _rel_F (also avoids clash with _F for Farad).

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024 1

I am quite in favour of absolute<quantity>, that's also what boost::units did. The reason is, for absolute quantities (i.e. coordinate points referring to a specific origin) many arithmetic operations do not have a physical meaning at all: If T refers to some temperature such as the freezing point of water, what would 2*T even mean? Blindly taking the numerical value would result in different temperatures, depending on the units you measure T in (even if you eventually convert to the same scale). Or take the sum of two temperatures U and V. If U is specified as 5 °C and V as 5 °F, then the sum U + V may have any of three different values (even if eventually converted to Kelvin) depending on which of them you consider temperature differences and which an absolute temperature if any at all (if both are absolute temperatures, the expression has no physical meaning). Because of that, the only symmetric interpretation is both values as differences. As the python Zen sais "explicit is better than implicit" and "in the face of ambiguity, refuse the temptation to guess", if any temperature is to be interpreted as absolute value, then it would have to be marked explicitly as such.

from mp-units.

jansende avatar jansende commented on June 11, 2024 1

The problem appears only with dimensions and their units where 0 is not the beginning of the scale (i.e. temperature in centigrade or Fahrenheit or time).

That is not quite true. Consider the following render function of a game engine:

/// \brief Draw a circle.
void draw(
    point<pixel>    x, ///< x position
    point<pixel>    y, ///< x position
    quantity<pixel> r  ///< radius
);

Now say, we have a render function which calculates if two circles collide and if so draws the midpoint.

/// \brief Draw collision point between two circles.
///
/// Left and right circles are just called that by
/// convention, they do not actually be in order.
///
/// The collision point is the midpoint between left
/// and right.
void draw_collision(
    point<pixel>    lhs_x, ///< left circle x position
    point<pixel>    lhs_y, ///< left circle y position 
    quantity<pixel> lhs_r, ///< left circle radius
    point<pixel>    rhs_x, ///< right circle x position 
    point<pixel>    rhs_y, ///< right circle y position 
    quantity<pixel> rhs_r  ///< right circle radius
)
{
    // Calculate midpoint
    auto col_x = (lhs_x - rhs_x) / 2;
    auto col_y = (lhs_x - rhs_y) / 2;

    // Calculate length between circles.
    auto length = sqrt(square(col_x) + square(col_y));

    // Do we have a collision?
    if (length > lhs_r + rhs_r)
    {
        draw(col_x, col_y, 5px);
    }
}

This function will not compile because draw actually takes a position point<pixel> x, point<pixel> y, but we only calculated the difference quantity<pixel> col_x, quantity<pixel> col_x. If allowed, this can lead to circles being drawn offscreen as the difference might be negative.

What the code should have been is:

draw(lhs_x + col_x, lhs_y + col_y, 5px);

My guess is, that these kind of errors are common. While it is not hard to debug them, it would surely help if the compiler catches them even earlier. Thus, I think adding type support for this behaviour seems like a good idea.

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024 1

@kwikius: I basically agree with all you said in those last two posts - especially that a library without support for affine quantities is a strict subset of one with support for affine quantities: Relative quantities should not need to know about any affine quantities, and a library with only those is useful in practice. For standardisation, the smaller scope is of particular advantage in giving a shorter path to adoption. The only thing I'm lobbying against is affine quantities that are not strict, i.e. trying to support conversion between different offsets but at the same time supporting scaling - if they are to be implemented.

That said, I like abstractions with semantic meaning, so if affine quantities are available, I use them to represent points vs. differences. I have done so in the past with boost::units in embedded systems, despite that boost::units is terribly inconvenient to use. The standard library does give us precedence for affine quantities with std::chrono::time_point - purely for its semantic value, without even implementing conversion between calendars. Given that the specification of an absolute wrapper is relatively simple, I could imagine that affine quantities for the case of temperatures could be reasonable - because people are used to work with those and sometimes expect conversion including origins. However, I am not interested in runtime-specified offsets: to me that is better solved simply by a pair of two quantities.

from mp-units.

mpusz avatar mpusz commented on June 11, 2024

from mp-units.

i-ky avatar i-ky commented on June 11, 2024

There are plenty of uses. Voltage is a difference in potentials, difference in potential energies is work by potential forces, etc. In these cases difference and absolute value are conceptually different quantities which are used in different equations and should not be mixed together (i.e. provide compile-time error on such occasion).

I've just watched your ACCU 2019 speech where you told you don't know how to deal with temperature scales because of additive offsets. Thinking about temperature from the standpoint of absolutes and differences can provide a clue, because temperature difference can be converted from Kelvins to centigrade to Fahrenheit using just a multiplier.

from mp-units.

mpusz avatar mpusz commented on June 11, 2024

Yes, that is right. For most of the dimensions "difference" and "absolute values" are really similar and sometimes hard to differentiate. In most cases, we think about them as a "difference". For example, 1 meter is a distance between 2 absolute points, 220 V is a difference in potentials, etc. The problem appears only with dimensions and their units where 0 is not the beginning of the scale (i.e. temperature in centigrade or Fahrenheit or time). In such cases, the values and meaning of "difference" and "absolute" are totally different and cannot be mixed with one another. In fact, for only such cases we need an absolute<quantity> and maybe that is the way to solve it?

from mp-units.

i-ky avatar i-ky commented on June 11, 2024

If this discussion wasn't complicated enough, there is also an aspect of intensive and extensive properties (additive/non-additive quantities).

Perhaps, there is no way to provide a perfect and all-encompassing generalization. Providing a type-safe way to implicitly convert mph to km/h can be considered an achievement.

from mp-units.

jansende avatar jansende commented on June 11, 2024

@i-ky Interesting point. I heard about this distinction in my undergrad and are now using it without ever thinking about 😄

Yet, it seems to me that this is something we do not need to support. When doing calculations, the properties will automatically be right. We would only need to implement intensive and extensive units if we wanted to save and scale them together. Something like:

struct SomeData
{
    in<quantity<gram_per_cubic_centimetre>> dens;
    ex<quantity<gram>> mass;
};
... = SomeData * 2;

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

I am not sure about the benefit of rigidly enforcing affine quantities.
An obvious temperature application is when you are reading a temperature sensor which may only have 8 bit accuracy, to get better accuracy
See mpusz/units example here.
https://github.com/kwikius/units/blob/andy_master/example/average_temperature.cpp

Maybe this doesnt satisfy the mathematicians, but most of us arent mathematicians and for us should it not work?

Provide an affine quantity type on top of the basic work for those that want it maybe?

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024

@kwikius: Affine quantities can be averaged - that is not the same as summing. One way to do so explicitly is to choose an arbitrary reference point (such as absolute zero temperature) and average the differences to the reference, and finally add the result to the same reference point.
For an IIR filter, you can even write it in a fashion that the reference point drops out completely: Just write y1=y0 + e*(x1-y0), and all operations are well-defined.

Your example implicitly does just that. This is a viable path to take for a units library, simply ignoring affine quantities. The only thing the library can't do in that circumstance is converting temperature-points between different scales. In other words, your example doesn't even use any affine quantities. You'll see that once you'd try to integrate a second sensor measuring temperature points in Fahrenheit. If the library only provided relative temperatures, you would have to manually subtract the reference point (i.e. add 441.67 °F to the measured value, if you chose absolute zero) before averaging. With support for affine quantities, you would still have to do that subtraction explicitly, but the library could guarantee the correct conversion by failing at compile time if you fail to explicitly state the reference point - independent in which units it is stated.

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024

Here is an example for filtering with explicit affine temperatures: https://godbolt.org/z/r3q3FN
It includes a minimal implementation of an absolute<...> wrapper, though certainly not production ready.

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

@burnpanck
I like the fledgling absolute quantity. I have modified your example in fun.
https://godbolt.org/z/gP9aRW
I hope you get the point I am trying to make.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0939r2.pdf
In Part 7
● articulating rationales for proposals.
facilities for the “average programmer,” who is seriously underrepresented on the committee.
● facilities aimed more at application builders than at builders of foundational libraries.
Please pay special attention to that last point. We feel that C++’s utility and reputation suffer badly from the committee lacking attention to improvements for relative novices and developers with relatively mundane requirements. Remember:
Most C++ programmers are not like the members of the committee

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

Since affine quantities is quite a fundamental issue, ideally there should be a fork where things are implemented that way to get some experience of working that way. Do you agree?
Then it will be fun to get some examples working there.

Here is another example to kick things off. https://godbolt.org/z/9uUTGf

Which physical quantities do you believe should be affine quantities?

from mp-units.

mpusz avatar mpusz commented on June 11, 2024

As I wrote in my last message in this thread, with time I changed my mind and I tend to agree with @burnpanck. Also to solve the problem with denoting absolute literals I wrote:

However, we will not have any UDLs or other shortcats to define those (they should be explicit and > verbose or otherwise people will make a lot of mistakes).

and (at least for now 😃) I believe it is the right way to do. I also think that if we are about to add a quantity_point it should cover not only compile-time known offsets (i.e. between K and °C) but also runtime ones:

  • I am doing a race and the start line is a 0 distance
  • I am an alpinist and my base is on 3214m I want to climb 345m today
  • I am in a hurry to work today, the speed limit is 70km/h and I can afford only +20km/h fine if I get caught 😉
  • I am developing an A/C for the office and it should provide -2, -1, 0, +1, +2 °C from an ideal office temperature (i.e. 21°C).

I think all of those are valid use cases here and the more I think about affine quantities I see more examples like those.

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

The problem has been argued over for years, often by those who have an academic but no practical interest or need for such a C++ library. My issue is that the more complicated the library is, the less likely it is to succeed in becoming part of the standard, especially if people demand features without which they wont even consider using the library, but aren't prepared to do the work to implement them.

Consider possible requirements and interaction with some affine quantities type for those that want them , but but do not let it pollute the main function of the work.

Is a library manipulating physical quantities without representing affine quantities useful ? Yes. I wrote my library because of the frustration of writing an engineering application using doubles(I think that was alluded to in mpusz youtube video in a painful looking function.):
https://github.com/kwikius/quan-trunk/blob/master/quan_matters/doc/quan/fixed_quantity/intro.html
The fixed_quantity is a C++ representation of a physical_quantity where the unit is fixed at compile-time.It has many similar features to a built-in type ...

"
Concrete type with simple, consistent semantics close to those of built-in types.
Matches speed and code size performance of built-in types.
Easy to use. Requires no special framework.

... but with added functionality ...

Strong type checking enforces dimensioned integrity, catching calculation errors at compile time.
Automated unit conversion reduces drudgery.
Self documenting; helps code clarity.

Motivation

Code clarity
Ease of use
Error detection
Portable type-safe I/O

"

My library takes the simple option. Provide one temperature unit, Kelvin and state that it ( and any other quantity that is ambiguous) represents a non-affine quantity. Now all quantities have one semantic. It has been in use for many years and I would not consider doing engineering calcs without it, despite it does not deal with affine quantities or have Fahrenheit temperature units.( I consider it trivial to convert in this special case if necessary)

As regards Fahrenheit and Celsius, I believe they are human-scale quantities rather than physical quantities. My advice would be to convert temperatures in these units to kelvin before any serious calculation. Is anyone that understands the concept of an affine quantity seriously going to do calculations using Fahrenheit as their temperature unit?

I have provided another example.

https://godbolt.org/z/s5pHdu

Note that according to the mathematicians on Wikipedia this is a perfectly valid calculation on affine quantities.

https://en.wikipedia.org/wiki/Affine_combination

I leave successful type checking of that calc with zero overhead as an exercise ;)

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

@mpusz, I would suggest it needs some code examples to see how that works out. Affine-quantity would appear to need some sort of space-tag ( e.g std::cronos 'epoch' ) to prevent binary ops on affine quantities from different spaces, but if the goal is standardisation, I don't see it as a priority.
It is entirely possible to build a useful physical quantity library using only non affine quantities ( aka quantity_difference, std::chrono::duration)
It is entirely possible to then build an affine quantity ( aka absolute_quantity, std::chrono::time_point) library on top.
The quantity_difference needs no knowledge of the absolute_quantity to be complete, whereas the affine quantity requires knowledge of the quantity_difference. ( If I am wrong on that please let me know and show me how quantity_difference depends on absolute_quantity)

The advantage of doing it that way is that the scope is limited and the chances of success are much greater in the smaller task.

from mp-units.

burnpanck avatar burnpanck commented on June 11, 2024

As for the exercise: Simply imbue affine quantities with a static ::scale_origin special value (akin to nullptr) that will be transformed at compile-time to a runtime no-op when subtracted from affine quantities or added to relative quantities of the same scale. Alternatively, have .offset_from_origin() accessors and from_offset() factory functions, also runtime no-op. Third alternative: Specify some sort of mean(...) and affine_combination(...) algorithms, while specifying what happens if the weights do not sum to one (e.g. "undefined behaviour", if you want zero overhead).

from mp-units.

kwikius avatar kwikius commented on June 11, 2024

I had a quick go at affine_combine function, though there has to be a more compact option maybe using a variadic function.
https://godbolt.org/z/gfw7b7
Some more complicated real world examples would help, as would examples for the other parts of the library, so I will try to rewrite some of the examples from my own library

from mp-units.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.