Support indexing with any type which implmented __index__ (#3210)
This commit is contained in:
+45
-23
@@ -15,8 +15,30 @@ bool is_none_slice(const nb::slice& in_slice) {
|
||||
nb::getattr(in_slice, "step").is_none());
|
||||
}
|
||||
|
||||
bool is_index_scalar(const nb::object& obj) {
|
||||
if (nb::isinstance<nb::bool_>(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!PyIndex_Check(obj.ptr())) {
|
||||
return false;
|
||||
}
|
||||
// Exclude multi-dimensional arrays (mx.array, np.ndarray) by checking ndim
|
||||
if (nb::hasattr(obj, "ndim")) {
|
||||
auto ndim = nb::getattr(obj, "ndim");
|
||||
if (nb::isinstance<nb::int_>(ndim) && nb::cast<int>(ndim) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int safe_to_int32(nb::object obj) {
|
||||
auto val = nb::cast<int64_t>(nb::cast<nb::int_>(obj));
|
||||
auto idx = nb::steal<nb::object>(PyNumber_Index(obj.ptr()));
|
||||
if (!idx.is_valid()) {
|
||||
throw nb::python_error();
|
||||
}
|
||||
|
||||
auto val = nb::cast<int64_t>(nb::cast<nb::int_>(idx));
|
||||
if (val > INT32_MAX || val < INT32_MIN) {
|
||||
throw std::invalid_argument("Slice indices must be 32-bit integers.");
|
||||
}
|
||||
@@ -25,7 +47,7 @@ int safe_to_int32(nb::object obj) {
|
||||
|
||||
int get_slice_int(nb::object obj, int default_val) {
|
||||
if (!obj.is_none()) {
|
||||
if (!nb::isinstance<nb::int_>(obj)) {
|
||||
if (!is_index_scalar(obj)) {
|
||||
throw std::invalid_argument("Slice indices must be integers or None.");
|
||||
}
|
||||
return safe_to_int32(obj);
|
||||
@@ -60,7 +82,7 @@ mx::array get_int_index(nb::object idx, int axis_size) {
|
||||
}
|
||||
|
||||
bool is_valid_index_type(const nb::object& obj) {
|
||||
return nb::isinstance<nb::slice>(obj) || nb::isinstance<nb::int_>(obj) ||
|
||||
return nb::isinstance<nb::slice>(obj) || is_index_scalar(obj) ||
|
||||
nb::isinstance<mx::array>(obj) || obj.is_none() ||
|
||||
nb::ellipsis().is(obj) || nb::isinstance<nb::list>(obj);
|
||||
}
|
||||
@@ -102,7 +124,7 @@ mx::array mlx_get_item_array(const mx::array& src, const mx::array& indices) {
|
||||
return take(src, indices, 0);
|
||||
}
|
||||
|
||||
mx::array mlx_get_item_int(const mx::array& src, const nb::int_& idx) {
|
||||
mx::array mlx_get_item_int(const mx::array& src, const nb::object& idx) {
|
||||
// Check input and raise error if 0 dim for parity with np
|
||||
if (src.ndim() == 0) {
|
||||
throw std::invalid_argument(
|
||||
@@ -139,7 +161,7 @@ mx::array mlx_gather_nd(
|
||||
gather_indices.push_back(arange(start, end, stride, mx::uint32));
|
||||
num_slices++;
|
||||
is_slice[i] = true;
|
||||
} else if (nb::isinstance<nb::int_>(idx)) {
|
||||
} else if (is_index_scalar(idx)) {
|
||||
gather_indices.push_back(get_int_index(idx, src.shape(i)));
|
||||
} else if (nb::isinstance<mx::array>(idx)) {
|
||||
auto arr = nb::cast<mx::array>(idx);
|
||||
@@ -289,7 +311,7 @@ mx::array mlx_get_item_nd(mx::array src, const nb::tuple& entries) {
|
||||
bool have_non_array = false;
|
||||
bool gather_first = false;
|
||||
for (auto& idx : indices) {
|
||||
if (nb::isinstance<mx::array>(idx) || (nb::isinstance<nb::int_>(idx))) {
|
||||
if (nb::isinstance<mx::array>(idx) || is_index_scalar(idx)) {
|
||||
if (have_array && have_non_array) {
|
||||
gather_first = true;
|
||||
break;
|
||||
@@ -312,7 +334,7 @@ mx::array mlx_get_item_nd(mx::array src, const nb::tuple& entries) {
|
||||
// Then find the last array
|
||||
for (last_array = indices.size() - 1; last_array >= 0; last_array--) {
|
||||
auto& idx = indices[last_array];
|
||||
if (nb::isinstance<mx::array>(idx) || nb::isinstance<nb::int_>(idx)) {
|
||||
if (nb::isinstance<mx::array>(idx) || is_index_scalar(idx)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -348,7 +370,7 @@ mx::array mlx_get_item_nd(mx::array src, const nb::tuple& entries) {
|
||||
} else {
|
||||
for (int i = 0; i < indices.size(); i++) {
|
||||
auto& idx = indices[i];
|
||||
if (nb::isinstance<mx::array>(idx) || nb::isinstance<nb::int_>(idx)) {
|
||||
if (nb::isinstance<mx::array>(idx) || is_index_scalar(idx)) {
|
||||
break;
|
||||
} else if (idx.is_none()) {
|
||||
remaining_indices.push_back(idx);
|
||||
@@ -385,8 +407,8 @@ mx::array mlx_get_item_nd(mx::array src, const nb::tuple& entries) {
|
||||
int axis = 0;
|
||||
for (auto& idx : remaining_indices) {
|
||||
if (!idx.is_none()) {
|
||||
if (!have_array && nb::isinstance<nb::int_>(idx)) {
|
||||
int st = nb::cast<int>(idx);
|
||||
if (!have_array && is_index_scalar(idx)) {
|
||||
int st = safe_to_int32(idx);
|
||||
st = (st < 0) ? st + src.shape(axis) : st;
|
||||
|
||||
starts[axis] = st;
|
||||
@@ -419,7 +441,7 @@ mx::array mlx_get_item_nd(mx::array src, const nb::tuple& entries) {
|
||||
auto& idx = remaining_indices[axis];
|
||||
if (unsqueeze_needed && idx.is_none()) {
|
||||
unsqueeze_axes.push_back(axis - squeeze_axes.size());
|
||||
} else if (squeeze_needed && nb::isinstance<nb::int_>(idx)) {
|
||||
} else if (squeeze_needed && is_index_scalar(idx)) {
|
||||
squeeze_axes.push_back(axis - unsqueeze_axes.size());
|
||||
}
|
||||
}
|
||||
@@ -439,8 +461,8 @@ mx::array mlx_get_item(const mx::array& src, const nb::object& obj) {
|
||||
return mlx_get_item_slice(src, nb::cast<nb::slice>(obj));
|
||||
} else if (nb::isinstance<mx::array>(obj)) {
|
||||
return mlx_get_item_array(src, nb::cast<mx::array>(obj));
|
||||
} else if (nb::isinstance<nb::int_>(obj)) {
|
||||
return mlx_get_item_int(src, nb::cast<nb::int_>(obj));
|
||||
} else if (is_index_scalar(obj)) {
|
||||
return mlx_get_item_int(src, obj);
|
||||
} else if (nb::isinstance<nb::tuple>(obj)) {
|
||||
return mlx_get_item_nd(src, nb::cast<nb::tuple>(obj));
|
||||
} else if (nb::isinstance<nb::ellipsis>(obj)) {
|
||||
@@ -457,7 +479,7 @@ mx::array mlx_get_item(const mx::array& src, const nb::object& obj) {
|
||||
std::tuple<std::vector<mx::array>, mx::array, std::vector<int>>
|
||||
mlx_scatter_args_int(
|
||||
const mx::array& src,
|
||||
const nb::int_& idx,
|
||||
const nb::object& idx,
|
||||
const mx::array& update) {
|
||||
if (src.ndim() == 0) {
|
||||
throw std::invalid_argument(
|
||||
@@ -685,7 +707,7 @@ mlx_scatter_args_nd(
|
||||
// Add the shape to the update
|
||||
update_shape[ax - 1] = 1;
|
||||
}
|
||||
} else if (nb::isinstance<nb::int_>(pyidx)) {
|
||||
} else if (is_index_scalar(pyidx)) {
|
||||
// Add index to arrays
|
||||
arr_indices.push_back(get_int_index(pyidx, src.shape(ax++)));
|
||||
// Add the shape to the update
|
||||
@@ -755,8 +777,8 @@ mlx_compute_scatter_args(
|
||||
return mlx_scatter_args_slice(src, nb::cast<nb::slice>(obj), vals);
|
||||
} else if (nb::isinstance<mx::array>(obj)) {
|
||||
return mlx_scatter_args_array(src, nb::cast<mx::array>(obj), vals);
|
||||
} else if (nb::isinstance<nb::int_>(obj)) {
|
||||
return mlx_scatter_args_int(src, nb::cast<nb::int_>(obj), vals);
|
||||
} else if (is_index_scalar(obj)) {
|
||||
return mlx_scatter_args_int(src, obj, vals);
|
||||
} else if (nb::isinstance<nb::tuple>(obj)) {
|
||||
return mlx_scatter_args_nd(src, nb::cast<nb::tuple>(obj), vals);
|
||||
} else if (obj.is_none()) {
|
||||
@@ -780,9 +802,9 @@ mlx_compute_slice_update_args(
|
||||
mx::Shape strides(src.ndim(), 1);
|
||||
|
||||
// Can't route to slice update if not slice, tuple, or int
|
||||
if (src.ndim() == 0 || nb::isinstance<nb::bool_>(obj) ||
|
||||
if (src.ndim() == 0 ||
|
||||
(!nb::isinstance<nb::slice>(obj) && !nb::isinstance<nb::tuple>(obj) &&
|
||||
!nb::isinstance<nb::int_>(obj))) {
|
||||
!is_index_scalar(obj))) {
|
||||
return std::make_tuple(
|
||||
std::nullopt, std::move(starts), std::move(stops), std::move(strides));
|
||||
}
|
||||
@@ -816,13 +838,13 @@ mlx_compute_slice_update_args(
|
||||
update = mx::squeeze(update, squeeze_axes);
|
||||
|
||||
// Single int then make it a slice of size 1
|
||||
if (nb::isinstance<nb::int_>(obj)) {
|
||||
if (is_index_scalar(obj)) {
|
||||
if (src.ndim() < 1) {
|
||||
std::ostringstream msg;
|
||||
msg << "Too many indices for array with " << src.ndim() << " dimensions.";
|
||||
throw std::invalid_argument(msg.str());
|
||||
}
|
||||
auto idx = nb::cast<int>(obj);
|
||||
auto idx = safe_to_int32(obj);
|
||||
idx = idx < 0 ? idx + stops[0] : idx;
|
||||
starts[0] = idx;
|
||||
stops[0] = idx + 1;
|
||||
@@ -884,8 +906,8 @@ mlx_compute_slice_update_args(
|
||||
src.shape(ax));
|
||||
ax--;
|
||||
upd_ax--;
|
||||
} else if (nb::isinstance<nb::int_>(pyidx)) {
|
||||
int st = nb::cast<int>(pyidx);
|
||||
} else if (is_index_scalar(pyidx)) {
|
||||
int st = safe_to_int32(pyidx);
|
||||
st = (st < 0) ? st + src.shape(i) : st;
|
||||
starts[ax] = st;
|
||||
stops[ax] = st + 1;
|
||||
|
||||
Reference in New Issue
Block a user