From c01832a64c90142f99288ae07ed0f3dbe19ca93f Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sun, 17 Dec 2023 13:16:03 -0600 Subject: [PATCH 1/3] (Logics):add high performance logical AND function with axis and keepdim support also test Upgrade to .NET 8. --- src/NumSharp.Core/Logic/np.all.cs | 141 +++++++++++++++--- .../Logic/np_all_axis_Test.cs | 82 ++++++++++ 2 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 test/NumSharp.UnitTest/Logic/np_all_axis_Test.cs diff --git a/src/NumSharp.Core/Logic/np.all.cs b/src/NumSharp.Core/Logic/np.all.cs index 59111e9c..d3eea431 100644 --- a/src/NumSharp.Core/Logic/np.all.cs +++ b/src/NumSharp.Core/Logic/np.all.cs @@ -26,23 +26,23 @@ public static bool all(NDArray a) #else #region Compute - switch (a.typecode) - { - case NPTypeCode.Boolean: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Byte: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Int16: return _all_linear(a.MakeGeneric()); - case NPTypeCode.UInt16: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Int32: return _all_linear(a.MakeGeneric()); - case NPTypeCode.UInt32: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Int64: return _all_linear(a.MakeGeneric()); - case NPTypeCode.UInt64: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Char: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Double: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Single: return _all_linear(a.MakeGeneric()); - case NPTypeCode.Decimal: return _all_linear(a.MakeGeneric()); - default: - throw new NotSupportedException(); - } + switch (a.typecode) + { + case NPTypeCode.Boolean: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Byte: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Int16: return _all_linear(a.MakeGeneric()); + case NPTypeCode.UInt16: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Int32: return _all_linear(a.MakeGeneric()); + case NPTypeCode.UInt32: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Int64: return _all_linear(a.MakeGeneric()); + case NPTypeCode.UInt64: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Char: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Double: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Single: return _all_linear(a.MakeGeneric()); + case NPTypeCode.Decimal: return _all_linear(a.MakeGeneric()); + default: + throw new NotSupportedException(); + } #endregion #endif } @@ -51,12 +51,113 @@ public static bool all(NDArray a) /// Test whether all array elements along a given axis evaluate to True. /// /// Input array or object that can be converted to an array. - /// Axis or axes along which a logical OR reduction is performed. The default (axis = None) is to perform a logical OR over all the dimensions of the input array. axis may be negative, in which case it counts from the last to the first axis. + /// Axis or axes along which a logical AND reduction is performed. The default (axis = None) is to perform a logical OR over all the dimensions of the input array. axis may be negative, in which case it counts from the last to the first axis. /// A new boolean or ndarray is returned unless out is specified, in which case a reference to out is returned. /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.all.html - public static NDArray all(NDArray nd, int axis) + public static NDArray all(NDArray nd, int axis, bool keepdims = false) { - throw new NotImplementedException(); //TODO + if (axis < 0) + axis = nd.ndim + axis; + if (axis < 0 || axis >= nd.ndim) + { + throw new ArgumentOutOfRangeException(nameof(axis)); + } + if (nd.ndim == 0) + { + throw new ArgumentException("Can't operate with zero array"); + } + if (nd == null) + { + throw new ArgumentException("Can't operate with null array"); + } + + int[] inputShape = nd.shape; + int[] outputShape = new int[keepdims ? inputShape.Length : inputShape.Length - 1]; + int outputIndex = 0; + for (int i = 0; i < inputShape.Length; i++) + { + if (i != axis) + { + outputShape[outputIndex++] = inputShape[i]; + } + else if (keepdims) + { + outputShape[outputIndex++] = 1; // 保留轴,但长度为1 + } + } + + NDArray resultArray = (NDArray)zeros(outputShape); + Span resultSpan = resultArray.GetData().AsSpan(); + + int axisSize = inputShape[axis]; + + // It help to build an index + int preAxisStride = 1; + for (int i = 0; i < axis; i++) + { + preAxisStride *= inputShape[i]; + } + + int postAxisStride = 1; + for (int i = axis + 1; i < inputShape.Length; i++) + { + postAxisStride *= inputShape[i]; + } + + + // Operate different logic by TypeCode + bool computationSuccess = false; + switch (nd.typecode) + { + case NPTypeCode.Boolean: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Byte: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int16: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt16: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int32: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt32: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int64: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt64: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Char: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Double: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Single: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Decimal: computationSuccess = ComputeAllPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + default: + throw new NotSupportedException($"Type {nd.typecode} is not supported"); + } + + if (!computationSuccess) + { + throw new InvalidOperationException("Failed to compute all() along the specified axis"); + } + + return resultArray; + } + + private static bool ComputeAllPerAxis(NDArray nd, int axis, int preAxisStride, int postAxisStride, int axisSize, Span resultSpan) where T : unmanaged + { + Span inputSpan = nd.GetData().AsSpan(); + + + for (int o = 0; o < resultSpan.Length; o++) + { + int blockIndex = o / postAxisStride; + int inBlockIndex = o % postAxisStride; + int inputStartIndex = blockIndex * axisSize * postAxisStride + inBlockIndex; + + bool currentResult = true; + for (int a = 0; a < axisSize; a++) + { + int inputIndex = inputStartIndex + a * postAxisStride; + if (inputSpan[inputIndex].Equals(default(T))) + { + currentResult = false; + break; + } + } + resultSpan[o] = currentResult; + } + + return true; } private static bool _all_linear(NDArray nd) where T : unmanaged diff --git a/test/NumSharp.UnitTest/Logic/np_all_axis_Test.cs b/test/NumSharp.UnitTest/Logic/np_all_axis_Test.cs new file mode 100644 index 00000000..2da7a608 --- /dev/null +++ b/test/NumSharp.UnitTest/Logic/np_all_axis_Test.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp; + +namespace NumSharp.UnitTest.Logic +{ + [TestClass] + public class np_all_axis_Test + { + [TestMethod] + public void np_all_axis_2D() + { + // Test array: [[true, false, true], [true, true, true]] + var arr = np.array(new bool[,] { { true, false, true }, { true, true, true } }); + + // Test axis=0 (along columns): should be [true, false, true] (all in each column) + var result_axis0 = np.all(arr, axis: 0); + var expected_axis0 = np.array(new bool[] { true, false, true }); + Assert.IsTrue(np.array_equal(result_axis0, expected_axis0)); + + // Test axis=1 (along rows): should be [false, true] (all in each row) + var result_axis1 = np.all(arr, axis: 1); + var expected_axis1 = np.array(new bool[] { false, true }); + Assert.IsTrue(np.array_equal(result_axis1, expected_axis1)); + } + + [TestMethod] + public void np_all_axis_3D() + { + // Create a 3D array for testing + var arr = np.ones(new int[] { 2, 3, 4 }); // All ones (truthy) + arr[0, 1, 2] = 0; // Add one falsy value + + // Test different axes + var result_axis0 = np.all(arr, axis: 0); // Shape should be (3, 4) + Assert.AreEqual(2, result_axis0.ndim); + Assert.AreEqual(3, result_axis0.shape[0]); + Assert.AreEqual(4, result_axis0.shape[1]); + + var result_axis1 = np.all(arr, axis: 1); // Shape should be (2, 4) + Assert.AreEqual(2, result_axis1.ndim); + Assert.AreEqual(2, result_axis1.shape[0]); + Assert.AreEqual(4, result_axis1.shape[1]); + + var result_axis2 = np.all(arr, axis: 2); // Shape should be (2, 3) + Assert.AreEqual(2, result_axis2.ndim); + Assert.AreEqual(2, result_axis2.shape[0]); + Assert.AreEqual(3, result_axis2.shape[1]); + } + + [TestMethod] + public void np_all_keepdims() + { + var arr = np.array(new bool[,] { { true, false, true }, { true, true, true } }); + + // Test with keepdims=true + var result_keepdims = np.all(arr, axis: 0, keepdims: true); + Assert.AreEqual(2, result_keepdims.ndim); // Should maintain original number of dimensions + Assert.AreEqual(1, result_keepdims.shape[0]); // The reduced axis becomes size 1 + Assert.AreEqual(3, result_keepdims.shape[1]); // Other dimensions remain + + var result_keepdims1 = np.all(arr, axis: 1, keepdims: true); + Assert.AreEqual(2, result_keepdims1.ndim); // Should maintain original number of dimensions + Assert.AreEqual(2, result_keepdims1.shape[0]); // Other dimensions remain + Assert.AreEqual(1, result_keepdims1.shape[1]); // The reduced axis becomes size 1 + } + + [TestMethod] + public void np_all_different_types() + { + // Test with integer array + var int_arr = np.array(new int[,] { { 1, 2, 3 }, { 4, 0, 6 } }); // Contains a zero (falsy value) + var int_result = np.all(int_arr, axis: 1); + // First row: all non-zero -> true, Second row: contains zero -> false + Assert.AreEqual(true, int_result[0]); + Assert.AreEqual(false, int_result[1]); + } + } +} \ No newline at end of file From 4edf91d13fa4fe6ef1e90c0ef99449ce53cd47ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B8=85=E6=B3=89?= <2664542771@qq.com> Date: Sat, 29 Nov 2025 21:49:00 +0800 Subject: [PATCH 2/3] update np.any function and add py.complex function --- src/NumSharp.Core/Logic/np.all.cs | 2 +- src/NumSharp.Core/Logic/np.any.cs | 106 +++++++++++++++++++- src/NumSharp.Core/Utilities/ArrayConvert.cs | 15 ++- src/NumSharp.Core/Utilities/py.cs | 48 ++++++++- test/NumSharp.UnitTest/Utilities/pyTest.cs | 12 +++ 5 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 test/NumSharp.UnitTest/Utilities/pyTest.cs diff --git a/src/NumSharp.Core/Logic/np.all.cs b/src/NumSharp.Core/Logic/np.all.cs index d3eea431..7691b25a 100644 --- a/src/NumSharp.Core/Logic/np.all.cs +++ b/src/NumSharp.Core/Logic/np.all.cs @@ -82,7 +82,7 @@ public static NDArray all(NDArray nd, int axis, bool keepdims = false) } else if (keepdims) { - outputShape[outputIndex++] = 1; // 保留轴,但长度为1 + outputShape[outputIndex++] = 1; // keep axis but length is one. } } diff --git a/src/NumSharp.Core/Logic/np.any.cs b/src/NumSharp.Core/Logic/np.any.cs index 4dd393e4..3aa0a920 100644 --- a/src/NumSharp.Core/Logic/np.any.cs +++ b/src/NumSharp.Core/Logic/np.any.cs @@ -55,9 +55,111 @@ public static bool any(NDArray a) /// Axis or axes along which a logical OR reduction is performed. The default (axis = None) is to perform a logical OR over all the dimensions of the input array. axis may be negative, in which case it counts from the last to the first axis. /// A new boolean or ndarray is returned unless out is specified, in which case a reference to out is returned. /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.any.html - public static NDArray any(NDArray nd, int axis) + public static NDArray any(NDArray nd, int axis, bool keepdims) { - throw new NotImplementedException(); //TODO + if (axis < 0) + axis = nd.ndim + axis; + if (axis < 0 || axis >= nd.ndim) + { + throw new ArgumentOutOfRangeException(nameof(axis)); + } + if (nd.ndim == 0) + { + throw new ArgumentException("Can't operate with zero array"); + } + if (nd == null) + { + throw new ArgumentException("Can't operate with null array"); + } + + int[] inputShape = nd.shape; + int[] outputShape = new int[keepdims ? inputShape.Length : inputShape.Length - 1]; + int outputIndex = 0; + for (int i = 0; i < inputShape.Length; i++) + { + if (i != axis) + { + outputShape[outputIndex++] = inputShape[i]; + } + else if (keepdims) + { + outputShape[outputIndex++] = 1; // keep axis but length is one. + } + } + + NDArray resultArray = (NDArray)zeros(outputShape); + Span resultSpan = resultArray.GetData().AsSpan(); + + int axisSize = inputShape[axis]; + + // It help to build an index + int preAxisStride = 1; + for (int i = 0; i < axis; i++) + { + preAxisStride *= inputShape[i]; + } + + int postAxisStride = 1; + for (int i = axis + 1; i < inputShape.Length; i++) + { + postAxisStride *= inputShape[i]; + } + + + // Operate different logic by TypeCode + bool computationSuccess = false; + switch (nd.typecode) + { + case NPTypeCode.Boolean: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Byte: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int16: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt16: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int32: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt32: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Int64: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.UInt64: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Char: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Double: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Single: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + case NPTypeCode.Decimal: computationSuccess = ComputeAnyPerAxis(nd.MakeGeneric(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break; + default: + throw new NotSupportedException($"Type {nd.typecode} is not supported"); + } + + + if (!computationSuccess) + { + throw new InvalidOperationException("Failed to compute all() along the specified axis"); + } + + return resultArray; + } + + private static bool ComputeAnyPerAxis(NDArray nd, int axis, int preAxisStride, int postAxisStride, int axisSize, Span resultSpan) where T : unmanaged + { + Span inputSpan = nd.GetData().AsSpan(); + + + for (int o = 0; o < resultSpan.Length; o++) + { + int blockIndex = o / postAxisStride; + int inBlockIndex = o % postAxisStride; + int inputStartIndex = blockIndex * axisSize * postAxisStride + inBlockIndex; + + bool currentResult = true; + for (int a = 0; a < axisSize; a++) + { + int inputIndex = inputStartIndex + a * postAxisStride; + if (inputSpan[inputIndex].Equals(default(T))) + { + currentResult = true; + break; + } + } + resultSpan[o] = currentResult; + } + + return false; } private static bool _any_linear(NDArray nd) where T : unmanaged diff --git a/src/NumSharp.Core/Utilities/ArrayConvert.cs b/src/NumSharp.Core/Utilities/ArrayConvert.cs index 71f4658c..b51f13de 100644 --- a/src/NumSharp.Core/Utilities/ArrayConvert.cs +++ b/src/NumSharp.Core/Utilities/ArrayConvert.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using System.Threading.Tasks; using NumSharp.Backends; @@ -4072,12 +4073,13 @@ public static Complex[] ToComplex(Decimal[] sourceArray) Parallel.For(0, length, i => new Complex(Converts.ToDouble(sourceArray[i]), 0d)); return output; } - + /// /// Converts array to a array. /// /// The array to convert /// Converted array of type Complex + /// A string in sourceArray has an invalid complex format [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Complex[] ToComplex(String[] sourceArray) { @@ -4087,7 +4089,16 @@ public static Complex[] ToComplex(String[] sourceArray) var length = sourceArray.Length; var output = new Complex[length]; - Parallel.For(0, length, i => new Complex(Converts.ToDouble(sourceArray[i]), 0d)); + Parallel.For(0, length, i => + { + string input = sourceArray[i]?.Trim() ?? string.Empty; + if (string.IsNullOrEmpty(input)) + { + output[i] = Complex.Zero; // NullString save as zero. + return; + } + var match = py.Complex(sourceArray[i]); + }); return output; } #endif diff --git a/src/NumSharp.Core/Utilities/py.cs b/src/NumSharp.Core/Utilities/py.cs index 927f52f0..3989abad 100644 --- a/src/NumSharp.Core/Utilities/py.cs +++ b/src/NumSharp.Core/Utilities/py.cs @@ -1,4 +1,8 @@ -namespace NumSharp.Utilities +using System; +using System.Numerics; +using System.Text.RegularExpressions; + +namespace NumSharp.Utilities { /// /// Implements Python utility functions that are often used in connection with numpy @@ -12,5 +16,47 @@ public static int[] range(int n) a[i] = i; return a; } + /// + /// 解析单个Python风格的复数字符串为Complex对象 + /// + private static readonly Regex _pythonComplexRegex = new Regex( +@"^(?-?\d+(\.\d+)?)?((?\+|-)?(?\d+(\.\d+)?)?)?j$|^(?-?\d+(\.\d+)?)$", +RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); + public static Complex Complex(string input) + { + var match = _pythonComplexRegex.Match(input); + if (!match.Success) + throw new FormatException($"Invalid Python complex format: '{input}'. Expected format like '10+5j', '3-2j', '4j' or '5'."); + + // 解析仅实部的场景 + if (match.Groups["onlyReal"].Success) + { + double real = double.Parse(match.Groups["onlyReal"].Value); + return new Complex(real, 0); + } + + // 解析实部(默认0) + double realPart = 0; + if (double.TryParse(match.Groups["real"].Value, out double r)) + realPart = r; + + // 解析虚部(处理特殊情况:j / -j / +j) + double imagPart = 0; + string imagStr = match.Groups["imag"].Value; + string imagSign = match.Groups["imagSign"].Value; + + if (string.IsNullOrEmpty(imagStr) && !string.IsNullOrEmpty(input.TrimEnd('j', 'J'))) + { + // 处理仅虚部的情况:j → 1j, -j → -1j, +j → 1j + imagStr = "1"; + } + + if (double.TryParse(imagStr, out double im)) + { + imagPart = im * (imagSign == "-" ? -1 : 1); + } + + return new Complex(realPart, imagPart); + } } } diff --git a/test/NumSharp.UnitTest/Utilities/pyTest.cs b/test/NumSharp.UnitTest/Utilities/pyTest.cs new file mode 100644 index 00000000..304cd814 --- /dev/null +++ b/test/NumSharp.UnitTest/Utilities/pyTest.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NumSharp.UnitTest.Utilities +{ + internal class pyTest + { + } +} From b4765e5535e0e5262c208a0ec9ebad7962defdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B8=85=E6=B3=89?= <2664542771@qq.com> Date: Sat, 29 Nov 2025 21:54:35 +0800 Subject: [PATCH 3/3] just modify the function name --- src/NumSharp.Core/Utilities/ArrayConvert.cs | 2 +- src/NumSharp.Core/Utilities/py.cs | 2 +- test/NumSharp.UnitTest/Logic/np.any.Test.cs | 108 ++++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 test/NumSharp.UnitTest/Logic/np.any.Test.cs diff --git a/src/NumSharp.Core/Utilities/ArrayConvert.cs b/src/NumSharp.Core/Utilities/ArrayConvert.cs index b51f13de..b92e6dbd 100644 --- a/src/NumSharp.Core/Utilities/ArrayConvert.cs +++ b/src/NumSharp.Core/Utilities/ArrayConvert.cs @@ -4097,7 +4097,7 @@ public static Complex[] ToComplex(String[] sourceArray) output[i] = Complex.Zero; // NullString save as zero. return; } - var match = py.Complex(sourceArray[i]); + var match = py.complex(sourceArray[i]); }); return output; } diff --git a/src/NumSharp.Core/Utilities/py.cs b/src/NumSharp.Core/Utilities/py.cs index 3989abad..6bf3219f 100644 --- a/src/NumSharp.Core/Utilities/py.cs +++ b/src/NumSharp.Core/Utilities/py.cs @@ -22,7 +22,7 @@ public static int[] range(int n) private static readonly Regex _pythonComplexRegex = new Regex( @"^(?-?\d+(\.\d+)?)?((?\+|-)?(?\d+(\.\d+)?)?)?j$|^(?-?\d+(\.\d+)?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); - public static Complex Complex(string input) + public static Complex complex(string input) { var match = _pythonComplexRegex.Match(input); if (!match.Success) diff --git a/test/NumSharp.UnitTest/Logic/np.any.Test.cs b/test/NumSharp.UnitTest/Logic/np.any.Test.cs new file mode 100644 index 00000000..db11390b --- /dev/null +++ b/test/NumSharp.UnitTest/Logic/np.any.Test.cs @@ -0,0 +1,108 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp; + +namespace NumSharp.UnitTest.Logic +{ + [TestClass] + public class NpAnyTest + { + [TestMethod] + public void Any1DArrayTest() + { + // 测试1维数组 + var arr = np.array(new int[] { 0, 1, 2 }); + var result = np.any(arr, axis: 0, keepdims: false); + Assert.AreEqual(true, result.GetItem(0)); + } + + [TestMethod] + public void Any2DArrayTest() + { + // 测试2维数组 + var arr = np.array(new int[,] { { 0, 0 }, { 1, 0 } }); + var result = np.any(arr, axis: 0, keepdims: false); + var expected = np.array(new bool[] { true, false }); + Assert.IsTrue(Enumerable.SequenceEqual(result.Data(), expected.Data())); + } + + [TestMethod] + public void Any2DArrayWithAxis1Test() + { + // 测试2维数组,axis=1 + var arr = np.array(new int[,] { { 0, 0 }, { 1, 0 } }); + var result = np.any(arr, axis: 1, keepdims: false); + var expected = np.array(new bool[] { false, true }); + Assert.IsTrue(Enumerable.SequenceEqual(result.Data(), expected.Data())); + } + + [TestMethod] + public void AnyWithKeepdimsTest() + { + // 测试keepdims参数 + var arr = np.array(new int[,] { { 0, 0 }, { 1, 0 } }); + var result = np.any(arr, axis: 0, keepdims: true); + // 结果形状应该是 (1, 2) 而不是 (2,) + Assert.AreEqual(2, result.ndim); + Assert.AreEqual(1, result.shape[0]); + Assert.AreEqual(2, result.shape[1]); + } + + [TestMethod] + public void AnyWithNegativeAxisTest() + { + // 测试负轴 + var arr = np.array(new int[,] { { 0, 0 }, { 1, 0 } }); + var result1 = np.any(arr, axis: 1, keepdims: false); // axis=1 + var result2 = np.any(arr, axis: -1, keepdims: false); // axis=-1 (应该是等价的) + Assert.IsTrue(Enumerable.SequenceEqual(result1.Data(), result2.Data())); + } + + [TestMethod] + public void AnyAllZerosTest() + { + // 测试全零数组 + var arr = np.array(new int[,] { { 0, 0 }, { 0, 0 } }); + var result = np.any(arr, axis: 0, keepdims: false); + var expected = np.array(new bool[] { false, false }); + Assert.IsTrue(Enumerable.SequenceEqual(result.Data(), expected.Data())); + } + + [TestMethod] + public void AnyAllNonZerosTest() + { + // 测试全非零数组 + var arr = np.array(new int[,] { { 1, 2 }, { 3, 4 } }); + var result = np.any(arr, axis: 0, keepdims: false); + var expected = np.array(new bool[] { true, true }); + Assert.IsTrue(Enumerable.SequenceEqual(result.Data(), expected.Data())); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void AnyInvalidAxisTest() + { + // 测试无效轴 + var arr = np.array(new int[,] { { 0, 1 }, { 2, 3 } }); + np.any(arr, axis: 5, keepdims: false); // 轴5不存在 + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void AnyZeroDimensionalArrayTest() + { + // 测试零维数组 + var arr = np.array(5); // 零维数组 + np.any(arr, axis: 0, keepdims: false); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void AnyNullArrayTest() + { + // 测试空数组 + NDArray arr = null; + np.any(arr, axis: 0, keepdims: false); + } + } +} \ No newline at end of file