mp-units icon indicating copy to clipboard operation
mp-units copied to clipboard

feat!: add number concepts

Open JohelEGP opened this issue 2 years ago • 36 comments

Resolves #29.


The number concepts are meant to be at a lower level than the units' library. This means that quantity can use it to constrain rep, and quantity itself can model vector_space. So number_one_v would supersede quantity_values::one(), and same for zero.


https://github.com/BobSteagall/wg21/ doesn't work because it doesn't provide compound assignments. https://github.com/mpusz/mp-units/blob/a356db749ef8651d6b8c48cf732673286a0faa39/test/unit_test/runtime/linear_algebra_test.cpp#L97 v + v is valid and the same type as v. But the equivalent for lvalues, v += v, isn't provided to mean the same thing.


There's still room for more integration. That would involve breaking up mp_units's specific concepts that couple checks on the reference and the representation: https://github.com/mpusz/mp-units/blob/a356db749ef8651d6b8c48cf732673286a0faa39/src/core/include/mp-units/quantity.h#L60-L67 Because concepts can't be generically composed (i.e., passed to a concept template parameter), interfaces would have to check the reference and representation separately.

JohelEGP avatar Sep 21 '23 19:09 JohelEGP

These are the reference documentations: mp_units.pdf. This is the paper on the number concepts: P3003R0 The design of a library of number concepts.

JohelEGP avatar Sep 21 '23 22:09 JohelEGP

https://github.com/BobSteagall/wg21/ doesn't work because it doesn't provide compound assignments. https://github.com/mpusz/mp-units/blob/a356db749ef8651d6b8c48cf732673286a0faa39/test/unit_test/runtime/linear_algebra_test.cpp#L97 v + v is valid and the same type as v. But the equivalent for lvalues, v += v, isn't provided to mean the same thing.

I imagine it doesn't provide compound assignments to encourage the use of Eigen's template expressions.

There are two other ways I think the concepts could fall short:

  • When using representation types that directly do template expressions.
  • When performing operations with heterogeneous representation types that don't model C++20's std::common_with (i.e., they need to specialize std::common_type, in contrast with std::complex, which doesn't need to).

JohelEGP avatar Sep 21 '23 22:09 JohelEGP

https://github.com/BobSteagall/wg21/ doesn't work because it doesn't provide compound assignments.

This probably should not be a hard requirement for a representation type. There are plenty of usage representation types that do not support compound assignments.

mpusz avatar Sep 22 '23 07:09 mpusz

But the equivalent for lvalues, v += v, isn't provided to mean the same thing.

Of course, we may, and probably should, submit a bug for this library as it strives to be standardized as well.

mpusz avatar Sep 22 '23 07:09 mpusz

When performing operations with heterogeneous representation types that don't model C++20's std::common_with (i.e., they need to specialize std::common_type, in contrast with std::complex, which doesn't need to).

quantity needs to specialize std::common_type (at least until operator?: is standardized) but you said that quantity satisfies vector_space

mpusz avatar Sep 22 '23 07:09 mpusz

These are the reference documentations: mp_units.pdf.

I love the docs. However, I think the documentation generation framework could be provided in a separate PR. Merging the framework forces us to provide all the rest of the documentation for the library in this framework.

I do not find myself too comfortable with LaTex for now (but probably I should learn it anyway). Do you volunteer to document the rest of the framework as well? 😉

mpusz avatar Sep 22 '23 08:09 mpusz

https://github.com/BobSteagall/wg21/ doesn't work because it doesn't provide compound assignments.

This probably should not be a hard requirement for a representation type. There are plenty of usage representation types that do not support compound assignments.

I think those representation types are wrong. That'd be akin to needing to write v = std::move(v) + number_one_v<decltype(v)>; because ++v isn't provided.

These are the reference documentations: mp_units.pdf.

I love the docs. However, I think the documentation generation framework could be provided in a separate PR. Merging the framework forces us to provide all the rest of the documentation for the library in this framework.

When you think the added concepts work fine and are ready to merge, if you still think it's OK to merge without documentation, I'll rip it off this PR.

I do not find myself too comfortable with LaTex for now (but probably I should learn it anyway). Do you volunteer to document the rest of the framework as well? 😉

Sure.

JohelEGP avatar Sep 22 '23 13:09 JohelEGP

Do you write all the LaTex markup by hand, or do you use some tools/IDE/WYSIWYG to do that?

mpusz avatar Sep 22 '23 14:09 mpusz

Yes, it's all manual right now.

JohelEGP avatar Sep 22 '23 14:09 JohelEGP

A while ago, I started #493, but I never had time to finish it. Please check how it relates to your changes.

mpusz avatar Sep 22 '23 17:09 mpusz

I'm all-in for a solution that is specific to mp-units' representation types.

I'm afraid that my more general solution here could stagnate the efforts of standardization. It's certainly incomplete, and not something that I expect to be done with design-by-committee.

JohelEGP avatar Sep 22 '23 18:09 JohelEGP

I'm afraid that my more general solution here could stagnate the efforts of standardization.

Yes, I am also a bit surprised by the scope of your change. Despite this being awesome, it may be a blocker for the library. It deserves a separate paper directed to SG6, for sure. The sooner we provide it, the better. I may champion it in the Committee, but you should be the primary author and be present in the room when we discuss that in the room as I will for sure not have all the answers. You will see soon that I have lots of questions in my review :wink:

mpusz avatar Sep 22 '23 19:09 mpusz

I'm all-in for a solution that is specific to mp-units' representation types.

Sure, but you did not touch vectors and tensors at all but we need them. Moreover, vector representations collide with vector_space. I think that in the initial papers, we can base the quantity implementation on is_scalar, is_vector, and is_tensor customization points, but in the mp-units actually experiment with your numbers to get more experience with it over the years.

In case the numbers paper will be well received in SG6 we can easily merge those later.

mpusz avatar Sep 22 '23 19:09 mpusz

Let's finish this review first and then regenerate the docs. I will send it then to some ISO C++ Committee friend with mathematic backgrounds to ask for their opinion. Maybe I will meet some of them at the CppCon as well.

mpusz avatar Sep 22 '23 19:09 mpusz

CI errors have to be fixed as well...

mpusz avatar Sep 22 '23 20:09 mpusz

I think my number concepts may be too immature to be proposed for C++ standardization.

I took into account the feedback on https://wg21.link/P1813 from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1673r12.html#constraining-matrix-and-vector-element-types-and-scalars (and https://wg21.link/P2402). My specification is more relaxed and thus more widely useful. But it does mean that semantic requirements might still be necessary.

For example, unsigned's addition is modulo UNSIGNED_MAX, but such an operation still makes for a valid vector space (see the last sentence of the "Note 1 to entry" of IEV 102-01-11). Let's consider a generic algorithm that calculates a "total", with an input range of elements that model a vector_space. It still needs a semantic requirement to protect against unsigned overflows, because vector_space isn't enough for it to guarantee its result won't be erroneous.

I have considered some alternatives to semantic requirements.

  • Adding a subsuming concept, or a non-subsuming concept that just adds the semantic requirement (e.g., like a std::regular_invocable for std::invocable). For example, a concept for integer (but that's also countably infinite).
  • A set of traits, orthogonal to the concepts, for when more control of semantics are needed. This would be most similar to https://wg21.link/P1813.
  • Nothing! The algorithm with the semantic constraint already works perfectly fine. Especially if the inputs to won't give an erroneous answer for a given application.

So I'm still working on it as the needs materialize.

By the way, I didn't craft this hierarchy of number concepts "by looking at the algorithms". Instead, I looked at the needs of templates like mp_units::quantity and mp_units::quantity_point.

JohelEGP avatar Sep 22 '23 21:09 JohelEGP

Hi all! Thank you @JohelEGP for all your work! @mpusz asked me to review this proposal.

I'll begin by saying that it's great that the proposal already has wording. That being said, would you consider also adding some motivation (non-wording content that explains why this proposal should go into the C++ Standard and why it has the design that it has)? I am confident that both SG6 and (especially LEWG) will very much want to understand the motivation behind this proposal. They will be uncomfortable reviewing this proposal without first having seen motivation.

It's important that a motivation section explain how this proposal relates to earlier SG6 work on number types. Several years ago, SG6 spent some effort coming up with something like a unified vision for number types. It would make sense to talk about how this proposal either fits or doesn't fit into that vision.

mhoemmen avatar Oct 06 '23 15:10 mhoemmen

Thank you for your answer.

I'll begin by saying that it's great that the proposal already has wording. That being said, would you consider also adding some motivation (non-wording content that explains why this proposal should go into the C++ Standard and why it has the design that it has)? I am confident that both SG6 and (especially LEWG) will very much want to understand the motivation behind this proposal. They will be uncomfortable reviewing this proposal without first having seen motivation.

As encouraged by Mateusz, I mentioned I'll be doing something like that at https://github.com/mpusz/mp-units/pull/492#discussion_r1335070616. However, it'll take the form of an informational paper rather than a proposal for adoption. It'll have wording, but doesn't have adoption experience, as should be required by anything going into the C++ standard.

It's important that a motivation section explain how this proposal relates to earlier SG6 work on number types. Several years ago, SG6 spent some effort coming up with something like a unified vision for number types. It would make sense to talk about how this proposal either fits or doesn't fit into that vision.

I do remember some proposals about having an unified interface for the number types on SG6's plate. But with regards to number concepts, I'm only aware of those I mention at https://github.com/mpusz/mp-units/pull/492#issuecomment-1732070030.

JohelEGP avatar Oct 06 '23 16:10 JohelEGP

@JohelEGP wrote:

However, it'll take the form of an informational paper rather than a proposal for adoption. It'll have wording, but doesn't have adoption experience, as should be required by anything going into the C++ standard.

I'm a bit confused -- a proposal with "wording" is a proposal to put something into the C++ Standard.

In my experience, WG21 generally prefers that papers with wording also have motivation. WG21 doesn't like to separate motivation from wording. The two should go into the same paper.

mhoemmen avatar Oct 06 '23 16:10 mhoemmen

Yeah, naming it "wording" would be a misnomer. It's actually the API of the concepts I have worked on. It just so happen that I know no documentation framework would do me justice, so I use the same framework as the C++ standard draft. (I rewrote it in Cpp2, and had to translate it to C++ for this PR. I really doubt there's a C++ documentation framework that would help me with Cpp2 code). I'm not separating the "wording" from everything else.

JohelEGP avatar Oct 06 '23 16:10 JohelEGP

@JohelEGP wrote:

I do remember some proposals about having an unified interface for the number types on SG6's plate. But with regards to number concepts, I'm only aware of those I mention at https://github.com/mpusz/mp-units/pull/492#issuecomment-1732070030.

Concepts didn't exist in the Standard back then, but SG6 still had some ideas about how number types should fit together. SG6 recorded these ideas in a big proposal. We don't have to like those ideas, but they do represent an earlier consensus. Due diligence requires that authors of a new proposal about number concepts at least be aware of prior work and have some opinion about whether prior work fits.

I'll look for that big proposal and post a link here. Matthias Kretz or some other SG6 person should be able to find it if I can't.

mhoemmen avatar Oct 06 '23 16:10 mhoemmen

I reviewed e-mail lists and found some SG6 papers relating to number types.

  • A vision of numeric types presented by John McFarlane (P0037, P0554, P0828, P1050, and P1751), that was last discussed in SG6 at the 2019 WG21 meeting in Cologne

  • P1889, which collects several papers together, but unfortunately does not track which papers it collected, and also did not keep so much design rationale

I can't speak for SG6. However, I think it's a good idea to review prior work that gives a unified vision on number types, because that should inform number concepts.

mhoemmen avatar Oct 06 '23 17:10 mhoemmen

Yeah, I remember some of those. Those are mostly templates to get some kind of "number". The kind that here are models of scalar_number.

JohelEGP avatar Oct 06 '23 17:10 JohelEGP

There are a bunch of locations where compound statements are requirements of concepts, they would almost all need to be removed to support safe-arithmetic. I didn't point them all out in the review, but I can if desired.

lukevalenty avatar Oct 06 '23 18:10 lukevalenty

Thank you for your review. Looks like I'll have to rework the requirements on compound operations to allow more interesting number types.

JohelEGP avatar Oct 06 '23 18:10 JohelEGP

What is the absolute minimum set of requirements for numerical values for them to work as expected with mp-units? Do these requirements belong on Quantity, or can they be split up to the various operations that can be performed on Quantity?

lukevalenty avatar Oct 06 '23 19:10 lukevalenty

It's as mentioned at https://github.com/mpusz/mp-units/issues/29#issuecomment-1712552324:

The current approach seems like the right one. By constraining an operator on the representation's operator, units::quantity can work with any algebraic strucutre.

Things have changed since. We have quantity and quantity_point, documented to model a vector space and point space, respectively (https://mpusz.github.io/mp-units/2.0/users_guide/framework_basics/the_affine_space/). So now we know that we require the number to model those (and not just any algebraic structure). We further support operations on a scalar quantity and modulo.

JohelEGP avatar Oct 06 '23 19:10 JohelEGP

@mpusz explained to me that the document isn't actually trying to propose changes to the Standard, but is just using the LaTeX format. Given the lack of motivation text, I'll do my best to try to reverse-engineer the intent.

number concept

It looks like users have to opt into this concept by specializing the enable_number trait. I find this a bit surprising; the point of a "number concept" to me would be to identify a set of requirements that makes an arbitrary type work with some set of algorithms. If a type satisfies those requirements, why should it have to "opt in" by specializing a trait? Furthermore, opting in to the trait doesn't actually make the type a number.

Providing a set of syntactic and semantic requirements for a number would make the trait unnecessary. number<T> would be true or false, without users needing to do anything.

Regarding the regular requirement, it's useful to know that value-initializing a number type results in a "zero" for that type. It's not enough just to be able to default-construct a value; you have to know that value-initializing (e.g., by invoking the default constructor) results in an additive identity.

ordered_number concept

Given that the concept depends on totally_ordered and number, why not call this concept totally_ordered_number? Floating-point types are not totally ordered, yet they are ordered; it seems reasonable to want a concept to describe them as well.

Arithmetic expression concepts

P1673 wants to be able to support mixed arithmetic expressions that might involve expression templates and/or proxy reference types. This means, for example, that x * y might not be a regular type, even if x and y are. (Expression templates generally hold references, so they aren't regular types.) Expressions might only relate to number types indirectly, through assignment or conversion.

Other concepts

Terms like "vector space" and "field" suggest linearity and associativity. Users may never use number types that are associative.

mhoemmen avatar Oct 06 '23 19:10 mhoemmen

number concept

It looks like users have to opt into this concept by specializing the enable_number trait. I find this a bit surprising; the point of a "number concept" to me would be to identify a set of requirements that makes an arbitrary type work with some set of algorithms. If a type satisfies those requirements, why should it have to "opt in" by specializing a trait? Furthermore, opting in to the trait doesn't actually make the type a number.

Remember std::views::iota. It works with ints. So without defaulting enable_number_v to something like !requires { std::iter_reference_t<T>; }, you could have iterators accidentally satisfying point_space. After a lot of thinking, I simply chose to make it an explicit opt-in. That's in no way final.

Regarding the regular requirement, it's useful to know that value-initializing a number type results in a "zero" for that type. It's not enough just to be able to default-construct a value; you have to know that value-initializing (e.g., by invoking the default constructor) results in an additive identity.

That's why number_zero is a separate type trait. I don't require anything from default initialization beyond what std::regular might guarantee.

ordered_number concept

Given that the concept depends on totally_ordered and number, why not call this concept totally_ordered_number? Floating-point types are not totally ordered, yet they are ordered; it seems reasonable to want a concept to describe them as well.

double is definitely a model: https://compiler-explorer.com/z/Yao9hv16x. Remember https://eel.is/c++draft/concepts#equality-2.

Arithmetic expression concepts

P1673 wants to be able to support mixed arithmetic expressions that might involve expression templates and/or proxy reference types. This means, for example, that x * y might not be a regular type, even if x and y are. (Expression templates generally hold references, so they aren't regular types.) Expressions might only relate to number types indirectly, through assignment or conversion.

I hadn't considered that expression templates might not be regular. I assume this would be fixed with https://github.com/mpusz/mp-units/pull/492#discussion_r1349200471.

Other concepts

Terms like "vector space" and "field" suggest linearity and associativity. Users may never use number types that are associative.

Neither do I. The solution I came up with was splitting the set addition happens from the mapping of the resulting element to a value of the type. This also allows things like https://github.com/mpusz/mp-units/pull/492#discussion_r1335073402. I think that's good, which is why scalar_number is only an approximation of a scalar number (which is a real or complex number, which double isn't, but it still models scalar_number).

JohelEGP avatar Oct 06 '23 19:10 JohelEGP

@JohelEGP wrote:

I think that's good, which is why scalar_number is only an approximation of a scalar number (which is a real or complex number, which double isn't, but it still models scalar_number).

I generally prefer to avoid the word "approximation" when referring to concepts.

First, a concept is binary. What does "approximating" a concept mean?

Second, "approximation" suggests rounding error, but error due to assuming that a nonassociative number type is associative can be unbounded. (Consider, for example, a saturating integer number type.)

mhoemmen avatar Oct 06 '23 20:10 mhoemmen