rust-ndarray / ndarray

ndarray: an N-dimensional array with array views, multidimensional slicing, and efficient operations
https://docs.rs/ndarray/
Apache License 2.0
3.61k stars 306 forks source link

`arr0` is able to create arrays with multiple dimensions #1253

Closed nbro closed 1 year ago

nbro commented 1 year ago

Similarly to https://github.com/rust-ndarray/ndarray/issues/1252, it seems that we can use the function arr0 to create arrays that actually have more dimensions. See the example below.

use ndarray::arr0;

fn main() {
    let a = arr0([0, 2]);
    println!("0-d array with shape {:?} is {:?}", a.shape(), a);

    let a = arr0([[0], [2]]);
    println!("0-d array with shape {:?} is {:?}", a.shape(), a);
}

The shape is [], but the array is displayed as higher-dimensional array, so I suppose that, under the hood, it's not actually a 0-d array (a number). In fact, looking at the implementation of arr0, it seems it just calls vec![], which of course may create any array. So, basically, not checks are performed on the dimensions and type of x. Looking at the documentation of from_shape_vec_unchecked (which is called from arr0), it states

Unsafe because dimension and strides are unchecked.

This is ok. However, if arr0 is supposed to be unsafe or the code above is supposed to be "fine" (which, in my view, should not), this should be documented at least in the arr0 page. I am happy to contribute to the documentation. It's possible that this issue also happens with all other similar functions arr1, arr2 and arr3 - I didn't check that yet.

LazaroHurtado commented 1 year ago

Just to add my opinion on this. The arr0 function explicitly states the dimensionality of the return type, which although the stride and ndim is accurate the contents of the array is not. Under the hood, it is treated as a 0-d array since it doesn't allow indexing and there are no strides, exactly how a scalar should be treated. So, the handling and usage of this function is left to the end user.

If some extra functionality were to be added to catch mismatched arguments to any of the arr[0,1,2,3] functions how would you go about ensuring there is exactly that amount of nesting occurring within the vec? As you mentioned a similar error occurs when calling array! with an array of any dimension D > 3, this is due to the macro only having 3 branches, which would also benefit from the hypothetical logic.

Overall, I think the best approach would be updating the implementation to take care of this edge case, if possible. The documentation already specifies the dimensions of the returned array for each function (and macro) so I don't see how much value a sentence like "This function will accept any argument but the dimension will still be 0" will add.

bluss commented 1 year ago

The arr0 function always creates an Array with ndarray dimensionality type Ix0, which in ndarray's type system is zero-dimensional. Just call .ndim() to verify on any such array. Thus this is not a bug. We can nest "plain rust arrays" inside ndarrays, that's confusing, but it is this way because ndarray is general: it can store values of any type.