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 double
is implicitly convertible to NDArray
(by implicit operator defined in NDArray
). So when you do:
arr.SetData(value, i);
and value
is of type double
- the overload being called is SetData(NDArray, int[)
, because it's the best fit given that double
is convertible to NDArray
.
However when value
is of type object
or even of unresolved generic type (T
) - then overload being called is SetData(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 the NDArray
overload 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 value
in your extension method instead of object or generic:
public static class Extensions
{
public static void SetMultiData(this NDArray arr, NDArray value, int[] indices)
{
foreach (var i in indices)
arr.SetData(value, i);
}
}