From e1c65d46b79ae6252afa89c2187340b47034363a Mon Sep 17 00:00:00 2001 From: Roy Sprowl Date: Tue, 16 Jul 2019 23:25:02 -0700 Subject: [PATCH] Fix NDArray.mgrid and add unit tests. Also corrects the Object comparer for NDArray to compare shape and data contents. --- src/NumSharp.Core/Creation/NdArray.Mgrid.cs | 18 ++--- .../Exceptions/IncorrectShapeException.cs | 13 ++- .../Operations/Elementwise/NDArray.Equals.cs | 47 ++++++++++- .../Creation/NdArray.Mgrid.Test.cs | 80 ++++++++++++++++--- .../Utilities/ndarray-generator.py | 34 ++++++++ 5 files changed, 165 insertions(+), 27 deletions(-) create mode 100644 test/NumSharp.UnitTest/Utilities/ndarray-generator.py diff --git a/src/NumSharp.Core/Creation/NdArray.Mgrid.cs b/src/NumSharp.Core/Creation/NdArray.Mgrid.cs index 735bd88c..8e645089 100644 --- a/src/NumSharp.Core/Creation/NdArray.Mgrid.cs +++ b/src/NumSharp.Core/Creation/NdArray.Mgrid.cs @@ -1,4 +1,4 @@ -using System; +using System; using NumSharp; namespace NumSharp @@ -8,7 +8,7 @@ public partial class NDArray public (NDArray,NDArray) mgrid(NDArray nd2) { if( !(this.ndim == 1 || nd2.ndim == 1)) - throw new IncorrectShapeException(); + throw new IncorrectShapeException("mgrid is implemented only for two single dimension arrays"); Array nd1Data = this.Storage.GetData(); Array nd2Data = nd2.Storage.GetData(); @@ -22,21 +22,17 @@ public partial class NDArray Array res2Arr = res2.Storage.GetData(); int counter = 0; - - for (int idx = 0; idx < nd2Data.Length; idx++) + for (int row = 0; row < nd1Data.Length; row++) { - for (int jdx = 0; jdx < nd1Data.Length; jdx++) + for (int col = 0; col < nd2Data.Length; col++) { - res1Arr.SetValue(nd1Data.GetValue(idx),counter); - res2Arr.SetValue(nd2Data.GetValue(idx),counter); + res1Arr.SetValue(nd1Data.GetValue(row), counter); + res2Arr.SetValue(nd2Data.GetValue(col),counter); counter++; } } - return (res1,res2); } - } - -} \ No newline at end of file +} diff --git a/src/NumSharp.Core/Exceptions/IncorrectShapeException.cs b/src/NumSharp.Core/Exceptions/IncorrectShapeException.cs index c0e39775..be12b95b 100644 --- a/src/NumSharp.Core/Exceptions/IncorrectShapeException.cs +++ b/src/NumSharp.Core/Exceptions/IncorrectShapeException.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace NumSharp { @@ -6,7 +6,12 @@ class IncorrectShapeException : System.Exception { public IncorrectShapeException() : base("This method does not work with this shape or was not already implemented.") { - + + } + + public IncorrectShapeException(string msg) : base(msg) + { + } - } -} \ No newline at end of file + } +} diff --git a/src/NumSharp.Core/Operations/Elementwise/NDArray.Equals.cs b/src/NumSharp.Core/Operations/Elementwise/NDArray.Equals.cs index cff04f53..ba0023d6 100644 --- a/src/NumSharp.Core/Operations/Elementwise/NDArray.Equals.cs +++ b/src/NumSharp.Core/Operations/Elementwise/NDArray.Equals.cs @@ -20,14 +20,55 @@ public override bool Equals(object obj) { case NDArray safeCastObj: { + // We aren't using array_equal here because it assumes that both arrays have + // non-null Storage data and shapes. var thatData = safeCastObj.Storage?.GetData(); - if (thatData == null) + var thisData = this.Storage?.GetData(); + if ((thatData == null && thisData != null) ||(thatData != null && thisData == null)) { return false; } + if (thisData != null) + { + if (thatData != null) + { + // Compare array contents, which is clumsy since we don't know the element type + if (thisData.Length == thatData.Length) + { + for(int i = 0; i < thisData.Length; i++) + { + if (!thisData.GetValue(i).Equals(thatData.GetValue(i))) + { + return false; + } + } + } + else + { + return false; + } + } + else + { + return false; + } + } + else + { + if (thatData != null) + { + return false; + } + } - var thisData = this.Storage?.GetData(); - return thisData == thatData && safeCastObj.shape == this.shape; + if (this.shape != null && safeCastObj.shape != null) + { + return this.shape.SequenceEqual(safeCastObj.shape); + } + else + { + return this.shape == safeCastObj.shape; + } } case int val: return Data(0) == val; diff --git a/test/NumSharp.UnitTest/Creation/NdArray.Mgrid.Test.cs b/test/NumSharp.UnitTest/Creation/NdArray.Mgrid.Test.cs index 41986cf4..d9c34c63 100644 --- a/test/NumSharp.UnitTest/Creation/NdArray.Mgrid.Test.cs +++ b/test/NumSharp.UnitTest/Creation/NdArray.Mgrid.Test.cs @@ -1,27 +1,89 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Text; using NumSharp.Extensions; using System.Linq; using NumSharp; +using System.Numerics; namespace NumSharp.UnitTest.Creation { [TestClass] public class NdArrayMGridTest { + // These C# NDArray declarations were generated using ndarray-generatory.py, + // which is located in the README.md of this NumSharp.UnitTest project + // using the following Python code: + /* + aa, bb = np.mgrid[0:5, 0:3] + cc, dd = np.mgrid[0:3, 0:5] + ee, ff = np.mgrid[0:5, 0:5] + cSharp.asCode2D("a53", aa) + cSharp.asCode2D("b53", bb) + cSharp.asCode2D("a35", cc) + cSharp.asCode2D("b35", dd) + cSharp.asCode2D("a55", ee) + cSharp.asCode2D("b55", ff) + */ + + static NDArray a53 = new NDArray(new Int32[] { + 0, 0, 0, + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, + 4, 4, 4 + }, new Shape(new int[] { 5, 3 })); + + static NDArray b53 = new NDArray(new Int32[] { + 0, 1, 2, + 0, 1, 2, + 0, 1, 2, + 0, 1, 2, + 0, 1, 2 + }, new Shape(new int[] { 5, 3 })); + + static NDArray a35 = new NDArray(new Int32[] { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2 + }, new Shape(new int[] { 3, 5 })); + + static NDArray b35 = new NDArray(new Int32[] { + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4 + }, new Shape(new int[] { 3, 5 })); + + static NDArray a55 = new NDArray(new Int32[] { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4 + }, new Shape(new int[] { 5, 5 })); + + static NDArray b55 = new NDArray(new Int32[] { + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4 + }, new Shape(new int[] { 5, 5 })); + [TestMethod] public void BaseTest() { - var X = np.arange(1, 11, 2).mgrid(np.arange(-12, -3, 3)); - - NDArray x = X.Item1; - NDArray y = X.Item2; + var V53 = np.arange(0, 5, 1).mgrid(np.arange(0, 3, 1)); + var V35 = np.arange(0, 3, 1).mgrid(np.arange(0, 5, 1)); + var V55 = np.arange(0, 5, 1).mgrid(np.arange(0, 5, 1)); - + Assert.AreEqual(V53.Item1, a53); + Assert.AreEqual(V53.Item2, b53); + Assert.AreEqual(V35.Item1, a35); + Assert.AreEqual(V35.Item2, b35); + Assert.AreEqual(V55.Item1, a55); + Assert.AreEqual(V55.Item2, b55); } - } - -} \ No newline at end of file +} diff --git a/test/NumSharp.UnitTest/Utilities/ndarray-generator.py b/test/NumSharp.UnitTest/Utilities/ndarray-generator.py new file mode 100644 index 00000000..01d1db93 --- /dev/null +++ b/test/NumSharp.UnitTest/Utilities/ndarray-generator.py @@ -0,0 +1,34 @@ +import math +import numpy as np + +# The asCode2D function generates NDArray declarations in C# for use in unit testing. +# This avoids some of the tedium and errors of hand-generation. +# For example, calling the function like this generates C# static variables named +# 'a53' and 'b53' from numpy's mgrid: +# aa, bb = np.mgrid[0:5, 0:3] +# cSharp.asCode2D("a53", aa) +# cSharp.asCode2D("b53", bb) + + +class cSharp: + def asCode2D(varName, v): + if v.dtype.name == "int32": + vType = "Int32" + elif v.dtype.name == "float64": + vType = "double" + else: + vType = "unknown" + print(" static NDArray {0} = new NDArray(new {1}[] {{".format(varName, vType)) + valstr = "" + commasToPrint = v.shape[0] * v.shape[1] - 1 + for i, row in enumerate(v): + rowStr = " " + for j, item in enumerate(row): + rowStr = rowStr + "{}".format(item) + if commasToPrint > 0: + rowStr = rowStr + ", " + commasToPrint -= 1 + #if (i < v) + print(rowStr) + print(" }}, new Shape(new int[] {{ {}, {} }}));".format(v.shape[0], v.shape[1])) + print("")