-
Notifications
You must be signed in to change notification settings - Fork 94
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
feat!: add number concepts #492
base: master
Are you sure you want to change the base?
Conversation
31ef007
to
4c6ab51
Compare
4c6ab51
to
bab7acf
Compare
These are the reference documentations: mp_units.pdf. |
I imagine it doesn't provide compound assignments There are two other ways I think the concepts could fall short:
|
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. |
Of course, we may, and probably should, submit a bug for this library as it strives to be standardized as well. |
|
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? 😉 |
I think those representation types are wrong.
When you think the added concepts work fine and are ready to merge,
Sure. |
Do you write all the LaTex markup by hand, or do you use some tools/IDE/WYSIWYG to do that? |
Yes, it's all manual right now. |
A while ago, I started #493, but I never had time to finish it. Please check how it relates to your changes. |
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. |
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 😉 |
Sure, but you did not touch vectors and tensors at all but we need them. Moreover, vector representations collide with In case the numbers paper will be well received in SG6 we can easily merge those later. |
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. |
cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml
Outdated
Show resolved
Hide resolved
docs/reference/CMakeLists.txt
Outdated
@@ -0,0 +1,21 @@ | |||
cmake_minimum_required(VERSION 3.25.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this version the minimum one required to make it work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but I'm not sure about the dependency itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible, I would like to keep the required version as low as possible to not force the users to install the latest CMake with no good reason.
Alternatively, we can install the required version of CMake with Conan but I would prefer to save it as a last resort option (i.e. to support C++ modules).
CI errors have to be fixed as well... |
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 For example, I have considered some alternatives to semantic requirements.
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". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edited #492 (comment) with the new PDF.
cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml
Outdated
Show resolved
Hide resolved
docs/reference/CMakeLists.txt
Outdated
@@ -0,0 +1,21 @@ | |||
cmake_minimum_required(VERSION 3.25.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but I'm not sure about the dependency itself.
docs/reference/intro.tex
Outdated
\indexdefn{modulo}% | ||
\definition{modulo}{def.mod} | ||
operation performed on a set for which a division\irefiev{102-01-21} and an addition are defined, | ||
the result of which, for elements $a$ and $b$ of the set, | ||
is the unique element $r$, if it exists in the set, | ||
such that $a = \lfloor a/b \rfloor b + r$ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not consistent with how modulo operation on quantities works.
I think you're referring to the cases for which the modulo of quantity
was made ill-formed.
This term modulo is used by a concept
which already checks for its validity.
And when it is valid, it better behaves like this.
Shouldn't we use https://eel.is/c++draft/expr.mul#4 instead of redefining the operation?
I did consider that.
I saw value in phrasing it like division.
That's certainly correct,
and doesn't run the risk of a bad specification due to saying something like
"behaves like %
for integral operands".
docs/reference/numbers.tex
Outdated
\lhdr{\fakegrammarterm{template-parameter-list}} | ||
& \chdr{\fakegrammarterm{template-argument-list}} | ||
& \rhdr{\Fundescx{Type}} \\ \rowsep | ||
\tcode{std::integral T} & \tcode{T} & \tcode{T} \\ \rowsep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But does it make sense to allow treating a bool
as a number? I mean semantically and not synthetically. Concepts only check syntactic correctness but know nothing about the semantics.
docs/reference/numbers.tex
Outdated
{ c + d } -> std::common_with<T>; | ||
{ d + c } -> std::common_with<T>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely needs some more work. std:common_with
probably may not necessarily imply a number
? The addition also is about commutativity so no matter if we do a + b
or b + a
we should get the same result so it seems strange that in both cases we constrain the results with common_with<T>
. Maybe that is not a good idea to use this concept to constrain the result at all?
}; | ||
|
||
template<class T, class U> concept @\defexposconceptnc{subtraction-with}@ = | ||
@\exposconcept{addition-with}@<T, U> && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::chrono::sys_seconds models point_space.
The result of the subtraction is something like std::chrono::seconds.
Right, but the concept will say it is not subtractive, which is confusing. But maybe that does not matter for an exposition only concept, which is just an implementation detail of point_space_for
.
@@ -44,16 +44,18 @@ | |||
|
|||
// \ref{num.assoc.types}, associated types | |||
template<typename T> | |||
struct number_scalar; | |||
struct vector_scalar; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we have scalar_quantity
that is a vector, and we can get its vector_scalar
? And we will also get vector_quantity
and tensor_quantity
in the future 😉 I am afraid that is still confusing.
It is also specialized for std::complex
, which is not a vector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I rename it to scalar_of_vector_space
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we have
scalar_quantity
that is a vector, and we can get itsvector_scalar
? And we will also getvector_quantity
andtensor_quantity
in the future 😉 I am afraid that is still confusing.
I don't know.
I provided scalar_quantity
(from https://www.electropedia.org/iev/iev.nsf/display?openform&ievref=102-02-19).
I used it once to constrain a Cartesian vector of quantities.
But then reworked it to have similar type names:
cartesian_vector2d<quantity<metre>>
-> cartesian_vector2d<metre>
.
Now it's mostly a placeholder for the part of the reference documentations for mp-units.
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. |
Thank you for your answer.
As encouraged by Mateusz, I mentioned I'll be doing something like that at #492 (comment).
I do remember some proposals about having an unified interface for the number types on SG6's plate. |
@JohelEGP wrote:
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. |
Yeah, naming it "wording" would be a misnomer. |
@JohelEGP wrote:
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. |
I reviewed e-mail lists and found some SG6 papers relating to number types.
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. |
Yeah, I remember some of those. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm reviewing this from the perspective of interop with intel/safe-arithmetic.
Overall, there are a few issues preventing interop:
safe::var
values may preclude0
, in which case there is no default constructor. It will not satisfystd::regular
.safe::var
values cannot support compound operations. This is because the resulting type of the operation may not fit within the original type.safe::var
operator/
protects against divide by zero.
|
||
template<typename T> | ||
concept negative = // clang-format off | ||
detail::compound_addition_with<T,T> && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why does negative
require compound_addition_with
?
issue: safe-arithmetic's safe::var
cannot support compound operations...adding any value to a safe::var
changes its type based on the new set of potential values the result could be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It requires that the set has addition (https://www.electropedia.org/iev/iev.nsf/display?openform&ievref=102-01-14).
That's done through compound_addition_with
because I haven't had an use case where that didn't work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safe::var
can support addition just fine, but compound addition is the problem. Can we separate out the compound requirements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a short synopsis for why this breaks safe arithmetic:
using namespace safe::literals;
// declare variable `x` to be between -10 and 10 inclusive, guaranteed at compile time:
safe::ival_s32<-10, 10> x{};
// adding `1` to `x` results in a different type:
safe::ival_s32<-9, 11> y = x + 1_s32;
// compound addition cannot work because y cannot be assigned to x:
static_assert(false == std::is_assignable_v<decltype(x), decltype(y)>);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one requires compound addition with the same type.
Is that supported?
constexpr T number_one_v = number_one<T>::value; | ||
|
||
template<typename T> | ||
concept number = enable_number_v<T> && std::regular<T>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: safe-arithmetic safe::var
values that require a non-zero value cannot satisfy std::regular
because they do not have a default constructor. They don't have a default constructor because they guarantee to be initialized and there is no obvious answer for what they should be initialized to (can't be '0').
suggestion: use something like a weakly_regular
concept that just drops the default_constructable
requirement from std::regular
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just like above,
I haven't had an use case where that didn't work.
I'll have to replace it with std::copyable<T> && std::equality_comparable<T>
.
|
||
template<typename T, typename U> | ||
concept scales_with = | ||
common_number_with<U, vector_scalar_t<T>> && weak_scalar<U> && multiplication_with<T, U, T> && set_with_inverse<U>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: A safe-arithmetic safe::var
instantiation may allow a zero value. In this case, set_with_inverse
will fail because safe-arithmetic does not allow division by a safe::var
that might be '0'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that dividing is a compile-time error if the divisor has 0
in its range?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For safe::var
, that's exactly the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using namespace safe::literals;
// declare variable `x` to be between -10 and 10 inclusive, guaranteed at compile time:
safe::ival_s32<-10, 10> x = 10_s32;
safe::ival_s32<1, 10> y = 10_s32;
// it's possible to have a number type that excludes '0', but still has positive and negative values
safe::var<int32_t, safe::ival<-10, -1> || safe::ival<1, 10>> z = 1_s32;
// these are fine:
auto a = x / y;
auto b = x / z;
// this is not fine and will cause a static_assert, because x _might_ be zero:
auto c = y / x;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the problem is that scales-with
<T, U>
shouldn't require set_with_inverse<U>
.
I think I should have moved it to field_for
and compound_field_for
.
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. |
Thank you for your review. |
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 |
It's as mentioned at #29 (comment):
|
@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.
|
Remember
That's why
I hadn't considered that expression templates might not be regular.
Neither do I. |
@JohelEGP wrote:
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.) |
It's the other way around. |
There's a note on this at [structure.requirements]:
|
Given that |
@JohelEGP Ah, thanks for reminding me; I was confusing "totally ordered" with "strong ordering" : - ) |
Please, see the comment with the PDF for the paper on the number concepts: #492 (comment). |
This comment was marked as duplicate.
This comment was marked as duplicate.
template<typename T> | ||
concept vector_space = @\seebelow@; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider also providing the affine_space
, which should be the generalization of the vector_space
?
See https://en.wikipedia.org/wiki/Affine_space for more details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have something like point_space_for<point_type, vector_type>
.
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 constrainrep
, andquantity
itself can modelvector_space
.So
number_one_v
would supersedequantity_values::one()
, and same forzero
.https://github.com/BobSteagall/wg21/ doesn't work because it doesn't provide compound assignments.
mp-units/test/unit_test/runtime/linear_algebra_test.cpp
Line 97 in a356db7
v + v
is valid and the same type asv
.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 conceptsthat couple checks on the reference and the representation:
mp-units/src/core/include/mp-units/quantity.h
Lines 60 to 67 in a356db7
Because
concept
s can't be generically composed (i.e., passed to a concept template parameter),interfaces would have to check the reference and representation separately.