-
Notifications
You must be signed in to change notification settings - Fork 432
Prerequisites for tiny_array/shape PR #403
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
Conversation
On this subject, so far we have advertised @JohanMabille do you have an opinion on this? |
|
Quick nitpicking on the formatting.
|
Fixed.
Fixed (however, you added too many whitespaces for the settings of my editor, so the macro formatting was actually broken on my screen :-)
I'd like to insist on these. Sinces namespace closings and General comment: My experience with VIGRA suggests that overly strict formatting requirements are not worth the trouble. For example: there will never be agreement about where the opening brace |
This raises a general question: if a function can be applied both element-wise and globally (such as |
Yeah, the xproperty case is a bit extreme. A long as the trailing backslashes are far enough that we can read the code as regular C++ without extra visual things, it is good!
No problem with the closing comments.
Actually, I use the In general I prefer having a uniform convention across a project, and this is the one that we picked here... |
note on this: in xtensor and numpy, things like |
To clarify: |
Gotcha |
According to this goal, my convention is preferrable: |
|
Having the same conventions everywhere in the code is also helpful when you start using linters and some tools performing static code analysis. I agree with @ukoethe on the closing comments. About the norm, I think that before C++11 the If I'm not mistaken, there is no That said, if we decide to change the implementation of |
Excellent point |
Agreed, it's definitely preferrable. But given the complexity of advanced C++, most VIGRA contributions had far more pressing issues than formatting, so I eventually relaxed... |
That is what I propose. We can also consider Renaming |
|
Speaking of linters: I think codacy's complaint is a false positive. The value parameter is used in line 220. |
|
Indeed. Can you try with constexpr instead of const ? (not sure that would fix the problem but it is worth giving it a try) |
|
The more I think about the naming of the norm function, the more I like the convention |
Didn't work either. Moreover, it doesn't feel right to implement workarounds for a broken sanity checker, one should probably file a bug report with the Codacy team. |
|
I agree. We don't need to pass every codacy checks to merge PRs but it has proven useful sometimes to detect legitimate issues. |
|
Although what can be constexpr should probably be constexpr (even if it does not fix the warning). |
|
I revised the PR and replaced |
|
|
The following comment by @wolfv got lost:
|
|
I vote for not using the name Regarding the current definition of So, making the type of norm explicit in the function name seems to be the cleanest solution. |
SylvainCorlay
left a comment
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.
A few inline comments and the following:
-
the new
xt::iscloseadded in this PR conflicts with the one in xmath.hpp. Note the xtensor universal functions accept both expressions and scalars, by wrapping scalars into a cheapxscalarexpression. So this is a bug. Maybe a better approach would be to modify the existing implementation to handle infinities like the one you are proposing. -
This comment also applies to a lot of the new scalar operators such as
dotwhich is coming up when we add the tensor dots. In general, I think that we should rather add new universal functions than scalar specific things. -
The
promote_tseems to be justified by the difference of behavior wrtstd::common_typefor integer types and the return type of arithmetic operations. In terms of coding style, we should probabably follow STL style, i.e.promote_type/promote_type_t, move it to xutils, and make it variadic likestd::common_type. -
A lot of what is in xconcepts does not really concern concepts. The new type traits should probably be in xutils.
-
There are some coding style points: macros should always be capitalized, some namespaces are not indented, and we have a format for section comments,
********************************************
* one space on each side and no empty line *
********************************************- We were looking at this together with Johan in the early afternoon and are a bit confused with the raison d'être of the norm traits, and guess you plan on using it for future development.
include/xtensor/xconcepts.hpp
Outdated
| template <bool CONCEPTS> | ||
| using concept_check = typename std::enable_if<CONCEPTS, require_ok>::type; | ||
|
|
||
| /** @brief Concept checking macro (more redable than sfinae). |
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.
redable -> readable
include/xtensor/xconcepts.hpp
Outdated
| For example, it tells the user that <tt>unsigned char + unsigned char => int</tt>. | ||
| */ | ||
| template <class T1, class T2 = T1> | ||
| using promote_t = decltype(*(std::decay_t<T1>*)0 + *(std::decay_t<T2>*)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.
Difference with std::common_type seems to justify separate type
- move to
xutils/xtl? - use naming convention
promote_type/promote_type_t? - variadic version?
include/xtensor/xconcepts.hpp
Outdated
| template<class T> | ||
| struct squared_norm_traits; | ||
|
|
||
| namespace concepts_detail { |
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.
formatting
include/xtensor/xexception.hpp
Outdated
| #define XTENSOR_ASSERT_MSG(PREDICATE, MESSAGE) | ||
| #endif | ||
|
|
||
| #define xtensor_precondition(PREDICATE, MESSAGE) \ |
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.
Capitalize macro
test/test_xexception.cpp
Outdated
| EXPECT_TRUE(0 == expected.compare(message.substr(0,expected.size()))); | ||
| } | ||
| } | ||
| } // namespace xt No newline at end of file |
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.
newline
include/xtensor/xmathutil.hpp
Outdated
|
|
||
| // scalar dot is needed for generic functions that should work with | ||
| // scalars and vectors alike | ||
| #define XTENSOR_DEFINE_SCALAR_DOT(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 would conflict with a generic xt::dot that takes expressions. Indeed, we wrap scalars into cheap xexpressions with xscalar. I don't think we need to add operators on scalars.
include/xtensor/xmathutil.hpp
Outdated
|
|
||
| /**********************************************************/ | ||
| /* */ | ||
| /* sq() */ |
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.
sqr?
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'd like to keep an edit distance of 2 to sqrt(). sqr() looks too much like 'did you mean sqrt()?'
| EXPECT_EQ(norm_sq(-2.5), 6.25); | ||
|
|
||
| std::complex<double> c{ 2.0, 3.0 }; | ||
| // EXPECT_EQ(norm_sq(c), 13.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.
outstanding debug code?
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 strange beast I have to dig into: The test failed on some versions of gcc with the error message 'expected 13, got 13', i.e. the typical error when two reals are off in the last bit. However, this should never happen in an integer expression like 2.0*2.0 + 3.0*3.0. I'm wondering if there is a bug in std::norm()?
include/xtensor/xconcepts.hpp
Outdated
|
|
||
| namespace concepts_detail { | ||
|
|
||
| template <class T, bool scalar = std::is_arithmetic<T>::value> |
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.
indenting in namespaces.
| namespace cmath | ||
| { | ||
|
|
||
| using std::abs; |
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, these are already available in the xmath namespace in the form of universal functions.
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 intention of this namespace is different: I wanted to put the standard algebraic functions into a namespace of their own, so that one can use the idiom
using namespace cmath;
auto x = sqrt(y);to have the compiler perform both argument dependent lookup and lookup in namespace std (if y is a scalar). Without namespace cmath, one would have to write using std::sqrt; (for every function one wants to use) or using namespace std; (possibly importing functions one didn't want to import).
The main purpose of promote and norm traits is to prevent overflow in array expressions, and to make this intention explicit. The following scenarios are common examples in image processing: xarray<uint8_t> a = ..., b = ...; // uint8_t is a popular value_type for images
xarray<promote_t<uint8_t>> c = a + b;If xarray<uint8_t>::shape_type shape { 1000, 1000, 1000}; // not an uncommon size nowadays
xarray<uint8_t> a(shape);
... // fill the array
norm_sq_t<uint8_t> squared_norm = norm_sq(a); Since the square of an 8-bit number needs 16 bits, and there are 2^30 pixels, the squared norm type must have at least 46 bits, i.e. it must be I'm aware that these problems can be solved in several ways, but I like the possibility to make the desired promotions explicit (remember the Zen of Python :-). |
I agree that |
|
We understand the need for Just that part can deserve a PR :) |
|
Regarding So xutils is a good place for these to land. |
I didn't realize that scalars are valid arguments to your implementation, because the documentation of
I had indeed addressed this bug in my original PR #374, but forgot about it when splitting up that PR. In fact, I'm concerned about xtensor defining xexpression templates with universal template arguments like this: template <class E1, class E2>
inline auto isclose(E1&& e1, E2&& e2, double rtol = 1e-05, double atol = 1e-08, bool equal_nan = false) noexceptIn my experience, declarations of the form Universal templates must be constrained by a concept declaration. In PR #374, I had changed the signature of template <class E1, class E2,
XTENSOR_REQUIRE<xexpression_concept<E1>::value || xexpression_concept<E2>::value> >
inline auto isclose(E1&& e1, E2&& e2, double rtol = 1e-05, double atol = 1e-08, bool equal_nan = false) noexceptand I strongly recommend to adopt a convention like this for all xexpression functions. (I'm willing to implement it if you agree to the change.) |
|
Regarding the existing
using std::abs; // or: using namespace xt::cmath;
return abs(a - b) ...;
I'm still not convined that functions should only be implemented as xexpressions. A botton-up design (implement the function for scalars first, then call the scalar version in the array expressions, just as you do for sqrt() etc.) would probably be more familiar for most users and thus more readable and easier to debug. I suggest to keep the scalar |
|
When an xexpression function should also match scalar arguments, I suggest to add the concept check: template <class E,
XTENSOR_REQUIRE<xexpression_concept<E>::value || std::is_arithmetic<E>::value>> |
|
Regarding All ufuncs accept scalars, so changing this would be a very major change to do for all ufuncs. Maybe an |
Here is an argument for changing the behavior nonetheless: I disabled my EXPECT_FALSE(isclose(numeric_constants<>::PI, 3.141));However, the test didn't compile because the ufunc's type is EXPECT_FALSE(eval(isclose(numeric_constants<>::PI, 3.141)));didn't work either because the result type is now EXPECT_FALSE(eval(isclose(numeric_constants<>::PI, 3.141))[0]);worked, but is really ugly and probably very slow due to array creation. I really don't see why ufuncs and lazy evaluation should be used for function calls that involve only scalar arguments. |
|
... just realized that the correct call is EXPECT_FALSE(isclose(numeric_constants<>::PI, 3.141)[0]);but this is still ugly. |
|
You should also be able to do EXPECT_FALSE(isclose(numeric_constants<>::PI, 3.141)());which is slightly less ugly maybe. |
ufuncs return xexpressions, even in the 0-D case.
|
|
Ok, |
Unacceptable is a strong statement! Universal functions can only return xexpressions even when it is 0-D since dimension is only know upon evaluation. It is a runtime attribute. So all ufuncs return (unevaluated) expressions. So as you said, ufuncs cannot be seen as overloads of scalar functions. |
Sorry for the wording.
I fully accept this, but it misses the point. I just suggested to implement scalar functions as normal scalar functions and leave xexpressions to array arithmetic. I really didn't expect that this statement would be controversial. |
no problem
I think we should open a discussion issue on the behavior of ufuncs. Maybe a solution would be
In any case, I think this deserves its own issue. (i.e. behavior of ufuncs with scalar inputs) |
Yes.
This could be done, but requires significant metaprogramming on the xexpression's result type, when a straightforward function implementation would be sufficient. According to my VIGRA experience, that's the kind of cleverness my users don't like at all (and I fell into this trap several times). The Zen of Python again: Explicit is better than implicit. Note that there will be no code duplication, because the xexpressions just call the corresponding scalar functions. |
|
I think this can be done with a reasonable amount of code in xscalar, but it is also likely to be very subtle. xscalar and xfunctions are proven difficult beasts, so think would rather have a well-scoped discussion for this. |
See #412 |
|
Closing the megathread as we are handling this in multiple PRs! |
This PR adds two files and corresponding tests:
xconcepts.hpp: concept checking macroXTENSOR_REQUIRE, some traits classesxmathutil.hpp: namespacext::cmath, additional algebraic functionsIt also extends
xexception.hppwith two new assertion macros and movesnumeric_constantsfromxmath.hpptoxmathutil.hpp.The most controversial aspect of the PR is probably the
norm()function. It actually returns the norm, whereasstd::norm()computes the squared norm. IMHO, this decision of the C++ standard makes no sense at all. Nonetheless, xtensor reproduces this behavior in its xexpressions, and one can argue that consistency with the C++ standard is more important than meeting the user's intuitions about a function's effect. What's your opinion? If you want me to rename mynorm(), what's a sensible name?