-
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
Nested quantity kinds #656
Comments
Regarding mixing two subkinds, my personal opinion is that If someone really wants to add or compare them, they should first be converted to dimensionless and then added or compared. For example: auto q = dimensionless(angle.in(one)) + dimensionless(solid_angle.in(one)); Please upvote this comment if you agree with the above. |
Answering the remaining questions is much harder as an angular measure belongs to both dimensionless and angular measure kinds. Counts and ratios are dimensionless, and they should be convertible to the root of the hierarchy. It should also be possible to add and compare them. Technically, an angular measure is also defined in terms of a ratio ( Let's assume that for our analysis, a dimensionless quantity we use is a count of cows. Should it really be possible to add a count of cows and an angle without any explicit annotations? There are two options here:
I personally prefer option 2. |
It doesn't feel good for me if |
ISO 80000 explicitly states that both The same applies to I just fixed the graph in the first post to make it more explicit. |
Are there any other arguments besides the, admittedly strong, 'ISO 80000 compliant' argument? |
This is how SI works as well:
|
Currently I think I would like That means I should accept |
The following works already today: using std::numbers::pi;
constexpr quantity q1 = 180 * deg + pi * one;
constexpr quantity q2 = isq::angular_measure(180 * deg) + pi * rad;
constexpr quantity q3 = (180. * deg).in(one) + pi; I am not trying to change it here. What I am trying to prevent here are: constexpr quantity q4 = 180 * deg + dimensionless(pi * one);
constexpr quantity q5 = 180 * deg + number_of_cows(pi * one); |
I think I also prefer option 2 |
Expanding on @mpusz 's example of counting cows there are a number of cases of developing custom dimensionless quantity for digital audio. For example to describe time expressed in digital samples or a digital sampling rate it is necessary to define a quantity for a count of digital samples: inline constexpr struct sample_count final : quantity_spec<dimensionless, is_kind> {} sample_count;
inline constexpr struct sample_duration final : quantity_spec<isq::time> {} sample_duration;
inline constexpr struct sample_rate final : quantity_spec<isq::frequency, sample_count / isq::time> {} sample_rate;
inline constexpr struct sample final : named_unit<"Smpl", one, kind_of<sample_count>> {} sample;
inline constexpr auto smpl = sample; Another digital audio example of counting is time expressed in musical note durations and beats-per-minute requires a counting a number of musical beats: inline constexpr struct beat_count final : quantity_spec<dimensionless, is_kind> {} beat_count;
inline constexpr struct beat_duration final : quantity_spec<isq::time> {} beat_duration;
inline constexpr struct tempo final : quantity_spect<isq::frequency, beat_count / isq::time> {} tempo;
inline constexpr struct beat : named_unit<"beat", one, kind_of<beat_count>> {} beat;
inline constexpr struct quarter_note final : named_unit<"q", one, kind_of<beat_duration>> {} quarter_note;
inline constexpr struct beats_per_minute final : named_unit<"bpm", quarter_note / non_si::minute> {} beats_per_minute; I agree with @mpusz on what should not compile for adding or comparing sub-kinds of dimensionless both because these separate counts should not be compatible quantities and we would lose type safety and understandability if they operations on a Variations on the examples of above with these audio examples: auto beats = beat_count(1 * beat);
auto samples = sample_count(1 * smpl);
auto q4 = beats + 1 * one; // beat_count(2 * beat)
// might be okay if these examples compile but it does seem a little confusing what the meaning or purpose
// if it does compile the result should probably be a beat_count quantity
// using a cast would be more clear so "option 2" above making this not compile is okay
auto q6 = beats + dimensionless(1 * one);
// might be okay if these examples compile but it does seem a little confusing what the meaning or purpose
// if it does compile the result should probably be a raw unit of smpl
// using a cast would be more clear so "option 2" above making this not compile is okay
auto q7 = 1 * smpl + dimensionless(1 * one);
// I'm not sure this is useful to allow this without a cast
// using a cast would be more clear so "option 2" above making this not compile is okay
auto q8 = beats.in(one) + dimensionless(1 * one);
// it should not be possible to add unrelated counts; it would not make sense to add a count of cows either
auto q9 = beats + samples;
// I'm not sure this is useful to allow this without a cast
// using a cast would be more clear so "option 2" above making this not compile is okay
auto q10 = beats.in(one) + samples.in(one);
// just like q9 this also does not make sense
auto q11 = 1 * beat + 1 * smpl;
// I'm not sure this is useful to allow this without a cast
// using a cast would be more clear so "option 2" above making this not compile is okay
bool cmp8 = (q4 == dimensionless(2 * one));
// just like q9 this also does not make sense
bool cmp9 = (q4 == sample_count(2 * smpl));
// just like q9 this also does not make sense
bool cmp10 = (q4 == 2 * smpl); |
Is |
Yes, it is a generic solution that is not specific to dimensionless quantities. Although dimensionless quantities may have the most use cases for it. There might be other cases as well. We planned to use |
I have nearly finished implementing this. The library framework is much simpler now. Many concepts could be simplified (e.g., The only side effect I saw so far that is worth mentioning happened for return quantity_cast<isq::distance>(earth_radius * central_angle); This does not work because it says that the unit of The solution for it is: return quantity_cast<isq::distance>((earth_radius * central_angle).in(earth_radius.unit)); where we explicitly remove the Please let me know your thoughts, especially if you think that it is an issue (SI typically treats angles as dimensionless in derived quantities and their units). I personally think that it adds a bit more safety than SI does daily. |
Resolved with 06cbfae |
We need to define the best possible logic for nested quantity kinds. The library's results should be correct and not surprise anyone.
For our analysis, let's use two simplified trees of length and dimensionless quantities:
Before we dig into details, let's mention that the discussion below relates to the above trees. If a user wants to model any of the above quantities differently to provide more or less safety, it is OK and possible, but it is not the subject of the discussion below. Let's scope on how the code should work when the trees are defined as above.
We model the above dimensionless quantities this way because ISQ explicitly states that angular measure should be allowed to be measured in both
one
andrad
. The same is true for solid angular measure with the unitsr
. This is why we nest their kind trees in the tree of dimensionless quantities. They are defined with a specialis_kind
tag in the definition:This allows us not only to state that the trees nest but also to assign unique units for them:
As a reminder, according to ISQ, it should be possible to compare, add, and compare quantities of the same kind. Additionally, in mp-units, when we just spell a unit, we say that we mean any quantity of its kind.
For lengths it is simple to reason about:
We can also compare
q1
to:However, it gets more interesting when we have several subkinds in our tree:
The
q4
andq5
cases are fairly easy. Adding angular measure and a quantity that behaves as any quantity of the dimensionless tree results in angular measure. Please also note that a unit ofone
is promoted torad
here to provide a more user-friendly result.Should
q6
-q8
compile? What should be the result? We could end up with a dimensionless quantity. This would force angular measure to go outside of its kind tree. Also, the unit could not berad
, which would conflict with unit promotion inq4
andq5
. What is the physical sense of such an equation anyway? Maybe it should not compile? Maybe in order to make such things compile, a user would need to explicitly convert angular measure to dimensionless and then continue with arithmetics?q9
andq11
are also interesting. Should they not compile or just result in a dimensionless quantity?And what about comparisons? Let's see the following options:
cmp4
-cmp7
are probably reasonable. What about the rest?What about conversion rules? We know that every height is length and not every length is height. Also, height is never a width but both are lengths. This is why we have the following logic:
What about nested kinds? How should those behave?
Let's discuss those cases. Ideally, the conversions, comparisons, and additions should be consistent with each other and not surprising to our users.
The text was updated successfully, but these errors were encountered: