-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Adding LinSpace, LogSpace, and Arange prototypes #18200
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
base: master
Are you sure you want to change the base?
Conversation
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.
Thanks for the good work you did to put together this PR. I proposed some changes which can be applied without much effort, or so I hope, and that will make the new functions (templates) even more useful.
Ah, and also, the equality of double precision floating point is not working too well because of the very meaning of equality of floating point ;-) |
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit b5b5620. ♻️ This comment has been updated with latest results. |
Hi @dpiparo Thank you for the quick response and feedback! For this next commit, I’ve updated the functions as follows:
|
Dear @edfink234 thanks. I am reviewing the code. One thing which we will need is to provide a clean commit, properly formatted with all the changes - it is fine to have some "history" when crafting PRs but we try to keep the history of the repo as clean as possible. |
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.
Good job. Once the changes are addressed and the tests pass, we are ready to merge.
Thanks for the quick response @dpiparo! I will get to these hopefully today (Tuesday PST) if not at some point this week. It's almost 2 am where I am atm 😅... |
@dpiparo I made another commit to address the new comments and modified the tests a bit to hopefully have them pass. |
Thanks @edfink234 . Let's see how this goes. |
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 a quite useful contribution, thanks a lot! I have left some comments for discussion
math/vecops/inc/ROOT/RVec.hxx
Outdated
template <typename T1 = double, typename T2 = double, typename T3 = double, typename Common_t = std::conditional_t<std::is_floating_point_v<std::common_type_t<T1, T2, T3>>, std::common_type_t<T1, T2, T3>, double>> | ||
inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128) |
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.
The template parameter T3
is not used anywhere in the template, so I guess it can be removed. Also, from what I understand the return type will always be an RVec of floating point type. I personally do not see the reason to not always just have RVec<double>
.
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 benefit could be if the user wants float or some other floating point type, they have some options on how they call these functions (Linspace
, Logspace
, Arange
) to yield the desired floating point type. For example (after the changes from the most recent commit to this PR), to illustrate some of this flexibility below:
root [1] Linspace(1, 10, 10)
(ROOT::VecOps::RVec<double>) { 1.0000000, 2.0000000, 3.0000000, 4.0000000, 5.0000000, 6.0000000, 7.0000000, 8.0000000, 9.0000000, 10.000000 }
root [2] Linspace(1.0f, 10.0f, 10)
(ROOT::VecOps::RVec<float>) { 1.00000f, 2.00000f, 3.00000f, 4.00000f, 5.00000f, 6.00000f, 7.00000f, 8.00000f, 9.00000f, 10.0000f }
root [3] Linspace<float, float>(1, 10, 10)
(ROOT::VecOps::RVec<float>) { 1.00000f, 2.00000f, 3.00000f, 4.00000f, 5.00000f, 6.00000f, 7.00000f, 8.00000f, 9.00000f, 10.0000f }
root [4] Linspace<long double, long double>(1, 10, 10)
(ROOT::VecOps::RVec<long double>) { 1.0000000L, 2.0000000L, 3.0000000L, 4.0000000L, 5.0000000L, 6.0000000L, 7.0000000L, 8.0000000L, 9.0000000L, 10.000000L }
root [5] Logspace(1.0f, 5.0f, 5, 10.0f)
(ROOT::VecOps::RVec<float>) { 10.0000f, 100.000f, 1000.00f, 10000.0f, 100000.f }
root [6] Logspace<float, float, float>(1, 5, 20)
(ROOT::VecOps::RVec<float>) { 10.0000f, 16.2378f, 26.3665f, 42.8133f, 69.5193f, 112.884f, 183.298f, 297.635f, 483.293f, 784.760f, 1274.28f, 2069.14f, 3359.82f, 5455.60f, 8858.67f, 14384.5f, 23357.2f, 37926.9f, 61584.8f, 100000.f }
root [7] Arange<float, float, float>(1, 10, 3)
(ROOT::VecOps::RVec<float>) { 1.00000f, 4.00000f, 7.00000f }
root [8] Arange<long double, long double, long double>(3, 15, 3.1)
(ROOT::VecOps::RVec<long double>) { 3.0000000L, 6.1000000L, 9.2000000L, 12.300000L }
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 we could get the desired functionality by simplifying this as follows:
template <typename T, typename Ret = double> Ret Linspace(T start, T end, unsigned long long n = 50, const bool endpoint=true) {
const long double step = static_cast<long double>(end - start) / (n - endpoint);
tmp.push_back(std::is_floating_point_v<Ret> ? start + i * step : std::floor(start + i * step));
}
math/vecops/inc/ROOT/RVec.hxx
Outdated
* @brief Produce RVec with N evenly-spaced entries from start to end inclusive. | ||
* | ||
* This function generates a vector of evenly spaced values, starting at @p start and ending at @p end, | ||
* with exactly @p n elements. The spacing is computed as | ||
* \f$\text{step} = \frac{\text{end} - \text{start}}{n-1}\f$, so that the vector includes both endpoints. |
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.
Throughout the ROOT codebase, the Doxygen token used as sequence beginner is \
rather than @
. See for example the docs of Construct
from above: \begin
, \tparam
, \p
etc. Please move all the @
instances to \
instead.
math/vecops/inc/ROOT/RVec.hxx
Outdated
template <typename T1 = double, typename T2 = double, typename T3 = double, typename Common_t = std::conditional_t<std::is_floating_point_v<std::common_type_t<T1, T2, T3>>, std::common_type_t<T1, T2, T3>, double>> | ||
inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3 base = 10.0) | ||
{ | ||
RVec<Common_t> temp; | ||
|
||
if (!n || (n > std::numeric_limits<long long>::max())) // Check for invalid or absurd n. | ||
{ | ||
return temp; | ||
} |
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.
Similar comments that I made for LinSpace
also apply here
Thanks a lot for this! I have some suggestions, since I think it would be good to mimick python numpy more:
|
I tried my best to address these, though for the second and third points, I don't know how to cleanly add a specialization without incurring an ambiguous compiler error wrt being unable to deduce the correct overload. |
Not sure, maybe it's easier to use just a ternary operator such as: double result = std::is_floating_point ? static_cast : floor ; |
1f49ed5
to
deed8c2
Compare
deed8c2
to
1a2bbfd
Compare
…tep computation
Hi @dpiparo @vepadulano @ferdymercury I added the changes suggested by @vepadulano and @ferdymercury. I'm not sure how I feel about the suggestion of @ferdymercury for step if I understand the need for all these casts and what the floor does exactly, but ok, I changed it and checked that it yields the expected output. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
template <typename T = double, typename Ret_t = std::conditional_t<std::is_floating_point_v<T>, T, double>> | ||
inline RVec<Ret_t> Arange(T start, T end, T step) | ||
{ | ||
unsigned long long n = std::ceil(static_cast<Ret_t>(end-start)/static_cast<Ret_t>(step)); // Ensure floating-point division. |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
static_cast<long double>(end - start) / (n - endpoint); | ||
|
||
RVec<Ret_t> temp(n); | ||
temp[0] = static_cast<Ret_t>(start); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
(static_cast<Ret_t>(end_c - start_c) / static_cast<Ret_t>(n - endpoint)) : | ||
static_cast<long double>(end - start) / (n - endpoint); | ||
|
||
temp[0] = static_cast<Ret_t>(std::pow(base_c, start_c)); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
RVec<Ret_t> temp(n); | ||
|
||
Ret_t start_c = static_cast<Ret_t>(start); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
long double step = std::is_floating_point_v<Ret_t> ? | ||
(static_cast<Ret_t>(end) - static_cast<Ret_t>(start)) / static_cast<Ret_t>(n - endpoint) : | ||
static_cast<long double>(end - start) / (n - endpoint); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
{ | ||
for (unsigned long long i = 1; i < n; i++) | ||
{ | ||
temp[i] = static_cast<Ret_t>(start) + static_cast<Ret_t>(i) * step; |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
long double step = std::is_floating_point_v<Ret_t> ? | ||
(static_cast<Ret_t>(end_c - start_c) / static_cast<Ret_t>(n - endpoint)) : | ||
static_cast<long double>(end - start) / (n - endpoint); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
long double step_c = std::is_floating_point_v<Ret_t> ? | ||
static_cast<Ret_t>(step) : | ||
static_cast<long double>(step); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
Ret_t end_c = static_cast<Ret_t>(end); | ||
Ret_t base_c = static_cast<Ret_t>(base); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
math/vecops/inc/ROOT/RVec.hxx
Outdated
} | ||
|
||
long double step = std::is_floating_point_v<Ret_t> ? | ||
(static_cast<Ret_t>(end) - static_cast<Ret_t>(start)) / static_cast<Ret_t>(n - endpoint) : |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
I just pushed the new commit with these changes! |
math/vecops/inc/ROOT/RVec.hxx
Outdated
Ret_t start_c = static_cast<Ret_t>(start); | ||
Ret_t end_c = static_cast<Ret_t>(end); | ||
Ret_t base_c = static_cast<Ret_t>(base); |
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.
Ret_t start_c = static_cast<Ret_t>(start); | |
Ret_t end_c = static_cast<Ret_t>(end); | |
Ret_t base_c = static_cast<Ret_t>(base); | |
long double start_c = start; | |
long double end_c = end; | |
long double base_c = base; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
long double step = std::is_floating_point_v<Ret_t> ? | ||
(end_c - start_c) / static_cast<long double>(n - endpoint) : | ||
(end >= start ? static_cast<long double>(end - start) / (n - endpoint) : (static_cast<long double>(end) - start) / (n - endpoint)); |
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.
long double step = std::is_floating_point_v<Ret_t> ? | |
(end_c - start_c) / static_cast<long double>(n - endpoint) : | |
(end >= start ? static_cast<long double>(end - start) / (n - endpoint) : (static_cast<long double>(end) - start) / (n - endpoint)); | |
long double step = (end_c - start_c) / (n - endpoint); |
math/vecops/inc/ROOT/RVec.hxx
Outdated
Ret_t exponent = start_c + i * step; | ||
temp[i] = std::pow(base_c, exponent); |
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.
Ret_t exponent = start_c + i * step; | |
temp[i] = std::pow(base_c, exponent); | |
auto exponent = start_c + i * step; | |
temp[i] = static_cast<Ret_t>(std::pow(base_c, exponent)); |
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
if (!n || (n > std::numeric_limits<long long>::max())) // Check for invalid or absurd n. | ||
{ | ||
return RVec<Ret_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.
return RVec<Ret_t>{}; | |
return {}; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
{ | ||
if (!n || (n > std::numeric_limits<long long>::max())) // Check for invalid or absurd n. | ||
{ | ||
return RVec<Ret_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.
return RVec<Ret_t>{}; | |
return {}; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
{ | ||
if (!n || (n > std::numeric_limits<long long>::max())) // Check for invalid or absurd n. | ||
{ | ||
return RVec<Ret_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.
return RVec<Ret_t>{}; | |
return {}; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
{ | ||
for (unsigned long long i = 1; i < n; i++) | ||
{ | ||
Ret_t exponent = start_c + i * step; |
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.
Ret_t exponent = start_c + i * step; | |
auto exponent = start_c + i * step; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
RVec<Ret_t> temp(n); | ||
|
||
Ret_t start_c = std::is_floating_point_v<Ret_t> ? static_cast<Ret_t>(start) : std::floor(start); |
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.
Ret_t start_c = std::is_floating_point_v<Ret_t> ? static_cast<Ret_t>(start) : std::floor(start); | |
long double start_c = start; |
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
long double step_c = step; | ||
|
||
temp[0] = start_c; |
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.
temp[0] = start_c; | |
temp[0] = std::is_floating_point_v<Ret_t> ? static_cast<Ret_t>(start) : std::floor(start); |
math/vecops/inc/ROOT/RVec.hxx
Outdated
{ | ||
for (unsigned long long i = 1; i < n; i++) | ||
{ | ||
temp[i] = start_c + i * step_c; |
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.
temp[i] = start_c + i * step_c; | |
temp[i] = static_cast<Ret_t>(start_c + i * step_c); |
Hi @ferdymercury @dpiparo @vepadulano! I made a commit with the most recent changes suggested by @ferdymercury. |
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.
Thanks a lot for the changes!
I have now some corrections for the documentation,
and one issue with the int-Linspace, I am getting locally different results than yours, please cross-check.
math/vecops/inc/ROOT/RVec.hxx
Outdated
* cout << Linspace(3, 12, 5, false) << "\n"; | ||
* // { 3, 4.8, 6.6, 8.4, 10.2 } | ||
* Linspace<int, int>(1, 10, 3) << "\n"; | ||
* // { 1, 5, 9 } |
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.
* // { 1, 5, 9 } | |
* // { 1, 5, 10 } |
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 get 10 with the current version of the code. You get sth different ?
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, you are right, that is indeed a typo. Thanks for the catch!
math/vecops/inc/ROOT/RVec.hxx
Outdated
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue) | ||
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd | ||
* amount of memory. | ||
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may cause rounding issues. Consequently, the resulting sequence may not end exactly at \p end. To ensure that the sequence ends exactly at \p end, consider casting the result as follows: `RVec<integral_type>(Linspace(...))` (which is equivalent in numpy to `np.linspace(...).astype(integral_type)`). This behavior is different than NumPy in Python. |
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.
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may cause rounding issues. Consequently, the resulting sequence may not end exactly at \p end. To ensure that the sequence ends exactly at \p end, consider casting the result as follows: `RVec<integral_type>(Linspace(...))` (which is equivalent in numpy to `np.linspace(...).astype(integral_type)`). This behavior is different than NumPy in Python. | |
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the returned results are rounded towards negative (std::floor) and then cast to the integer type. This is equivalent to setting `dtype = int` in numpy.linspace. To cast to integer without rounding, use instead `RVec<integral_type>(Linspace(...))`, which would be equivalent to .astype(integral_type) in numpy. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue) | ||
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd | ||
* amount of memory. | ||
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the sequence may not end exactly at \f$base^{end}\f>. To ensure that the final element is exactly \f$base^{end}\f>, consider casting the result as follows: `RVec<integral_type>(Logspace(...))` (which is equivalent in numpy to `np.logspace(...).astype(integral_type)`). This behavior is different than NumPy in Python. |
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.
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the sequence may not end exactly at \f$base^{end}\f>. To ensure that the final element is exactly \f$base^{end}\f>, consider casting the result as follows: `RVec<integral_type>(Logspace(...))` (which is equivalent in numpy to `np.logspace(...).astype(integral_type)`). This behavior is different than NumPy in Python. | |
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the returned results are rounded towards negative (std::floor) and then cast to the integer type. This is equivalent to setting `dtype = int` in numpy.linspace. To cast to integer without rounding, use instead `RVec<integral_type>(Logspace(...))`, which would be equivalent to .astype(integral_type) in numpy. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue) | ||
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd | ||
* amount of memory. | ||
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the produced sequence may not strictly adhere to the intended progression. If an exact sequence is desired, consider casting the result as follows: `RVec<integral_type>(Arange(...))` (which is equivalent in numpy to `np.arange(...).astype(integral_type)`). This behavior is different than NumPy in Python. |
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.
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the produced sequence may not strictly adhere to the intended progression. If an exact sequence is desired, consider casting the result as follows: `RVec<integral_type>(Arange(...))` (which is equivalent in numpy to `np.arange(...).astype(integral_type)`). This behavior is different than NumPy in Python. | |
* \note If the template parameter \c Ret_t is explicitly overridden with an integral type, the returned results are rounded towards negative (std::floor) and then cast to the integer type. This is equivalent to setting `dtype = int` in numpy. To cast to integer without rounding, use instead `RVec<integral_type>(Arange(...))`, which would be equivalent to .astype(integral_type) in numpy. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
template <typename T = double, typename Ret_t = std::conditional_t<std::is_floating_point_v<T>, T, double>> | ||
inline RVec<Ret_t> Arange(T start, T end, T step) | ||
{ | ||
unsigned long long n = std::ceil(static_cast<Ret_t>(end-start)/static_cast<long double>(step)); // Ensure floating-point division. |
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.
unsigned long long n = std::ceil(static_cast<Ret_t>(end-start)/static_cast<long double>(step)); // Ensure floating-point division. | |
unsigned long long n = std::ceil(( end >= start ? (end - start) : static_cast<long double>(end)-start)/static_cast<long double>(step)); // Ensure floating-point division. |
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 need for Ret_t here. And we need split, to prevent issues if end<start and type is unsigned int.
math/vecops/inc/ROOT/RVec.hxx
Outdated
* The function is templated to allow for different arithmetic types. The return type \c Ret_t, if not explicitly specified, | ||
* iis determined as follows: if \p T is a floating point type, that type is used; otherwise, the arithmetic is performed using \c double. |
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.
* The function is templated to allow for different arithmetic types. The return type \c Ret_t, if not explicitly specified, | |
* iis determined as follows: if \p T is a floating point type, that type is used; otherwise, the arithmetic is performed using \c double. | |
* The function is templated to allow for different return types. The return type \c Ret_t, if not explicitly specified, | |
* is determined as follows: if \p T is a floating point type, that type is used; otherwise, the return type is \c double. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
* iis determined as follows: if \p T is a floating point type, that type is used; otherwise, the arithmetic is performed using \c double. | ||
* | ||
* \tparam T Type of the start and end exponents and the base. Default is double. | ||
* \tparam Ret_t Deduced type used for arithmetic, which, if not explicitly specified, is \p T if that is a floating point type, or double otherwise. |
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.
* \tparam Ret_t Deduced type used for arithmetic, which, if not explicitly specified, is \p T if that is a floating point type, or double otherwise. | |
* \tparam Ret_t Deduced type used for return type, which, if not explicitly specified, is \p T if that is a floating point type, or double otherwise. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
* The function is templated to allow for different arithmetic types. The return type \c Ret_t, if | ||
* not explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | ||
* otherwise, the arithmetic is performed using \c double. | ||
* | ||
* \tparam T Type of the start and end value. Default is double. | ||
* \tparam Ret_t Return type used for arithmetic, which, if not explicitly specified | ||
* in the template, is \p T if that is a floating point type, or double otherwise. |
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.
* The function is templated to allow for different arithmetic types. The return type \c Ret_t, if | |
* not explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | |
* otherwise, the arithmetic is performed using \c double. | |
* | |
* \tparam T Type of the start and end value. Default is double. | |
* \tparam Ret_t Return type used for arithmetic, which, if not explicitly specified | |
* in the template, is \p T if that is a floating point type, or double otherwise. | |
* The function is templated to allow for different return types. The return type \c Ret_t, if | |
* not explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | |
* otherwise, the return type is \c double. | |
* | |
* \tparam T Type of the start and end value. Default is double. | |
* \tparam Ret_t Return type used, which, if not explicitly specified | |
* in the template, is \p T if that is a floating point type, or double otherwise. |
math/vecops/inc/ROOT/RVec.hxx
Outdated
* The function is templated to allow for different arithmetic types. The deduced type \c Ret_t, if not | ||
* explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | ||
* otherwise, the arithmetic is performed using \c double. | ||
* | ||
* \tparam T Type of the start, end, and step values. Default is double. | ||
* \tparam Ret_t Deduced type used for arithmetic, which, if not explicitly | ||
* specified, is \p T if that is a floating point type, or double otherwise. |
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.
* The function is templated to allow for different arithmetic types. The deduced type \c Ret_t, if not | |
* explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | |
* otherwise, the arithmetic is performed using \c double. | |
* | |
* \tparam T Type of the start, end, and step values. Default is double. | |
* \tparam Ret_t Deduced type used for arithmetic, which, if not explicitly | |
* specified, is \p T if that is a floating point type, or double otherwise. | |
* The function is templated to allow for different return types. The return type \c Ret_t, if not | |
* explicitly specified, is determined as follows: if \p T is a floating point type, that type is used; | |
* otherwise, the return type is \c double. | |
* | |
* \tparam T Type of the start, end, and step values. Default is double. | |
* \tparam Ret_t Return type, which, if not explicitly | |
* specified, is \p T if that is a floating point type, or double otherwise. |
math/vecops/test/vecops_rvec.cxx
Outdated
CheckNear(Linspace(3, 12, 5, false), RVecD{ 3, 4.8, 6.6, 8.4, 10.2 }); | ||
CheckNear(Linspace(3, 12, 1, false), RVecD{ 3 }); | ||
CheckNear(Linspace(3, 12, 1, true), RVecD{ 3 }); | ||
CheckEqual(Linspace<int, int>(1, 10, 3), RVecI{ 1, 5, 9 }); |
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.
CheckEqual(Linspace<int, int>(1, 10, 3), RVecI{ 1, 5, 9 }); | |
CheckEqual(Linspace<int, int>(1, 10, 3), RVecI{ 1, 5, 10 }); |
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.
Some more minor comments, I feel like we're getting very close to the conclusion, thanks for bearing through!
math/vecops/inc/ROOT/RVec.hxx
Outdated
static_cast<Ret_t>(std::pow(base_c, start_c)) : | ||
std::floor(std::pow(base_c, start_c)); | ||
|
||
if (std::is_floating_point_v<Ret_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.
Can't this be
if (std::is_floating_point_v<Ret_t>) | |
if constexpr (std::is_floating_point_v<Ret_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.
Does this turn it into a compile-time instruction (I've never seen it before 😅)
math/vecops/inc/ROOT/RVec.hxx
Outdated
long double step_c = step; | ||
|
||
temp[0] = std::is_floating_point_v<Ret_t> ? static_cast<Ret_t>(start) : std::floor(start); | ||
if (std::is_floating_point_v<Ret_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.
similar comment
math/vecops/inc/ROOT/RVec.hxx
Outdated
|
||
RVec<Ret_t> temp(n); | ||
temp[0] = std::is_floating_point_v<Ret_t> ? static_cast<Ret_t>(start) : std::floor(start); | ||
if (std::is_floating_point_v<Ret_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.
similar comment
* \par C++23 Enumerate Support: | ||
* With C++23, you can use the range-based enumerate view to iterate over the resulting vector with both the index | ||
* and the value, similar to Python's `enumerate`. For example: | ||
* ~~~{.cpp} | ||
* for (auto const [index, val] : std::views::enumerate(ROOT::VecOps::Linspace(6, 10, 16))) { | ||
* // Process index and val. | ||
* } | ||
* ~~~ |
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.
Why is this section needed? Looks unrelated to this feature. Any std::vector
produced in any other way will support the same syntax, it's not specific to the new functions you're introducing
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 @ferdymercury recommended this... @ferdymercury?
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 thought to put it here since we are dealing with Python equivalent functions in C++, but yes, it's not really related, I agree. My vote is still to keep this information, since many people are unaware of it. But I am fine with removing it, too.
@ferdymercury @vepadulano @dpiparo I made the updates! I also just wanted to share an updated benchmark using the actual RVec
This is better than I get with
vs Python numpy (
|
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.
Thanks a lot! LGTM, awesome work.
As a final comment, it's inconsistent that endpoint is const but not n, start and end. Same when you define n in Arange function within the function code. I think they should be either all const or none. (I guess neither choice will affect your nice benchmark speed if compiled with -O2).
Thanks for the comment. I was looking into marking pass-by-value parameters as const vs non-const and stumbled upon this lengthy Stack Overflow post with mixed opinions... I looked at the RVec.hxx file and it seems like the convention is to only mark parameters with default arguments as const and just leave regular pass-by-value parameters without the const specifier... |
@vepadulano @dpiparo Any updates? I wasn't sure from the discussion about |
@vepadulano @dpiparo Any updates? I wasn't sure from the discussion about std::views if it was wanted to delete this documentation about it... |
@vepadulano @dpiparo Any updates? I wasn't sure from the discussion about std::views if it was wanted to delete this documentation about it. |
2 similar comments
@vepadulano @dpiparo Any updates? I wasn't sure from the discussion about std::views if it was wanted to delete this documentation about it. |
@vepadulano @dpiparo Any updates? I wasn't sure from the discussion about std::views if it was wanted to delete this documentation about it. |
This Pull request:
Changes or fixes:
Adds
linSpace
,logSpace
, andarange
functions toROOT::VecOps
namespaceChecklist:
Fixes #17855