Any consideration to support using Range for Slicing?
JackTheSpades opened this issue · 5 comments
Range being the C# notation of start..end to with start being inclusive and end exclusive.
Range/Index also support backwards indexing with the ^ prefix.
I've quickly cobbled together a proof of concept.
It has no way of supporting step width so not quite as flexible as slices / python-notation but the fact that it has actual build in language support and allows the usage of variables more directly, I think, makes up for it.
using NumpyDotNet;
ndarray A = np.arange(0.0, 120.0).reshape([2, 3, 4, 5]);
ndArrayWrapper wA = new(A);
Console.WriteLine(wA[0, 0, 0, 0]); // first element
Console.WriteLine(wA[^1, ^1, ^1, ^1]); // last element
Console.WriteLine(wA[.., 0, 0, 0]); // first slice (2,)
Console.WriteLine(wA[.., 1, .., 1..4]); // mixed slice (2,4,4)
int a = 1, b = 3;
Console.WriteLine(wA[..a, a..b, .., b..]); // support variables
// wrapper class for ndarray to have a custom index
class ndArrayWrapper(ndarray A)
{
public object this[params object[] indices]
{
get
{
if (A.ndim != indices.Length)
throw new ArgumentException();
var dims = A.dims;
var slices = indices.Select((my, i) =>
{
if (my is int it)
return (object)it;
if (my is Index idx)
return (object)idx.GetOffset((int)dims[i]);
else if (my is Range ran)
return (object)new Slice(
ran.Start.GetOffset((int)dims[i]),
ran.End.GetOffset((int)dims[i]),
1);
else
throw new Exception();
}).ToArray();
return A[slices];
}
}
}I think I looked at this feature a few years ago. What I recall is that the slices are "local" only. In other words, if you take a slice of something, you can't pass that slice to another function. I may not be remembering it exactly right but there was some limitation like that.
If I did accept this as some sort of additional way to specify slice, how do I pass it to the lower layers of code which need to operate on the sliced view? Does the framework allow me to detect that what the start and end indexes are?
What happens if you try to apply this Range syntax to a ndarray that is already numpy sliced? As you may know, the original array is untouched in a slice view. All we really do is build a data structure that allows numpy code to walk the specified slice values. It seems like lots of chances for the .net range to mess up if the array is also sliced.
I don't know about the internal handling, like if it can be further optimized but if you look at my cobbled together example, I just convert the Index and Range parameters into int and the build-in Slice objects.
So really, anything that works in the examples listed in the readme should work with Range and Index. I tried this with the same wrapper class as above and it seems to work just fine:
var all = np.arange(0, 10);
var allWrapper = new ndArrayWrapper(all);
Console.WriteLine(all);
var slice1 = (ndarray)allWrapper[2..^2];
Console.WriteLine(slice1);
var slice1Wrapper = new ndArrayWrapper(slice1);
var slice2 = (ndarray)slice1Wrapper[2..^2];
Console.WriteLine(slice2);
all[5] = -5;
Console.WriteLine(all);
Console.WriteLine(slice1);
Console.WriteLine(slice2);Which outputs the following:
shape=(10,), INT32
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
shape=(6,), INT32
{ 2, 3, 4, 5, 6, 7 }
shape=(2,), INT32
{ 4, 5 }
shape=(10,), INT32
{ 0, 1, 2, 3, 4, -5, 6, 7, 8, 9 }
shape=(6,), INT32
{ 2, 3, 4, -5, 6, 7 }
shape=(2,), INT32
{ 4, -5 }
Slices are local in the sense that if you apply them to a normal C# array, they will create a copy. See this example:
int[] arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
int[] arr2 = arr[2..^2];
Console.WriteLine(string.Join(",", arr)); // 0,1,2,3,4,5,6,7,8,9
Console.WriteLine(string.Join(",", arr2)); // 2,3,4,5,6,7
arr[5] = -5;
Console.WriteLine(string.Join(",", arr)); // 0,1,2,3,4,-5,6,7,8,9
Console.WriteLine(string.Join(",", arr2)); // 2,3,4,5,6,7However, Range and Index are just that... indices. They just use the length (see my usage of dims) to calculate the int based index. If you have a custom class that provides a Length or Count property with an this[int index] getter, then C# will automatically support usage of Index and Range... but if you just handle them yourself you can do whatever you want with the information.
I will take a closer look at this. thanks.
I can see how this could add some value.
However, I think this language feature requires upgrading the toolset to a new compiler/.net standard version.
I am not sure I am ready to do that as that might break existing users.
I almost want to include your code as some sort of extension or sample code for those applications using the new versions of .NET that can make use of the range syntax.
I am thinking I could add your code to the install package as a wrapper class and update the documentation to call it out.
Do you have any other suggestions?