I am working with NumSharp and a bit new to it. I would like to set multiple data values and there are two main functions of concern:
NDArray.SetValue(object value, param int[] indices)
NDArray.SetData(object value, param int[] indices)
The first one SetValue expects full index of a single value element, for example, if you have np.zeros(3,3) it expects full indexing, and if there is missing index it will use 0 in its place.
For example:
var arr = np.zeros(3,3);
arr.SetValue(10.0, 0);
arr.SetValue(10.0, 0, 0);
Both lines are equivalent.
On the other hand, SetData will modify an entire sub-array based on the index, and if you give full index, it works like SetValue. For example:
arr.SetData(10.0, 0); // sets all arr[0] values to 10
arr.SetData(10.0, 0, 1); // sets arr[0][1] to 10
Now, for my use case, I would like to redefine a function that accepts multiple values and treats each one as a sub-array index.
arr.SetMultiData(10.0, 0, 1); /// sets arr[0] and arr[10] values to 10
Naturally, I went for an extension method:
public static class Extensions
{
public static void SetMultiData(this NDArray arr, object value, int[] indices)
{
foreach (var i in indices)
arr.SetData(value, i);
}
}
But here is what completely lost me. This code, which does not use extension method, works:
static void Main(string[] args)
{
NDArray arr = np.zeros(3, 3);
int[] indices = new int[] { 0, 1 };
double value = 10;
foreach (var i in indices)
arr.SetData(value, i);
Console.WriteLine(arr.ToString());
}
Output:
[[10, 10, 10],[10, 10, 10],[0, 0, 0]]
This code doesn't:
static void Main(string[] args)
{
NDArray arr = np.zeros(3, 3);
int[] indices = new int[] { 0, 1 };
double value = 10;
arr.SetMultiData(value, indices);
Console.WriteLine(arr.ToString());
}
Output:
[[10, 0, 0],[0, 0, 0],[0, 0, 0]]
This isn't always going to print, in most cases it just fails to print and I believe the underlying arr structure is corrupted accordingly, which leads to undefined behavior when I run it multiple times.
Now, the last bit. When I change the Extension method to accept double instead of object, it works perfectly. But I need the extension method to work with more than one type given it is just a wrapper around SetData. Given this, I also tried changing the method to be generic:
public static class Extensions
{
public static void SetMultiData<T>(this NDArray arr, object value, int[] indices)
{
T val = (T)value;
foreach (var i in indices)
arr.SetData(val, i);
}
}
I was shocked when this didn't work and produced the same output. Basically, I made the explicit cast from object to type. And I called the function like this: arr.SetMultiData<double>(value, indicies); and I used the debugger and compared the code that worked with this and the types are exactly the same ones that are passed to arr.SetValue given i = int and val = double, yet one works and one doesn't.
What causes such behavior? And why does it work when I explicitly set it to double when it fails both using generic/object types?
For reference, here is the SetData implementation. which is open-source.
The reason is
doubleis implicitly convertible toNDArray(by implicit operator defined inNDArray). So when you do:and
valueis of typedouble- the overload being called isSetData(NDArray, int[), because it's the best fit given thatdoubleis convertible toNDArray.However when
valueis of typeobjector even of unresolved generic type (T) - then overload being called isSetData(object, int[]), so things go wild (because those overloads do quite different things IF value you pass as object is not NDArray, and it's not - it is double). Overload is chosen at compile time, and there is no way compiler can choose theNDArrayoverload even in generic case (since overload chosen should fit for any possible generic types you can pass).In your case you can "solve" this by using
NDArray valuein your extension method instead of object or generic: