-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
numpy: Provide concrete size aliases, test equivalence for dtype(...).num
#1329
Conversation
Patch between running diff --git a/tmp/a.txt b/tmp/b.txt
index cda8c6e..23fd223 100644
--- a/tmp/a.txt
+++ b/tmp/b.txt
@@ -18,9 +18,7 @@ test_numpy_array.py <DtypeSizeCheck name='short' size_cpp=2 size_numpy=2 dtype=i
<DtypeCheck numpy=int32 pybind11=int32>
<DtypeCheck numpy=uint32 pybind11=uint32>
<DtypeCheck numpy=int64 pybind11=int64>
-NOTE: typenum mismatch for <DtypeCheck numpy=int64 pybind11=int64>: 7 != 9
<DtypeCheck numpy=uint64 pybind11=uint64>
-NOTE: typenum mismatch for <DtypeCheck numpy=uint64 pybind11=uint64>: 8 != 10
.................................
-========================== 33 passed in 0.08 seconds ===========================
+========================== 33 passed in 0.07 seconds =========================== |
return (sizeof(Concrete) == sizeof(Check1)) ? check1 : | ||
(sizeof(Concrete) == sizeof(Check2)) ? check2 : | ||
(sizeof(Concrete) == sizeof(Check3)) ? check3 : -1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a template that makes this a little more generic, along with a compile-time check for the error case (rather than returning -1). It also requires passing the arguments as an { ... }
array because that seemed simpler.
using namespace pybind11::detail;
template <typename T> struct same_size { template <typename U> using as = bool_constant<sizeof(T) == sizeof(U)>; };
template <typename Concrete, typename... Check>
constexpr int platform_lookup(std::array<int, sizeof...(Check)> codes) {
using code_index = std::integral_constant<int, constexpr_first<same_size<Concrete>::template as, Check...>()>;
static_assert(code_index::value != sizeof...(Check), "Unable to match type on this platform");
return codes[code_index::value];
}
int main() {
// fails (short not matched):
//int bar = platform_lookup<short, int, unsigned, unsigned long, int>({4, 9, 11, 13});
int foo = platform_lookup<std::uint32_t, unsigned int, unsigned long>({8,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.
Unfortunately, C++11 doesn't like multiple statements in the function - it deems it non-constexpr
:(
Will briefly tinker with it to see if I can add some indirection to preserve the constexpr
-ness.
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.
Hup, was wrong: Just needed to make the array<>
argument const array<>
.
Thanks!
include/pybind11/numpy.h
Outdated
NPY_USHORT_, NPY_UINT_, NPY_ULONG_), | ||
NPY_INT64_ = platform_lookup<int64_t, int, long, long long>( | ||
NPY_INT_, NPY_LONG_, NPY_LONGLONG_), | ||
NPY_UINT64_ = platform_lookup<uint64_t, uint, unsigned long, unsigned long long>( |
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.
uint
isn't a standard C or C++ type (even if commonly available); that should be unsigned int
, or if you're feeling lazy, just unsigned
(though some people strongly dislike the latter).
The int32_t
etc. types are more portable, but should still technically be prefixed with std::
(we should also have a #include <cstdint>
in here to properly bring them in).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like uint
isn't available on MSVC.
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.
Done. Thanks!
It looks like the i386 tests broke, as well (one of the tests is coming up with a 4 instead of an 8). |
Reverted a portion of my change to see if this is something I introduced vs. something to be fixed. The discrepancies here between the numpy size (4) and its dtype (
EDIT: Realized that I'm using |
TravisCI passed, but now AppVeyor seems to have failed due to some flakes? Failure mode A:
Failure mode B:
Should I just close + re-open the PR to retrigger the builds? |
I found the easiest thing was to set up a free appveyor account and attach it to your own repository; then you can start, restart, etc. builds as required. I'm not exactly sure what we need to do to fix Failure A, but that certainly isn't caused by your PR. Failure B is more serious: it looks like MSVC 2015 hates my template generalization (though 2017 seems fine with it). You could try this version: template <int, typename...> struct matches_sizeof_impl : std::integral_constant<int, -1> {};
template <int N, typename T, typename A, typename... B>
struct matches_sizeof_impl<N, T, A, B...> : std::integral_constant<int,
sizeof(T) == sizeof(A) ? N : matches_sizeof_impl<N+1, T, B...>::value> {};
template <typename T, typename... A> using matches_sizeof_index = matches_sizeof_impl<0, T, A...>;
template <typename Concrete, typename... Check, typename code_index = matches_sizeof_index<Concrete, Check...>>
constexpr int platform_lookup(const std::array<int, sizeof...(Check)> &codes) {
static_assert(code_index::value >= 0, "Unable to match type on this platform");
return codes[code_index::value];
} It's getting a bit messier, but at least it keeps that compilation failure if the type isn't found. |
Here's a better implementation; this makes template <int N, bool... Bool> struct first_true_impl : std::integral_constant<int, -1> {};
template <int N, bool B, bool... BMore> struct first_true_impl<N, B, BMore...> :
std::integral_constant<int, B ? N : first_true_impl<N+1, BMore...>::value> {};
/// Takes a parameter pack of bool values, return the index of the first one that is true or -1 if
/// none of the template arguments are true.
template <bool... B> using first_true_index = first_true_impl<0, B...>;
template <typename Concrete, typename... Check, typename code_index = first_true_index<sizeof(Concrete) == sizeof(Check)...>>
constexpr int platform_lookup(const std::array<int, sizeof...(Check)> &codes) {
static_assert(code_index::value >= 0, "Unable to match type on this platform");
return codes[code_index::value];
} |
Done - sorry about the delay! |
9630d04
to
cd496ce
Compare
Just rebased - sorry for the drawn out delay! Actually ended up needing this, as implicit conversions for |
@EricCousineau-TRI, great work! I've run into this issue recently. I got into that by passing pybind int64_t array into OpenCV functions. Without this fix you'll get "data type = 9 is not supported" Exception. Any plans on merging this soon? |
Lemme see if I can fix these pesky MSVC issues... EDIT: Rebased (just to see if it was fixed by other things); EDIT 2: Looks like the original bits failed, but only with VS 2015. Trying the different approach. |
… do not exactly match dtype(...).num
28fadb3
to
64e896a
Compare
Test for dtype checks now succeed without warnings
64e896a
to
8f28b8e
Compare
K, finally found the magic incantation to make MSVC happy. @jagerman Might you have a chance to give this another glance? |
Hup! It looks like AppVeyor is happy now, huzzah! ( @jagerman or @wjakob Can I ask if this might be good to merge? |
This looks good to me -- merged! :) |
Awesome, thanks! |
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.
npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_, | ||
npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually correct? Normally, it should be the other way around, int16
, int32
, and int64
being aliases, and l
should mean long
, q
longlong
, ...?
See e.g.:
- https://numpy.org/doc/stable/reference/arrays.scalars.html#built-in-scalar-types
- https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/include/numpy/npy_common.h#L545
- https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/include/numpy/ndarraytypes.h#L121
- https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/code_generators/generate_umath.py#L204
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 point! I've (explicitly) cross-ref'd your mention in the issue:
#1908 (comment)
The dumb question is, what's the correct testing fix? (it's been a while since I wrote this...)
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 don't know. Still trying to figure out. I actually came across this by accident and wanted to make sure I'm correct, before starting to dig further into this! :-)
To make things more complex, I just found this as well:
>>> np.dtype('i1')
dtype('int8')
>>> np.dtype('i2')
dtype('int16')
>>> np.dtype('i4')
dtype('int32')
>>> np.dtype('i8')
dtype('int64')
>>> np.dtype('u8')
dtype('uint64')
But yes, maybe let's discuss further in #1908 (though I'm not yet convinced that that one is actually an issue); easier to have things grouped in one place.
Closes #1328
See the linked issue for context.
Note that this is split into two commits:
numpy.h
)NPY_INT32
). This solves an issue wherepy::dtype::of<int64_t>().attr("num")
did not match to NumPy's definition ofNPY_INT64
.TBH, I can understand some hesitation for incorporating the second commit. However, I would like to see the first commit make it in - took a bit to understand this issue and debug.
Todo: