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

feat!: add number concepts #492

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft

Conversation

JohelEGP
Copy link
Collaborator

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.

const auto v = vector<int>{3, 2, 1} * isq::position_vector[km];

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:

template<quantity_character Ch, typename Func, typename T, typename U>
concept InvokeResultOf = std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
template<typename Func, typename Q1, typename Q2>
concept InvocableQuantities = Quantity<Q1> && Quantity<Q2> &&
InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, Func,
typename Q1::rep, typename Q2::rep> &&
requires { common_reference(Q1::reference, Q2::reference); };

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
Copy link
Collaborator Author

JohelEGP commented Sep 21, 2023

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
Copy link
Collaborator Author

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

const auto v = vector<int>{3, 2, 1} * isq::position_vector[km];

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).

@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

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
Copy link
Owner

mpusz commented Sep 22, 2023

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
Copy link
Owner

mpusz commented Sep 22, 2023

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
Copy link
Owner

mpusz commented Sep 22, 2023

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? 😉

@JohelEGP
Copy link
Collaborator Author

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.

@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

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

@JohelEGP
Copy link
Collaborator Author

Yes, it's all manual right now.

@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

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

@JohelEGP
Copy link
Collaborator Author

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.

@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

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 😉

@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

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
Copy link
Owner

mpusz commented Sep 22, 2023

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.

docs/reference/CMakeLists.txt Show resolved Hide resolved
@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.25.0)
Copy link
Owner

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?

Copy link
Collaborator Author

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.

Copy link
Owner

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).

docs/reference/CMakeLists.txt Outdated Show resolved Hide resolved
docs/reference/CMakeLists.txt Outdated Show resolved Hide resolved
src/core/include/mp-units/numbers.h Outdated Show resolved Hide resolved
src/core/include/mp-units/numbers.h Outdated Show resolved Hide resolved
src/core/include/mp-units/quantity_point.h Outdated Show resolved Hide resolved
test/unit_test/runtime/linear_algebra_test.cpp Outdated Show resolved Hide resolved
docs/reference/numbers.tex Outdated Show resolved Hide resolved
@mpusz
Copy link
Owner

mpusz commented Sep 22, 2023

CI errors have to be fixed as well...

@JohelEGP
Copy link
Collaborator Author

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.

Copy link
Collaborator Author

@JohelEGP JohelEGP left a 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.

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.25.0)
Copy link
Collaborator Author

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 Show resolved Hide resolved
Comment on lines 88 to 93
\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$
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 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/intro.tex Show resolved Hide resolved
example/include/ranged_representation.h Outdated Show resolved Hide resolved
src/core/include/mp-units/numbers.h Outdated Show resolved Hide resolved
src/core/include/mp-units/numbers.h Outdated Show resolved Hide resolved
src/core/include/mp-units/numbers.h Outdated Show resolved Hide resolved
src/core/include/mp-units/quantity_point.h Outdated Show resolved Hide resolved
example/include/ranged_representation.h Show resolved Hide resolved
example/include/ranged_representation.h Outdated Show resolved Hide resolved
example/include/ranged_representation.h Outdated Show resolved Hide resolved
docs/reference/itemdecl/.clang-format Outdated Show resolved Hide resolved
docs/reference/intro.tex Show resolved Hide resolved
\lhdr{\fakegrammarterm{template-parameter-list}}
& \chdr{\fakegrammarterm{template-argument-list}}
& \rhdr{\Fundescx{Type}} \\ \rowsep
\tcode{std::integral T} & \tcode{T} & \tcode{T} \\ \rowsep
Copy link
Owner

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.

Comment on lines 343 to 344
{ c + d } -> std::common_with<T>;
{ d + c } -> std::common_with<T>;
Copy link
Owner

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> &&
Copy link
Owner

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.

docs/reference/numbers.tex Outdated Show resolved Hide resolved
@@ -44,16 +44,18 @@

// \ref{num.assoc.types}, associated types
template<typename T>
struct number_scalar;
struct vector_scalar;
Copy link
Owner

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renaming it also made it inconsistent with number_difference_t.
Maybe I should rename that to point_difference_t.
1695509969

Copy link
Collaborator Author

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.

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.

@mhoemmen
Copy link

mhoemmen commented Oct 6, 2023

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.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

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 #492 (comment).
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 #492 (comment).

@mhoemmen
Copy link

mhoemmen commented Oct 6, 2023

@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.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

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.

@mhoemmen
Copy link

mhoemmen commented Oct 6, 2023

@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 #492 (comment).

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
Copy link

mhoemmen commented Oct 6, 2023

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.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

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.

Copy link

@lukevalenty lukevalenty left a 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:

  1. safe::var values may preclude 0, in which case there is no default constructor. It will not satisfy std::regular.
  2. safe::var values cannot support compound operations. This is because the resulting type of the operation may not fit within the original type.
  3. safe::var operator/ protects against divide by zero.


template<typename T>
concept negative = // clang-format off
detail::compound_addition_with<T,T> &&

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.

Copy link
Collaborator Author

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.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Of course.

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)>);

https://github.com/intel/safe-arithmetic

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 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>;

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

Copy link
Collaborator Author

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>;

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'.

Copy link
Collaborator Author

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?

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.

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;

https://github.com/intel/safe-arithmetic

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 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.

@lukevalenty
Copy link

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.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

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

@lukevalenty
Copy link

lukevalenty commented Oct 6, 2023

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?

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

It's as mentioned at #29 (comment):

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.

@mhoemmen
Copy link

mhoemmen commented Oct 6, 2023

@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.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

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 #492 (comment).

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 #492 (comment).
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).

@mhoemmen
Copy link

mhoemmen commented Oct 6, 2023

@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.)

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 6, 2023

What does "approximating" a concept mean?

It's the other way around.
The concept approximates an algebraic structure.
In this case, scalar_number is an approximation of scalar.
Fortunately, I just use it in a note, which can be axed:
1696627414

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 7, 2023

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.

There's a note on this at [structure.requirements]:

8
#
Required operations of any concept defined in this document need not be total functions; that is, some arguments to a required operation may result in the required semantics failing to be met.
[Example 1: The required < operator of the totally_ordered concept ([concept.totallyordered]) does not meet the semantic requirements of that concept when operating on NaNs.
— end example]
This does not affect whether a type models the concept.

@JohelEGP
Copy link
Collaborator Author

JohelEGP commented Oct 8, 2023

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.

Given that double is a model of std::totally_ordered,
it seemed redundant to me to include totally in the name.
There might be algebraic structures that don't model that,
but I haven't had the need to support anything like that.

@mhoemmen
Copy link

mhoemmen commented Oct 9, 2023

@JohelEGP Ah, thanks for reminding me; I was confusing "totally ordered" with "strong ordering" : - )

@JohelEGP
Copy link
Collaborator Author

Please, see the comment with the PDF for the paper on the number concepts: #492 (comment).

@JohelEGP

This comment was marked as duplicate.

@mpusz mpusz marked this pull request as draft January 5, 2024 10:50
Comment on lines +93 to +94
template<typename T>
concept vector_space = @\seebelow@;
Copy link
Owner

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.

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 have something like point_space_for<point_type, vector_type>.

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

Successfully merging this pull request may close these issues.

Poll: How to constrain operations in a class template?
4 participants