-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
ENH/API: Change flexible types to indicate resizability with elsize == -1 #8970
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
I think this all makes sense to me. I never paid as close attention to the cases of using |
977268a
to
0d96866
Compare
numpy/core/src/multiarray/ctors.c
Outdated
return NULL; | ||
} | ||
PyTuple_SET_ITEM(tuple, 0, type); | ||
PyTuple_SET_ITEM(tuple, 1, one); |
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 should be just Py_BuildValue('(Oi)', type, 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.
Yeah, I did it this way for speed - perhaps premature optimization
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.
type needs casting to PyObject then.
The error checking above, while it won#t happen in practice may throw errors with static reference checker
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.
also for speed PyTuple_Pack
is usually good
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 catch. Think I might go for Py_BuildValue
anyway here, since creating zeros of string type doesn't sound like something that should be a bottleneck
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 be surprised if what I have here is slower than PyTuple_Pack
, since that at best does a loop with these lines 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.
PyTuple_Pack
is (marginally) slower than directly setting a few entries but faster than Py_BuildValue
if you already have objects.
Here it would be useful to save some lines of code, this doesn't seem to be a performance relevant path to me.
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.
Alright, changed to use Py_BuildValue("Ni", (PyObject*)type, 1)
doc/release/1.13.0-notes.rst
Outdated
``PyArray_Descr.elsize`` is now ``-1`` for unsized flexible dtypes | ||
------------------------------------------------------------------ | ||
Previously it was ``0`` - but that made it impossible to distinguish unsized | ||
from sized-to-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.
adding and example and dtypes is better, e.g. unsized dtypes (``S``) from sized-to-zero dtypes (``S0``)
it should also mention that the python-side returns None for unsized dtypes.
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 too breaking a change? Any C code that constructs flexible dtypes manually is in for a bad time - but hopefully nothing ever should, since PyArray_DescrFromTypeObject(Py_String)
etc already do the right things
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.
yes it previously returned a number now it doesn't. Likely not a big deal in practice but it should be mentioned.
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.
Sorry, I mean from the C api perspective. But yes, I agree it at the very least needs to be in the release notes
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.
Yes it has the potential to break others code that assumes itemsize is always positive.
But at least it should be an easy to detect error as it will probably crash by trying to allocate 2GiB of memory.
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 was thinking code that assumed setting elsize
to 0
would allow it to resize would be surprised when it no longer does
numpy/core/src/multiarray/ctors.c
Outdated
PyTuple_SET_ITEM(tuple, 0, type); | ||
PyTuple_SET_ITEM(tuple, 1, one); | ||
|
||
status = PyArray_DescrConverter(tuple, &type); |
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 work with overlapping input and output?
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.
These don't overlap - tuple
contains a copy of the value in type
, not a pointer to the variable &type
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.
really? I though setitem will just increase the reference count of type by 1 not actually make a copy of it.
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.
SET_ITEM
actually doesn't touch the reference count at all - it steals the reference.
But SetItem
copies type
but increases the reference count of *type
. We would be free to set type = NULL
after adding it to the tuple, and the tuple would still contain the right thing
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.
yes but PyArray_DescrConverter would still be working inplace, its output type is the same object as its input type. It probably works fine but that should be checked.
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 it isn't, because PyArray_DescrConverter
does essentially *(&type) = CreateNewDescr
. It never touches the original object - because normally that original object is NULL
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.
if it does this it doesn't work because creating a new type into type
overwrites the input before it is read.
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.
oh it does work as its &type
not type
never mind then
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 it doesn't, because the input is no longer the type
variable - it's the copy of the pointer the type
variable held, which is stored inside tuple. So for example
type = (PyObject *) 0x00001230
Tuple_SETITEM(tuple, 0, type);
Tuple_GETITEM(tuple, 0); // returns (PyObject *) 0x00001230
type = NULL
Tuple_GETITEM(tuple, 0); // still returns (PyObject *) 0x00001230 - we didn't store a pointer to type
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.
looks good with some minor comments
Any suggestions for what to call |
hm it seems to crash astropys testsuite which would make it a too invasive change for now. |
Which part causes the crash? It might be that we can just warn and keep the old behaviour in those places |
no clue, astropys test system is not exactly easy to use |
Comments addressed, hopefully. @mhvk: Mind taking a look at the astropy failure, and seeing whether it can be worked around here? |
seems to be division by 0 in |
Nice catch! There are more of those elsewhere too... |
If you feel it's too complicated please open an issue on the astropy tracker or on the astropy-dev mailing list. It would be definetly helpful to get this kind of feedback - at least I would like to know what makes it "not easy to use". :) Sorry for being off-topic here. |
@MSeifert04 |
@eric-wieser - I cannot seem to install this branch:
|
@juliantaylor - astropy uses
|
@mhvk: fixed. Note that this last commit is fixing bugs that were already present, just really hard to come across before this changeset |
numpy/core/src/multiarray/ctors.c
Outdated
/* | ||
Grow PyArray_DATA(ret): | ||
this is similar for the strategy for PyListObject, but we use | ||
50% overallocation => 0, 4, 8, 14, 23, 36, 56, 86 ... | ||
*/ | ||
elcount = (i >> 1) + (i < 4 ? 4 : 2) + i; | ||
if (elcount <= NPY_MAX_INTP/elsize) { | ||
new_data = PyDataMem_RENEW(PyArray_DATA(ret), elcount * elsize); | ||
if (!npy_mul_with_overflow_intp(elcount, elsize, &nbytes)) { |
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.
your mul_with_overflow are wrong, its *result, a, b
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.
Alarming that runtest.py -b
on windows does not warn me about that
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.
our travis warning check catches it
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.
Indeed, but that's pretty late in the pipeline, and it runs a bunch of other tests first
14e5c4e
to
9f746e6
Compare
(rebased on top of #8977) |
☔ The latest upstream changes (presumably #8971) made this pull request unmergeable. Please resolve the merge conflicts. |
This allows us to change how flexible types with no length are represented in future, to allow zero-size dtypes (numpy#8970).
(Rebased on top of #9953) |
additional rebase needed |
This allows empty strings to be unambigiously specified. Unsized strings continue to promote to single-character strings.
This argument is no longer used
d9b2d23
to
bc393a8
Compare
This means that zero-size strings/unicode/void are now properly supported - in that
S0
,V0
, andU0
always mean zero-size, not resizable. Follows from #6430, cc @embraySome changes resulting from this:
np.dtype('S')
andnp.dtype('S0')
now mean different things - "resizable" and "empty"np.dtype(x).itemsize
isNone
whenx
is:str
,bytes
,unicode
,np.void
,'S'
,'U'
,'V'
Previously, it was
0
np.dtype([('a', float), ('b', str)])
is now aValueError
Previously, it implied
np.dtype([('a', float), ('b', str, 0)])
, with the last field as size0
, likely masking bugs. This explicit form works without errors- removed since this change gains nothingnp.empty(10, str)
now has dtypeS0
, notS1
.We could be a little stricter here, and make it a
ValueError
as aboveFor the last two, we could take a more gradual deprecation path if necessary, keeping the existing behaviour but with a
FutureWarning
It's probably worth adding a#define PyDescr_Resizable -1
somewhere, and using that everywhere - but I'm sure of the best name, nor the best file to put it in