Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 121 additions & 20 deletions src/NumSharp.Core/Logic/np.all.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ public static bool all(NDArray a)
#else

#region Compute
switch (a.typecode)
{
case NPTypeCode.Boolean: return _all_linear<bool>(a.MakeGeneric<bool>());
case NPTypeCode.Byte: return _all_linear<byte>(a.MakeGeneric<byte>());
case NPTypeCode.Int16: return _all_linear<short>(a.MakeGeneric<short>());
case NPTypeCode.UInt16: return _all_linear<ushort>(a.MakeGeneric<ushort>());
case NPTypeCode.Int32: return _all_linear<int>(a.MakeGeneric<int>());
case NPTypeCode.UInt32: return _all_linear<uint>(a.MakeGeneric<uint>());
case NPTypeCode.Int64: return _all_linear<long>(a.MakeGeneric<long>());
case NPTypeCode.UInt64: return _all_linear<ulong>(a.MakeGeneric<ulong>());
case NPTypeCode.Char: return _all_linear<char>(a.MakeGeneric<char>());
case NPTypeCode.Double: return _all_linear<double>(a.MakeGeneric<double>());
case NPTypeCode.Single: return _all_linear<float>(a.MakeGeneric<float>());
case NPTypeCode.Decimal: return _all_linear<decimal>(a.MakeGeneric<decimal>());
default:
throw new NotSupportedException();
}
switch (a.typecode)
{
case NPTypeCode.Boolean: return _all_linear<bool>(a.MakeGeneric<bool>());
case NPTypeCode.Byte: return _all_linear<byte>(a.MakeGeneric<byte>());
case NPTypeCode.Int16: return _all_linear<short>(a.MakeGeneric<short>());
case NPTypeCode.UInt16: return _all_linear<ushort>(a.MakeGeneric<ushort>());
case NPTypeCode.Int32: return _all_linear<int>(a.MakeGeneric<int>());
case NPTypeCode.UInt32: return _all_linear<uint>(a.MakeGeneric<uint>());
case NPTypeCode.Int64: return _all_linear<long>(a.MakeGeneric<long>());
case NPTypeCode.UInt64: return _all_linear<ulong>(a.MakeGeneric<ulong>());
case NPTypeCode.Char: return _all_linear<char>(a.MakeGeneric<char>());
case NPTypeCode.Double: return _all_linear<double>(a.MakeGeneric<double>());
case NPTypeCode.Single: return _all_linear<float>(a.MakeGeneric<float>());
case NPTypeCode.Decimal: return _all_linear<decimal>(a.MakeGeneric<decimal>());
default:
throw new NotSupportedException();
}
#endregion
#endif
}
Expand All @@ -51,12 +51,113 @@ public static bool all(NDArray a)
/// Test whether all array elements along a given axis evaluate to True.
/// </summary>
/// <param name="a">Input array or object that can be converted to an array.</param>
/// <param name="axis">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.</param>
/// <param name="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.</param>
/// <returns>A new boolean or ndarray is returned unless out is specified, in which case a reference to out is returned.</returns>
/// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.all.html</remarks>
public static NDArray<bool> all(NDArray nd, int axis)
public static NDArray<bool> 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; // keep axis but length is one.
}
}

NDArray<bool> resultArray = (NDArray<bool>)zeros<bool>(outputShape);
Span<bool> resultSpan = resultArray.GetData().AsSpan<bool>();

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<bool>(nd.MakeGeneric<bool>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Byte: computationSuccess = ComputeAllPerAxis<byte>(nd.MakeGeneric<byte>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int16: computationSuccess = ComputeAllPerAxis<short>(nd.MakeGeneric<short>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt16: computationSuccess = ComputeAllPerAxis<ushort>(nd.MakeGeneric<ushort>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int32: computationSuccess = ComputeAllPerAxis<int>(nd.MakeGeneric<int>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt32: computationSuccess = ComputeAllPerAxis<uint>(nd.MakeGeneric<uint>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int64: computationSuccess = ComputeAllPerAxis<long>(nd.MakeGeneric<long>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt64: computationSuccess = ComputeAllPerAxis<ulong>(nd.MakeGeneric<ulong>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Char: computationSuccess = ComputeAllPerAxis<char>(nd.MakeGeneric<char>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Double: computationSuccess = ComputeAllPerAxis<double>(nd.MakeGeneric<double>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Single: computationSuccess = ComputeAllPerAxis<float>(nd.MakeGeneric<float>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Decimal: computationSuccess = ComputeAllPerAxis<decimal>(nd.MakeGeneric<decimal>(), 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<T>(NDArray<T> nd, int axis, int preAxisStride, int postAxisStride, int axisSize, Span<bool> resultSpan) where T : unmanaged
{
Span<T> inputSpan = nd.GetData().AsSpan<T>();


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<T>(NDArray<T> nd) where T : unmanaged
Expand Down
106 changes: 104 additions & 2 deletions src/NumSharp.Core/Logic/np.any.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,111 @@ public static bool any(NDArray a)
/// <param name="axis">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.</param>
/// <returns>A new boolean or ndarray is returned unless out is specified, in which case a reference to out is returned.</returns>
/// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.any.html</remarks>
public static NDArray<bool> any(NDArray nd, int axis)
public static NDArray<bool> 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<bool> resultArray = (NDArray<bool>)zeros<bool>(outputShape);
Span<bool> resultSpan = resultArray.GetData().AsSpan<bool>();

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<bool>(nd.MakeGeneric<bool>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Byte: computationSuccess = ComputeAnyPerAxis<byte>(nd.MakeGeneric<byte>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int16: computationSuccess = ComputeAnyPerAxis<short>(nd.MakeGeneric<short>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt16: computationSuccess = ComputeAnyPerAxis<ushort>(nd.MakeGeneric<ushort>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int32: computationSuccess = ComputeAnyPerAxis<int>(nd.MakeGeneric<int>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt32: computationSuccess = ComputeAnyPerAxis<uint>(nd.MakeGeneric<uint>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Int64: computationSuccess = ComputeAnyPerAxis<long>(nd.MakeGeneric<long>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.UInt64: computationSuccess = ComputeAnyPerAxis<ulong>(nd.MakeGeneric<ulong>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Char: computationSuccess = ComputeAnyPerAxis<char>(nd.MakeGeneric<char>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Double: computationSuccess = ComputeAnyPerAxis<double>(nd.MakeGeneric<double>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Single: computationSuccess = ComputeAnyPerAxis<float>(nd.MakeGeneric<float>(), axis, preAxisStride, postAxisStride, axisSize, resultSpan); break;
case NPTypeCode.Decimal: computationSuccess = ComputeAnyPerAxis<decimal>(nd.MakeGeneric<decimal>(), 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<T>(NDArray<T> nd, int axis, int preAxisStride, int postAxisStride, int axisSize, Span<bool> resultSpan) where T : unmanaged
{
Span<T> inputSpan = nd.GetData().AsSpan<T>();


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<T>(NDArray<T> nd) where T : unmanaged
Expand Down
15 changes: 13 additions & 2 deletions src/NumSharp.Core/Utilities/ArrayConvert.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using NumSharp.Backends;

Expand Down Expand Up @@ -4072,12 +4073,13 @@ public static Complex[] ToComplex(Decimal[] sourceArray)
Parallel.For(0, length, i => new Complex(Converts.ToDouble(sourceArray[i]), 0d));
return output;
}

/// <summary>
/// Converts <see cref="String"/> array to a <see cref="Complex"/> array.
/// </summary>
/// <param name="sourceArray">The array to convert</param>
/// <returns>Converted array of type Complex</returns>
/// <exception cref="FormatException">A string in sourceArray has an invalid complex format</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Complex[] ToComplex(String[] sourceArray)
{
Expand All @@ -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
Expand Down
48 changes: 47 additions & 1 deletion src/NumSharp.Core/Utilities/py.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace NumSharp.Utilities
using System;
using System.Numerics;
using System.Text.RegularExpressions;

namespace NumSharp.Utilities
{
/// <summary>
/// Implements Python utility functions that are often used in connection with numpy
Expand All @@ -12,5 +16,47 @@ public static int[] range(int n)
a[i] = i;
return a;
}
/// <summary>
/// 解析单个Python风格的复数字符串为Complex对象
/// </summary>
private static readonly Regex _pythonComplexRegex = new Regex(
@"^(?<real>-?\d+(\.\d+)?)?((?<imagSign>\+|-)?(?<imag>\d+(\.\d+)?)?)?j$|^(?<onlyReal>-?\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);
}
}
}
Loading
Loading