Skip to content

Commit b6f99b9

Browse files
authored
Support NumPy 2.5.0 (#2308)
1 parent ea5016d commit b6f99b9

11 files changed

Lines changed: 67 additions & 55 deletions

File tree

.github/workflows/daily-test-build-numpy.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ jobs:
2828
- { python-version: "3.10", numpy-version: "1.25.2" }
2929
- { python-version: "3.10", numpy-version: "2.2.6" }
3030
- { python-version: "3.11", numpy-version: "1.25.2" }
31-
- { python-version: "3.11", numpy-version: "2.3.4" }
31+
- { python-version: "3.11", numpy-version: "2.4.6" }
3232
- { python-version: "3.12", numpy-version: "1.26.4" }
33-
- { python-version: "3.12", numpy-version: "2.3.4" }
33+
- { python-version: "3.12", numpy-version: "2.5.0" }
3434
- { python-version: "3.13", numpy-version: "2.1.3" }
35-
- { python-version: "3.13", numpy-version: "2.3.4" }
36-
- { python-version: "3.14", numpy-version: "2.3.4" }
35+
- { python-version: "3.13", numpy-version: "2.5.0" }
36+
- { python-version: "3.14", numpy-version: "2.3.5" }
37+
- { python-version: "3.14", numpy-version: "2.5.0" }
3738
fail-fast: false
3839
env:
3940
TILEDB_VERSION: ${{ inputs.libtiledb_version }}

tiledb/array.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,9 +1187,7 @@ def set_query(self, serialized_query):
11871187

11881188
out = OrderedDict()
11891189
for name in results.keys():
1190-
arr = results[name][0]
1191-
arr.dtype = q.buffer_dtype(name)
1192-
out[name] = arr
1190+
out[name] = results[name][0].view(q.buffer_dtype(name))
11931191
return out
11941192

11951193
# pickling support: this is a lightweight pickle for distributed use.

tiledb/core.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,8 +1447,14 @@ class PyQuery {
14471447
auto arr = py::array(py::dtype("int8"), size, data_ptr);
14481448
o = py::memoryview(arr);
14491449
} else {
1450-
o = py::array(py::dtype("uint8"), size, data_ptr);
1451-
o.attr("dtype") = dtype;
1450+
// `size` is the cell's byte length and must be a whole number
1451+
// of dtype elements; a partial element means corrupt offsets.
1452+
if (size % dtype.itemsize() != 0) {
1453+
TPY_ERROR_LOC(
1454+
"internal error: buffer size is not a multiple of the "
1455+
"attribute itemsize");
1456+
}
1457+
o = py::array(dtype, size / dtype.itemsize(), data_ptr);
14521458
}
14531459

14541460
result_p[i - 1] = o;

tiledb/dataframe_.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,13 @@ def create_dim(dtype, values, full_domain, tile, **kwargs):
372372
if np.issubdtype(dtype, np.datetime64):
373373
date_unit = np.datetime_data(dtype)[0]
374374
dim_min = np.datetime64(dtype_min, date_unit)
375-
tile_max = np.iinfo(np.uint64).max - tile
376-
if np.uint64(dtype_max - dtype_min) > tile_max:
377-
dim_max = np.datetime64(dtype_max - tile, date_unit)
375+
# Subtract in Python ints; a full-domain datetime64 difference
376+
# overflows int64.
377+
dtype_min_i = int(dtype_min.astype(np.int64))
378+
dtype_max_i = int(dtype_max.astype(np.int64))
379+
tile_max = int(np.iinfo(np.uint64).max) - int(tile)
380+
if (dtype_max_i - dtype_min_i) > tile_max:
381+
dim_max = np.datetime64(dtype_max_i - int(tile), date_unit)
378382
elif np.issubdtype(dtype, np.integer):
379383
tile_max = np.iinfo(np.uint64).max - tile
380384
if np.uint64(dtype_max - dtype_min) > tile_max:
@@ -383,9 +387,14 @@ def create_dim(dtype, values, full_domain, tile, **kwargs):
383387
dim_min, dim_max = None, None
384388

385389
# TODO: simplify this logic and/or move to DataType.cast_tile_extent
386-
if np.issubdtype(dtype, np.integer) or np.issubdtype(dtype, np.datetime64):
390+
if np.issubdtype(dtype, np.integer):
387391
# we can't make a tile larger than the dimension range or lower than 1
388392
tile = max(1, min(tile, 1 + np.uint64(dim_max - dim_min)))
393+
elif np.issubdtype(dtype, np.datetime64):
394+
# Clamp as for integers, computing the range in Python ints since a
395+
# full-domain datetime64 difference overflows int64.
396+
dim_range = int(dim_max.astype(np.int64)) - int(dim_min.astype(np.int64))
397+
tile = max(1, min(tile, 1 + dim_range))
389398
elif np.issubdtype(dtype, np.floating):
390399
# this difference can be inf
391400
with np.errstate(over="ignore"):

tiledb/dense_array.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def _read_dense_subarray(
345345
# Note: fixed blobs are always 1 byte per cell
346346
arr = arr.view("S1")
347347
else:
348-
arr.dtype = dtype
348+
arr = arr.view(dtype)
349349
if len(arr) == 0:
350350
# special case: the C API returns 0 len for blank arrays
351351
arr = np.zeros(output_shape, dtype=dtype)
@@ -357,13 +357,11 @@ def _read_dense_subarray(
357357
)
358358

359359
if layout == lt.LayoutType.ROW_MAJOR:
360-
arr.shape = output_shape
361-
arr = np.require(arr, requirements="C")
360+
arr = np.require(arr.reshape(output_shape), requirements="C")
362361
elif layout == lt.LayoutType.COL_MAJOR:
363-
arr.shape = output_shape
364-
arr = np.require(arr, requirements="F")
362+
arr = np.require(arr.reshape(output_shape), requirements="F")
365363
else:
366-
arr.shape = np.prod(output_shape)
364+
arr = arr.reshape(np.prod(output_shape))
367365

368366
out[name] = arr
369367

@@ -837,13 +835,12 @@ def read_subarray(self, subarray):
837835
if len(item[1]) > 0:
838836
arr = pyquery.unpack_buffer(name, item[0], item[1])
839837
else:
840-
arr = item[0]
841-
arr.dtype = (
838+
arr = item[0].view(
842839
self.schema.attr_or_dim_dtype(name)
843840
if not self.schema.has_dim_label(name)
844841
else self.schema.dim_label(name).dtype
845842
)
846-
arr.shape = result_shape
843+
arr = arr.reshape(result_shape)
847844
result_dict[name if name != "__attr" else ""] = arr
848845

849846
return result_dict

tiledb/multirange_indexing.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def _run_query(self) -> Dict[str, np.ndarray]:
409409
for name, arr in result_dict.items():
410410
# TODO check/test layout
411411
if not self.array.schema.has_dim_label(name):
412-
arr.shape = self.result_shape
412+
result_dict[name] = arr.reshape(self.result_shape)
413413
return result_dict
414414

415415

@@ -825,8 +825,7 @@ def _get_pyquery_results(pyquery: PyQuery, array: Array) -> Dict[str, np.ndarray
825825
if len(item[1]) > 0:
826826
arr = pyquery.unpack_buffer(name, item[0], item[1])
827827
else:
828-
arr = item[0]
829-
arr.dtype = (
828+
arr = item[0].view(
830829
schema.attr_or_dim_dtype(name)
831830
if not schema.has_dim_label(name)
832831
else schema.dim_label(name).dtype

tiledb/npbuffer.cc

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -471,22 +471,18 @@ class NumpyConvert {
471471
NumpyConvert(py::array input) {
472472
// require a flat buffer
473473
if (input.ndim() != 1) {
474-
// try to take a 1D view on the input
475-
auto v = input.attr("view")();
476-
// this will throw if the shape cannot be modified zero-copy,
477-
// which is what we want
478-
try {
479-
v.attr("shape") = py::int_(input.size());
480-
} catch (py::error_already_set& e) {
481-
if (e.matches(PyExc_AttributeError)) {
482-
use_iter_ = true;
483-
} else {
484-
throw;
485-
}
486-
} catch (std::exception& e) {
487-
std::cout << e.what() << std::endl;
474+
// reshape(-1) returns a zero-copy view when the array can be
475+
// flattened in place; the bulk paths need that view, so use it
476+
// when the buffer address is unchanged. Otherwise the array is
477+
// not contiguous: discard the copy and iterate the original
478+
// element-wise instead.
479+
py::array flat = input.attr("reshape")(py::int_(-1));
480+
if (flat.data() == input.data()) {
481+
input_ = flat;
482+
} else {
483+
use_iter_ = true;
484+
input_ = input;
488485
}
489-
input_ = v;
490486
} else {
491487
input_ = input;
492488
}

tiledb/sparse_array.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,7 @@ def read_subarray(self, subarray):
439439
if len(item[1]) > 0:
440440
arr = pyquery.unpack_buffer(name, item[0], item[1])
441441
else:
442-
arr = item[0]
443-
arr.dtype = (
442+
arr = item[0].view(
444443
self.schema.attr_or_dim_dtype(name)
445444
if not self.schema.has_dim_label(name)
446445
else self.schema.dim_label(name).dtype
@@ -597,8 +596,7 @@ def _read_sparse_subarray(self, subarray, attr_names: list, cond, layout):
597596
if len(results[name][1]) > 0: # note: len(offsets) > 0
598597
arr = q.unpack_buffer(name, results[name][0], results[name][1])
599598
else:
600-
arr = results[name][0]
601-
arr.dtype = self.schema.attr_or_dim_dtype(name)
599+
arr = results[name][0].view(self.schema.attr_or_dim_dtype(name))
602600
out[final_name] = arr
603601
else:
604602
arr = results[name][0]
@@ -623,8 +621,7 @@ def _read_sparse_subarray(self, subarray, attr_names: list, cond, layout):
623621
elif el_dtype == np.dtype("U0"):
624622
out[final_name] = ""
625623
else:
626-
arr.dtype = el_dtype
627-
out[final_name] = arr
624+
out[final_name] = arr.view(el_dtype)
628625

629626
if self.schema.has_attr(final_name) and self.attr(final_name).isnullable:
630627
out[final_name] = np.ma.array(

tiledb/tests/test_core.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ def test_pyquery_basic(self):
3737
subarray.add_ranges([[(0, 3)]])
3838
q2.set_subarray(subarray)
3939
q2.submit()
40-
res = q2.results()[""][0]
41-
res.dtype = np.double
40+
res = q2.results()[""][0].view(np.double)
4241
assert_array_equal(res, a[:])
4342

4443
def test_pyquery_init(self):

tiledb/tests/test_libtiledb.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,7 +2697,11 @@ def test_dense_datetime_vector(self):
26972697
(np.datetime64("2010-11-01") - start) / np.timedelta64(1, "D")
26982698
)
26992699
read_ndays = int(
2700-
(np.datetime64("2011-01-31") - np.datetime64("2010-11-01") + 1)
2700+
(
2701+
np.datetime64("2011-01-31")
2702+
- np.datetime64("2010-11-01")
2703+
+ np.timedelta64(1, "D")
2704+
)
27012705
/ np.timedelta64(1, "D")
27022706
)
27032707
expected = a1_vals[read_offset : read_offset + read_ndays]
@@ -2712,7 +2716,11 @@ def test_dense_datetime_vector(self):
27122716
(np.datetime64("2010-01-01") - start) / np.timedelta64(1, "D")
27132717
)
27142718
read_ndays = int(
2715-
(np.datetime64("2011-01-01") - np.datetime64("2010-01-01") + 1)
2719+
(
2720+
np.datetime64("2011-01-01")
2721+
- np.datetime64("2010-01-01")
2722+
+ np.timedelta64(1, "D")
2723+
)
27162724
/ np.timedelta64(1, "D")
27172725
)
27182726
expected = a1_vals[read_offset : read_offset + read_ndays]
@@ -2725,7 +2733,11 @@ def test_dense_datetime_vector(self):
27252733
(np.datetime64("2010-01-01") - start) / np.timedelta64(1, "D")
27262734
)
27272735
read_ndays = int(
2728-
(np.datetime64("2011-01-31") - np.datetime64("2010-01-01") + 1)
2736+
(
2737+
np.datetime64("2011-01-31")
2738+
- np.datetime64("2010-01-01")
2739+
+ np.timedelta64(1, "D")
2740+
)
27292741
/ np.timedelta64(1, "D")
27302742
)
27312743
expected = a1_vals[read_offset : read_offset + read_ndays]

0 commit comments

Comments
 (0)